final merge down from 1.1.2 maintenance branch (cvs up -j FLAC_RELEASE_1_1_2_MAINTENA...
[flac.git] / src / plugin_winamp2 / infobox.c
1 /* in_flac - Winamp2 FLAC input plugin\r
2  * Copyright (C) 2002,2003,2004,2005,2006  Josh Coalson\r
3  *\r
4  * This library is free software; you can redistribute it and/or\r
5  * modify it under the terms of the GNU Lesser General Public\r
6  * License as published by the Free Software Foundation; either\r
7  * version 2.1 of the License, or (at your option) any later version.\r
8  *\r
9  * This library is distributed in the hope that it will be useful,\r
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
12  * Lesser General Public License for more details.\r
13  *\r
14  * You should have received a copy of the GNU Lesser General Public\r
15  * License along with this library; if not, write to the Free Software\r
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
17  */\r
18 \r
19 #if HAVE_CONFIG_H\r
20 #  include <config.h>\r
21 #endif\r
22 \r
23 #include <windows.h>\r
24 #include <stdio.h>\r
25 #include "FLAC/all.h"\r
26 #include "plugin_common/all.h"\r
27 #include "infobox.h"\r
28 #include "configure.h"\r
29 #include "resource.h"\r
30 \r
31 \r
32 typedef struct\r
33 {\r
34         char filename[MAX_PATH];\r
35         FLAC__StreamMetadata *tags;\r
36 } LOCALDATA;\r
37 \r
38 static char buffer[8192];\r
39 static char *genres = NULL;\r
40 static DWORD genresSize = 0, genresCount = 0;\r
41 static BOOL genresChanged = FALSE, isNT;\r
42 \r
43 static const char infoTitle[] = "FLAC File Info";\r
44 \r
45 /*\r
46  *  Genres\r
47  */\r
48 \r
49 /* TODO: write genres in utf-8 ? */\r
50 \r
51 static __inline int GetGenresFileName(char *buffer, int size)\r
52 {\r
53         char *c;\r
54 \r
55         if (!GetModuleFileName(NULL, buffer, size))\r
56                 return 0;\r
57         c = strrchr(buffer, '\\');\r
58         if (!c) return 0;\r
59         strcpy(c+1, "genres.txt");\r
60 \r
61         return 1;\r
62 }\r
63 \r
64 static void LoadGenres()\r
65 {\r
66         HANDLE hFile;\r
67         DWORD  spam;\r
68         char  *c;\r
69 \r
70         FLAC__ASSERT(0 != genres);\r
71 \r
72         if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
73         /* load file */\r
74         hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
75         if (hFile == INVALID_HANDLE_VALUE) return;\r
76         genresSize = GetFileSize(hFile, 0);\r
77         if (genresSize && (genres = (char*)malloc(genresSize+2)))\r
78         {\r
79                 if (!ReadFile(hFile, genres, genresSize, &spam, NULL) || spam!=genresSize)\r
80                 {\r
81                         free(genres);\r
82                         genres = NULL;\r
83                 }\r
84                 else\r
85                 {\r
86                         genres[genresSize] = 0;\r
87                         genres[genresSize+1] = 0;\r
88                         /* replace newlines */\r
89                         genresChanged = FALSE;\r
90                         genresCount = 1;\r
91 \r
92                         for (c=genres; *c; c++)\r
93                         {\r
94                                 if (*c == 10)\r
95                                 {\r
96                                         *c = 0;\r
97                                         if (*(c+1))\r
98                                                 genresCount++;\r
99                                         else genresSize--;\r
100                                 }\r
101                         }\r
102                 }\r
103         }\r
104 \r
105         CloseHandle(hFile);\r
106 }\r
107 \r
108 static void SaveGenres(HWND hlist)\r
109 {\r
110         HANDLE hFile;\r
111         DWORD  spam;\r
112         int i, count, len;\r
113 \r
114         if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
115         /* write file */\r
116         hFile = CreateFile(buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\r
117         if (hFile == INVALID_HANDLE_VALUE) return;\r
118 \r
119         count = SendMessage(hlist, CB_GETCOUNT, 0, 0);\r
120         for (i=0; i<count; i++)\r
121         {\r
122                 SendMessage(hlist, CB_GETLBTEXT, i, (LPARAM)buffer);\r
123                 len = strlen(buffer);\r
124                 if (i != count-1)\r
125                 {\r
126                         buffer[len] = 10;\r
127                         len++;\r
128                 }\r
129                 WriteFile(hFile, buffer, len, &spam, NULL);\r
130         }\r
131 \r
132         CloseHandle(hFile);\r
133 }\r
134 \r
135 static void AddGenre(HWND hwnd, const char *genre)\r
136 {\r
137         HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
138 \r
139         if (SendMessage(hgen, CB_FINDSTRINGEXACT, -1, (LPARAM)genre) == CB_ERR)\r
140         {\r
141                 genresChanged = TRUE;\r
142                 SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)genre);\r
143         }\r
144 }\r
145 \r
146 static void InitGenres(HWND hwnd)\r
147 {\r
148         HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
149         char *c;\r
150 \r
151         /* set text length limit to 64 chars */\r
152         SendMessage(hgen, CB_LIMITTEXT, 64, 0);\r
153         /* try to load genres */\r
154         if (!genres)\r
155                 LoadGenres(hgen);\r
156         /* add the to list */\r
157         if (genres)\r
158         {\r
159                 SendMessage(hgen, CB_INITSTORAGE, genresCount, genresSize);\r
160 \r
161                 for (c = genres; *c; c += strlen(c)+1)\r
162                         SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)c);\r
163         }\r
164 }\r
165 \r
166 static void DeinitGenres(HWND hwnd, BOOL final)\r
167 {\r
168         if (genresChanged && hwnd)\r
169         {\r
170                 SaveGenres(GetDlgItem(hwnd, IDC_GENRE));\r
171                 genresChanged = FALSE;\r
172                 final = TRUE;\r
173         }\r
174         if (final)\r
175         {\r
176                 free(genres);\r
177                 genres = 0;\r
178         }\r
179 }\r
180 \r
181 static wchar_t *AnsiToWide(const char *src)\r
182 {\r
183         int len;\r
184         wchar_t *dest;\r
185 \r
186         FLAC__ASSERT(0 != src);\r
187 \r
188         len = strlen(src) + 1;\r
189         /* copy */\r
190         dest = (wchar_t*)malloc(len*sizeof(wchar_t));\r
191         if (dest) mbstowcs(dest, src, len);\r
192         return dest;\r
193 }\r
194 \r
195 /*\r
196  *  Infobox helpers\r
197  */\r
198 \r
199 #define SetText(x,y)            ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, y); \\r
200                                 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, ucs2, -1, buffer, sizeof(buffer), NULL, NULL); \\r
201                                 if(ucs2) free(ucs2); \\r
202                                 SetDlgItemText(hwnd, x, buffer)\r
203 \r
204 #define GetText(x,y)            GetDlgItemText(hwnd, x, buffer, sizeof(buffer));                        \\r
205                                 if (*buffer) { ucs2 = AnsiToWide(buffer); FLAC_plugin__tags_set_tag_ucs2(data->tags, y, ucs2, /*replace_all=*/false); free(ucs2); } \\r
206                                 else FLAC_plugin__tags_delete_tag(data->tags, y)\r
207 \r
208 #define SetTextW(x,y)           ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, y); \\r
209                                 SetDlgItemTextW(hwnd, x, ucs2); \\r
210                                 free(ucs2)\r
211 \r
212 #define GetTextW(x,y)           GetDlgItemTextW(hwnd, x, (WCHAR*)buffer, sizeof(buffer)/2);                     \\r
213                                 if (*(WCHAR*)buffer) FLAC_plugin__tags_set_tag_ucs2(data->tags, y, (WCHAR*)buffer, /*replace_all=*/false); \\r
214                                 else FLAC_plugin__tags_delete_tag(data->tags, y)\r
215 \r
216 \r
217 static BOOL InitInfoboxInfo(HWND hwnd, const char *file)\r
218 {\r
219         LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));\r
220         wchar_t *ucs2;\r
221         FLAC__StreamMetadata streaminfo;\r
222         DWORD    length, bps, ratio, rg;\r
223         LONGLONG filesize;\r
224 \r
225         SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);\r
226         /* file name */\r
227         strncpy(data->filename, file, sizeof(data->filename));\r
228         SetDlgItemText(hwnd, IDC_NAME, file);\r
229         /* stream data and vorbis comment */\r
230         filesize = FileSize(file);\r
231         if (!filesize) return FALSE;\r
232         if (!FLAC__metadata_get_streaminfo(file, &streaminfo))\r
233                 return FALSE;\r
234         ReadTags(file, &data->tags, false);\r
235 \r
236         length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);\r
237         bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate));\r
238         ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample);\r
239         rg  = FLAC_plugin__tags_get_tag_utf8(data->tags, "REPLAYGAIN_TRACK_GAIN") ? 1 : 0;\r
240         rg |= FLAC_plugin__tags_get_tag_utf8(data->tags, "REPLAYGAIN_ALBUM_GAIN") ? 2 : 0;\r
241 \r
242         sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n"\r
243                         "File size: %I64d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n"\r
244                         "ReplayGain: %s\n",\r
245             streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample,\r
246             streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples,\r
247             length/60, length%60, bps, ratio/10, ratio%10,\r
248             rg==3 ? "track gain\nReplayGain: album gain" : rg==2 ? "album gain" : rg==1 ? "track gain" : "not present");\r
249 \r
250         SetDlgItemText(hwnd, IDC_INFO, buffer);\r
251         /* tag */\r
252         if (isNT)\r
253         {\r
254                 SetTextW(IDC_TITLE,   "TITLE");\r
255                 SetTextW(IDC_ARTIST,  "ARTIST");\r
256                 SetTextW(IDC_ALBUM,   "ALBUM");\r
257                 SetTextW(IDC_COMMENT, "COMMENT");\r
258                 SetTextW(IDC_YEAR,    "DATE");\r
259                 SetTextW(IDC_TRACK,   "TRACKNUMBER");\r
260                 SetTextW(IDC_GENRE,   "GENRE");\r
261         }\r
262         else\r
263         {\r
264                 SetText(IDC_TITLE,   "TITLE");\r
265                 SetText(IDC_ARTIST,  "ARTIST");\r
266                 SetText(IDC_ALBUM,   "ALBUM");\r
267                 SetText(IDC_COMMENT, "COMMENT");\r
268                 SetText(IDC_YEAR,    "DATE");\r
269                 SetText(IDC_TRACK,   "TRACKNUMBER");\r
270                 SetText(IDC_GENRE,   "GENRE");\r
271         }\r
272 \r
273         return TRUE;\r
274 }\r
275 \r
276 static void __inline SetTag(HWND hwnd, const char *filename, FLAC__StreamMetadata *tags)\r
277 {\r
278         strcpy(buffer, infoTitle);\r
279 \r
280         if (FLAC_plugin__tags_set(filename, tags))\r
281                 strcat(buffer, " [Updated]");\r
282         else strcat(buffer, " [Failed]");\r
283 \r
284         SetWindowText(hwnd, buffer);\r
285 }\r
286 \r
287 static void UpdateTag(HWND hwnd)\r
288 {\r
289         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
290         wchar_t *ucs2;\r
291 \r
292         /* get fields */\r
293         if (isNT)\r
294         {\r
295                 GetTextW(IDC_TITLE,   "TITLE");\r
296                 GetTextW(IDC_ARTIST,  "ARTIST");\r
297                 GetTextW(IDC_ALBUM,   "ALBUM");\r
298                 GetTextW(IDC_COMMENT, "COMMENT");\r
299                 GetTextW(IDC_YEAR,    "DATE");\r
300                 GetTextW(IDC_TRACK,   "TRACKNUMBER");\r
301                 GetTextW(IDC_GENRE,   "GENRE");\r
302 \r
303                 ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, "GENRE");\r
304                 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, ucs2, -1, buffer, sizeof(buffer), NULL, NULL);\r
305                 free(ucs2);\r
306         }\r
307         else\r
308         {\r
309                 GetText(IDC_TITLE,   "TITLE");\r
310                 GetText(IDC_ARTIST,  "ARTIST");\r
311                 GetText(IDC_ALBUM,   "ALBUM");\r
312                 GetText(IDC_COMMENT, "COMMENT");\r
313                 GetText(IDC_YEAR,    "DATE");\r
314                 GetText(IDC_TRACK,   "TRACKNUMBER");\r
315                 GetText(IDC_GENRE,   "GENRE");\r
316         }\r
317 \r
318         /* update genres list (buffer should contain genre) */\r
319         if (buffer[0]) AddGenre(hwnd, buffer);\r
320 \r
321         /* write tag */\r
322         SetTag(hwnd, data->filename, data->tags);\r
323 }\r
324 \r
325 static void RemoveTag(HWND hwnd)\r
326 {\r
327         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
328         FLAC_plugin__tags_delete_all(data->tags);\r
329 \r
330         SetDlgItemText(hwnd, IDC_TITLE,   "");\r
331         SetDlgItemText(hwnd, IDC_ARTIST,  "");\r
332         SetDlgItemText(hwnd, IDC_ALBUM,   "");\r
333         SetDlgItemText(hwnd, IDC_COMMENT, "");\r
334         SetDlgItemText(hwnd, IDC_YEAR,    "");\r
335         SetDlgItemText(hwnd, IDC_TRACK,   "");\r
336         SetDlgItemText(hwnd, IDC_GENRE,   "");\r
337 \r
338         SetTag(hwnd, data->filename, data->tags);\r
339 }\r
340 \r
341 \r
342 static INT_PTR CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
343 {\r
344         switch (msg)\r
345         {\r
346         /* init */\r
347         case WM_INITDIALOG:\r
348                 SetWindowText(hwnd, infoTitle);\r
349                 InitGenres(hwnd);\r
350                 /* init fields */\r
351                 if (!InitInfoboxInfo(hwnd, (const char*)lParam))\r
352                         PostMessage(hwnd, WM_CLOSE, 0, 0);\r
353                 return TRUE;\r
354         /* destroy */\r
355         case WM_DESTROY:\r
356                 {\r
357                         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
358                         FLAC_plugin__tags_destroy(&data->tags);\r
359                         LocalFree(data);\r
360                         DeinitGenres(hwnd, FALSE);\r
361                 }\r
362                 break;\r
363         /* commands */\r
364         case WM_COMMAND:\r
365                 switch (LOWORD(wParam))\r
366                 {\r
367                 /* ok/cancel */\r
368                 case IDOK:\r
369                 case IDCANCEL:\r
370                         EndDialog(hwnd, LOWORD(wParam));\r
371                         return TRUE;\r
372                 /* save */\r
373                 case IDC_UPDATE:\r
374                         UpdateTag(hwnd);\r
375                         break;\r
376                 /* remove */\r
377                 case IDC_REMOVE:\r
378                         RemoveTag(hwnd);\r
379                         break;\r
380                 }\r
381                 break;\r
382         }\r
383 \r
384         return 0;\r
385 }\r
386 \r
387 /*\r
388  *  Helpers\r
389  */\r
390 \r
391 ULONGLONG FileSize(const char *fileName)\r
392 {\r
393         LARGE_INTEGER res;\r
394         HANDLE hFile = CreateFile(fileName, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
395 \r
396         if (hFile == INVALID_HANDLE_VALUE) return 0;\r
397         res.LowPart = GetFileSize(hFile, &res.HighPart);\r
398         CloseHandle(hFile);\r
399         return res.QuadPart;\r
400 }\r
401 \r
402 static __inline char *GetFileName(const char *fullname)\r
403 {\r
404         const char *c = fullname + strlen(fullname) - 1;\r
405 \r
406         while (c > fullname)\r
407         {\r
408                 if (*c=='\\' || *c=='/')\r
409                 {\r
410                         c++;\r
411                         break;\r
412                 }\r
413                 c--;\r
414         }\r
415 \r
416         return (char*)c;\r
417 }\r
418 \r
419 void ReadTags(const char *fileName, FLAC__StreamMetadata **tags, BOOL forDisplay)\r
420 {\r
421         if(FLAC_plugin__tags_get(fileName, tags)) {\r
422 \r
423                 /* add file name */\r
424                 if (forDisplay)\r
425                 {\r
426                         char *c;\r
427                         wchar_t *ucs2;\r
428                         ucs2 = AnsiToWide(fileName);\r
429                         FLAC_plugin__tags_set_tag_ucs2(*tags, "filepath", ucs2, /*replace_all=*/true);\r
430                         free(ucs2);\r
431 \r
432                         strcpy(buffer, GetFileName(fileName));\r
433                         if (c = strrchr(buffer, '.')) *c = 0;\r
434                         ucs2 = AnsiToWide(buffer);\r
435                         FLAC_plugin__tags_set_tag_ucs2(*tags, "filename", ucs2, /*replace_all=*/true);\r
436                         free(ucs2);\r
437                 }\r
438         }\r
439 }\r
440 \r
441 /*\r
442  *  Front-end\r
443  */\r
444 \r
445 void InitInfobox()\r
446 {\r
447         isNT = !(GetVersion() & 0x80000000);\r
448 }\r
449 \r
450 void DeinitInfobox()\r
451 {\r
452         DeinitGenres(NULL, true);\r
453 }\r
454 \r
455 void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename)\r
456 {\r
457         DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename);\r
458 }\r