* corrected ListView_SelectItem

* corrected ListView_SelectItem

[Code]
type
  TWndProc = function(handle: Integer; msg: UINT; wParam, lParam: LongInt): LongInt;
  TCWPSTRUCT = record
    lParam, wParam: LongInt;
    msg: LongInt;
    hWnd: Integer;
  end;
  TCWPSTRUCTProc = procedure(code: Integer; wParam: LongInt; lParam: TCWPSTRUCT);
  TNMHDR = record
    hwndFrom: LongInt;
    idFrom: LongInt;
    code: LongInt;
  end;
  TNMLISTVIEW = record
    hdr: TNMHDR;
    iItem, iSubItem: Integer;
    uNewState, uOldState, uChanged: UINT;
    ptAction: TPoint;
    lParam: LongInt;
  end;
  TLVData = record
    handle: Integer;
    parHandle: Integer; {Parent window hwnd}
    oldWndProc: Integer;
  end;
  TWinListViewNotifyEvent = procedure(data: TLVData; lvn: TNMLISTVIEW);
  TWinListViewChangingEvent = function(data: TLVData; lvn: TNMLISTVIEW): Boolean;
  TLVBehavior = record
    OnClick: TWinListViewNotifyEvent;
    OnChanging: TWinListViewChangingEvent;
  end;
  TWinListView = record
    data: TLVData;
    behavior: TLVBehavior;
  end;

const
  WM_NOTIFY = $4e;
  WS_EX_CLIENTEDGE = $0200;
  TNMLISTVIEWSIZE = $2c;

{listview-related helpers}
type
  TInitCommonCtls = record
    dwSize, dwICC: Integer;
  end;
  TWinRect = record
    left, top, right, bottom: LongInt;
  end;
  TLVColumn = record
    mask: LongInt;
    fmt: Integer;
    cx: Integer;
    value: WideString;
    valueMax, iSubItem : Integer;
    iImage, iOrder: Integer;
  end;
  TLVItem = record
    mask: LongInt;
    iItem, iSubItem: Integer;
    state, stateMask: LongInt;
    value: WideString;
    {value: Integer;}
    valueMax: Integer;
    iImage: Integer;
    lParam: LongInt;
    iIndent: Integer;   {_if (_WIN32_IE >= 0x0300)}
    iGroupId: Integer;  {_if (_WIN32_WINNT >= 0x0501)}
    cColumns, puColumns: LongInt;
    {piColFmt, iGroup: LongInt;}  {_if (_WIN32_WINNT >= 0x0600)}
  end;

const
  {list view}
  ICC_LISTVIEW_CLASSES = $1;
  WC_LISTVIEW = 'SysListView32';
  LVM_FIRST = $1000;
  NM_FIRST = $0;
  LVM_SETBKCOLOR = (LVM_FIRST + 1);
  LVM_GETITEMCOUNT = (LVM_FIRST + 4);
  LVM_DELETEALLITEMS = (LVM_FIRST + 9);
  LVM_GETNEXTITEM = (LVM_FIRST + 12);

  LVSCW_AUTOSIZE = -1;
  LVSCW_AUTOSIZE_USEHEADER = -2;
  LVM_GETCOLUMNWIDTH = (LVM_FIRST + 29);
  LVM_SETCOLUMNWIDTH = (LVM_FIRST + 30);

  LVM_GETHEADER = (LVM_FIRST + 31);

  LVM_SETTEXTBKCOLOR = (LVM_FIRST + 38);
  LVM_SETITEMSTATE = (LVM_FIRST + 43);
  LVM_SETEXTENDEDLISTVIEWSTYLE = (LVM_FIRST + 54);
  LVM_SETITEM = (LVM_FIRST + 76);
  LVM_INSERTITEM = (LVM_FIRST + 77);
  LVM_INSERTCOLUMN = (LVM_FIRST + 97);
  LVM_GETITEMTEXT = (LVM_FIRST + 115);
  LVM_SETITEMTEXT = (LVM_FIRST + 116);
  LVM_SETOUTLINECOLOR = (LVM_FIRST + 177);
  LPSTR_TEXTCALLBACK = -1;  {((LPWSTR)-1L)}
  {listview: styles}
  LVS_REPORT = $1;
  LVS_EDITLABELS = $0200;
  LVS_SINGLESEL = $4;
  LVS_SHOWSELALWAYS = $8;
  LVS_EX_FULLROWSELECT = $20;
  LVS_EX_GRIDLINES = $1;
  LVS_EX_FLATSB = $100;
  LVS_EX_HEADERDRAGDROP = $10;
  LVS_EX_AUTOSIZECOLUMNS = $10000000;
  LVS_EX_LABELTIP = $4000;
  LVS_EX_BORDERSELECT = $8000;
  LVS_EX_COLUMNOVERFLOW = $80000000;
  {listview: columns}
  LVCFMT_LEFT = $0;
  LVCFMT_RIGHT = $1;
  LVCF_FMT = $1;
  LVCF_WIDTH = $2;
  LVCF_TEXT = $4;
  LVCF_SUBITEM = $8;
  {listview: items}
  LVIF_TEXT = $1;
  LVIF_IMAGE = $2;
  LVIF_STATE = $8;
  LVIS_FOCUSED = $1;
  LVIS_SELECTED = $2;
  LVNI_SELECTED = $2;
  {listview: notifications}
  LVN_FIRST = (0-100);
  LVN_ITEMCHANGING = (LVN_FIRST-0);
  LVN_ITEMCHANGED = (LVN_FIRST-1);
  LVN_DELETEALLITEMS = (LVN_FIRST-4);
  LVN_BEGINLABELEDIT = (LVN_FIRST-75);
  NM_CLICK = (NM_FIRST-2);
  {header}
  HDM_FIRST = $1200;
  HDM_GETITEMCOUNT = (HDM_FIRST + 0);
  {windows}
  WS_CHILD = $40000000;
  HC_ACTION = $0;
  GWL_WNDPROC = (-4);

type
  TLVCache = Array of TWinListView;
var
  _LVControlCache: TLVCache;

function InitCommonCtls(var init: TInitCommonCtls): Boolean; external 'InitCommonControlsEx@comctl32.dll stdcall';
function GetClientRect(handle: Integer; var rect: TWinRect): Boolean; external 'GetClientRect@user32.dll stdcall';
function CreateWindow(exstyle: LongInt; clsName, wndName: String; style: LongInt; x, y, w, h: Integer; parHandle, menu, inst, params: Integer): Integer; external 'CreateWindowExW@user32.dll stdcall';
function MoveWindow(handle, x, y, w, h: Integer; bRepaint: Boolean): Boolean; external 'MoveWindow@user32.dll stdcall';
function ShowWindow(handle: Integer; cmdShow: Integer): Boolean; external 'ShowWindow@user32.dll stdcall';
function LVSendMessageColumn(handle: Integer; msg, wParam: Longint; var LParam: TLVColumn): Longint; external 'SendMessageW@user32.dll stdcall';
function LVSendMessageItem(handle: Integer; msg, wParam: Longint; var LParam: TLVItem): Longint; external 'SendMessageW@user32.dll stdcall';
function CallWindowProc(prevWndProc, handle: Integer; msg: UINT; wParam, LParam: LongInt): Longint; external 'CallWindowProcW@user32.dll stdcall';
function GetWindowLong(handle, index: LongInt): LongInt; external 'GetWindowLongW@user32.dll stdcall setuponly';
function SetWindowLong(handle, index, newvalue: LongInt): LongInt; external 'SetWindowLongW@user32.dll stdcall setuponly';
function WrapWndHookProc(callback: TCWPSTRUCTProc; paramsCount: Integer): LongInt; external 'wrapcallback@files:innocallback.dll';
function WrapWndProc(wndProc: TWndProc; numParams: Integer): LongWord; external 'wrapcallback@files:innocallback.dll stdcall';
procedure MapToNMLV(ptr, size: LongInt; var hdr: TNMLISTVIEW); external 'CopyPtrValue@files:listviewhelpers.dll stdcall loadwithalteredsearchpath';

function _ListView_FindCtrl(ctrlHandle: Integer): TWinListView;
var
  i: Integer;
begin
  for i :=0 to GetArrayLength(_LVControlCache)-1 do begin
    if _LVControlCache[i].data.handle = ctrlHandle then begin
      Result := _LVControlCache[i];
      Exit;
    end;
  end;
end;

function _ListView_FindCtrlByParent(parHandle: Integer): TWinListView;
var
  i: Integer;
begin
  for i :=0 to GetArrayLength(_LVControlCache)-1 do begin
    if _LVControlCache[i].data.parHandle = parHandle then begin
      Result := _LVControlCache[i];
      Exit;
    end;
  end;
end;

procedure _ListView_AddCtrl(lv: TWinListView);
var
  i: Integer;
begin
  i := GetArrayLength(_LVControlCache);
  SetArrayLength(_LVControlCache, GetArrayLength(_LVControlCache)+1);
  _LVControlCache[i] := lv;
end;

procedure _ListView_RemoveCtrl(ctrlHandle: Integer);
var
  i: Integer;
begin
  for i :=0 to GetArrayLength(_LVControlCache)-1 do begin
    if _LVControlCache[i].data.handle = ctrlHandle then begin
      _LVControlCache[i].data.handle := 0;
      _LVControlCache[i].data.parHandle := 0;
      Exit;
    end;
  end;
end;

function _ListView_WndProc(handle: Integer; msg: UINT; wParam, lParam: LongInt): LongInt;
var
  lvn: TNMLISTVIEW;
  lv: TWinListView;
  allowChange: Boolean;
begin
  lv := _ListView_FindCtrlByParent(handle);
  if lv.data.handle = 0 then begin
    Log(Format('ListView_WndProc: unable to match control by parent hWnd=%X', [handle]));
    Exit;
  end;
  if msg = WM_NOTIFY then begin
    MapToNMLV(lParam, TNMLISTVIEWSIZE, lvn);
    if lvn.hdr.code = LVN_ITEMCHANGING then begin
      allowChange := True;
      if lv.behavior.OnChanging <> nil then begin
        allowChange := lv.behavior.OnChanging(lv.data, lvn);
      end;
      Result := Integer(not allowChange);
      Exit;
    end
    else if (lvn.hdr.code = LVN_ITEMCHANGED) and (lv.behavior.OnClick <> nil) then
      lv.behavior.OnClick(lv.data, lvn)
    else if (lvn.hdr.code = LVN_BEGINLABELEDIT) then begin
      Result := 1;  {Readonly labels}
      Exit;
    end;
  end;
  Result := CallWindowProc(lv.data.oldWndProc, handle, msg, wParam, lParam);
end;

function WinRect(l, t, w, h: Integer): TWinRect;
begin
  Result.left := l;
  Result.top := t;
  Result.right := l + w;
  Result.bottom := t + h;
end;

procedure ListView_Init;
var
  init: TInitCommonCtls;
begin
  init.dwSize := 8;
  init.dwICC := ICC_LISTVIEW_CLASSES;
  InitCommonCtls(init);
end;

function ListView_BeginCreate(parHandle: Integer; rc: TWinRect): TWinListView;
var
  r: Integer;
begin
  Result.data.parHandle := parHandle;
  Result.data.handle := CreateWindow(0, WC_LISTVIEW, '',
    WS_CHILD or LVS_REPORT or LVS_EDITLABELS or LVS_SINGLESEL or LVS_SHOWSELALWAYS,
    rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, parHandle, 0, 0, 0);
  {Log(Format('ListView_Create: ctrl hWnd=%X', [Result.data.handle]));}
  SendMessage(Result.data.handle, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, WS_EX_CLIENTEDGE or LVS_EX_FULLROWSELECT or
              LVS_EX_FLATSB or LVS_EX_GRIDLINES or LVS_EX_AUTOSIZECOLUMNS or LVS_EX_LABELTIP or
              LVS_EX_BORDERSELECT or LVS_EX_COLUMNOVERFLOW);
  r := SendMessage(Result.data.handle, LVM_SETOUTLINECOLOR, 0, $0);
  Result.data.oldWndProc := SetWindowLong(parHandle, GWL_WNDPROC, WrapWndProc(@_ListView_WndProc, 4));
end;

procedure ListView_FinalizeCreate(lv: TWinListView);
begin
  _ListView_AddCtrl(lv);
end;

procedure ListView_Free(lv: TWinListView);
begin
  SetWindowLong(lv.data.handle, GWL_WNDPROC, lv.data.oldWndProc);
  lv.data.oldWndProc := 0;
  _ListView_RemoveCtrl(lv.data.handle);
end;

procedure ListView_AddColumn(lv: TWinListView; col, width: Integer; caption: String);
var
  colData: TLVColumn;
  r: Longint;
begin
  with colData do begin
    {format, width, text and subitem members of the structure are valid}
    mask := LVCF_FMT or LVCF_WIDTH or LVCF_TEXT or LVCF_SUBITEM;
    iSubItem := col;
    value := caption;
    cx := ScaleX(width);  {column width}
    fmt := LVCFMT_LEFT;
  end;
  r := LVSendMessageColumn(lv.data.handle, LVM_INSERTCOLUMN, col, colData);
  if r = -1 then
    Log('ListView_InsertColumn failed.');
end;

procedure ListView_AddItem(lv: TWinListView; item: Integer; caption: String);
var
  itemData: TLVItem;
  r: Longint;
begin
  with itemData do begin
    {text, image, state members of the structure are valid}
    mask := LVIF_TEXT or LVIF_IMAGE or LVIF_STATE;
    iSubItem := 0;
    {value := -1;} {Sends an LVN_GETDISPINFO message}
    value := caption;
    iItem := item;
    iImage := item;
    state := 0;
    stateMask := 0;
  end;
  r := LVSendMessageItem(lv.data.handle, LVM_INSERTITEM, 0, itemData);
  if r = -1 then
    Log('ListView_InsertItem failed.');
end;

procedure ListView_AddSubitem(lv: TWinListView; item, subitem: Integer; caption: String);
var
  itemData: TLVItem;
  r: Longint;
begin
  with itemData do begin
    {text, image, state members of the structure are valid}
    mask := LVIF_TEXT;
    iSubItem := subitem;
    value := caption;
  end;
  r := LVSendMessageItem(lv.data.handle, LVM_SETITEMTEXT, item, itemData);
  if r = -1 then
    Log('ListView_SetItemText failed.');
end;

function ListView_GetItemCount(lv: TWinListView): Integer;
begin
  Result := SendMessage(lv.data.handle, LVM_GETITEMCOUNT, 0, 0);
end;

function ListView_GetItemText(lv: TWinListView; item, subitem: Integer): String;
var
  itemData: TLVItem;
  r: Integer;
  buf: String;
begin
  buf := StringOfChar(#0, MAXPATH);
  with itemData do begin
    {text member(s) of the structure are valid}
    mask := LVIF_TEXT;
    iSubItem := subitem;
    value := PAnsiChar(buf);
    valueMax := MAXPATH;
  end;
  r := LVSendMessageItem(lv.data.handle, LVM_GETITEMTEXT, item, itemData);
  if r = -1 then
    Log('ListView_GetItem failed.')
  else
    Result := Copy(itemData.value, 0, r);
end;

function ListView_GetSelectedItem(lv: TWinListView): Integer;
begin
  Result := SendMessage(lv.data.handle, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
end;

procedure ListView_SelectItem(lv: TWinListView; item: Integer);
var
  itemData: TLVItem;
  r: Longint;
begin
  with itemData do begin
    stateMask := LVIS_FOCUSED or LVIS_SELECTED;
    state := $0F;
  end;
  r := LVSendMessageItem(lv.data.handle, LVM_SETITEMSTATE, item, itemData);
  if r = -1 then
    Log('ListView_SetItemText failed.');
end;

procedure ListView_SetBkColor(lv: TWinListView; color: Integer);
begin
  SendMessage(lv.data.handle, LVM_SETBKCOLOR, 0, color);
end;

procedure ListView_SetTextBkColor(lv: TWinListView; color: Integer);
begin
  SendMessage(lv.data.handle, LVM_SETTEXTBKCOLOR, 0, color);
end;

procedure ListView_DeleteAllItems(lv: TWinListView);
var
  r: Longint;
begin
  r := SendMessage(lv.data.handle, LVM_DELETEALLITEMS, 0, 0);
  if r = -1 then
    Log('ListView_DeleteAllItems failed.');
end;

function ListView_GetColumnCount(lv: TWinListView): Integer;
var
  hH: Integer;
begin
  hH := SendMessage(lv.data.handle, LVM_GETHEADER, 0, 0);
  Result := SendMessage(hH, HDM_GETITEMCOUNT, 0, 0);
end;

procedure ListView_AutoSize(lv: TWinListView);
var
  r, hW, cW, lvW, i, numCols: Integer;
begin
  i := 0; {only resize the first column}
  SendMessage(lv.data.handle, LVM_SETCOLUMNWIDTH, i, LVSCW_AUTOSIZE_USEHEADER);
  hW := hW + SendMessage(lv.data.handle, LVM_GETCOLUMNWIDTH, i, 0);
  SendMessage(lv.data.handle, LVM_SETCOLUMNWIDTH, i, LVSCW_AUTOSIZE);
  cW := cW + SendMessage(lv.data.handle, LVM_GETCOLUMNWIDTH, i, 0);
  if hW > cW then begin
    SendMessage(lv.data.handle, LVM_SETCOLUMNWIDTH, i, hW);
    lvW := lvW + hW;
  end
  else
    lvW := lvW + cW;
end;

[Code]
type
  TWndProc = function(handle: Integer; msg: UINT; wParam, lParam: LongInt): LongInt;
  TCWPSTRUCT = record
    lParam, wParam: LongInt;
    msg: LongInt;
    hWnd: Integer;
  end;
  TCWPSTRUCTProc = procedure(code: Integer; wParam: LongInt; lParam: TCWPSTRUCT);
  TNMHDR = record
    hwndFrom: LongInt;
    idFrom: LongInt;
    code: LongInt;
  end;
  TNMLISTVIEW = record
    hdr: TNMHDR;
    iItem, iSubItem: Integer;
    uNewState, uOldState, uChanged: UINT;
    ptAction: TPoint;
    lParam: LongInt;
  end;
  TLVData = record
    handle: Integer;
    parHandle: Integer; {Parent window hwnd}
    oldWndProc: Integer;
  end;
  TWinListViewNotifyEvent = procedure(data: TLVData; lvn: TNMLISTVIEW);
  TWinListViewChangingEvent = function(data: TLVData; lvn: TNMLISTVIEW): Boolean;
  TLVBehavior = record
    OnClick: TWinListViewNotifyEvent;
    OnChanging: TWinListViewChangingEvent;
  end;
  TWinListView = record
    data: TLVData;
    behavior: TLVBehavior;
  end;

const
  WM_NOTIFY = $4e;
  WS_EX_CLIENTEDGE = $0200;
  TNMLISTVIEWSIZE = $2c;

{listview-related helpers}
type
  TInitCommonCtls = record
    dwSize, dwICC: Integer;
  end;
  TWinRect = record
    left, top, right, bottom: LongInt;
  end;
  TLVColumn = record
    mask: LongInt;
    fmt: Integer;
    cx: Integer;
    value: WideString;
    valueMax, iSubItem : Integer;
    iImage, iOrder: Integer;
  end;
  TLVItem = record
    mask: LongInt;
    iItem, iSubItem: Integer;
    state, stateMask: LongInt;
    value: WideString;
    {value: Integer;}
    valueMax: Integer;
    iImage: Integer;
    lParam: LongInt;
    iIndent: Integer;   {_if (_WIN32_IE >= 0x0300)}
    iGroupId: Integer;  {_if (_WIN32_WINNT >= 0x0501)}
    cColumns, puColumns: LongInt;
    {piColFmt, iGroup: LongInt;}  {_if (_WIN32_WINNT >= 0x0600)}
  end;

const
  {list view}
  ICC_LISTVIEW_CLASSES = $1;
  WC_LISTVIEW = 'SysListView32';
  LVM_FIRST = $1000;
  NM_FIRST = $0;
  LVM_SETBKCOLOR = (LVM_FIRST + 1);
  LVM_GETITEMCOUNT = (LVM_FIRST + 4);
  LVM_DELETEALLITEMS = (LVM_FIRST + 9);
  LVM_GETNEXTITEM = (LVM_FIRST + 12);

  LVSCW_AUTOSIZE = -1;
  LVSCW_AUTOSIZE_USEHEADER = -2;
  LVM_GETCOLUMNWIDTH = (LVM_FIRST + 29);
  LVM_SETCOLUMNWIDTH = (LVM_FIRST + 30);

  LVM_GETHEADER = (LVM_FIRST + 31);

  LVM_SETTEXTBKCOLOR = (LVM_FIRST + 38);
  LVM_SETITEMSTATE = (LVM_FIRST + 43);
  LVM_SETEXTENDEDLISTVIEWSTYLE = (LVM_FIRST + 54);
  LVM_SETITEM = (LVM_FIRST + 76);
  LVM_INSERTITEM = (LVM_FIRST + 77);
  LVM_INSERTCOLUMN = (LVM_FIRST + 97);
  LVM_GETITEMTEXT = (LVM_FIRST + 115);
  LVM_SETITEMTEXT = (LVM_FIRST + 116);
  LVM_SETOUTLINECOLOR = (LVM_FIRST + 177);
  LPSTR_TEXTCALLBACK = -1;  {((LPWSTR)-1L)}
  {listview: styles}
  LVS_REPORT = $1;
  LVS_EDITLABELS = $0200;
  LVS_SINGLESEL = $4;
  LVS_SHOWSELALWAYS = $8;
  LVS_EX_FULLROWSELECT = $20;
  LVS_EX_GRIDLINES = $1;
  LVS_EX_FLATSB = $100;
  LVS_EX_HEADERDRAGDROP = $10;
  LVS_EX_AUTOSIZECOLUMNS = $10000000;
  LVS_EX_LABELTIP = $4000;
  LVS_EX_BORDERSELECT = $8000;
  LVS_EX_COLUMNOVERFLOW = $80000000;
  {listview: columns}
  LVCFMT_LEFT = $0;
  LVCFMT_RIGHT = $1;
  LVCF_FMT = $1;
  LVCF_WIDTH = $2;
  LVCF_TEXT = $4;
  LVCF_SUBITEM = $8;
  {listview: items}
  LVIF_TEXT = $1;
  LVIF_IMAGE = $2;
  LVIF_STATE = $8;
  LVIS_FOCUSED = $1;
  LVIS_SELECTED = $2;
  LVNI_SELECTED = $2;
  {listview: notifications}
  LVN_FIRST = (0-100);
  LVN_ITEMCHANGING = (LVN_FIRST-0);
  LVN_ITEMCHANGED = (LVN_FIRST-1);
  LVN_DELETEALLITEMS = (LVN_FIRST-4);
  LVN_BEGINLABELEDIT = (LVN_FIRST-75);
  NM_CLICK = (NM_FIRST-2);
  {header}
  HDM_FIRST = $1200;
  HDM_GETITEMCOUNT = (HDM_FIRST + 0);
  {windows}
  WS_CHILD = $40000000;
  HC_ACTION = $0;
  GWL_WNDPROC = (-4);

type
  TLVCache = Array of TWinListView;
var
  _LVControlCache: TLVCache;

function InitCommonCtls(var init: TInitCommonCtls): Boolean; external 'InitCommonControlsEx@comctl32.dll stdcall';
function GetClientRect(handle: Integer; var rect: TWinRect): Boolean; external 'GetClientRect@user32.dll stdcall';
function CreateWindow(exstyle: LongInt; clsName, wndName: String; style: LongInt; x, y, w, h: Integer; parHandle, menu, inst, params: Integer): Integer; external 'CreateWindowExW@user32.dll stdcall';
function MoveWindow(handle, x, y, w, h: Integer; bRepaint: Boolean): Boolean; external 'MoveWindow@user32.dll stdcall';
function ShowWindow(handle: Integer; cmdShow: Integer): Boolean; external 'ShowWindow@user32.dll stdcall';
function LVSendMessageColumn(handle: Integer; msg, wParam: Longint; var LParam: TLVColumn): Longint; external 'SendMessageW@user32.dll stdcall';
function LVSendMessageItem(handle: Integer; msg, wParam: Longint; var LParam: TLVItem): Longint; external 'SendMessageW@user32.dll stdcall';
function CallWindowProc(prevWndProc, handle: Integer; msg: UINT; wParam, LParam: LongInt): Longint; external 'CallWindowProcW@user32.dll stdcall';
function GetWindowLong(handle, index: LongInt): LongInt; external 'GetWindowLongW@user32.dll stdcall setuponly';
function SetWindowLong(handle, index, newvalue: LongInt): LongInt; external 'SetWindowLongW@user32.dll stdcall setuponly';
function WrapWndHookProc(callback: TCWPSTRUCTProc; paramsCount: Integer): LongInt; external 'wrapcallback@files:innocallback.dll';
function WrapWndProc(wndProc: TWndProc; numParams: Integer): LongWord; external 'wrapcallback@files:innocallback.dll stdcall';
procedure MapToNMLV(ptr, size: LongInt; var hdr: TNMLISTVIEW); external 'CopyPtrValue@files:listviewhelpers.dll stdcall loadwithalteredsearchpath';

function _ListView_FindCtrl(ctrlHandle: Integer): TWinListView;
var
  i: Integer;
begin
  for i :=0 to GetArrayLength(_LVControlCache)-1 do begin
    if _LVControlCache[i].data.handle = ctrlHandle then begin
      Result := _LVControlCache[i];
      Exit;
    end;
  end;
end;

function _ListView_FindCtrlByParent(parHandle: Integer): TWinListView;
var
  i: Integer;
begin
  for i :=0 to GetArrayLength(_LVControlCache)-1 do begin
    if _LVControlCache[i].data.parHandle = parHandle then begin
      Result := _LVControlCache[i];
      Exit;
    end;
  end;
end;

procedure _ListView_AddCtrl(lv: TWinListView);
var
  i: Integer;
begin
  i := GetArrayLength(_LVControlCache);
  SetArrayLength(_LVControlCache, GetArrayLength(_LVControlCache)+1);
  _LVControlCache[i] := lv;
end;

procedure _ListView_RemoveCtrl(ctrlHandle: Integer);
var
  i: Integer;
begin
  for i :=0 to GetArrayLength(_LVControlCache)-1 do begin
    if _LVControlCache[i].data.handle = ctrlHandle then begin
      _LVControlCache[i].data.handle := 0;
      _LVControlCache[i].data.parHandle := 0;
      Exit;
    end;
  end;
end;

function _ListView_WndProc(handle: Integer; msg: UINT; wParam, lParam: LongInt): LongInt;
var
  lvn: TNMLISTVIEW;
  lv: TWinListView;
  allowChange: Boolean;
begin
  lv := _ListView_FindCtrlByParent(handle);
  if lv.data.handle = 0 then begin
    Log(Format('ListView_WndProc: unable to match control by parent hWnd=%X', [handle]));
    Exit;
  end;
  if msg = WM_NOTIFY then begin
    MapToNMLV(lParam, TNMLISTVIEWSIZE, lvn);
    if lvn.hdr.code = LVN_ITEMCHANGING then begin
      allowChange := True;
      if lv.behavior.OnChanging <> nil then begin
        allowChange := lv.behavior.OnChanging(lv.data, lvn);
      end;
      Result := Integer(not allowChange);
      Exit;
    end
    else if (lvn.hdr.code = LVN_ITEMCHANGED) and (lv.behavior.OnClick <> nil) then
      lv.behavior.OnClick(lv.data, lvn)
    else if (lvn.hdr.code = LVN_BEGINLABELEDIT) then begin
      Result := 1;  {Readonly labels}
      Exit;
    end;
  end;
  Result := CallWindowProc(lv.data.oldWndProc, handle, msg, wParam, lParam);
end;

function WinRect(l, t, w, h: Integer): TWinRect;
begin
  Result.left := l;
  Result.top := t;
  Result.right := l + w;
  Result.bottom := t + h;
end;

procedure ListView_Init;
var
  init: TInitCommonCtls;
begin
  init.dwSize := 8;
  init.dwICC := ICC_LISTVIEW_CLASSES;
  InitCommonCtls(init);
end;

function ListView_BeginCreate(parHandle: Integer; rc: TWinRect): TWinListView;
var
  r: Integer;
begin
  Result.data.parHandle := parHandle;
  Result.data.handle := CreateWindow(0, WC_LISTVIEW, '',
    WS_CHILD or LVS_REPORT or LVS_EDITLABELS or LVS_SINGLESEL or LVS_SHOWSELALWAYS,
    rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, parHandle, 0, 0, 0);
  {Log(Format('ListView_Create: ctrl hWnd=%X', [Result.data.handle]));}
  SendMessage(Result.data.handle, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, WS_EX_CLIENTEDGE or LVS_EX_FULLROWSELECT or
              LVS_EX_FLATSB or LVS_EX_GRIDLINES or LVS_EX_AUTOSIZECOLUMNS or LVS_EX_LABELTIP or
              LVS_EX_BORDERSELECT or LVS_EX_COLUMNOVERFLOW);
  r := SendMessage(Result.data.handle, LVM_SETOUTLINECOLOR, 0, $0);
  Result.data.oldWndProc := SetWindowLong(parHandle, GWL_WNDPROC, WrapWndProc(@_ListView_WndProc, 4));
end;

procedure ListView_FinalizeCreate(lv: TWinListView);
begin
  _ListView_AddCtrl(lv);
end;

procedure ListView_Free(lv: TWinListView);
begin
  SetWindowLong(lv.data.handle, GWL_WNDPROC, lv.data.oldWndProc);
  lv.data.oldWndProc := 0;
  _ListView_RemoveCtrl(lv.data.handle);
end;

procedure ListView_AddColumn(lv: TWinListView; col, width: Integer; caption: String);
var
  colData: TLVColumn;
  r: Longint;
begin
  with colData do begin
    {format, width, text and subitem members of the structure are valid}
    mask := LVCF_FMT or LVCF_WIDTH or LVCF_TEXT or LVCF_SUBITEM;
    iSubItem := col;
    value := caption;
    cx := ScaleX(width);  {column width}
    fmt := LVCFMT_LEFT;
  end;
  r := LVSendMessageColumn(lv.data.handle, LVM_INSERTCOLUMN, col, colData);
  if r = -1 then
    Log('ListView_InsertColumn failed.');
end;

procedure ListView_AddItem(lv: TWinListView; item: Integer; caption: String);
var
  itemData: TLVItem;
  r: Longint;
begin
  with itemData do begin
    {text, image, state members of the structure are valid}
    mask := LVIF_TEXT or LVIF_IMAGE or LVIF_STATE;
    iSubItem := 0;
    {value := -1;} {Sends an LVN_GETDISPINFO message}
    value := caption;
    iItem := item;
    iImage := item;
    state := 0;
    stateMask := 0;
  end;
  r := LVSendMessageItem(lv.data.handle, LVM_INSERTITEM, 0, itemData);
  if r = -1 then
    Log('ListView_InsertItem failed.');
end;

procedure ListView_AddSubitem(lv: TWinListView; item, subitem: Integer; caption: String);
var
  itemData: TLVItem;
  r: Longint;
begin
  with itemData do begin
    {text, image, state members of the structure are valid}
    mask := LVIF_TEXT;
    iSubItem := subitem;
    value := caption;
  end;
  r := LVSendMessageItem(lv.data.handle, LVM_SETITEMTEXT, item, itemData);
  if r = -1 then
    Log('ListView_SetItemText failed.');
end;

function ListView_GetItemCount(lv: TWinListView): Integer;
begin
  Result := SendMessage(lv.data.handle, LVM_GETITEMCOUNT, 0, 0);
end;

function ListView_GetItemText(lv: TWinListView; item, subitem: Integer): String;
var
  itemData: TLVItem;
  r: Integer;
  buf: String;
begin
  buf := StringOfChar(#0, MAXPATH);
  with itemData do begin
    {text member(s) of the structure are valid}
    mask := LVIF_TEXT;
    iSubItem := subitem;
    value := PAnsiChar(buf);
    valueMax := MAXPATH;
  end;
  r := LVSendMessageItem(lv.data.handle, LVM_GETITEMTEXT, item, itemData);
  if r = -1 then
    Log('ListView_GetItem failed.')
  else
    Result := Copy(itemData.value, 0, r);
end;

function ListView_GetSelectedItem(lv: TWinListView): Integer;
begin
  Result := SendMessage(lv.data.handle, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
end;

procedure ListView_SelectItem(lv: TWinListView; item: Integer);
var
  itemData: TLVItem;
  r: Longint;
begin
  with itemData do begin
    state := LVIS_FOCUSED or LVIS_SELECTED;
    mask := $0F;
  end;
  r := LVSendMessageItem(lv.data.handle, LVM_SETITEMSTATE, item, itemData);
  if r = -1 then
    Log('ListView_SetItemText failed.');
end;

procedure ListView_SetBkColor(lv: TWinListView; color: Integer);
begin
  SendMessage(lv.data.handle, LVM_SETBKCOLOR, 0, color);
end;

procedure ListView_SetTextBkColor(lv: TWinListView; color: Integer);
begin
  SendMessage(lv.data.handle, LVM_SETTEXTBKCOLOR, 0, color);
end;

procedure ListView_DeleteAllItems(lv: TWinListView);
var
  r: Longint;
begin
  r := SendMessage(lv.data.handle, LVM_DELETEALLITEMS, 0, 0);
  if r = -1 then
    Log('ListView_DeleteAllItems failed.');
end;

function ListView_GetColumnCount(lv: TWinListView): Integer;
var
  hH: Integer;
begin
  hH := SendMessage(lv.data.handle, LVM_GETHEADER, 0, 0);
  Result := SendMessage(hH, HDM_GETITEMCOUNT, 0, 0);
end;

procedure ListView_AutoSize(lv: TWinListView);
var
  r, hW, cW, lvW, i, numCols: Integer;
begin
  i := 0; {only resize the first column}
  SendMessage(lv.data.handle, LVM_SETCOLUMNWIDTH, i, LVSCW_AUTOSIZE_USEHEADER);
  hW := hW + SendMessage(lv.data.handle, LVM_GETCOLUMNWIDTH, i, 0);
  SendMessage(lv.data.handle, LVM_SETCOLUMNWIDTH, i, LVSCW_AUTOSIZE);
  cW := cW + SendMessage(lv.data.handle, LVM_GETCOLUMNWIDTH, i, 0);
  if hW > cW then begin
    SendMessage(lv.data.handle, LVM_SETCOLUMNWIDTH, i, hW);
    lvW := lvW + hW;
  end
  else
    lvW := lvW + cW;
end;


bug fixed: in the Windows installer version, the shortcut to the manual didn't work

#define FileHandle FileOpen("version.txt")
#define MyAppVer FileRead(FileHandle)

#define FileHandle FileOpen("version-name.txt")
#define MyAppVerName FileRead(FileHandle)

#define FileHandle FileOpen("stage.txt")
#define MyAppStage FileRead(FileHandle)

#define FileHandle FileOpen("stage-name.txt")
#define MyAppStageName FileRead(FileHandle)

#if MyAppStage == "stable"
#define MyAppStage
#define MyAppStageName
#endif

[Setup]
AppName=SoundRTS {#MyAppStageName}
AppVerName=SoundRTS {#MyAppVerName}
AppPublisher=SoundMud
AppPublisherURL=http://jlpo.free.fr/soundrts
AppSupportURL=http://jlpo.free.fr/soundrts
AppUpdatesURL=http://jlpo.free.fr/soundrts
DefaultDirName={pf}\SoundRTS {#MyAppStage}
DefaultGroupName=SoundRTS {#MyAppStageName}
Compression=lzma
SolidCompression=true
OutputBaseFilename=soundrts-{#MyAppVer}
OutputDir=..\dist
ShowLanguageDialog=auto
PrivilegesRequired=none

[Files]
Source: soundrts-{#MyAppVer}-windows\*; DestDir: {app}; Flags: ignoreversion recursesubdirs createallsubdirs

[Dirs]
Name: {userappdata}\SoundRTS {#MyAppStage}

[Icons]
Name: {group}\SoundRTS; Filename: {app}\soundrts.exe; WorkingDir: {app}
Name: {group}\Manual; Filename: {app}\doc\help-index.htm
Name: {group}\Web site; Filename: http://jlpo.free.fr/soundrts
Name: {group}\{cm:UninstallProgram, SoundRTS}; Filename: {uninstallexe}
Name: {group}\user; Filename: {userappdata}\SoundRTS {#MyAppStage}

[Languages]
Name: English; MessagesFile: compiler:Default.isl
Name: BrazilianPortuguese; MessagesFile: compiler:Languages\BrazilianPortuguese.isl
Name: Chinese; MessagesFile: ChineseSimp-12-5.1.11.isl
Name: Czech; MessagesFile: compiler:Languages\Czech.isl
Name: Dutch; MessagesFile: compiler:Languages\Dutch.isl
Name: French; MessagesFile: compiler:Languages\French.isl
Name: German; MessagesFile: compiler:Languages\German.isl
Name: Italian; MessagesFile: compiler:Languages\Italian.isl
Name: Polish; MessagesFile: compiler:Languages\Polish.isl
Name: Russian; MessagesFile: compiler:Languages\Russian.isl
Name: Slovak; MessagesFile: compiler:Languages\Slovak.isl
Name: Spanish; MessagesFile: compiler:Languages\Spanish.isl

[_ISTool]
UseAbsolutePaths=true

#define FileHandle FileOpen("version.txt")
#define MyAppVer FileRead(FileHandle)

#define FileHandle FileOpen("version-name.txt")
#define MyAppVerName FileRead(FileHandle)

#define FileHandle FileOpen("stage.txt")
#define MyAppStage FileRead(FileHandle)

#define FileHandle FileOpen("stage-name.txt")
#define MyAppStageName FileRead(FileHandle)

#if MyAppStage == "stable"
#define MyAppStage
#define MyAppStageName
#endif

[Setup]
AppName=SoundRTS {#MyAppStageName}
AppVerName=SoundRTS {#MyAppVerName}
AppPublisher=SoundMud
AppPublisherURL=http://jlpo.free.fr/soundrts
AppSupportURL=http://jlpo.free.fr/soundrts
AppUpdatesURL=http://jlpo.free.fr/soundrts
DefaultDirName={pf}\SoundRTS {#MyAppStage}
DefaultGroupName=SoundRTS {#MyAppStageName}
Compression=lzma
SolidCompression=true
OutputBaseFilename=soundrts-{#MyAppVer}
OutputDir=..\dist
ShowLanguageDialog=auto
PrivilegesRequired=none

[Files]
Source: soundrts-{#MyAppVer}-windows\*; DestDir: {app}; Flags: ignoreversion recursesubdirs createallsubdirs

[Dirs]
Name: {userappdata}\SoundRTS {#MyAppStage}

[Icons]
Name: {group}\SoundRTS; Filename: {app}\soundrts.exe; WorkingDir: {app}
Name: {group}\Manual; Filename: {app}\help-index.htm
Name: {group}\Web site; Filename: http://jlpo.free.fr/soundrts
Name: {group}\{cm:UninstallProgram, SoundRTS}; Filename: {uninstallexe}
Name: {group}\user; Filename: {userappdata}\SoundRTS {#MyAppStage}

[Languages]
Name: English; MessagesFile: compiler:Default.isl
Name: BrazilianPortuguese; MessagesFile: compiler:Languages\BrazilianPortuguese.isl
Name: Chinese; MessagesFile: ChineseSimp-12-5.1.11.isl
Name: Czech; MessagesFile: compiler:Languages\Czech.isl
Name: Dutch; MessagesFile: compiler:Languages\Dutch.isl
Name: French; MessagesFile: compiler:Languages\French.isl
Name: German; MessagesFile: compiler:Languages\German.isl
Name: Italian; MessagesFile: compiler:Languages\Italian.isl
Name: Polish; MessagesFile: compiler:Languages\Polish.isl
Name: Russian; MessagesFile: compiler:Languages\Russian.isl
Name: Slovak; MessagesFile: compiler:Languages\Slovak.isl
Name: Spanish; MessagesFile: compiler:Languages\Spanish.isl

[_ISTool]
UseAbsolutePaths=true


Adding Cabbage.

Adding Cabbage.

  ; C S O U N D   6   I N N O   S E T U P   S C R I P T

; Copyright (C) 2013 by Michael Gogins.
; This software is licensed under the terms of the
; Lesser (or Library) GNU Public License.

; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

; This file assumes that you are Michael Gogins,
; but comments indicate what you should change if you are not me.
; All of these changes SHOULD be in the #defines immediately following this.
; Also, this installer assumes LuaJIT and CsoundAC are part of Csound core.
; At this time the following features are not included in the installer:
; faustgen.
; I hope to change this soon.

; Uncomment the following line to build for Cabbage, CsoundVST, and vst4cs.
#define CSOUNDVST

#define MyAppName "Csound6"
#define MyAppVersion "6"
#ifdef CSOUNDVST
#define MyAppMinVersion "6.02.0-vst"
#else
#define MyAppMinVersion "6.02.0"
#endif
#define MyAppPublisher "Csound"
#define MyAppURL "http://sourceforge.net/projects/csound"
; If you are not Michael Gogins, change this to your MinGW dll directory.
#define MyMinGwBinDir "D:\mingw32-4.8.1\bin\"
; If you are not Michael Gogins, change this to your MSys /bin/ directory.
#define MyMSysBinDir "D:\msys\bin\"
; If you are not Michael Gogins, change this to your MSys /usr/local/ directory.
#define MyMSysUsrLocalDir "D:\msys\local\"
; If you are not Michael Gogins, change this to your Csound build directory.
#define MySourceDir "C:\Users\mike\csound-csound6-git\"
; If you are not Michael Gogins, change this to your Csound reference manual build directory.
#define MyManualSourceDir "C:\Users\mike\csound-manual6-git\"
; If you are not Michael Gogins, change this to your Csound tutorial directory.
#define MyCsoundTutorialSourceDir "D:\Dropbox\tutorial\"
; If you are not Michael Gogins, change this to your CsoundAC tutorial directory.
#define MyCsoundAcTutorialSourceDir "D:\Dropbox\tutorial\"
; If you are not Michael Gogins, change this to your libsndfile install directory.
#define MyLibSndfileSourceDir "D:\msys\local\opt\libsndfile\"
; If you are not Michael Gogins, change this to your PortAudio lib directory.
#define MyPortAudioSourceDir "D:\msys\local\src\portaudio\"
; If you are not Michael Gogins, change this to your PortMidi build directory.
#define MyPortMidiSourceDir "D:\msys\local\src\portmidi\"
; If you are not Michael Gogins, change this to your LuaJIT bin directory.
#define MyLuaJitSourceDir "D:\msys\local\src\luajit-2.0\src\"
; If you are not Michael Gogins, change this to your FLTK dll directory.
#define MyFltkSourceDir "D:\msys\local\src\fltk-1.3.2\src\"
; If you are not Michael Gogins, change this to your libmusicxml dll directory.
#define MyLibMusicXmlSourceDir "D:\msys\local\src\libmusicxml\"
; If you are not Michael Gogins, change this to your FluidSynth dll directory.
#define MyFluidSynthSourceDir "D:\msys\local\src\fluidsynth\src\.libs\"
; If you are not Michael Gogins, change this to your OSC library dll directory.
#define MyLibLoSourceDir "D:\msys\local\src\liblo-0.26\"
; If you are not Michael Gogins, change this to your STK dll directory.
#define MyLibStkSourceDir "D:\msys\local\src\stk-4.4.4\"
; If you are not Michael Gogins, change this to your CsoundQt bin directory.
#define MyCsoundQtBinDir "C:\Users\mike\qutecsound-code\bin\"
; If you are not Michael Gogins, change this to your Qt SDK DLL directory.
#define MyQtSdkBinDir "D:\qt-everywhere-opensource-src-5.1.1\qtbase\bin\"
; If you are not Michael Gogins, change this to your unzipped cabbage-master directory.
#define MyCabbageDir "D:\cabbage-master\"

[Components]
Name: "core"; Description: "Core Csound"; Types: full custom; Flags: fixed
Name: "python"; Description: "Python features (requires Python 2.7)"; Types: full;
Name: "pd"; Description: "Pure Data csound~ object (requires Pure Data)"; Types: full;
#ifdef CSOUNDVST
Name: "csoundvst"; Description: "Cabbage, Csound VST plugin, and vst4cs opcodes"; Types: full;
#endif

[Dirs]
; ALL programs and shared libraries (except opcodes and other Csound modules) go here.
Name: "{app}\bin"; Permissions: users-modify
#define APP_BIN "{app}\bin\"
; ALL Csound opcodes and other Csound modules go here.
Name: "{app}\plugins64"; Permissions: users-modify
#define APP_PLUGINS64 "{app}\plugins64\"
; All C or C++ include files for Csound and other components used by Csound go here.
; This is a convenience for people like me who program in C or C++ and might use
; features of these third party components.
Name: "{app}\include"
#define APP_INCLUDE "{app}\include\"
; The Csound reference manual goes here.
Name: "{app}\doc\manual"
#define APP_MANUAL "{app}\doc\manual\"
; The Csound API reference manuals go here.
Name: "{app}\doc\apiref"
#define APP_APIREF "{app}\doc\apiref\"
; All Csound examples go here.
Name: "{app}\examples"; Permissions: users-modify
#define APP_EXAMPLES "{app}\examples\"
; Any SoundFonts or sound samples used by Csound examples go here.
Name: "{app}\samples"
#define APP_SAMPLES "{app}\samples\"
; Tutorials go here.
Name: "{app}\doc\tutorial"
#define APP_TUTORIAL "{app}\doc\tutorial\"
Name: "{app}\cabbage"
#define APP_CABBAGE "{app}\cabbage\"
; These are the Csound environment variables related to directories.

#define SFDIR
#define SSDIR
#define SADIR
#define INCDIR
#define OPCODE6DIR64
#define SNAPDIR
#define RAWWAVE_PATH
#define MFDIR
#define PYTHONPATH

[Setup]
ChangesEnvironment=true
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{4BCEDC5F-C6D2-4869-9097-3F1753739F35}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppMinVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName=Csound 6
AllowNoIcons=yes
LicenseFile=readme-csound6.txt
;InfoBeforeFile=readme-csound6.txt
OutputDir=installer\windows
OutputBaseFilename=Setup_{#MyAppName}_{#MyAppMinVersion}
Compression=lzma
SolidCompression=yes
SourceDir={#MySourceDir}

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Files]
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
Source: "{#MyMinGwBinDir}*.dll"; DestDir: "{#APP_BIN}"; Components: core;
; No idea why this other name is needed.
Source: "{#MyMSysBinDir}libiconv-2.dll"; DestDir: "{#APP_BIN}"; DestName: "iconv.dll"; Components: core;
Source: "{#MyMSysUsrLocalDir}bin/*.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "csound64.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "csnd6.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "*.exe"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "*.jar"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "*.pyd"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: python;
Source: "*.py";  DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: python;
Source: "csound6~.dll";  DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: pd;
Source: "CsoundAC.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
#ifdef CSOUNDVST
Source: "CsoundVST.dll";  DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: csoundvst;
#endif
Source: "luaCsnd6.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "luaCsoundAC.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "_jcsound6.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;

#ifdef CSOUNDVST
Source: "{#MyCabbageDir}cabbage.exe"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: csoundvst
Source: "{#MyCabbageDir}CabbagePluginSynth.dat"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: csoundvst
Source: "{#MyCabbageDir}CabbagePluginEffect.dat"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: csoundvst
Source: "{#MyCabbageDir}Examples\*"; DestDir: "{#APP_BIN}\Examples"; Flags: ignoreversion recursesubdirs; Components: csoundvst
Source: "{#MyCabbageDir}Docs\*"; DestDir: "{#APP_BIN}\Docs"; Flags: ignoreversion recursesubdirs; Components: csoundvst
#endif

Source: "LuaCsound.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "ampmidid.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "cellular.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "chua.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "cs_date.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "csladspa.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "doppler.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "fareygen.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "fluidOpcodes.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "fractalnoise.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "image.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "ipmidi.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "linear_algebra.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "mixer.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "osc.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "platerev.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "pmidi.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "py.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: python;
Source: "rtpa.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "rtwinmm.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "scansyn.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "serial.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "signalflowgraph.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "stdutil.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "stk.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "system_call.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "virtual.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
#ifdef CSOUNDVST
Source: "vst4cs.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: csoundvst;
#endif
Source: "widgets.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;

Source: "{#MyCsoundQtBinDir}CsoundQt-d-cs6.exe"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5PrintSupport.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5Widgets.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5Xml.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5Gui.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5Core.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}..\plugins\platforms\qwindows.dll"; DestDir: "{#APP_BIN}\platforms"; Components: core;
Source: "{#MyQtSdkBinDir}..\plugins\accessible\qtaccessiblewidgets.dll"; DestDir: "{#APP_BIN}\plugins\accessible"; Components: core;
Source: "{#MyQtSdkBinDir}..\plugins\imageformats\*.dll"; DestDir: "{#APP_BIN}\plugins\imageformats"; Components: core;
Source: "{#MyQtSdkBinDir}..\plugins\printsupport\windowsprintersupport.dll"; DestDir: "{#APP_BIN}\plugins\printsupport"; Components: core;

Source: {#MyLibSndfileSourceDir}\bin\*.*; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: {#MyLibSndfileSourceDir}\include\*.*; DestDir: "{#APP_INCLUDE}\sndfile"; Flags: ignoreversion; Components: core;

; Ignore the unspeakably stupid libtool crap.
Source: {#MyPortAudioSourceDir}\lib\.libs\*.dll; DestDir: "{#APP_BIN}"; Components: core
Source: {#MyPortAudioSourceDir}\bin\.libs\pa_devs.exe; DestDir: "{#APP_BIN}"; Components: core
Source: {#MyPortAudioSourceDir}\bin\.libs\pa_minlat.exe; DestDir: "{#APP_BIN}"; Components: core
; Required by pre-built portaudio_x86.dll (built with MSVC).
Source: "C:\Windows\system32\msvcr110.dll"; DestDir: "{#APP_BIN}"; Components: core

Source: {#MyPortMidiSourceDir}*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core

Source: {#MyLuaJitSourceDir}luajit.exe; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

Source: {#MyLuaJitSourceDir}lua51.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core
; No idea why this other name is needed.
Source: {#MyLuaJitSourceDir}lua51.dll; DestDir: "{#APP_BIN}"; DestName: "lua5.1.dll"; Flags: ignoreversion;  Components: core
Source: {#MyLuaJitSourceDir}lua51.dll; DestDir: "{#APP_BIN}"; DestName: "luajit.dll"; Flags: ignoreversion;  Components: core
Source: {#MyLuaJitSourceDir}lua*.h; DestDir: "{#APP_INCLUDE}\luajit"; Flags: ignoreversion;  Components: core

Source: {#MyFltkSourceDir}*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

Source: {#MyLibMusicXmlSourceDir}\cmake\*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

; Source: {#MyFluidSynthSourceDir}*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

; Source: {#MyLibLoSourceDir}*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

Source: include/*.h*; DestDir: "{#APP_INCLUDE}\csound"; Flags: ignoreversion;  Components: core
Source: interfaces/*.h*; DestDir: "{#APP_INCLUDE}\csound"; Flags: ignoreversion;  Components: core
Source: frontends/CsoundAC/*.hpp; DestDir: "{#APP_INCLUDE}\csoundac"; Flags: ignoreversion;  Components: core

Source: {#MyManualSourceDir}html\*.*; DestDir: "{#APP_MANUAL}"; Flags: ignoreversion recursesubdirs;  Components: core

Source: "doc\csound\html\*.*"; DestDir: "{#APP_APIREF}/csound"; Flags: ignoreversion recursesubdirs;  Components: core
Source: "doc\csoundac\html\*.*"; DestDir: "{#APP_APIREF}/csoundac"; Flags: ignoreversion recursesubdirs;  Components: core

Source: "examples\*.*"; DestDir: "{#APP_EXAMPLES}"; Excludes: "*.wav"; Flags: ignoreversion recursesubdirs;  Components: core

Source: "samples\*.*"; DestDir: "{#APP_SAMPLES}"; Flags: ignoreversion recursesubdirs;  Components: core
Source: "{#MyLibStkSourceDir}rawwaves\*.*"; DestDir: "{#APP_SAMPLES}"; Flags: ignoreversion recursesubdirs;  Components: core

Source: {#MyCsoundTutorialSourceDir}tutorial.pdf; DestDir: "{#APP_TUTORIAL}"; Flags: ignoreversion recursesubdirs;  Components: core
Source: {#MyCsoundAcTutorialSourceDir}Csound_Algorithmic_Composition_Tutorial.pdf; DestDir: "{#APP_TUTORIAL}"; Flags: ignoreversion recursesubdirs;  Components: core
Source: {#MyCsoundAcTutorialSourceDir}code\*.*; DestDir: "{#APP_TUTORIAL}code\"; Excludes: "*.wav"; Flags: ignoreversion recursesubdirs;  Components: core

[Icons]
Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}";  Components: core;
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{group}\Csound"; Filename: "cmd.exe"; Parameters: "/K csound.exe"; WorkingDir: "{#APP_BIN}"; Flags: dontcloseonexit;  Components: core
Name: "{group}\CsoundQt"; Filename: "{#APP_BIN}CsoundQt-d-cs6.exe"; WorkingDir: "{#APP_BIN}";  Components: core
#ifdef CSOUNDVST
Name: "{group}\Cabbage"; Filename: "{#APP_BIN}cabbage.exe"; WorkingDir: "{#APP_BIN}";  Components: csoundvst
#endif
Name: "{group}\LuaJIT"; Filename: "{#APP_BIN}luajit.exe"; WorkingDir: "{#APP_BIN}";  Components: core
Name: "{group}\Audio device information"; Filename: "cmd"; Parameters: "/K pa_devs.exe"; WorkingDir: "{#APP_BIN}"; Flags: dontcloseonexit;  Components: core
Name: "{group}\Audio device latency"; Filename: "cmd"; Parameters: "/K pa_minlat.exe"; WorkingDir: "{#APP_BIN}"; Flags: dontcloseonexit;  Components: core
Name: "{group}\Csound Reference Manual"; Filename: "{#APP_MANUAL}indexframes.html";  Components: core
Name: "{group}\Csound API Reference Manual"; Filename: "{#APP_APIREF}csound\index.html";  Components: core
Name: "{group}\CsoundAC API Reference Manual"; Filename: "{#APP_APIREF}csoundac\index.html";  Components: core
Name: "{group}\Csound Tutorial"; Filename: "{#APP_TUTORIAL}tutorial.pdf";  Components: core
Name: "{group}\CsoundAC Tutorial"; Filename: "{#APP_TUTORIAL}Csound_Algorithmic_Composition_Tutorial.pdf";  Components: core

[Registry]
Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName:"RAWWAVE_PATH"; ValueData:"{#APP_SAMPLES}"; Flags: preservestringtype uninsdeletekey;  Components: core
Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName:"OPCODE6DIR64"; ValueData:"{#APP_PLUGINS64}"; Flags: preservestringtype uninsdeletekey;  Components: core
Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName:"PYTHONPATH"; ValueData:"{#APP_BIN}"; Flags: preservestringtype uninsdeletekey;  Components: python

[Tasks]
Name: modifypath; Description: &Add application directory to your PATH environment variable; Components: core;

[Code]
//	ModPathName defines the name of the task defined above
//	ModPathType defines whether the 'user' or 'system' path will be modified;
//		this will default to user if anything other than system is set
//	setArrayLength must specify the total number of dirs to be added
//	Result[0] contains first directory, Result[1] contains second, etc.
const ModPathName = 'modifypath';
ModPathType = 'system';

function ModPathDir(): TArrayOfString;
begin
	setArrayLength(Result, 1);
	Result[0] := ExpandConstant('{app}\bin');
end;

procedure ModPath();
var
	oldpath:	String;
	newpath:	String;
	updatepath:	Boolean;
	pathArr:	TArrayOfString;
	aExecFile:	String;
	aExecArr:	TArrayOfString;
	i, d:		Integer;
	pathdir:	TArrayOfString;
	regroot:	Integer;
	regpath:	String;
begin
	// Get constants from main script and adjust behavior accordingly
	// ModPathType MUST be 'system' or 'user'; force 'user' if invalid
	if ModPathType = 'system' then begin
		regroot := HKEY_LOCAL_MACHINE;
		regpath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
	end else begin
		regroot := HKEY_CURRENT_USER;
		regpath := 'Environment';
	end;

	// Get array of new directories and act on each individually
	pathdir := ModPathDir();
	for d := 0 to GetArrayLength(pathdir)-1 do begin
		updatepath := true;

		// Modify WinNT path
		if UsingWinNT() = true then begin

			// Get current path, split into an array
			RegQueryStringValue(regroot, regpath, 'Path', oldpath);
			oldpath := oldpath + ';';
			i := 0;

			while (Pos(';', oldpath) > 0) do begin
				SetArrayLength(pathArr, i+1);
				pathArr[i] := Copy(oldpath, 0, Pos(';', oldpath)-1);
				oldpath := Copy(oldpath, Pos(';', oldpath)+1, Length(oldpath));
				i := i + 1;

				// Check if current directory matches app dir
				if pathdir[d] = pathArr[i-1] then begin
					// if uninstalling, remove dir from path
					if IsUninstaller() = true then begin
						continue;
					// if installing, flag that dir already exists in path
					end else begin
						updatepath := false;
					end;
				end;

				// Add current directory to new path
				if i = 1 then begin
					newpath := pathArr[i-1];
				end else begin
					newpath := newpath + ';' + pathArr[i-1];
				end;
			end;

			// Append app dir to path if not already included
			if (IsUninstaller() = false) AND (updatepath = true) then
				newpath := newpath + ';' + pathdir[d];

			// Write new path
			RegWriteStringValue(regroot, regpath, 'Path', newpath);

		// Modify Win9x path
		end else begin

			// Convert to shortened dirname
			pathdir[d] := GetShortName(pathdir[d]);

			// If autoexec.bat exists, check if app dir already exists in path
			aExecFile := 'C:\AUTOEXEC.BAT';
			if FileExists(aExecFile) then begin
				LoadStringsFromFile(aExecFile, aExecArr);
				for i := 0 to GetArrayLength(aExecArr)-1 do begin
					if IsUninstaller() = false then begin
						// If app dir already exists while installing, skip add
						if (Pos(pathdir[d], aExecArr[i]) > 0) then
							updatepath := false;
							break;
					end else begin
						// If app dir exists and = what we originally set, then delete at uninstall
						if aExecArr[i] = 'SET PATH=%PATH%;' + pathdir[d] then
							aExecArr[i] := '';
					end;
				end;
			end;

			// If app dir not found, or autoexec.bat didn't exist, then (create and) append to current path
			if (IsUninstaller() = false) AND (updatepath = true) then begin
				SaveStringToFile(aExecFile, #13#10 + 'SET PATH=%PATH%;' + pathdir[d], True);

			// If uninstalling, write the full autoexec out
			end else begin
				SaveStringsToFile(aExecFile, aExecArr, False);
			end;
		end;
	end;
end;

// Split a string into an array using passed delimeter
procedure MPExplode(var Dest: TArrayOfString; Text: String; Separator: String);
var
	i: Integer;
begin
	i := 0;
	repeat
		SetArrayLength(Dest, i+1);
		if Pos(Separator,Text) > 0 then	begin
			Dest[i] := Copy(Text, 1, Pos(Separator, Text)-1);
			Text := Copy(Text, Pos(Separator,Text) + Length(Separator), Length(Text));
			i := i + 1;
		end else begin
			 Dest[i] := Text;
			 Text := '';
		end;
	until Length(Text)=0;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
	taskname:	String;
begin
	taskname := ModPathName;
	if CurStep = ssPostInstall then
		if IsTaskSelected(taskname) then
			ModPath();
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
	aSelectedTasks:	TArrayOfString;
	i:				Integer;
	taskname:		String;
	regpath:		String;
	regstring:		String;
	appid:			String;
begin
	// only run during actual uninstall
	if CurUninstallStep = usUninstall then begin
		// get list of selected tasks saved in registry at install time
		appid := '{#emit SetupSetting("AppId")}';
		if appid = '' then appid := '{#emit SetupSetting("AppName")}';
		regpath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\'+appid+'_is1');
		RegQueryStringValue(HKLM, regpath, 'Inno Setup: Selected Tasks', regstring);
		if regstring = '' then RegQueryStringValue(HKCU, regpath, 'Inno Setup: Selected Tasks', regstring);

		// check each task; if matches modpath taskname, trigger patch removal
		if regstring <> '' then begin
			taskname := ModPathName;
			MPExplode(aSelectedTasks, regstring, ',');
			if GetArrayLength(aSelectedTasks) > 0 then begin
				for i := 0 to GetArrayLength(aSelectedTasks)-1 do begin
					if comparetext(aSelectedTasks[i], taskname) = 0 then
						ModPath();
				end;
			end;
		end;
	end;
end;

function NeedRestart(): Boolean;
var
	taskname:	String;
begin
	taskname := ModPathName;
	if IsTaskSelected(taskname) and not UsingWinNT() then begin
		Result := True;
	end else begin
		Result := False;
	end;
end;

  ; C S O U N D   6   I N N O   S E T U P   S C R I P T

; Copyright (C) 2013 by Michael Gogins.
; This software is licensed under the terms of the
; Lesser (or Library) GNU Public License.

; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

; This file assumes that you are Michael Gogins,
; but comments indicate what you should change if you are not me.
; All of these changes SHOULD be in the #defines immediately following this.
; Also, this installer assumes LuaJIT and CsoundAC are part of Csound core.
; At this time the following features are not included in the installer:
; Cabbage, faustgen.
; I hope to change this soon.

; Uncomment the following line to build for CsoundVST.
#define CSOUNDVST

#define MyAppName "Csound6"
#define MyAppVersion "6"
#ifdef CSOUNDVST
#define MyAppMinVersion "6.02.0-vst"
#else
#define MyAppMinVersion "6.02.0"
#endif
#define MyAppPublisher "Csound"
#define MyAppURL "http://sourceforge.net/projects/csound"
; If you are not Michael Gogins, change this to your MinGW dll directory.
#define MyMinGwBinDir "D:\mingw32-4.8.1\bin\"
; If you are not Michael Gogins, change this to your MSys /bin/ directory.
#define MyMSysBinDir "D:\msys\bin\"
; If you are not Michael Gogins, change this to your MSys /usr/local/ directory.
#define MyMSysUsrLocalDir "D:\msys\local\"
; If you are not Michael Gogins, change this to your Csound build directory.
#define MySourceDir "C:\Users\mike\csound-csound6-git\"
; If you are not Michael Gogins, change this to your Csound reference manual build directory.
#define MyManualSourceDir "C:\Users\mike\csound-manual6-git\"
; If you are not Michael Gogins, change this to your Csound tutorial directory.
#define MyCsoundTutorialSourceDir "D:\Dropbox\tutorial\"
; If you are not Michael Gogins, change this to your CsoundAC tutorial directory.
#define MyCsoundAcTutorialSourceDir "D:\Dropbox\tutorial\"
; If you are not Michael Gogins, change this to your libsndfile install directory.
#define MyLibSndfileSourceDir "D:\msys\local\opt\libsndfile\"
; If you are not Michael Gogins, change this to your PortAudio lib directory.
#define MyPortAudioSourceDir "D:\msys\local\src\portaudio\"
; If you are not Michael Gogins, change this to your PortMidi build directory.
#define MyPortMidiSourceDir "D:\msys\local\src\portmidi\"
; If you are not Michael Gogins, change this to your LuaJIT bin directory.
#define MyLuaJitSourceDir "D:\msys\local\src\luajit-2.0\src\"
; If you are not Michael Gogins, change this to your FLTK dll directory.
#define MyFltkSourceDir "D:\msys\local\src\fltk-1.3.2\src\"
; If you are not Michael Gogins, change this to your libmusicxml dll directory.
#define MyLibMusicXmlSourceDir "D:\msys\local\src\libmusicxml\"
; If you are not Michael Gogins, change this to your FluidSynth dll directory.
#define MyFluidSynthSourceDir "D:\msys\local\src\fluidsynth\src\.libs\"
; If you are not Michael Gogins, change this to your OSC library dll directory.
#define MyLibLoSourceDir "D:\msys\local\src\liblo-0.26\"
; If you are not Michael Gogins, change this to your STK dll directory.
#define MyLibStkSourceDir "D:\msys\local\src\stk-4.4.4\"
; If you are not Michael Gogins, change this to your CsoundQt bin directory.
#define MyCsoundQtBinDir "C:\Users\mike\qutecsound-code\bin\"
; If you are not Michael Gogins, change this to your Qt SDK DLL directory.
#define MyQtSdkBinDir "D:\qt-everywhere-opensource-src-5.1.1\qtbase\bin\"

[Components]
Name: "core"; Description: "Core Csound"; Types: full custom; Flags: fixed
Name: "python"; Description: "Python features (requires Python 2.7)"; Types: full;
Name: "pd"; Description: "Pure Data csound~ object (requires Pure Data)"; Types: full;
#ifdef CSOUNDVST
Name: "csoundvst"; Description: "Csound VST plugin and vst4cs opcodes"; Types: full;
#endif

[Dirs]
; ALL programs and shared libraries (except opcodes and other Csound modules) go here.
Name: "{app}\bin"; Permissions: users-modify
#define APP_BIN "{app}\bin\"
; ALL Csound opcodes and other Csound modules go here.
Name: "{app}\plugins64"; Permissions: users-modify
#define APP_PLUGINS64 "{app}\plugins64\"
; All C or C++ include files for Csound and other components used by Csound go here.
; This is a convenience for people like me who program in C or C++ and might use
; features of these third party components.
Name: "{app}\include"
#define APP_INCLUDE "{app}\include\"
; The Csound reference manual goes here.
Name: "{app}\doc\manual"
#define APP_MANUAL "{app}\doc\manual\"
; The Csound API reference manuals go here.
Name: "{app}\doc\apiref"
#define APP_APIREF "{app}\doc\apiref\"
; All Csound examples go here.
Name: "{app}\examples"; Permissions: users-modify
#define APP_EXAMPLES "{app}\examples\"
; Any SoundFonts or sound samples used by Csound examples go here.
Name: "{app}\samples"
#define APP_SAMPLES "{app}\samples\"
; Tutorials go here.
Name: "{app}\doc\tutorial"
#define APP_TUTORIAL "{app}\doc\tutorial\"

; These are the Csound environment variables related to directories.

#define SFDIR
#define SSDIR
#define SADIR
#define INCDIR
#define OPCODE6DIR64
#define SNAPDIR
#define RAWWAVE_PATH
#define MFDIR
#define PYTHONPATH

[Setup]
ChangesEnvironment=true
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{4BCEDC5F-C6D2-4869-9097-3F1753739F35}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppMinVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName=Csound 6
AllowNoIcons=yes
LicenseFile=readme-csound6.txt
;InfoBeforeFile=readme-csound6.txt
OutputDir=installer\windows
OutputBaseFilename=Setup_{#MyAppName}_{#MyAppMinVersion}
Compression=lzma
SolidCompression=yes
SourceDir={#MySourceDir}

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Files]
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
Source: "{#MyMinGwBinDir}*.dll"; DestDir: "{#APP_BIN}"; Components: core;
; No idea why this other name is needed.
Source: "{#MyMSysBinDir}libiconv-2.dll"; DestDir: "{#APP_BIN}"; DestName: "iconv.dll"; Components: core;
Source: "{#MyMSysUsrLocalDir}bin/*.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "csound64.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "csnd6.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "*.exe"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "*.jar"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "*.pyd"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: python;
Source: "*.py";  DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: python;
Source: "csound6~.dll";  DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: pd;
Source: "CsoundAC.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
#ifdef CSOUNDVST
Source: "CsoundVST.dll";  DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: csoundvst;
#endif
Source: "luaCsnd6.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "luaCsoundAC.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: "_jcsound6.dll"; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;

Source: "LuaCsound.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "ampmidid.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "cellular.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "chua.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "cs_date.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "csladspa.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "doppler.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "fareygen.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "fluidOpcodes.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "fractalnoise.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "image.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "ipmidi.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "linear_algebra.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "mixer.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "osc.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "platerev.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "pmidi.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "rtpa.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "rtwinmm.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "scansyn.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "serial.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "signalflowgraph.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "stdutil.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "stk.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "system_call.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
Source: "virtual.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;
#ifdef CSOUNDVST
Source: "vst4cs.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: csoundvst;
#endif
Source: "widgets.dll"; DestDir: "{#APP_PLUGINS64}"; Flags: ignoreversion; Components: core;

Source: "{#MyCsoundQtBinDir}CsoundQt-d-cs6.exe"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5PrintSupport.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5Widgets.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5Xml.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5Gui.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}Qt5Core.dll"; DestDir: "{#APP_BIN}"; Components: core;
Source: "{#MyQtSdkBinDir}..\plugins\platforms\qwindows.dll"; DestDir: "{#APP_BIN}\platforms"; Components: core;
Source: "{#MyQtSdkBinDir}..\plugins\accessible\qtaccessiblewidgets.dll"; DestDir: "{#APP_BIN}\plugins\accessible"; Components: core;
Source: "{#MyQtSdkBinDir}..\plugins\imageformats\*.dll"; DestDir: "{#APP_BIN}\plugins\imageformats"; Components: core;
Source: "{#MyQtSdkBinDir}..\plugins\printsupport\windowsprintersupport.dll"; DestDir: "{#APP_BIN}\plugins\printsupport"; Components: core;

Source: {#MyLibSndfileSourceDir}\bin\*.*; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core;
Source: {#MyLibSndfileSourceDir}\include\*.*; DestDir: "{#APP_INCLUDE}\sndfile"; Flags: ignoreversion; Components: core;

; Ignore the unspeakably stupid libtool crap.
Source: {#MyPortAudioSourceDir}\lib\.libs\*.dll; DestDir: "{#APP_BIN}"; Components: core
Source: {#MyPortAudioSourceDir}\bin\.libs\pa_devs.exe; DestDir: "{#APP_BIN}"; Components: core
Source: {#MyPortAudioSourceDir}\bin\.libs\pa_minlat.exe; DestDir: "{#APP_BIN}"; Components: core
; Required by pre-built portaudio_x86.dll (built with MSVC).
Source: "C:\Windows\system32\msvcr110.dll"; DestDir: "{#APP_BIN}"; Components: core

Source: {#MyPortMidiSourceDir}*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion; Components: core

Source: {#MyLuaJitSourceDir}luajit.exe; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

Source: {#MyLuaJitSourceDir}lua51.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core
; No idea why this other name is needed.
Source: {#MyLuaJitSourceDir}lua51.dll; DestDir: "{#APP_BIN}"; DestName: "lua5.1.dll"; Flags: ignoreversion;  Components: core
Source: {#MyLuaJitSourceDir}lua51.dll; DestDir: "{#APP_BIN}"; DestName: "luajit.dll"; Flags: ignoreversion;  Components: core
Source: {#MyLuaJitSourceDir}lua*.h; DestDir: "{#APP_INCLUDE}\luajit"; Flags: ignoreversion;  Components: core

Source: {#MyFltkSourceDir}*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

Source: {#MyLibMusicXmlSourceDir}\cmake\*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

; Source: {#MyFluidSynthSourceDir}*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

; Source: {#MyLibLoSourceDir}*.dll; DestDir: "{#APP_BIN}"; Flags: ignoreversion;  Components: core

Source: include/*.h*; DestDir: "{#APP_INCLUDE}\csound"; Flags: ignoreversion;  Components: core
Source: interfaces/*.h*; DestDir: "{#APP_INCLUDE}\csound"; Flags: ignoreversion;  Components: core
Source: frontends/CsoundAC/*.hpp; DestDir: "{#APP_INCLUDE}\csoundac"; Flags: ignoreversion;  Components: core

Source: {#MyManualSourceDir}html\*.*; DestDir: "{#APP_MANUAL}"; Flags: ignoreversion recursesubdirs;  Components: core

Source: "doc\csound\html\*.*"; DestDir: "{#APP_APIREF}/csound"; Flags: ignoreversion recursesubdirs;  Components: core
Source: "doc\csoundac\html\*.*"; DestDir: "{#APP_APIREF}/csoundac"; Flags: ignoreversion recursesubdirs;  Components: core

Source: "examples\*.*"; DestDir: "{#APP_EXAMPLES}"; Excludes: "*.wav"; Flags: ignoreversion recursesubdirs;  Components: core

Source: "samples\*.*"; DestDir: "{#APP_SAMPLES}"; Flags: ignoreversion recursesubdirs;  Components: core
Source: "{#MyLibStkSourceDir}rawwaves\*.*"; DestDir: "{#APP_SAMPLES}"; Flags: ignoreversion recursesubdirs;  Components: core

Source: {#MyCsoundTutorialSourceDir}tutorial.pdf; DestDir: "{#APP_TUTORIAL}"; Flags: ignoreversion recursesubdirs;  Components: core
Source: {#MyCsoundAcTutorialSourceDir}Csound_Algorithmic_Composition_Tutorial.pdf; DestDir: "{#APP_TUTORIAL}"; Flags: ignoreversion recursesubdirs;  Components: core
Source: {#MyCsoundAcTutorialSourceDir}code\*.*; DestDir: "{#APP_TUTORIAL}code\"; Excludes: "*.wav"; Flags: ignoreversion recursesubdirs;  Components: core

[Icons]
Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}";  Components: core;
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{group}\Csound"; Filename: "cmd.exe"; Parameters: "/K csound.exe"; WorkingDir: "{#APP_BIN}"; Flags: dontcloseonexit;  Components: core
Name: "{group}\CsoundQt"; Filename: "{#APP_BIN}CsoundQt-d-cs6.exe"; WorkingDir: "{#APP_BIN}";  Components: core
Name: "{group}\WinSound"; Filename: "{#APP_BIN}winsound.exe"; WorkingDir: "{#APP_BIN}";  Components: core
Name: "{group}\LuaJIT"; Filename: "{#APP_BIN}luajit.exe"; WorkingDir: "{#APP_BIN}";  Components: core
Name: "{group}\Audio device information"; Filename: "cmd"; Parameters: "/K pa_devs.exe"; WorkingDir: "{#APP_BIN}"; Flags: dontcloseonexit;  Components: core
Name: "{group}\Audio device latency"; Filename: "cmd"; Parameters: "/K pa_minlat.exe"; WorkingDir: "{#APP_BIN}"; Flags: dontcloseonexit;  Components: core
Name: "{group}\Csound Reference Manual"; Filename: "{#APP_MANUAL}indexframes.html";  Components: core
Name: "{group}\Csound API Reference Manual"; Filename: "{#APP_APIREF}csound\index.html";  Components: core
Name: "{group}\CsoundAC API Reference Manual"; Filename: "{#APP_APIREF}csoundac\index.html";  Components: core
Name: "{group}\Csound Tutorial"; Filename: "{#APP_TUTORIAL}tutorial.pdf";  Components: core
Name: "{group}\CsoundAC Tutorial"; Filename: "{#APP_TUTORIAL}Csound_Algorithmic_Composition_Tutorial.pdf";  Components: core

[Registry]
Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName:"RAWWAVE_PATH"; ValueData:"{#APP_SAMPLES}"; Flags: preservestringtype uninsdeletekey;  Components: core
Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName:"OPCODE6DIR64"; ValueData:"{#APP_PLUGINS64}"; Flags: preservestringtype uninsdeletekey;  Components: core
Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName:"PYTHONPATH"; ValueData:"{#APP_BIN}"; Flags: preservestringtype uninsdeletekey;  Components: python

[Tasks]
Name: modifypath; Description: &Add application directory to your PATH environment variable; Components: core;

[Code]
//	ModPathName defines the name of the task defined above
//	ModPathType defines whether the 'user' or 'system' path will be modified;
//		this will default to user if anything other than system is set
//	setArrayLength must specify the total number of dirs to be added
//	Result[0] contains first directory, Result[1] contains second, etc.
const ModPathName = 'modifypath';
ModPathType = 'system';

function ModPathDir(): TArrayOfString;
begin
	setArrayLength(Result, 1);
	Result[0] := ExpandConstant('{app}\bin');
end;

procedure ModPath();
var
	oldpath:	String;
	newpath:	String;
	updatepath:	Boolean;
	pathArr:	TArrayOfString;
	aExecFile:	String;
	aExecArr:	TArrayOfString;
	i, d:		Integer;
	pathdir:	TArrayOfString;
	regroot:	Integer;
	regpath:	String;
begin
	// Get constants from main script and adjust behavior accordingly
	// ModPathType MUST be 'system' or 'user'; force 'user' if invalid
	if ModPathType = 'system' then begin
		regroot := HKEY_LOCAL_MACHINE;
		regpath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
	end else begin
		regroot := HKEY_CURRENT_USER;
		regpath := 'Environment';
	end;

	// Get array of new directories and act on each individually
	pathdir := ModPathDir();
	for d := 0 to GetArrayLength(pathdir)-1 do begin
		updatepath := true;

		// Modify WinNT path
		if UsingWinNT() = true then begin

			// Get current path, split into an array
			RegQueryStringValue(regroot, regpath, 'Path', oldpath);
			oldpath := oldpath + ';';
			i := 0;

			while (Pos(';', oldpath) > 0) do begin
				SetArrayLength(pathArr, i+1);
				pathArr[i] := Copy(oldpath, 0, Pos(';', oldpath)-1);
				oldpath := Copy(oldpath, Pos(';', oldpath)+1, Length(oldpath));
				i := i + 1;

				// Check if current directory matches app dir
				if pathdir[d] = pathArr[i-1] then begin
					// if uninstalling, remove dir from path
					if IsUninstaller() = true then begin
						continue;
					// if installing, flag that dir already exists in path
					end else begin
						updatepath := false;
					end;
				end;

				// Add current directory to new path
				if i = 1 then begin
					newpath := pathArr[i-1];
				end else begin
					newpath := newpath + ';' + pathArr[i-1];
				end;
			end;

			// Append app dir to path if not already included
			if (IsUninstaller() = false) AND (updatepath = true) then
				newpath := newpath + ';' + pathdir[d];

			// Write new path
			RegWriteStringValue(regroot, regpath, 'Path', newpath);

		// Modify Win9x path
		end else begin

			// Convert to shortened dirname
			pathdir[d] := GetShortName(pathdir[d]);

			// If autoexec.bat exists, check if app dir already exists in path
			aExecFile := 'C:\AUTOEXEC.BAT';
			if FileExists(aExecFile) then begin
				LoadStringsFromFile(aExecFile, aExecArr);
				for i := 0 to GetArrayLength(aExecArr)-1 do begin
					if IsUninstaller() = false then begin
						// If app dir already exists while installing, skip add
						if (Pos(pathdir[d], aExecArr[i]) > 0) then
							updatepath := false;
							break;
					end else begin
						// If app dir exists and = what we originally set, then delete at uninstall
						if aExecArr[i] = 'SET PATH=%PATH%;' + pathdir[d] then
							aExecArr[i] := '';
					end;
				end;
			end;

			// If app dir not found, or autoexec.bat didn't exist, then (create and) append to current path
			if (IsUninstaller() = false) AND (updatepath = true) then begin
				SaveStringToFile(aExecFile, #13#10 + 'SET PATH=%PATH%;' + pathdir[d], True);

			// If uninstalling, write the full autoexec out
			end else begin
				SaveStringsToFile(aExecFile, aExecArr, False);
			end;
		end;
	end;
end;

// Split a string into an array using passed delimeter
procedure MPExplode(var Dest: TArrayOfString; Text: String; Separator: String);
var
	i: Integer;
begin
	i := 0;
	repeat
		SetArrayLength(Dest, i+1);
		if Pos(Separator,Text) > 0 then	begin
			Dest[i] := Copy(Text, 1, Pos(Separator, Text)-1);
			Text := Copy(Text, Pos(Separator,Text) + Length(Separator), Length(Text));
			i := i + 1;
		end else begin
			 Dest[i] := Text;
			 Text := '';
		end;
	until Length(Text)=0;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
	taskname:	String;
begin
	taskname := ModPathName;
	if CurStep = ssPostInstall then
		if IsTaskSelected(taskname) then
			ModPath();
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
	aSelectedTasks:	TArrayOfString;
	i:				Integer;
	taskname:		String;
	regpath:		String;
	regstring:		String;
	appid:			String;
begin
	// only run during actual uninstall
	if CurUninstallStep = usUninstall then begin
		// get list of selected tasks saved in registry at install time
		appid := '{#emit SetupSetting("AppId")}';
		if appid = '' then appid := '{#emit SetupSetting("AppName")}';
		regpath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\'+appid+'_is1');
		RegQueryStringValue(HKLM, regpath, 'Inno Setup: Selected Tasks', regstring);
		if regstring = '' then RegQueryStringValue(HKCU, regpath, 'Inno Setup: Selected Tasks', regstring);

		// check each task; if matches modpath taskname, trigger patch removal
		if regstring <> '' then begin
			taskname := ModPathName;
			MPExplode(aSelectedTasks, regstring, ',');
			if GetArrayLength(aSelectedTasks) > 0 then begin
				for i := 0 to GetArrayLength(aSelectedTasks)-1 do begin
					if comparetext(aSelectedTasks[i], taskname) = 0 then
						ModPath();
				end;
			end;
		end;
	end;
end;

function NeedRestart(): Boolean;
var
	taskname:	String;
begin
	taskname := ModPathName;
	if IsTaskSelected(taskname) and not UsingWinNT() then begin
		Result := True;
	end else begin
		Result := False;
	end;
end;