Second patch from X-Fixer: file info dialog, tag editor, tag formatting in plugin...
[flac.git] / src / plugin_winamp2 / infobox.c
1 #include <windows.h>\r
2 #include <stdio.h>\r
3 #include "FLAC/all.h"\r
4 #include "plugin_common/all.h"\r
5 #include "infobox.h"\r
6 #include "resource.h"\r
7 \r
8 \r
9 typedef struct\r
10 {\r
11     char filename[MAX_PATH];\r
12 } LOCALDATA;\r
13 \r
14 static char buffer[256];\r
15 static char *genres = NULL;\r
16 static int  genresSize = 0, genresCount = 0, genresChanged = 0;\r
17 \r
18 static const char infoTitle[] = "FLAC File Info";\r
19 \r
20 //fixme int64\r
21 static __inline DWORD FileSize(const char *file)\r
22 {\r
23     HANDLE hFile = CreateFile(file, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
24     DWORD res;\r
25 \r
26     if (hFile == INVALID_HANDLE_VALUE) return 0;\r
27     res = GetFileSize(hFile, 0);\r
28     CloseHandle(hFile);\r
29     return res;\r
30 }\r
31 \r
32 static __inline int GetGenresFileName(char *buffer, int size)\r
33 {\r
34     char *c;\r
35 \r
36     if (!GetModuleFileName(NULL, buffer, size))\r
37         return 0;\r
38     c = strrchr(buffer, '\\');\r
39     if (!c) return 0;\r
40     strcpy(c+1, "genres.txt");\r
41 \r
42     return 1;\r
43 }\r
44 \r
45 static void LoadGenres()\r
46 {\r
47     HANDLE hFile;\r
48     DWORD  spam;\r
49     char  *c;\r
50 \r
51     if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
52     // load file\r
53     hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
54     if (hFile == INVALID_HANDLE_VALUE) return;\r
55     genresSize = GetFileSize(hFile, 0);\r
56     if (!genresSize) return;\r
57     genres = (char*)malloc(genresSize+2);\r
58     if (!genres) return;\r
59     if (!ReadFile(hFile, genres, genresSize, &spam, NULL))\r
60     {\r
61         free(genres);\r
62         genres = NULL;\r
63         return;\r
64     }\r
65     genres[genresSize] = 0;\r
66     genres[genresSize+1] = 0;\r
67     // replace newlines\r
68     genresChanged = 0;\r
69     genresCount = 1;\r
70 \r
71     for (c=genres; *c; c++)\r
72     {\r
73         if (*c == 10)\r
74         {\r
75             *c = 0;\r
76             if (*(c+1))\r
77                 genresCount++;\r
78             else genresSize--;\r
79         }\r
80     }\r
81 \r
82     CloseHandle(hFile);\r
83 }\r
84 \r
85 static void SaveGenres(HWND hlist)\r
86 {\r
87     HANDLE hFile;\r
88     DWORD  spam;\r
89     int i, count, len;\r
90 \r
91     if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
92     // write file\r
93     hFile = CreateFile(buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\r
94     if (hFile == INVALID_HANDLE_VALUE) return;\r
95 \r
96     count = SendMessage(hlist, CB_GETCOUNT, 0, 0);\r
97     for (i=0; i<count; i++)\r
98     {\r
99         SendMessage(hlist, CB_GETLBTEXT, i, (LPARAM)buffer);\r
100         len = strlen(buffer);\r
101         if (i != count-1)\r
102         {\r
103             buffer[len] = 10;\r
104             len++;\r
105         }\r
106         WriteFile(hFile, buffer, len, &spam, NULL);\r
107     }\r
108 \r
109     CloseHandle(hFile);\r
110 }\r
111 \r
112 \r
113 #define SetText(x,y)            SetDlgItemText(hwnd, x, y)\r
114 #define GetText(x,y)            (GetDlgItemText(hwnd, x, buffer, sizeof(buffer)), y = buffer[0] ? strdup(buffer) : 0)\r
115 \r
116 static BOOL InitInfobox(HWND hwnd, const char *file)\r
117 {\r
118     LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));\r
119         FLAC__StreamMetadata streaminfo;\r
120     FLAC_Plugin__CanonicalTag tag;\r
121     DWORD filesize, length, bps, ratio;\r
122 \r
123     SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);\r
124     // file name\r
125     strncpy(data->filename, file, sizeof(data->filename));\r
126     SetDlgItemText(hwnd, IDC_NAME, file);\r
127     // stream data\r
128     filesize = FileSize(file);\r
129     if (!filesize) return FALSE;\r
130         if (!FLAC__metadata_get_streaminfo(file, &streaminfo))\r
131         return FALSE;\r
132 \r
133     length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);\r
134     bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate));\r
135     ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample);\r
136 \r
137     sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n"\r
138                     "File size: %d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n",\r
139         streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample,\r
140         streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples,\r
141         length/60, length%60, bps, ratio/10, ratio%10);\r
142     //todo: replaygain\r
143 \r
144     SetDlgItemText(hwnd, IDC_INFO, buffer);\r
145     // tag\r
146         FLAC_plugin__canonical_tag_init(&tag);\r
147         FLAC_plugin__canonical_tag_get_combined(file, &tag);\r
148 \r
149     SetText(IDC_TITLE,   tag.title);\r
150     SetText(IDC_ARTIST,  tag.performer ? tag.performer : tag.composer);\r
151     SetText(IDC_ALBUM,   tag.album);\r
152     SetText(IDC_COMMENT, tag.comment);\r
153     SetText(IDC_YEAR,    tag.year_recorded ? tag.year_recorded : tag.year_performed);\r
154     SetText(IDC_TRACK,   tag.track_number);\r
155     SetText(IDC_GENRE,   tag.genre);\r
156 \r
157         FLAC_plugin__canonical_tag_clear(&tag);\r
158 \r
159     return TRUE;\r
160 }\r
161 \r
162 static void __inline SetTag(HWND hwnd, const char *filename, FLAC_Plugin__CanonicalTag *tag)\r
163 {\r
164     strcpy(buffer, infoTitle);\r
165 \r
166         if (FLAC_plugin__vorbiscomment_set(filename, tag))\r
167         strcat(buffer, " [Updated]");\r
168     else strcat(buffer, " [Failed]");\r
169 \r
170     SetWindowText(hwnd, buffer);\r
171 }\r
172 \r
173 static void UpdateTag(HWND hwnd)\r
174 {\r
175     LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
176     FLAC_Plugin__CanonicalTag tag;\r
177         FLAC_plugin__canonical_tag_init(&tag);\r
178 \r
179     // get fields\r
180     GetText(IDC_TITLE,   tag.title);\r
181     GetText(IDC_ARTIST,  tag.composer);\r
182     GetText(IDC_ALBUM,   tag.album);\r
183     GetText(IDC_COMMENT, tag.comment);\r
184     GetText(IDC_YEAR,    tag.year_recorded);\r
185     GetText(IDC_TRACK,   tag.track_number);\r
186     GetText(IDC_GENRE,   tag.genre);\r
187 \r
188     // update genres list\r
189     if (tag.genre)\r
190     {\r
191         HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
192 \r
193         if (SendMessage(hgen, CB_FINDSTRINGEXACT, -1, (LPARAM)tag.genre) == CB_ERR)\r
194         {\r
195             genresChanged = 1;\r
196             SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)tag.genre);\r
197         }\r
198     }\r
199 \r
200     // write tag\r
201     SetTag(hwnd, data->filename, &tag);\r
202         FLAC_plugin__canonical_tag_clear(&tag);\r
203 }\r
204 \r
205 static void RemoveTag(HWND hwnd)\r
206 {\r
207     LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
208     FLAC_Plugin__CanonicalTag tag;\r
209         FLAC_plugin__canonical_tag_init(&tag);\r
210 \r
211     SetText(IDC_TITLE,   "");\r
212     SetText(IDC_ARTIST,  "");\r
213     SetText(IDC_ALBUM,   "");\r
214     SetText(IDC_COMMENT, "");\r
215     SetText(IDC_YEAR,    "");\r
216     SetText(IDC_TRACK,   "");\r
217     SetText(IDC_GENRE,   "");\r
218 \r
219     SetTag(hwnd, data->filename, &tag);\r
220 }\r
221 \r
222 \r
223 static INT_PTR CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
224 {\r
225     switch (msg)\r
226     {\r
227     // init\r
228     case WM_INITDIALOG:\r
229         SetWindowText(hwnd, infoTitle);\r
230         // init genres list\r
231         {\r
232             HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
233             char *c;\r
234 \r
235             // set text length limit to 64 chars\r
236             SendMessage(hgen, CB_LIMITTEXT, 64, 0);\r
237             // try to load genres\r
238             if (!genres) LoadGenres(hgen);\r
239             // add the to list\r
240             if (genres)\r
241             {\r
242                 SendMessage(hgen, CB_INITSTORAGE, genresCount, genresSize);\r
243 \r
244                 for (c = genres; *c; c += strlen(c)+1)\r
245                     SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)c);\r
246             }\r
247         }\r
248         // init fields\r
249         if (!InitInfobox(hwnd, (const char*)lParam))\r
250             PostMessage(hwnd, WM_CLOSE, 0, 0);\r
251         return TRUE;\r
252     // destroy\r
253     case WM_DESTROY:\r
254         if (genresChanged)\r
255         {\r
256             SaveGenres(GetDlgItem(hwnd, IDC_GENRE));\r
257             free(genres);\r
258             genres = 0;\r
259         }\r
260         LocalFree((LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA));\r
261         break;\r
262     // commands\r
263     case WM_COMMAND:\r
264         switch (LOWORD(wParam))\r
265         {\r
266         // ok/cancel\r
267         case IDOK:\r
268         case IDCANCEL:\r
269             EndDialog(hwnd, LOWORD(wParam));\r
270             return TRUE;\r
271         // save\r
272         case IDC_UPDATE:\r
273             UpdateTag(hwnd);\r
274             break;\r
275         // remove\r
276         case IDC_REMOVE:\r
277             RemoveTag(hwnd);\r
278             break;\r
279         }\r
280         break;\r
281     }\r
282 \r
283     return 0;\r
284 }\r
285 \r
286 \r
287 void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename)\r
288 {\r
289     DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename);\r
290 }\r