// Copyright (c) 2022 Giorgos Vougioukas
//
// The license can be found in the LICENSE file.

#include "RSI/library_wnd.h"
#include "RSI/render_system.h"
#include "RSI/preferences.h"
#include "RSI/main_wnd.h"
#include "RSI/track.h"

#include "WDL/fpcmp.h"

#define RSI_LIBRARY_CLEAR_EDIT 310
#define RSI_LIBRARY_CLEAR_EDIT_MS 10

#define RSI_DB_SCAN_PROGRESS 320
#define RSI_DB_SCAN_PROGRESS_MS 200

static WNDPROC PrevListProc = NULL;
static WNDPROC PrevEditProc = NULL;
//static HWND s_list = NULL;
//static HWND s_edit = NULL;

static const char *metadata_column_name[] =
{
  "type",
  "index", // does nothing
  "title",
  "artist",
  "album",
  "length",
  "genre",
  "samplerate",
  "file_name",
  "file_path",
  "file_ext",
  "file_size",
  "comment"
};

static int m_x, m_y;
static WDL_FastString m_xstr, m_ystr;
static WDL_DLGRET DatabaseProgressWnd(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case WM_INITDIALOG:
      {
        m_x = GetPrivateProfileInt(RSI_NAME, "db_prog_wnd_x", 50, g_inipath.Get());
        m_y = GetPrivateProfileInt(RSI_NAME, "db_prog_wnd_y", 50, g_inipath.Get());
        SetWindowPos(hwnd, NULL, m_x, m_y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);

        ShowWindow(hwnd, SW_SHOW);
        SetTimer(hwnd, RSI_DB_SCAN_PROGRESS, RSI_DB_SCAN_PROGRESS_MS, NULL);
      }
      break;
    case WM_DESTROY:
      {
        KillTimer(hwnd, RSI_DB_SCAN_PROGRESS);
        WritePrivateProfileString(RSI_NAME, "db_prog_wnd_x", m_xstr.Get(), g_inipath.Get());
        WritePrivateProfileString(RSI_NAME, "db_prog_wnd_y", m_ystr.Get(), g_inipath.Get());
      }
      break;
    case WM_SYSCOMMAND:
      {
        if (wparam == SC_CLOSE)
        {
          if (g_librarywnd->IsScanning()) g_librarywnd->AbortScanning();
          EndDialog(hwnd, 0);
        }
      }
      break;
    case WM_COMMAND:
      {
        switch(LOWORD(wparam))
        {
        case IDC_BUTTON1:
        case IDCANCEL:
          {
            if (g_librarywnd->IsScanning()) g_librarywnd->AbortScanning();
            EndDialog(hwnd, 0);
          }
          break;
        }
      }
      break;
    case WM_TIMER:
      {
        if (wparam == RSI_DB_SCAN_PROGRESS)
        {
          if (g_librarywnd->IsScanning())
          {
            SetWindowText(GetDlgItem(hwnd, IDC_EDIT1), g_librarywnd->GetScanningStage());
            SetWindowText(GetDlgItem(hwnd, IDC_EDIT2), g_librarywnd->GetScanningFilename());
          }
          else
          {
            g_librarywnd->ScanCompleted();
            EndDialog(hwnd, 0);
          }
        }
      }
      break;
    case WM_MOVE:
      {
        RECT r;
        GetWindowRect(hwnd, &r);

        m_xstr.SetFormatted(32, "%d", r.left);
        m_ystr.SetFormatted(32, "%d", r.top);
      }
      break;
  }

  return 0;
}

RSI_LibraryWnd::RSI_LibraryWnd()
  : m_hwnd(NULL)
  , m_list(NULL)
  , m_edit(NULL)
  , m_column(1)
  , m_showsearch(true)
{}

RSI_LibraryWnd::~RSI_LibraryWnd()
{
  DestroyWindow(m_hwnd);
}

void RSI_LibraryWnd::LoadFileTrack()
{
  char text[2048];
  int subitem = 0;
  int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED | LVIS_FOCUSED);

  if (pos >= 0)
  {
    for (int i = 0; i < m_column_header.GetSize(); i++)
    {
      ColumnHeader *ch = m_column_header.Get();
      if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
      {
        subitem = i;
        break;
      }
    }

    ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
    g_rendersystem->LoadFileTrack(text);
  }
}

void RSI_LibraryWnd::ShowSearch(bool show)
{
  if (m_showsearch == show) return;
  m_showsearch = show;
  if (m_showsearch)
  {
    RECT r;
    GetWindowRect(m_edit, &r);
    int h = r.bottom - r.top;

    ShowWindow(m_edit, SW_SHOW);
    WDL_WndSizer__rec *rec = m_resize.get_itembywnd(m_list);
    if (rec)
    {
      rec->orig.top = h;
    }

    int pos = ListView_GetNextItem(m_list, -1, LVNI_SELECTED);
    while (pos != -1)
    {
      ListView_SetItemState(m_list, pos, ~LVIS_SELECTED, LVIS_SELECTED);
      pos = ListView_GetNextItem(m_list, pos, LVNI_SELECTED);
    }

    pos = ListView_GetNextItem(m_list, -1, LVNI_FOCUSED);
    while (pos != -1)
    {
      ListView_SetItemState(m_list, pos, ~LVIS_FOCUSED, LVIS_FOCUSED);
      pos = ListView_GetNextItem(m_list, pos, LVNI_FOCUSED);
    }

    SetFocus(m_edit);
  }
  else
  {
    SetWindowText(m_edit, "");
    ShowWindow(m_edit, SW_HIDE);
    WDL_WndSizer__rec *rec = m_resize.get_itembywnd(m_list);
    if (rec)
    {
      rec->orig.top = 0;
    }
    if (m_query.Query()) FeedListView(); else ClearListView();
    SetFocus(m_list);
  }
  m_resize.onResize();
}

void RSI_LibraryWnd::Search()
{
  char text[2048];
  WDL_String str;
  WDL_FastString sql;
  GetWindowText(m_edit, text, sizeof(text));

  str.Set(text);
  WDL_TypedBuf<int> qp;

  for (int j = 0; j < str.GetLength(); j++)
  {
    char *quote = str.Get();

    if (quote[j] == '\'')
    {
      // 1 2 3 4 5 6 7 8 9 10
      //         '          '
      int pos = j + qp.GetSize();
      qp.Add(pos);
    }
  }

  for (int k = 0; k < qp.GetSize(); k++)
  {
    str.Insert("'", qp.Get()[k]);
  }

  bool whitespace = true;
  for (int i = 0; i < str.GetLength(); i++)
  {
    if (str.Get()[i] == ' ') continue; else { whitespace = false; break; }
  }

  if (!whitespace)
  {
    if (m_query.Query(text)) FeedListView(); else ClearListView();
  }
}

void RSI_LibraryWnd::ScanFiles()
{
  if (!m_db.IsScanInProgress())
  {
    if (WDL_NORMALLY(m_query.IsOpen())) m_query.Close();
    if (WDL_NOT_NORMALLY(m_db.IsOpen())) m_db.Close();

    m_db.Open();
    m_db.ScanFiles();

    DialogBox(g_inst, MAKEINTRESOURCE(IDD_DBPROGRESS), m_hwnd, DatabaseProgressWnd);
  }
}

bool RSI_LibraryWnd::IsScanning() const
{
  return m_db.IsScanInProgress();
}

void RSI_LibraryWnd::AbortScanning()
{
  if (WDL_NORMALLY(m_db.IsScanInProgress()))
  {
    WDL_ASSERT(m_db.IsOpen());
    m_db.StopScan();
    m_db.Close();
  }

  WDL_ASSERT(!m_query.IsOpen());

  m_query.Open();
  if (m_query.Query()) FeedListView(); else ClearListView();
}

void RSI_LibraryWnd::ScanCompleted()
{
  if (WDL_NORMALLY(!m_db.IsScanInProgress()))
  {
    WDL_ASSERT(m_db.IsOpen());
    m_db.Close();
  }

  WDL_ASSERT(!m_query.IsOpen());

  m_query.Open();
  if (m_query.Query()) FeedListView(); else ClearListView();
}

const char *RSI_LibraryWnd::GetScanningStage() const
{
  return m_db.GetProgressStage();
}

const char *RSI_LibraryWnd::GetScanningFilename() const
{
  return m_db.GetProgressFilename();
}

void RSI_LibraryWnd::FeedListView()
{
  int nrow = 0;

  ListView_DeleteAllItems(m_list);

  SendMessage(m_list, WM_SETREDRAW, FALSE, 0);

  while (m_query.HasRow())
  {
    LVITEM lvi = { 0, };
    int si = 0, col = 0;
    lvi.mask = LVIF_TEXT | LVIF_PARAM;
    lvi.lParam = (LPARAM)nrow;
    lvi.iItem = nrow;
    lvi.iSubItem = si++; // type
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_InsertItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // index
    m_strbuf.SetFormatted(128, "%d", nrow + 1);
    lvi.pszText = (char *)m_strbuf.Get();
    lvi.cchTextMax = m_strbuf.GetLength();
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // title
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // artist
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // album
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // length
    int minutes = 0;
    int seconds = 0;
    double time = 0.0;
    time = m_query.GetColumnDouble(col++);
    if (WDL_DefinitelyGreaterThan(time, 0.0))
    {
      minutes = (int)time / 60;
      seconds = (int)time % 60;
    }
    m_strbuf.SetFormatted(32, "%01d:%02d", minutes, seconds);
    lvi.pszText = (char *)m_strbuf.Get();
    lvi.cchTextMax = m_strbuf.GetLength();
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // genre
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // samplerate
    m_strbuf.SetFormatted(128, "%.0f Hz", m_query.GetColumnDouble(col++));
    lvi.pszText = (char *)m_strbuf.Get();
    lvi.cchTextMax = m_strbuf.GetLength();
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // filename
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // filepath
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // fileext
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // filesize
    double mb = 0.0;
    mb = (double)m_query.GetColumnInt64(col++) / 1024 / 1024;
    m_strbuf.SetFormatted(32, "%.2f MB", mb);
    lvi.pszText = (char *)m_strbuf.Get();
    lvi.cchTextMax = m_strbuf.GetLength();
    ListView_SetItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++; // comment
    lvi.pszText = (char *)m_query.GetColumnText(col);
    lvi.cchTextMax = (int)strlen((char *)m_query.GetColumnText(col++));
    ListView_SetItem(m_list, &lvi);

    WDL_UTF8_HookListView(m_list);

    m_query.NextStep(); nrow++;
  }

  SendMessage(m_list, WM_SETREDRAW, TRUE, 0);
}

void RSI_LibraryWnd::ClearListView()
{
  ListView_DeleteAllItems(m_list);
}

void RSI_LibraryWnd::OnInitDialog()
{
  m_list = GetDlgItem(m_hwnd, IDC_LIST1);
  m_edit = GetDlgItem(m_hwnd, IDC_EDIT1);
  //s_list = m_list;
  //s_edit = m_edit;

  m_resize.init(m_hwnd);
  m_resize.init_itemhwnd(m_edit, 0.5f, 0.0f, 0.5f, 0.0f);
  m_resize.init_itemhwnd(m_list, 0.0f, 0.0f, 1.0f, 1.0f);

#if defined(_WIN32)
  ListView_SetExtendedListViewStyleEx(m_list,
    LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER,
    LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
#else
  ListView_SetExtendedListViewStyleEx(m_list,
    LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT,
    LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT);
#endif

  ColumnHeader default_column_header[] =
  {
    // The alignment of the leftmost column is
    // always LVCFMT_LEFT; it cannot be changed.
    { ID_LIBRARY_HEADER_TYPE, "Type", 50, LVCFMT_LEFT, 1, 0 },
    { ID_LIBRARY_HEADER_INDEX, "#", 50, LVCFMT_RIGHT, 1, 1 },
    { ID_LIBRARY_HEADER_TITLE, "Title", 200, LVCFMT_LEFT, 1, 0 },
    { ID_LIBRARY_HEADER_ARTIST, "Artist", 200, LVCFMT_LEFT, 1, 0 },
    { ID_LIBRARY_HEADER_ALBUM, "Album", 200, LVCFMT_LEFT, 1, 0 },
    { ID_LIBRARY_HEADER_LENGTH, "Length", 50, LVCFMT_RIGHT, 1, 1 },
    { ID_LIBRARY_HEADER_GENRE, "Genre", 80, LVCFMT_LEFT, 1, 0 },
    { ID_LIBRARY_HEADER_SAMPLERATE, "Sample rate", 80, LVCFMT_RIGHT, 1, 1 },
    { ID_LIBRARY_HEADER_FILENAME, "File name", 150, LVCFMT_LEFT, 1, 0 },
    { ID_LIBRARY_HEADER_FILEPATH, "File path", 150, LVCFMT_LEFT, 1 , 0 },
    { ID_LIBRARY_HEADER_FILEEXTENSION, "File extension", 80, LVCFMT_LEFT, 1, 0 },
    { ID_LIBRARY_HEADER_FILESIZE, "File size", 100, LVCFMT_RIGHT, 1, 1 },
    { ID_LIBRARY_HEADER_COMMENT, "Comment", 100, LVCFMT_LEFT, 1, 0 }
  };

  m_column_header.Resize(sizeof(default_column_header) /
    sizeof(default_column_header[0]));

  for (int i = 0; i < m_column_header.GetSize(); i++)
  {
    ColumnHeader *ch = m_column_header.Get();
    ch[i] = default_column_header[i];
  }

  LVCOLUMN lvc = { 0, };
  lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;

  int w = 0;
  WDL_FastString s;

  ColumnHeader *ch = m_column_header.Get();

  for (int i = 0; i < m_column_header.GetSize(); i++)
  {
    s.SetFormatted(128, "library_column_width_%02d", i);
    w = GetPrivateProfileInt(RSI_NAME, s.Get(), ch[i].width, g_inipath.Get());
    lvc.cx = w;
    lvc.fmt = ch[i].fmt;
    lvc.pszText = (char *)ch[i].text;
    ListView_InsertColumn(m_list, i, &lvc);
  }

  WDL_TypedBuf<int> order_array;
  order_array.Resize(Header_GetItemCount(ListView_GetHeader(m_list)));
  int *oa = order_array.Get();

  for (int i = 0; i < order_array.GetSize(); i++)
  {
    s.SetFormatted(128, "library_column_order_%02d", i);
    oa[i] = GetPrivateProfileInt(RSI_NAME, s.Get(), i, g_inipath.Get());
  }

  ListView_SetColumnOrderArray(m_list, order_array.GetSize(), order_array.Get());

  ListView_SetBkColor(m_list, RGB(51, 51, 51));
  ListView_SetTextBkColor(m_list, RGB(51, 51, 51));
  ListView_SetTextColor(m_list, RGB(210, 210, 210));

  m_context_menu = (HMENU)GetSubMenu(LoadMenu(g_inst, MAKEINTRESOURCE(IDR_LIBRARY_CONTEXT)), 0);
  m_column_menu = (HMENU)GetSubMenu(LoadMenu(g_inst, MAKEINTRESOURCE(IDR_LIBRARY_CONTEXT)), 1);

  PrevListProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_LIST1),
    GWLP_WNDPROC, (LONG_PTR)NewListProc);
  PrevEditProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_EDIT1),
    GWLP_WNDPROC, (LONG_PTR)NewEditProc);

  int defcol = GetPrivateProfileInt(RSI_NAME, "library_defcol", 1, g_inipath.Get());

  if (defcol)
  {
    SendMessage(m_hwnd, WM_COMMAND, ID_LIBRARY_HEADER_DEFAULT, 0);
  }

  m_query.Open();
  ShowSearch(false);
  SetFocus(m_list);
}

void RSI_LibraryWnd::OnDestroy()
{
  WDL_FastString s;
  WDL_TypedBuf<int> order_array;
  WDL_FastString str;

  order_array.Resize(Header_GetItemCount(ListView_GetHeader(m_list)));
  ListView_GetColumnOrderArray(m_list, order_array.GetSize(), order_array.Get());

  int *oa = order_array.Get();

  for (int i = 0; i < order_array.GetSize(); i++)
  {
    s.SetFormatted(128, "library_column_order_%02d", i);
    str.SetFormatted(32, "%d", oa[i]);
    WritePrivateProfileString(RSI_NAME, s.Get(), str.Get(), g_inipath.Get());
  }

  for (int i = 0; i < Header_GetItemCount(ListView_GetHeader(m_list)); i++)
  {
    s.SetFormatted(128, "library_column_width_%02d", i);
    str.SetFormatted(32, "%d", ListView_GetColumnWidth(m_list, i));
    WritePrivateProfileString(RSI_NAME, s.Get(), str.Get(), g_inipath.Get());
  }

  m_query.Close();
}

void RSI_LibraryWnd::OnSize()
{
  if (m_wparam != SIZE_MINIMIZED)
  {
    m_resize.onResize();
  }
  InvalidateRect(m_hwnd, NULL, FALSE);
}

void RSI_LibraryWnd::OnCommand()
{
  switch (LOWORD(m_wparam))
  {
    case ID_LIBRARY_HEADER_TYPE:
    case ID_LIBRARY_HEADER_TITLE:
    case ID_LIBRARY_HEADER_ARTIST:
    case ID_LIBRARY_HEADER_ALBUM:
    case ID_LIBRARY_HEADER_LENGTH:
    case ID_LIBRARY_HEADER_GENRE:
    case ID_LIBRARY_HEADER_SAMPLERATE:
    case ID_LIBRARY_HEADER_FILENAME:
    case ID_LIBRARY_HEADER_FILEPATH:
    case ID_LIBRARY_HEADER_FILEEXTENSION:
    case ID_LIBRARY_HEADER_FILESIZE:
    case ID_LIBRARY_HEADER_INDEX:
    case ID_LIBRARY_HEADER_COMMENT:
      for (int i = 0; i < m_column_header.GetSize(); i++)
      {
        ColumnHeader *ch = m_column_header.Get();

        if (ch[i].id == LOWORD(m_wparam))
        {
          MENUITEMINFO mii = { 0 };
          mii.cbSize = sizeof(mii);
          mii.fMask = MIIM_STATE;

          if (GetMenuItemInfo(m_column_menu, ch[i].id, FALSE, &mii))
          {
            if (!(mii.fState & MFS_CHECKED))
            {
              ListView_SetColumnWidth(m_list, i, ch[i].width);
            }
            else if (mii.fState & MFS_CHECKED)
            {
              ListView_SetColumnWidth(m_list, i, 0);
            }

            //mii.fState ^= MFS_CHECKED;
            //SetMenuItemInfo(m_column_menu, pl_column_info[i].id, FALSE, &mii);
            WDL_FastString str;
            str.SetFormatted(32, "%d", 0);
            WritePrivateProfileString(RSI_NAME, "library_defcol", str.Get(), g_inipath.Get());
          }
        }
      }
      break;
    case ID_CONTEXT_LOADMEDIAFILE:
      {
        char text[2048];
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F1:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(1);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F2:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(2);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F3:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(3);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F4:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(4);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F5:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(5);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F6:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(6);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F7:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(7);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F8:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(8);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F9:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(9);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F10:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(10);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F11:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(11);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_LOADMEDIAFILETO_F12:
      {
        char text[2048];
        g_rendersystem->SetActiveTrack(12);
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);
        if (pos != -1)
        {
          int subitem = 0;
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i; break;
            }
          }
          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    case ID_CONTEXT_OPENFILELOCATION:
      {
        char text[2048];
        int subitem = 0;

        int pos = ListView_GetNextItem(m_list, -1, LVNI_SELECTED | LVNI_FOCUSED);
        if (pos < 0) return;

        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
          {
            subitem = i; break;
          }
        }

        ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));

        WDL_FastString fn(text);
        fn.remove_filepart();

        ShellExecute(m_hwnd, "", "explorer.exe", fn.Get(), "", SW_SHOWNORMAL);
      }
      break;
    case ID_SORTBY_TYPE:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_TYPE)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_TITLE:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_TITLE)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_ARTIST:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_ARTIST)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_ALBUM:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_ALBUM)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_LENGTH:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_LENGTH)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_GENRE:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_GENRE)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_SAMPLERATE:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_SAMPLERATE)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_FILENAME:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_FILENAME)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_FILEPATH:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_FILEEXTENSION:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_FILEEXTENSION)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_FILESIZE:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_FILESIZE)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_SORTBY_COMMENT:
      {
        int subitem = 0;
        ColumnHeader *ch = m_column_header.Get();

        for (int i = 0; i < m_column_header.GetSize(); i++)
        {
          if (ch[i].id == ID_LIBRARY_HEADER_COMMENT)
          {
            subitem = i; break;
          }
        }

        if (WDL_NORMALLY(m_query.Order(metadata_column_name[subitem], true)))
        {
          FeedListView();
          m_lastsortorder.col.Set(metadata_column_name[subitem]);
          m_lastsortorder.sort_dir = 1;
        }
      }
      break;
    case ID_LIBRARY_HEADER_DEFAULT:
      {
        int defzero[] =
        {
          ID_LIBRARY_HEADER_TYPE,
          ID_LIBRARY_HEADER_GENRE,
          ID_LIBRARY_HEADER_FILENAME,
          ID_LIBRARY_HEADER_FILEPATH,
          ID_LIBRARY_HEADER_FILEEXTENSION,
          ID_LIBRARY_HEADER_COMMENT
        };

        int defstd[] =
        {
          ID_LIBRARY_HEADER_INDEX,
          ID_LIBRARY_HEADER_TITLE,
          ID_LIBRARY_HEADER_ARTIST,
          ID_LIBRARY_HEADER_ALBUM,
          ID_LIBRARY_HEADER_LENGTH,
          ID_LIBRARY_HEADER_SAMPLERATE,
          ID_LIBRARY_HEADER_FILESIZE
        };

        for (int i = 0, it = 0; i < (sizeof(defzero) / sizeof(defzero[0])); i++)
        {
          for (int j = 0; j < m_column_header.GetSize(); j++)
          {
            if (m_column_header.Get()[j].id == defzero[i])
            {
              it = j;
              break;
            }
          }

          ListView_SetColumnWidth(m_list, it, 0);
        }

        for (int i = 0, it = 0, w = 0; i < (sizeof(defstd) / sizeof(defstd[0])); i++)
        {
          for (int j = 0; j < m_column_header.GetSize(); j++)
          {
            if (m_column_header.Get()[j].id == defstd[i])
            {
              it = j;
              w = m_column_header.Get()[j].width;
              break;
            }
          }

          ListView_SetColumnWidth(m_list, it, w);
        }
      }
      break;
  }
}

void RSI_LibraryWnd::OnContextMenu()
{
  POINT pt;
  pt.x = GET_X_LPARAM(m_lparam);
  pt.y = GET_Y_LPARAM(m_lparam);
  ScreenToClient(m_list, (LPPOINT)&pt);

  HWND h = ListView_GetHeader(GetDlgItem(m_hwnd, IDC_LIST1));
  RECT r;
  GetWindowRect(h, &r);
  ScreenToClient(m_list, (LPPOINT)&r);
  ScreenToClient(m_list, ((LPPOINT)&r)+1);

  ColumnHeader *ch = m_column_header.Get();

  EnableMenuItem(m_column_menu, ID_LIBRARY_HEADER_INFO, MF_GRAYED);

#if defined(__APPLE__) || defined(__linux__)
  r.bottom = r.top + SWELL_GetListViewHeaderHeight(m_list);
#endif

  if (pt.x >= r.left && pt.y >= r.top && pt.x <= r.right && pt.y <= r.bottom)
  {
    for (int i = 0; i < m_column_header.GetSize(); i++)
    {
      if (ListView_GetColumnWidth(m_list, i))
      {
        CheckMenuItem(m_column_menu, ch[i].id, MF_CHECKED);
      }
      else
      {
        CheckMenuItem(m_column_menu, ch[i].id, MF_UNCHECKED);
      }
    }

    ClientToScreen(m_hwnd, (LPPOINT)&pt);
    TrackPopupMenu(m_column_menu, TPM_LEFTALIGN, pt.x, pt.y, 0, m_hwnd, NULL);
  }
  else
  {
    int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED);

    if (pos >= 0)
    {
      RECT rr;

      // NOTE: the Cocoa versions of these functions behave differently than swell-generic and Windows:
      // they return the absolute (unscrolled) coordinates. In order to behave properly, the caller should
      // use ClientToScreen in order to get to screen coordinates, and then convert those as desired.
      ListView_GetItemRect(m_list, pos, &rr, LVIR_BOUNDS);

      if (pt.x >= rr.left && pt.y >= rr.top && pt.x <= rr.right && pt.y <= rr.bottom)
      {
        GetCursorPos(&pt);
      }
      else
      {
        int hh = (rr.bottom - rr.top) / 2;
        ClientToScreen(m_list, (LPPOINT)&rr);
        ClientToScreen(m_list, ((LPPOINT)&rr)+1);
        pt.x = (rr.right - rr.left) / 2;
        pt.y = (rr.top + hh);
      }

      if (g_rendersystem->IsTrackActive())
      {
        EnableMenuItem(m_context_menu, ID_CONTEXT_LOADMEDIAFILE, MF_GRAYED);
      }
      else
      {
        EnableMenuItem(m_context_menu, ID_CONTEXT_LOADMEDIAFILE, MF_ENABLED);
      }

      WDL_TypedBuf<int> act;

      for (int i = 0; i < g_tracks.GetSize(); i++)
      {
        int key;
        RSI_Track *trk = g_tracks.Enumerate(i, &key);
        if (trk)
        {
          if (trk->IsActive()) act.Add(key);
        }
      }

      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F1, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F2, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F3, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F4, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F5, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F6, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F7, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F8, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F9, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F10, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F11, MF_ENABLED);
      EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F12, MF_ENABLED);

      for (int i = 0; i < act.GetSize(); i++)
      {
        switch (act.Get()[i])
        {
          case 1: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F1, MF_GRAYED); break;
          case 2: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F2, MF_GRAYED); break;
          case 3: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F3, MF_GRAYED); break;
          case 4: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F4, MF_GRAYED); break;
          case 5: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F5, MF_GRAYED); break;
          case 6: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F6, MF_GRAYED); break;
          case 7: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F7, MF_GRAYED); break;
          case 8: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F8, MF_GRAYED); break;
          case 9: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F9, MF_GRAYED); break;
          case 10: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F10, MF_GRAYED); break;
          case 11: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F11, MF_GRAYED); break;
          case 12: EnableMenuItem(m_context_menu, ID_LOADMEDIAFILETO_F12, MF_GRAYED); break;
        }
      }

      TrackPopupMenu(m_context_menu, TPM_LEFTALIGN, pt.x, pt.y, 0, m_hwnd, NULL);
    }
  }
}

void RSI_LibraryWnd::OnTimer()
{
  if (m_wparam == RSI_LIBRARY_CLEAR_EDIT)
  {
    SetWindowText(m_edit, "");
    KillTimer(m_hwnd, RSI_LIBRARY_CLEAR_EDIT);
  }
}

void RSI_LibraryWnd::OnNotify()
{
  switch (LOWORD(m_wparam))
  {
  case IDC_LIST1:
    switch (((LPNMHDR)m_lparam)->code)
    {
      case LVN_COLUMNCLICK:
      {
        LPNMLISTVIEW lv = (LPNMLISTVIEW)m_lparam;
        if WDL_unlikely((!strcmp(m_lastsortorder.col.Get(),
          metadata_column_name[lv->iSubItem])
          && (m_lastsortorder.sort_dir == 1)))
        {
          if (WDL_NORMALLY(m_query.Order(metadata_column_name[lv->iSubItem], false)))
          {
            FeedListView();
            m_lastsortorder.col.Set(metadata_column_name[lv->iSubItem]);
            m_lastsortorder.sort_dir = -1;
          }
        }
        else
        {
          if (WDL_NORMALLY(m_query.Order(metadata_column_name[lv->iSubItem], true)))
          {
            FeedListView();
            m_lastsortorder.col.Set(metadata_column_name[lv->iSubItem]);
            m_lastsortorder.sort_dir = 1;
          }
        }
      }
      break;
      case NM_DBLCLK:
      {
        char text[2048];
        int subitem = 0;
        int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED | LVIS_FOCUSED);

        if (pos >= 0)
        {
          for (int i = 0; i < m_column_header.GetSize(); i++)
          {
            ColumnHeader *ch = m_column_header.Get();
            if (ch[i].id == ID_LIBRARY_HEADER_FILEPATH)
            {
              subitem = i;
              break;
            }
          }

          ListView_GetItemText(m_list, pos, subitem, text, sizeof(text));
          g_rendersystem->LoadFileTrack(text);
        }
      }
      break;
    }
  }
}

void RSI_LibraryWnd::OnPaint()
{
  m_painter.PaintBegin(m_hwnd, RGB(51, 51, 51));
  m_painter.PaintEnd();
}

WDL_DLGRET RSI_LibraryWnd::NewListProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
  case WM_MOUSEWHEEL:
  case WM_MOUSEHWHEEL:
    //MessageBox(NULL, "playlist mousewheel", "", MB_OK);
    if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
    {
      //MessageBox(NULL, "playlist shift mousewheel", "", MB_OK);
      return 0;
    }
    break;
  case WM_SYSKEYDOWN:
  case WM_KEYDOWN:
    {
      switch(wparam)
      {
      case 'A':
        {
          //if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
          //{
          //  for (int i = 0; i < ListView_GetItemCount(s_list); i++)
          //  {
          //    ListView_SetItemState(s_list, i, LVIS_SELECTED, LVIS_SELECTED);
          //  }

          //  SetFocus(s_list);
          //}
        }
        break;
      case VK_UP:
        {
          HWND lst = GetDlgItem(g_librarywnd->Handle(), IDC_LIST1);
          HWND edt = GetDlgItem(g_librarywnd->Handle(), IDC_EDIT1);
          if (g_librarywnd->IsShowSearch())
          {
            int pos = ListView_GetNextItem(lst, -1, LVIS_SELECTED);
            if (pos == 0)
            {
              SendMessage(edt, EM_SETSEL, 0, -1);
              SetFocus(edt);
            }
          }
        }
        break;
      }
    }
    break;
  }

  return CallWindowProc(PrevListProc, hwnd, msg, wparam, lparam);
}

WDL_DLGRET RSI_LibraryWnd::NewEditProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
  case WM_MOUSEWHEEL:
  case WM_MOUSEHWHEEL:
    //MessageBox(NULL, "playlist mousewheel", "", MB_OK);
    if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
    {
      //MessageBox(NULL, "playlist shift mousewheel", "", MB_OK);
      return 0;
    }
    break;
  case WM_SYSKEYDOWN:
  case WM_KEYDOWN:
    {
      switch(wparam)
      {
      case VK_DOWN:
        {
          HWND lst = GetDlgItem(g_librarywnd->Handle(), IDC_LIST1);
          int pos = ListView_GetNextItem(lst, -1, LVIS_SELECTED);
          while (pos != -1)
          {
            ListView_SetItemState(lst, pos, ~LVIS_SELECTED, LVIS_SELECTED);
            pos = ListView_GetNextItem(lst, -1, LVIS_SELECTED);
          }
          ListView_SetItemState(lst, 0, LVIS_SELECTED|LVIS_FOCUSED,
            LVIS_SELECTED|LVIS_FOCUSED);
          SetFocus(lst);
        }
        break;
      case VK_BACK:
        {
          if ((GetAsyncKeyState(VK_CONTROL) & 0x8000))
          {
            SetTimer(g_librarywnd->Handle(), RSI_LIBRARY_CLEAR_EDIT,
              RSI_LIBRARY_CLEAR_EDIT_MS, NULL);
          }
        }
        break;
      }
    }
    break;
  }

  return CallWindowProc(PrevEditProc, hwnd, msg, wparam, lparam);
}

WDL_DLGRET RSI_LibraryWnd::LibraryWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  RSI_LibraryWnd *self = (RSI_LibraryWnd *)GetWindowLongPtr(hwnd, GWLP_USERDATA);

  if (!self && msg == WM_INITDIALOG)
  {
    SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
    self = (RSI_LibraryWnd *)lparam;
    self->m_hwnd = hwnd;
  }

  if (WDL_likely(self))
  {
    return self->LibraryWndLoop(msg, wparam, lparam);
  }
  else
  {
    return 0;
  }
}

WDL_DLGRET RSI_LibraryWnd::LibraryWndLoop(UINT msg, WPARAM wparam, LPARAM lparam)
{
  m_msg = msg;
  m_wparam = wparam;
  m_lparam = lparam;

  switch (msg)
  {
    case WM_INITDIALOG: OnInitDialog(); break;
    case WM_DESTROY: OnDestroy(); break;
    case WM_SIZE: OnSize(); break;
    case WM_COMMAND: OnCommand(); break;
    case WM_CONTEXTMENU: OnContextMenu(); break;
    case WM_TIMER: OnTimer(); break;
    case WM_ERASEBKGND: return 1; break;
    case WM_NOTIFY: OnNotify(); break;
    case WM_PAINT: OnPaint(); break;
  }

  return 0;
}
