1/* in_flac - Winamp2 FLAC input plugin
2 * Copyright (C) 2002,2003,2004,2005,2006,2007  Josh Coalson
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#if HAVE_CONFIG_H
20#  include <config.h>
21#endif
22
23#include <windows.h>
24#include <commctrl.h>
25#include <stdio.h>
26#include "configure.h"
27#include "tagz.h"
28#include "resource.h"
29#include "share/alloc.h"
30
31
32static char buffer[256];
33static char ini_name[MAX_PATH];
34
35/*
36 *  Read/write config
37 */
38
39#define RI(x, def)          (x = GetPrivateProfileInt("FLAC", #x, def, ini_name))
40#define WI(x)               WritePrivateProfileString("FLAC", #x, itoa(x, buffer, 10), ini_name)
41#define RS(x, n, def)       GetPrivateProfileString("FLAC", #x, def, x, n, ini_name)
42#define WS(x)               WritePrivateProfileString("FLAC", #x, x, ini_name)
43
44static const char default_format[] = "[%artist% - ]$if2(%title%,%filename%)";
45static const char default_sep[] = ", ";
46
47static wchar_t *convert_ansi_to_wide_(const char *src)
48{
49	int len;
50	wchar_t *dest;
51
52	FLAC__ASSERT(0 != src);
53
54	len = strlen(src) + 1;
55	/* copy */
56	dest = safe_malloc_mul_2op_(len, /*times*/sizeof(wchar_t));
57	if (dest) mbstowcs(dest, src, len);
58	return dest;
59}
60
61void InitConfig()
62{
63	char *p;
64
65	GetModuleFileName(NULL, ini_name, sizeof(ini_name));
66	p = strrchr(ini_name, '.');
67	if (!p) p = ini_name + strlen(ini_name);
68	strcpy(p, ".ini");
69
70	flac_cfg.title.tag_format_w = NULL;
71}
72
73void ReadConfig()
74{
75	RS(flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format), default_format);
76	if (flac_cfg.title.tag_format_w)
77		free(flac_cfg.title.tag_format_w);
78	flac_cfg.title.tag_format_w = convert_ansi_to_wide_(flac_cfg.title.tag_format);
79	/* @@@ FIXME: trailing spaces */
80	RS(flac_cfg.title.sep, sizeof(flac_cfg.title.sep), default_sep);
81	RI(flac_cfg.tag.reserve_space, 1);
82
83	RI(flac_cfg.display.show_bps, 1);
84	RI(flac_cfg.output.misc.stop_err, 0);
85	RI(flac_cfg.output.replaygain.enable, 1);
86	RI(flac_cfg.output.replaygain.album_mode, 0);
87	RI(flac_cfg.output.replaygain.hard_limit, 0);
88	RI(flac_cfg.output.replaygain.preamp, 0);
89	RI(flac_cfg.output.resolution.normal.dither_24_to_16, 0);
90	RI(flac_cfg.output.resolution.replaygain.dither, 0);
91	RI(flac_cfg.output.resolution.replaygain.noise_shaping, 1);
92	RI(flac_cfg.output.resolution.replaygain.bps_out, 16);
93}
94
95void WriteConfig()
96{
97	WS(flac_cfg.title.tag_format);
98	WI(flac_cfg.tag.reserve_space);
99	WS(flac_cfg.title.sep);
100
101	WI(flac_cfg.display.show_bps);
102	WI(flac_cfg.output.misc.stop_err);
103	WI(flac_cfg.output.replaygain.enable);
104	WI(flac_cfg.output.replaygain.album_mode);
105	WI(flac_cfg.output.replaygain.hard_limit);
106	WI(flac_cfg.output.replaygain.preamp);
107	WI(flac_cfg.output.resolution.normal.dither_24_to_16);
108	WI(flac_cfg.output.resolution.replaygain.dither);
109	WI(flac_cfg.output.resolution.replaygain.noise_shaping);
110	WI(flac_cfg.output.resolution.replaygain.bps_out);
111}
112
113/*
114 *  Dialog
115 */
116
117#define PREAMP_RANGE            24
118
119#define Check(x,y)              CheckDlgButton(hwnd, x, y ? BST_CHECKED : BST_UNCHECKED)
120#define GetCheck(x)             (IsDlgButtonChecked(hwnd, x)==BST_CHECKED)
121#define GetSel(x)               SendDlgItemMessage(hwnd, x, CB_GETCURSEL, 0, 0)
122#define GetPos(x)               SendDlgItemMessage(hwnd, x, TBM_GETPOS, 0, 0)
123#define Enable(x,y)             EnableWindow(GetDlgItem(hwnd, x), y)
124
125static INT_PTR CALLBACK GeneralProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
126{
127	switch (msg)
128	{
129	/* init */
130	case WM_INITDIALOG:
131		SendDlgItemMessage(hwnd, IDC_TITLE, EM_LIMITTEXT, 255, 0);
132		SendDlgItemMessage(hwnd, IDC_SEP, EM_LIMITTEXT, 15, 0);
133
134		SetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format);
135		SetDlgItemText(hwnd, IDC_SEP, flac_cfg.title.sep);
136		Check(IDC_ID3V1, 0);
137/*!		Check(IDC_RESERVE, flac_cfg.tag.reserve_space); */
138		Check(IDC_BPS, flac_cfg.display.show_bps);
139		Check(IDC_ERRORS, flac_cfg.output.misc.stop_err);
140		return TRUE;
141	/* commands */
142	case WM_COMMAND:
143		switch (LOWORD(wParam))
144		{
145		/* ok */
146		case IDOK:
147			GetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format));
148			if (flac_cfg.title.tag_format_w)
149				free(flac_cfg.title.tag_format_w);
150			GetDlgItemText(hwnd, IDC_SEP, flac_cfg.title.sep, sizeof(flac_cfg.title.sep));
151			flac_cfg.title.tag_format_w = convert_ansi_to_wide_(flac_cfg.title.tag_format);
152
153/*!			flac_cfg.tag.reserve_space = GetCheck(IDC_RESERVE); */
154			flac_cfg.display.show_bps = GetCheck(IDC_BPS);
155			flac_cfg.output.misc.stop_err = GetCheck(IDC_ERRORS);
156			break;
157		/* reset */
158		case IDC_RESET:
159			Check(IDC_ID3V1, 0);
160			Check(IDC_RESERVE, 1);
161			Check(IDC_BPS, 1);
162			Check(IDC_ERRORS, 0);
163			/* fall throught */
164		/* default */
165		case IDC_TAGZ_DEFAULT:
166			SetDlgItemText(hwnd, IDC_TITLE, default_format);
167			break;
168		/* help */
169		case IDC_TAGZ_HELP:
170			MessageBox(hwnd, tagz_manual, "Help", 0);
171			break;
172		}
173		break;
174	}
175
176	return 0;
177}
178
179
180static void UpdatePreamp(HWND hwnd, HWND hamp)
181{
182	int pos = SendMessage(hamp, TBM_GETPOS, 0, 0) - PREAMP_RANGE;
183	sprintf(buffer, "%d dB", pos);
184	SetDlgItemText(hwnd, IDC_PA, buffer);
185}
186
187static void UpdateRG(HWND hwnd)
188{
189	int on = GetCheck(IDC_ENABLE);
190	Enable(IDC_ALBUM, on);
191	Enable(IDC_LIMITER, on);
192	Enable(IDC_PREAMP, on);
193	Enable(IDC_PA, on);
194}
195
196static void UpdateDither(HWND hwnd)
197{
198	int on = GetCheck(IDC_DITHERRG);
199	Enable(IDC_SHAPE, on);
200}
201
202static INT_PTR CALLBACK OutputProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
203{
204	switch (msg)
205	{
206	/* init */
207	case WM_INITDIALOG:
208		Check(IDC_ENABLE, flac_cfg.output.replaygain.enable);
209		Check(IDC_ALBUM, flac_cfg.output.replaygain.album_mode);
210		Check(IDC_LIMITER, flac_cfg.output.replaygain.hard_limit);
211		Check(IDC_DITHER, flac_cfg.output.resolution.normal.dither_24_to_16);
212		Check(IDC_DITHERRG, flac_cfg.output.resolution.replaygain.dither);
213		/* prepare preamp slider */
214		{
215			HWND hamp = GetDlgItem(hwnd, IDC_PREAMP);
216			SendMessage(hamp, TBM_SETRANGE, 1, MAKELONG(0, PREAMP_RANGE*2));
217			SendMessage(hamp, TBM_SETPOS, 1, flac_cfg.output.replaygain.preamp+PREAMP_RANGE);
218			UpdatePreamp(hwnd, hamp);
219		}
220		/* fill comboboxes */
221		{
222			HWND hlist = GetDlgItem(hwnd, IDC_TO);
223			SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"16 bps");
224			SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"24 bps");
225			SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.bps_out/8 - 2, 0);
226
227			hlist = GetDlgItem(hwnd, IDC_SHAPE);
228			SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"None");
229			SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Low");
230			SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Medium");
231			SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"High");
232			SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.noise_shaping, 0);
233		}
234		UpdateRG(hwnd);
235		UpdateDither(hwnd);
236		return TRUE;
237	/* commands */
238	case WM_COMMAND:
239		switch (LOWORD(wParam))
240		{
241		/* ok */
242		case IDOK:
243			flac_cfg.output.replaygain.enable = GetCheck(IDC_ENABLE);
244			flac_cfg.output.replaygain.album_mode = GetCheck(IDC_ALBUM);
245			flac_cfg.output.replaygain.hard_limit = GetCheck(IDC_LIMITER);
246			flac_cfg.output.replaygain.preamp = GetPos(IDC_PREAMP) - PREAMP_RANGE;
247			flac_cfg.output.resolution.normal.dither_24_to_16 = GetCheck(IDC_DITHER);
248			flac_cfg.output.resolution.replaygain.dither = GetCheck(IDC_DITHERRG);
249			flac_cfg.output.resolution.replaygain.noise_shaping = GetSel(IDC_SHAPE);
250			flac_cfg.output.resolution.replaygain.bps_out = (GetSel(IDC_TO)+2)*8;
251			break;
252		/* reset */
253		case IDC_RESET:
254			Check(IDC_ENABLE, 1);
255			Check(IDC_ALBUM, 0);
256			Check(IDC_LIMITER, 0);
257			Check(IDC_DITHER, 0);
258			Check(IDC_DITHERRG, 0);
259
260			SendDlgItemMessage(hwnd, IDC_PREAMP, TBM_SETPOS, 1, PREAMP_RANGE);
261			SendDlgItemMessage(hwnd, IDC_TO, CB_SETCURSEL, 0, 0);
262			SendDlgItemMessage(hwnd, IDC_SHAPE, CB_SETCURSEL, 1, 0);
263
264			UpdatePreamp(hwnd, GetDlgItem(hwnd, IDC_PREAMP));
265			UpdateRG(hwnd);
266			UpdateDither(hwnd);
267			break;
268		/* active check-boxes */
269		case IDC_ENABLE:
270			UpdateRG(hwnd);
271			break;
272		case IDC_DITHERRG:
273			UpdateDither(hwnd);
274			break;
275		}
276		break;
277	/* scroller */
278	case WM_HSCROLL:
279		if (GetDlgCtrlID((HWND)lParam)==IDC_PREAMP)
280			UpdatePreamp(hwnd, (HWND)lParam);
281		return 0;
282	}
283
284	return 0;
285}
286
287#define NUM_PAGES       2
288
289typedef struct
290{
291	HWND htab;
292	HWND hdlg;
293	RECT r;
294	HWND all[NUM_PAGES];
295} LOCALDATA;
296
297static void ScreenToClientRect(HWND hwnd, RECT *rect)
298{
299	POINT pt = { rect->left, rect->top };
300	ScreenToClient(hwnd, &pt);
301	rect->left = pt.x;
302	rect->top  = pt.y;
303
304	pt.x = rect->right;
305	pt.y = rect->bottom;
306	ScreenToClient(hwnd, &pt);
307	rect->right  = pt.x;
308	rect->bottom = pt.y;
309}
310
311static void SendCommand(HWND hwnd, int command)
312{
313	LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
314	SendMessage(data->hdlg, WM_COMMAND, command, 0);
315}
316
317static void BroadcastCommand(HWND hwnd, int command)
318{
319	LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
320	int i;
321
322	for (i=0; i<NUM_PAGES; i++)
323		SendMessage(data->all[i], WM_COMMAND, command, 0);
324}
325
326static void OnSelChange(HWND hwnd)
327{
328	LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
329	int index = TabCtrl_GetCurSel(data->htab);
330	if (index < 0) return;
331	/* hide previous */
332	if (data->hdlg)
333		ShowWindow(data->hdlg, SW_HIDE);
334	/* display */
335	data->hdlg = data->all[index];
336	SetWindowPos(data->hdlg, HWND_TOP, data->r.left, data->r.top, data->r.right-data->r.left, data->r.bottom-data->r.top, SWP_SHOWWINDOW);
337	SetFocus(hwnd);
338}
339
340static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
341{
342	static activePage = 0;
343
344	switch (msg)
345	{
346	/* init */
347	case WM_INITDIALOG:
348		{
349			LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));
350			HINSTANCE inst = (HINSTANCE)lParam;
351			TCITEM item;
352
353			/* init */
354			SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);
355			data->htab = GetDlgItem(hwnd, IDC_TABS);
356			data->hdlg = NULL;
357			/* add pages */
358			item.mask = TCIF_TEXT;
359			data->all[0] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_GENERAL), hwnd, GeneralProc);
360			item.pszText = "General";
361			TabCtrl_InsertItem(data->htab, 0, &item);
362
363			data->all[1] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_OUTPUT), hwnd, OutputProc);
364			item.pszText = "Output";
365			TabCtrl_InsertItem(data->htab, 1, &item);
366			/* get rect (after adding pages) */
367			GetWindowRect(data->htab, &data->r);
368			ScreenToClientRect(hwnd, &data->r);
369			TabCtrl_AdjustRect(data->htab, 0, &data->r);
370			/* simulate item change */
371			TabCtrl_SetCurSel(data->htab, activePage);
372			OnSelChange(hwnd);
373		}
374		return TRUE;
375	/* destory */
376	case WM_DESTROY:
377		{
378			LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
379			int i;
380
381			activePage = TabCtrl_GetCurSel(data->htab);
382
383			for (i=0; i<NUM_PAGES; i++)
384				DestroyWindow(data->all[i]);
385
386			LocalFree(data);
387		}
388		break;
389	/* commands */
390	case WM_COMMAND:
391		switch (LOWORD(wParam))
392		{
393		/* ok/cancel */
394		case IDOK:
395			BroadcastCommand(hwnd, IDOK);
396			/* fall through */
397		case IDCANCEL:
398			EndDialog(hwnd, LOWORD(wParam));
399			return TRUE;
400		case IDC_RESET:
401			SendCommand(hwnd, IDC_RESET);
402			break;
403		}
404		break;
405	/* notification */
406	case WM_NOTIFY:
407		if (LOWORD(wParam) == IDC_TABS)
408		{
409			NMHDR *hdr = (NMHDR*)lParam;
410
411			switch (hdr->code)
412			{
413			case TCN_SELCHANGE:
414				OnSelChange(hwnd);
415				break;
416			}
417		}
418		break;
419	}
420
421	return 0;
422}
423
424
425int DoConfig(HINSTANCE inst, HWND parent)
426{
427	return DialogBoxParam(inst, MAKEINTRESOURCE(IDD_CONFIG), parent, DialogProc, (LONG)inst) == IDOK;
428}
429