comparison src/audacious/titlestring.c @ 2431:3ec22a11c83e trunk

[svn] - moved titlestring.* and xconvert.* from libaudacious to audacious
author mf0102
date Sat, 27 Jan 2007 12:10:21 -0800
parents
children 864c12eafb49
comparison
equal deleted inserted replaced
2430:4e2fc64d95ef 2431:3ec22a11c83e
1 /*
2 * Copyright (C) 2001, Espen Skoglund <esk@ira.uka.de>
3 * Copyright (C) 2001, Haavard Kvaalen <havardk@xmms.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA.
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #define GETTEXT_PACKAGE PACKAGE_NAME
27
28 #include <glib.h>
29 #include <glib/gi18n-lib.h>
30 #include <gtk/gtk.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "titlestring.h"
35
36 #define CHECK(input, field) \
37 (((gchar *) &input->field - (gchar *) input) < input->__size)
38
39 #define VS(input, field) (CHECK(input, field) ? input->field : NULL)
40 #define VI(input, field) (CHECK(input, field) ? input->field : 0)
41
42 /**
43 * bmp_title_input_new:
44 *
45 * #BmpTitleInput tuple factory.
46 *
47 * Return value: A #BmpTitleInput object.
48 **/
49 BmpTitleInput *
50 bmp_title_input_new()
51 {
52 BmpTitleInput *input;
53 input = g_new0(BmpTitleInput, 1);
54 input->__size = XMMS_TITLEINPUT_SIZE;
55 input->__version = XMMS_TITLEINPUT_VERSION;
56 return input;
57 }
58
59 /**
60 * bmp_title_input_free:
61 * @input: A #BmpTitleInput tuple to destroy.
62 *
63 * Destroys a #BmpTitleInput tuple.
64 **/
65 void
66 bmp_title_input_free(BmpTitleInput * input)
67 {
68 if (input == NULL)
69 return;
70
71 if (input->performer != NULL)
72 g_free(input->performer);
73
74 if (input->album_name != NULL)
75 g_free(input->album_name);
76
77 if (input->track_name != NULL)
78 g_free(input->track_name);
79
80 if (input->date != NULL)
81 g_free(input->date);
82
83 if (input->genre != NULL)
84 g_free(input->genre);
85
86 if (input->comment != NULL)
87 g_free(input->comment);
88
89 if (input->file_name != NULL)
90 g_free(input->file_name);
91
92 if (input->file_path != NULL)
93 g_free(input->file_path);
94
95 g_free(input);
96 }
97
98 /**
99 * xmms_get_titlestring:
100 * @fmt: A format string.
101 * @input: A tuple to use for data.
102 *
103 * Generates a formatted string from a tuple.
104 *
105 * Return value: A formatted tuple string.
106 **/
107 gchar *
108 xmms_get_titlestring(const gchar * fmt, TitleInput * input)
109 {
110 GString *outstr;
111 const gchar *string;
112 gchar c, convert[16];
113 gint numdigits, numpr, val, i;
114 gint f_left, f_space, f_zero, someflag, width, precision;
115 gboolean did_output = FALSE;
116 gchar digits[] = "0123456789";
117
118 #define PUTCH(ch) g_string_append_c(outstr, ch)
119
120 #define LEFTPAD(num) \
121 G_STMT_START { \
122 gint cnt = (num); \
123 if ( ! f_left && cnt > 0 ) \
124 while ( cnt-- > 0 ) \
125 PUTCH(f_zero ? '0' : ' '); \
126 } G_STMT_END;
127
128 #define RIGHTPAD(num) \
129 G_STMT_START { \
130 gint cnt = (num); \
131 if ( f_left && cnt > 0 ) \
132 while ( cnt-- > 0 ) \
133 PUTCH( ' ' ); \
134 } G_STMT_END;
135
136 if (fmt == NULL || input == NULL)
137 return NULL;
138 outstr = g_string_new("");
139
140 for (;;) {
141 /* Copy characters until we encounter '%'. */
142 while ((c = *fmt++) != '%') {
143 if (c == '\0')
144 goto Done;
145 g_string_append_c(outstr, c);
146 }
147
148 f_left = f_space = f_zero = 0;
149 someflag = 1;
150
151
152 /* Parse flags. */
153 while (someflag) {
154 switch (*fmt) {
155 case '-':
156 f_left = 1;
157 fmt++;
158 break;
159 case ' ':
160 f_space = 1;
161 fmt++;
162 break;
163 case '0':
164 f_zero = 1;
165 fmt++;
166 break;
167 default:
168 someflag = 0;
169 break;
170 }
171 }
172
173
174 /* Parse field width. */
175 if ((c = *fmt) >= '0' && c <= '9') {
176 width = 0;
177 while ((c = *fmt++) >= '0' && c <= '9') {
178 width *= 10;
179 width += c - '0';
180 }
181 fmt--;
182 }
183 else
184 width = -1;
185
186
187 /* Parse precision. */
188 if (*fmt == '.') {
189 if ((c = *++fmt) >= '0' && c <= '9') {
190 precision = 0;
191 while ((c = *fmt++) >= '0' && c <= '9') {
192 precision *= 10;
193 precision += c - '0';
194 }
195 fmt--;
196 }
197 else
198 precision = -1;
199 }
200 else
201 precision = -1;
202
203
204 /* Parse format conversion. */
205 switch (c = *fmt++) {
206 case '}': /* close optional, just ignore */
207 continue;
208
209 case '{':{ /* optional entry: %{n:...%} */
210 char n = *fmt++;
211 if (!((n == 'a' && VS(input, album_name)) ||
212 (n == 'c' && VS(input, comment)) ||
213 (n == 'd' && VS(input, date)) ||
214 (n == 'e' && VS(input, file_ext)) ||
215 (n == 'f' && VS(input, file_name)) ||
216 (n == 'F' && VS(input, file_path)) ||
217 (n == 'g' && VS(input, genre)) ||
218 (n == 'n' && VI(input, track_number)) ||
219 (n == 'p' && VS(input, performer)) ||
220 (n == 't' && VS(input, track_name)) ||
221 (n == 'y' && VI(input, year)))) {
222 int nl = 0;
223 char c;
224 while ((c = *fmt++)) /* until end of string */
225 if (c == '}') /* if end of opt */
226 if (!nl)
227 break; /* if outmost indent level */
228 else
229 --nl; /* else reduce indent */
230 else if (c == '{')
231 ++nl; /* increase indent */
232 }
233 else
234 ++fmt;
235 break;
236 }
237
238 case 'a':
239 string = VS(input, album_name);
240 goto Print_string;
241 case 'c':
242 string = VS(input, comment);
243 goto Print_string;
244 case 'd':
245 string = VS(input, date);
246 goto Print_string;
247 case 'e':
248 string = VS(input, file_ext);
249 goto Print_string;
250 case 'f':
251 string = VS(input, file_name);
252 goto Print_string;
253 case 'F':
254 string = VS(input, file_path);
255 goto Print_string;
256 case 'g':
257 string = VS(input, genre);
258 goto Print_string;
259 case 'n':
260 val = VI(input, track_number);
261 goto Print_number;
262 case 'p':
263 string = VS(input, performer);
264 goto Print_string;
265 case 't':
266 string = VS(input, track_name);
267 goto Print_string;
268 case 'y':
269 val = VI(input, year);
270 goto Print_number;
271
272 Print_string:
273 if (string == NULL)
274 break;
275 did_output = TRUE;
276
277 numpr = 0;
278 if (width > 0) {
279 /* Calculate printed size. */
280 numpr = strlen(string);
281 if (precision >= 0 && precision < numpr)
282 numpr = precision;
283
284 LEFTPAD(width - numpr);
285 }
286
287 /* Insert string. */
288 if (precision >= 0) {
289 glong offset_max = precision, offset;
290 gchar *uptr = NULL;
291 const gchar *tmpstring = string;
292 while (precision > 0) {
293 offset = offset_max - precision;
294 uptr = g_utf8_offset_to_pointer(tmpstring, offset);
295 if (*uptr == '\0')
296 break;
297 g_string_append_unichar(outstr, g_utf8_get_char(uptr));
298 precision--;
299 }
300 }
301 else {
302 while ((c = *string++) != '\0')
303 PUTCH(c);
304 }
305
306 RIGHTPAD(width - numpr);
307 break;
308
309 Print_number:
310 if (val == 0)
311 break;
312 if (c != 'N')
313 did_output = TRUE;
314
315 /* Create reversed number string. */
316 numdigits = 0;
317 do {
318 convert[numdigits++] = digits[val % 10];
319 val /= 10;
320 }
321 while (val > 0);
322
323 numpr = numdigits > precision ? numdigits : precision;
324
325 /* Insert left padding. */
326 if (!f_left && width > numpr) {
327 if (f_zero)
328 numpr = width;
329 else
330 for (i = width - numpr; i-- > 0;)
331 PUTCH(' ');
332 }
333
334 /* Insert zero padding. */
335 for (i = numpr - numdigits; i-- > 0;)
336 PUTCH('0');
337
338 /* Insert number. */
339 while (numdigits > 0)
340 PUTCH(convert[--numdigits]);
341
342 RIGHTPAD(width - numpr);
343 break;
344
345 case '%':
346 PUTCH('%');
347 break;
348
349 default:
350 PUTCH('%');
351 PUTCH(c);
352 break;
353 }
354 }
355
356 Done:
357 if (did_output)
358 return g_string_free(outstr, FALSE);
359 else
360 return NULL;
361 }
362
363 struct _TagDescription {
364 gchar tag;
365 gchar *description;
366 };
367
368 typedef struct _TagDescription TagDescription;
369
370 static TagDescription tag_descriptions[] = {
371 {'p', N_("Performer/Artist")},
372 {'a', N_("Album")},
373 {'g', N_("Genre")},
374 {'f', N_("File name")},
375 {'F', N_("File path")},
376 {'e', N_("File extension")},
377 {'t', N_("Track name")},
378 {'n', N_("Track number")},
379 {'d', N_("Date")},
380 {'y', N_("Year")},
381 {'c', N_("Comment")}
382 };
383
384 gint tag_descriptions_length =
385 sizeof(tag_descriptions) / sizeof(TagDescription);
386
387 /**
388 * xmms_titlestring_descriptions:
389 * @tags: A list of formatters to provide.
390 * @columns: A number of columns to arrange them in.
391 *
392 * Generates a box explaining how to use the formatters.
393 *
394 * Return value: A GtkWidget containing the table.
395 **/
396 GtkWidget *
397 xmms_titlestring_descriptions(gchar * tags, gint columns)
398 {
399 GtkWidget *table, *label;
400 gchar tag_str[5];
401 gint num = strlen(tags);
402 gint r = 0, c, i;
403
404 g_return_val_if_fail(tags != NULL, NULL);
405 g_return_val_if_fail(columns <= num, NULL);
406
407 table = gtk_table_new((num + columns - 1) / columns, columns * 2, FALSE);
408 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
409 gtk_table_set_col_spacings(GTK_TABLE(table), 5);
410
411 for (c = 0; c < columns; c++) {
412 for (r = 0; r < (num + columns - 1 - c) / columns; r++) {
413 g_snprintf(tag_str, sizeof(tag_str), "%%%c:", *tags);
414 label = gtk_label_new(tag_str);
415 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
416 gtk_table_attach(GTK_TABLE(table), label, 2 * c, 2 * c + 1, r,
417 r + 1, GTK_FILL, GTK_FILL, 0, 0);
418 gtk_widget_show(label);
419
420 for (i = 0; i < tag_descriptions_length; i++) {
421 if (*tags == tag_descriptions[i].tag) {
422 label = gtk_label_new(_(tag_descriptions[i].description));
423 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
424 gtk_table_attach(GTK_TABLE(table), label, 2 * c + 1,
425 2 * c + 2, r, r + 1,
426 GTK_EXPAND | GTK_FILL,
427 GTK_EXPAND | GTK_FILL, 0, 0);
428 gtk_widget_show(label);
429 break;
430 }
431 }
432
433 if (i == tag_descriptions_length)
434 g_warning("Invalid tag: %c", *tags);
435
436 tags++;
437 }
438
439 }
440
441 label = gtk_label_new(_("%{n:...%}: Display \"...\" only if element "
442 "%n is present"));
443 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
444 gtk_table_attach(GTK_TABLE(table), label, 0, r + 1,
445 r + 1, r + 2, GTK_FILL, GTK_FILL, 0, 0);
446 gtk_widget_show(label);
447
448 return table;
449 }