comparison src/skins/util.c @ 2573:c0b08527b121

allow loading compressed skins
author Tomasz Mon <desowin@gmail.com>
date Sun, 18 May 2008 16:27:48 +0200
parents
children 88009fb3fbe6
comparison
equal deleted inserted replaced
2572:d0daee216c8d 2573:c0b08527b121
1 /* Audacious - Cross-platform multimedia player
2 * Copyright (C) 2005-2008 Audacious development team
3 *
4 * Based on BMP:
5 * Copyright (C) 2003-2004 BMP development team.
6 *
7 * Based on XMMS:
8 * Copyright (C) 1998-2003 XMMS development team.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; under version 3 of the License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses>.
21 *
22 * The Audacious team does not consider modular code linking to
23 * Audacious or using our public API to be a derived work.
24 */
25
26 /*#define AUD_DEBUG*/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include "util.h"
33
34 #include <glib.h>
35 #include <glib/gi18n.h>
36 #include <gtk/gtk.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <math.h>
41
42 #include "platform/smartinclude.h"
43 #include <errno.h>
44
45 #ifdef HAVE_FTS_H
46 # include <sys/types.h>
47 # include <sys/stat.h>
48 # include <fts.h>
49 #endif
50
51 #include <audacious/input.h>
52 #include <audacious/playback.h>
53
54 #ifdef USE_CHARDET
55 # include "../libguess/libguess.h"
56 # ifdef HAVE_UDET
57 # include <libudet_c.h>
58 # endif
59 #endif
60
61 #include "plugin.h"
62
63 /*
64 * find <file> in directory <dirname> or subdirectories. return
65 * pointer to complete filename which has to be freed by calling
66 * "g_free()" after use. Returns NULL if file could not be found.
67 */
68
69 /* somebody tell me how to make use of those funcs from string.h that are in core... */
70 gboolean
71 str_has_suffix_nocase(const gchar * str, const gchar * suffix)
72 {
73 return (strcasecmp(str + strlen(str) - strlen(suffix), suffix) == 0);
74 }
75
76 static gchar *
77 str_replace_char(gchar * str, gchar old, gchar new)
78 {
79 gchar *match;
80
81 g_return_val_if_fail(str != NULL, NULL);
82
83 match = str;
84 while ((match = strchr(match, old)))
85 *match = new;
86
87 return str;
88 }
89
90 static gchar *
91 str_replace_drive_letter(gchar * str)
92 {
93 gchar *match, *match_end;
94
95 g_return_val_if_fail(str != NULL, NULL);
96
97 while ((match = strstr(str, ":\\"))) {
98 match--;
99 match_end = match + 3;
100 *match++ = '/';
101 while (*match_end)
102 *match++ = *match_end++;
103 *match = 0; /* the end of line */
104 }
105
106 return str;
107 }
108
109 gchar *
110 convert_dos_path(gchar * path)
111 {
112 g_return_val_if_fail(path != NULL, NULL);
113
114 /* replace drive letter with '/' */
115 str_replace_drive_letter(path);
116
117 /* replace '\' with '/' */
118 str_replace_char(path, '\\', '/');
119
120 return path;
121 }
122
123 gchar *
124 escape_shell_chars(const gchar * string)
125 {
126 const gchar *special = "$`\"\\"; /* Characters to escape */
127 const gchar *in = string;
128 gchar *out, *escaped;
129 gint num = 0;
130
131 while (*in != '\0')
132 if (strchr(special, *in++))
133 num++;
134
135 escaped = g_malloc(strlen(string) + num + 1);
136
137 in = string;
138 out = escaped;
139
140 while (*in != '\0') {
141 if (strchr(special, *in))
142 *out++ = '\\';
143 *out++ = *in++;
144 }
145 *out = '\0';
146
147 return escaped;
148 }
149
150
151 typedef struct {
152 const gchar *to_match;
153 gchar *match;
154 gboolean found;
155 } FindFileContext;
156
157 static const struct {
158 AFormat afmt;
159 SAD_sample_format sadfmt;
160 } format_table[] = {
161 {FMT_U8, SAD_SAMPLE_U8},
162 {FMT_S8, SAD_SAMPLE_S8},
163
164 {FMT_S16_LE, SAD_SAMPLE_S16_LE},
165 {FMT_S16_BE, SAD_SAMPLE_S16_BE},
166 {FMT_S16_NE, SAD_SAMPLE_S16},
167
168 {FMT_U16_LE, SAD_SAMPLE_U16_LE},
169 {FMT_U16_BE, SAD_SAMPLE_U16_BE},
170 {FMT_U16_NE, SAD_SAMPLE_U16},
171
172 {FMT_S24_LE, SAD_SAMPLE_S24_LE},
173 {FMT_S24_BE, SAD_SAMPLE_S24_BE},
174 {FMT_S24_NE, SAD_SAMPLE_S24},
175
176 {FMT_U24_LE, SAD_SAMPLE_U24_LE},
177 {FMT_U24_BE, SAD_SAMPLE_U24_BE},
178 {FMT_U24_NE, SAD_SAMPLE_U24},
179
180 {FMT_S32_LE, SAD_SAMPLE_S32_LE},
181 {FMT_S32_BE, SAD_SAMPLE_S32_BE},
182 {FMT_S32_NE, SAD_SAMPLE_S32},
183
184 {FMT_U32_LE, SAD_SAMPLE_U32_LE},
185 {FMT_U32_BE, SAD_SAMPLE_U32_BE},
186 {FMT_U32_NE, SAD_SAMPLE_U32},
187
188 {FMT_FLOAT, SAD_SAMPLE_FLOAT},
189 {FMT_FIXED32, SAD_SAMPLE_FIXED32},
190 };
191
192 SAD_sample_format
193 sadfmt_from_afmt(AFormat fmt)
194 {
195 int i;
196 for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++) {
197 if (format_table[i].afmt == fmt) return format_table[i].sadfmt;
198 }
199
200 return -1;
201 }
202
203
204 static gboolean
205 find_file_func(const gchar * path, const gchar * basename, gpointer data)
206 {
207 FindFileContext *context = data;
208
209 if (strlen(path) > FILENAME_MAX) {
210 AUDDBG("Ignoring path: name too long (%s)\n", path);
211 return TRUE;
212 }
213
214 if (aud_vfs_file_test(path, G_FILE_TEST_IS_REGULAR)) {
215 if (!strcasecmp(basename, context->to_match)) {
216 context->match = g_strdup(path);
217 context->found = TRUE;
218 return TRUE;
219 }
220 }
221 else if (aud_vfs_file_test(path, G_FILE_TEST_IS_DIR)) {
222 dir_foreach(path, find_file_func, context, NULL);
223 if (context->found)
224 return TRUE;
225 }
226
227 return FALSE;
228 }
229
230 gchar *
231 find_file_recursively(const gchar * path, const gchar * filename)
232 {
233 FindFileContext context;
234 gchar *out = NULL;
235
236 context.to_match = filename;
237 context.match = NULL;
238 context.found = FALSE;
239
240 dir_foreach(path, find_file_func, &context, NULL);
241
242 if (context.match)
243 {
244 out = g_filename_to_uri(context.match, NULL, NULL);
245 g_free(context.match);
246 }
247
248 return out;
249 }
250
251 gchar *
252 find_path_recursively(const gchar * path, const gchar * filename)
253 {
254 FindFileContext context;
255
256 context.to_match = filename;
257 context.match = NULL;
258 context.found = FALSE;
259
260 dir_foreach(path, find_file_func, &context, NULL);
261
262 return context.match;
263 }
264
265
266 typedef enum {
267 ARCHIVE_UNKNOWN = 0,
268 ARCHIVE_DIR,
269 ARCHIVE_TAR,
270 ARCHIVE_TGZ,
271 ARCHIVE_ZIP,
272 ARCHIVE_TBZ2
273 } ArchiveType;
274
275 typedef gchar *(*ArchiveExtractFunc) (const gchar *, const gchar *);
276
277 typedef struct {
278 ArchiveType type;
279 const gchar *ext;
280 } ArchiveExtensionType;
281
282 static ArchiveExtensionType archive_extensions[] = {
283 {ARCHIVE_TAR, ".tar"},
284 {ARCHIVE_ZIP, ".wsz"},
285 {ARCHIVE_ZIP, ".zip"},
286 {ARCHIVE_TGZ, ".tar.gz"},
287 {ARCHIVE_TGZ, ".tgz"},
288 {ARCHIVE_TBZ2, ".tar.bz2"},
289 {ARCHIVE_TBZ2, ".bz2"},
290 {ARCHIVE_UNKNOWN, NULL}
291 };
292
293 static gchar *archive_extract_tar(const gchar * archive, const gchar * dest);
294 static gchar *archive_extract_zip(const gchar * archive, const gchar * dest);
295 static gchar *archive_extract_tgz(const gchar * archive, const gchar * dest);
296 static gchar *archive_extract_tbz2(const gchar * archive, const gchar * dest);
297
298 static ArchiveExtractFunc archive_extract_funcs[] = {
299 NULL,
300 NULL,
301 archive_extract_tar,
302 archive_extract_tgz,
303 archive_extract_zip,
304 archive_extract_tbz2
305 };
306
307
308 /* FIXME: these functions can be generalised into a function using a
309 * command lookup table */
310
311 static const gchar *
312 get_tar_command(void)
313 {
314 static const gchar *command = NULL;
315
316 if (!command) {
317 if (!(command = getenv("TARCMD")))
318 command = "tar";
319 }
320
321 return command;
322 }
323
324 static const gchar *
325 get_unzip_command(void)
326 {
327 static const gchar *command = NULL;
328
329 if (!command) {
330 if (!(command = getenv("UNZIPCMD")))
331 command = "unzip";
332 }
333
334 return command;
335 }
336
337
338 static gchar *
339 archive_extract_tar(const gchar * archive, const gchar * dest)
340 {
341 return g_strdup_printf("%s >/dev/null xf \"%s\" -C %s",
342 get_tar_command(), archive, dest);
343 }
344
345 static gchar *
346 archive_extract_zip(const gchar * archive, const gchar * dest)
347 {
348 return g_strdup_printf("%s >/dev/null -o -j \"%s\" -d %s",
349 get_unzip_command(), archive, dest);
350 }
351
352 static gchar *
353 archive_extract_tgz(const gchar * archive, const gchar * dest)
354 {
355 return g_strdup_printf("%s >/dev/null xzf \"%s\" -C %s",
356 get_tar_command(), archive, dest);
357 }
358
359 static gchar *
360 archive_extract_tbz2(const gchar * archive, const gchar * dest)
361 {
362 return g_strdup_printf("bzip2 -dc \"%s\" | %s >/dev/null xf - -C %s",
363 archive, get_tar_command(), dest);
364 }
365
366
367 ArchiveType
368 archive_get_type(const gchar * filename)
369 {
370 gint i = 0;
371
372 if (g_file_test(filename, G_FILE_TEST_IS_DIR))
373 return ARCHIVE_DIR;
374
375 while (archive_extensions[i].ext) {
376 if (g_str_has_suffix(filename, archive_extensions[i].ext)) {
377 return archive_extensions[i].type;
378 }
379 i++;
380 }
381
382 return ARCHIVE_UNKNOWN;
383 }
384
385 gboolean
386 file_is_archive(const gchar * filename)
387 {
388 return (archive_get_type(filename) > ARCHIVE_DIR);
389 }
390
391 gchar *
392 archive_basename(const gchar * str)
393 {
394 gint i = 0;
395
396 while (archive_extensions[i].ext) {
397 if (str_has_suffix_nocase(str, archive_extensions[i].ext)) {
398 const gchar *end = g_strrstr(str, archive_extensions[i].ext);
399 if (end) {
400 return g_strndup(str, end - str);
401 }
402 break;
403 }
404 i++;
405 }
406
407 return NULL;
408 }
409
410 /*
411 decompress_archive
412
413 Decompresses the archive "filename" to a temporary directory,
414 returns the path to the temp dir, or NULL if failed,
415 watch out tho, doesn't actually check if the system command succeeds :-|
416 */
417
418 gchar *
419 archive_decompress(const gchar * filename)
420 {
421 gchar *tmpdir, *cmd, *escaped_filename;
422 ArchiveType type;
423 #ifndef HAVE_MKDTEMP
424 mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
425 #endif
426
427 if ((type = archive_get_type(filename)) <= ARCHIVE_DIR)
428 return NULL;
429
430 #ifdef HAVE_MKDTEMP
431 tmpdir = g_build_filename(g_get_tmp_dir(), "audacious.XXXXXXXX", NULL);
432 if (!mkdtemp(tmpdir)) {
433 g_free(tmpdir);
434 AUDDBG("Unable to load skin: Failed to create temporary "
435 "directory: %s\n", g_strerror(errno));
436 return NULL;
437 }
438 #else
439 tmpdir = g_strdup_printf("%s/audacious.%ld", g_get_tmp_dir(), (long) rand());
440 make_directory(tmpdir, mode755);
441 #endif
442
443 escaped_filename = escape_shell_chars(filename);
444 cmd = archive_extract_funcs[type] (escaped_filename, tmpdir);
445 g_free(escaped_filename);
446
447 if (!cmd) {
448 AUDDBG("extraction function is NULL!\n");
449 g_free(tmpdir);
450 return NULL;
451 }
452
453 AUDDBG("Attempt to execute \"%s\"\n", cmd);
454
455 if(system(cmd) != 0)
456 {
457 AUDDBG("could not execute cmd %s\n",cmd);
458 g_free(cmd);
459 return NULL;
460 }
461 g_free(cmd);
462
463 return tmpdir;
464 }
465
466
467 #ifdef HAVE_FTS_H
468
469 void
470 del_directory(const gchar * dirname)
471 {
472 gchar *const argv[2] = { (gchar *) dirname, NULL };
473 FTS *fts;
474 FTSENT *p;
475
476 fts = fts_open(argv, FTS_PHYSICAL, (gint(*)())NULL);
477 while ((p = fts_read(fts))) {
478 switch (p->fts_info) {
479 case FTS_D:
480 break;
481 case FTS_DNR:
482 case FTS_ERR:
483 break;
484 case FTS_DP:
485 rmdir(p->fts_accpath);
486 break;
487 default:
488 unlink(p->fts_accpath);
489 break;
490 }
491 }
492 fts_close(fts);
493 }
494
495 #else /* !HAVE_FTS */
496
497 gboolean
498 del_directory_func(const gchar * path, const gchar * basename,
499 gpointer params)
500 {
501 if (!strcmp(basename, ".") || !strcmp(path, ".."))
502 return FALSE;
503
504 if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
505 dir_foreach(path, del_directory_func, NULL, NULL);
506 rmdir(path);
507 return FALSE;
508 }
509
510 unlink(path);
511
512 return FALSE;
513 }
514
515 void
516 del_directory(const gchar * path)
517 {
518 dir_foreach(path, del_directory_func, NULL, NULL);
519 rmdir(path);
520 }
521
522 #endif /* ifdef HAVE_FTS */
523
524 static void
525 strip_string(GString *string)
526 {
527 while (string->len > 0 && string->str[0] == ' ')
528 g_string_erase(string, 0, 1);
529
530 while (string->len > 0 && string->str[string->len - 1] == ' ')
531 g_string_erase(string, string->len - 1, 1);
532 }
533
534 static void
535 strip_lower_string(GString *string)
536 {
537 gchar *lower;
538 strip_string(string);
539
540 lower = g_ascii_strdown(string->str, -1);
541 g_free(string->str);
542 string->str = lower;
543 }
544
545 static void
546 close_ini_file_free_value(gpointer value)
547 {
548 g_free((gchar*)value);
549 }
550
551 static void
552 close_ini_file_free_section(gpointer section)
553 {
554 g_hash_table_destroy((GHashTable*)section);
555 }
556
557 INIFile *
558 open_ini_file(const gchar *filename)
559 {
560 GHashTable *ini_file = NULL;
561 GHashTable *section = NULL;
562 GString *section_name, *key_name, *value;
563 gpointer section_hash, key_hash;
564 gchar *buffer = NULL;
565 gsize off = 0;
566 gsize filesize = 0;
567
568 unsigned char x[] = { 0xff, 0xfe, 0x00 };
569
570 g_return_val_if_fail(filename, NULL);
571 aud_vfs_file_get_contents(filename, &buffer, &filesize);
572 if (buffer == NULL)
573 return NULL;
574
575 /*
576 * Convert UTF-16 into something useful. Original implementation
577 * by incomp@#audacious. Cleanups \nenolod
578 * FIXME: can't we use a GLib function for that? -- 01mf02
579 */
580 if (filesize > 2 && !memcmp(&buffer[0],&x,2))
581 {
582 gchar *outbuf = g_malloc (filesize); /* it's safe to waste memory. */
583 guint counter;
584
585 for (counter = 2; counter < filesize; counter += 2)
586 {
587 if (!memcmp(&buffer[counter+1], &x[2], 1)) {
588 outbuf[(counter-2)/2] = buffer[counter];
589 } else {
590 g_free(buffer);
591 g_free(outbuf);
592 return NULL;
593 }
594 }
595
596 outbuf[(counter-2)/2] = '\0';
597
598 if ((filesize - 2) / 2 == (counter - 2) / 2)
599 {
600 g_free(buffer);
601 buffer = outbuf;
602 }
603 else
604 {
605 g_free(buffer);
606 g_free(outbuf);
607 return NULL; /* XXX wrong encoding */
608 }
609 }
610
611 section_name = g_string_new("");
612 key_name = g_string_new(NULL);
613 value = g_string_new(NULL);
614
615 ini_file = g_hash_table_new_full(NULL, NULL, NULL,
616 close_ini_file_free_section);
617 section = g_hash_table_new_full(NULL, NULL, NULL,
618 close_ini_file_free_value);
619 /* make a nameless section which should store all entries that are not
620 * embedded in a section */
621 section_hash = GINT_TO_POINTER(g_string_hash(section_name));
622 g_hash_table_insert(ini_file, section_hash, section);
623
624 while (off < filesize)
625 {
626 /* ignore the following characters */
627 if (buffer[off] == '\r' || buffer[off] == '\n' ||
628 buffer[off] == ' ' || buffer[off] == '\t')
629 {
630 if (buffer[off] == '\n')
631 {
632 g_string_free(key_name, TRUE);
633 g_string_free(value, TRUE);
634 key_name = g_string_new(NULL);
635 value = g_string_new(NULL);
636 }
637
638 off++;
639 continue;
640 }
641
642 /* if we encounter a possible section statement */
643 if (buffer[off] == '[')
644 {
645 g_string_free(section_name, TRUE);
646 section_name = g_string_new(NULL);
647 off++;
648
649 if (off >= filesize)
650 goto return_sequence;
651
652 while (buffer[off] != ']')
653 {
654 /* if the section statement has not been closed before a
655 * linebreak */
656 if (buffer[off] == '\n')
657 break;
658
659 g_string_append_c(section_name, buffer[off]);
660 off++;
661 if (off >= filesize)
662 goto return_sequence;
663 }
664 if (buffer[off] == '\n')
665 continue;
666 if (buffer[off] == ']')
667 {
668 off++;
669 if (off >= filesize)
670 goto return_sequence;
671
672 strip_lower_string(section_name);
673 section_hash = GINT_TO_POINTER(g_string_hash(section_name));
674
675 /* if this section already exists, we don't make a new one,
676 * but reuse the old one */
677 if (g_hash_table_lookup(ini_file, section_hash) != NULL)
678 section = g_hash_table_lookup(ini_file, section_hash);
679 else
680 {
681 section = g_hash_table_new_full(NULL, NULL, NULL,
682 close_ini_file_free_value);
683 g_hash_table_insert(ini_file, section_hash, section);
684 }
685
686 continue;
687 }
688 }
689
690 if (buffer[off] == '=')
691 {
692 off++;
693 if (off >= filesize)
694 goto return_sequence;
695
696 while (buffer[off] != '\n' && buffer[off] != '\r')
697 {
698 g_string_append_c(value, buffer[off]);
699 off++;
700 if (off >= filesize)
701 break;
702 }
703
704 strip_lower_string(key_name);
705 key_hash = GINT_TO_POINTER(g_string_hash(key_name));
706 strip_string(value);
707
708 if (key_name->len > 0 && value->len > 0)
709 g_hash_table_insert(section, key_hash, g_strdup(value->str));
710 }
711 else
712 {
713 g_string_append_c(key_name, buffer[off]);
714 off++;
715 if (off >= filesize)
716 goto return_sequence;
717 }
718 }
719
720 return_sequence:
721 g_string_free(section_name, TRUE);
722 g_string_free(key_name, TRUE);
723 g_string_free(value, TRUE);
724 g_free(buffer);
725 return ini_file;
726 }
727
728 /**
729 * Frees the memory allocated for inifile.
730 */
731 void
732 close_ini_file(INIFile *inifile)
733 {
734 g_return_if_fail(inifile);
735 g_hash_table_destroy(inifile);
736 }
737
738 /**
739 * Returns a string that corresponds to correct section and key in inifile.
740 *
741 * Returns NULL if value was not found in inifile. Otherwise returns a copy
742 * of string pointed by "section" and "key". Returned string should be freed
743 * after use.
744 */
745 gchar *
746 read_ini_string(INIFile *inifile, const gchar *section, const gchar *key)
747 {
748 GString *section_string;
749 GString *key_string;
750 gchar *value = NULL;
751 gpointer section_hash, key_hash;
752 GHashTable *section_table;
753
754 g_return_val_if_fail(inifile, NULL);
755
756 section_string = g_string_new(section);
757 key_string = g_string_new(key);
758 value = NULL;
759
760 strip_lower_string(section_string);
761 strip_lower_string(key_string);
762 section_hash = GINT_TO_POINTER(g_string_hash(section_string));
763 key_hash = GINT_TO_POINTER(g_string_hash(key_string));
764 section_table = g_hash_table_lookup(inifile, section_hash);
765
766 if (section_table) {
767 value = g_strdup(g_hash_table_lookup(section_table,
768 GINT_TO_POINTER(key_hash)));
769 }
770
771 g_string_free(section_string, TRUE);
772 g_string_free(key_string, TRUE);
773
774 g_return_val_if_fail(value, NULL);
775 return value;
776 }
777
778 GArray *
779 read_ini_array(INIFile *inifile, const gchar *section, const gchar *key)
780 {
781 gchar *temp;
782 GArray *a;
783
784 g_return_val_if_fail((temp = read_ini_string(inifile, section, key)), NULL);
785
786 a = string_to_garray(temp);
787 g_free(temp);
788 return a;
789 }
790
791 GArray *
792 string_to_garray(const gchar * str)
793 {
794 GArray *array;
795 gint temp;
796 const gchar *ptr = str;
797 gchar *endptr;
798
799 array = g_array_new(FALSE, TRUE, sizeof(gint));
800 for (;;) {
801 temp = strtol(ptr, &endptr, 10);
802 if (ptr == endptr)
803 break;
804 g_array_append_val(array, temp);
805 ptr = endptr;
806 while (!isdigit((int) *ptr) && (*ptr) != '\0')
807 ptr++;
808 if (*ptr == '\0')
809 break;
810 }
811 return (array);
812 }
813
814 void
815 glist_movedown(GList * list)
816 {
817 gpointer temp;
818
819 if (g_list_next(list)) {
820 temp = list->data;
821 list->data = list->next->data;
822 list->next->data = temp;
823 }
824 }
825
826 void
827 glist_moveup(GList * list)
828 {
829 gpointer temp;
830
831 if (g_list_previous(list)) {
832 temp = list->data;
833 list->data = list->prev->data;
834 list->prev->data = temp;
835 }
836 }
837
838 GdkFont *
839 util_font_load(const gchar * name)
840 {
841 GdkFont *font;
842 PangoFontDescription *desc;
843
844 desc = pango_font_description_from_string(name);
845 font = gdk_font_from_description(desc);
846
847 return font;
848 }
849 #if 0
850 /* text_get_extents() taken from The GIMP (C) Spencer Kimball, Peter
851 * Mattis et al */
852 gboolean
853 text_get_extents(const gchar * fontname,
854 const gchar * text,
855 gint * width, gint * height, gint * ascent, gint * descent)
856 {
857 PangoFontDescription *font_desc;
858 PangoLayout *layout;
859 PangoRectangle rect;
860
861 g_return_val_if_fail(fontname != NULL, FALSE);
862 g_return_val_if_fail(text != NULL, FALSE);
863
864 /* FIXME: resolution */
865 layout = gtk_widget_create_pango_layout(GTK_WIDGET(mainwin), text);
866
867 font_desc = pango_font_description_from_string(fontname);
868 pango_layout_set_font_description(layout, font_desc);
869 pango_font_description_free(font_desc);
870 pango_layout_get_pixel_extents(layout, NULL, &rect);
871
872 if (width)
873 *width = rect.width;
874 if (height)
875 *height = rect.height;
876
877 if (ascent || descent) {
878 PangoLayoutIter *iter;
879 PangoLayoutLine *line;
880
881 iter = pango_layout_get_iter(layout);
882 line = pango_layout_iter_get_line(iter);
883 pango_layout_iter_free(iter);
884
885 pango_layout_line_get_pixel_extents(line, NULL, &rect);
886
887 if (ascent)
888 *ascent = PANGO_ASCENT(rect);
889 if (descent)
890 *descent = -PANGO_DESCENT(rect);
891 }
892
893 g_object_unref(layout);
894
895 return TRUE;
896 }
897 #endif
898 /* counts number of digits in a gint */
899 guint
900 gint_count_digits(gint n)
901 {
902 guint count = 0;
903
904 n = ABS(n);
905 do {
906 count++;
907 n /= 10;
908 } while (n > 0);
909
910 return count;
911 }
912
913 gboolean
914 dir_foreach(const gchar * path, DirForeachFunc function,
915 gpointer user_data, GError ** error)
916 {
917 GError *error_out = NULL;
918 GDir *dir;
919 const gchar *entry;
920 gchar *entry_fullpath;
921
922 if (!(dir = g_dir_open(path, 0, &error_out))) {
923 g_propagate_error(error, error_out);
924 return FALSE;
925 }
926
927 while ((entry = g_dir_read_name(dir))) {
928 entry_fullpath = g_build_filename(path, entry, NULL);
929
930 if ((*function) (entry_fullpath, entry, user_data)) {
931 g_free(entry_fullpath);
932 break;
933 }
934
935 g_free(entry_fullpath);
936 }
937
938 g_dir_close(dir);
939
940 return TRUE;
941 }
942
943 GtkWidget *
944 make_filebrowser(const gchar *title, gboolean save)
945 {
946 GtkWidget *dialog;
947 GtkWidget *button;
948
949 g_return_val_if_fail(title != NULL, NULL);
950
951 dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(mainwin),
952 save ?
953 GTK_FILE_CHOOSER_ACTION_SAVE :
954 GTK_FILE_CHOOSER_ACTION_OPEN,
955 NULL, NULL);
956
957 button = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL,
958 GTK_RESPONSE_REJECT);
959
960 gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
961 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
962
963 button = gtk_dialog_add_button(GTK_DIALOG(dialog), save ?
964 GTK_STOCK_SAVE : GTK_STOCK_OPEN,
965 GTK_RESPONSE_ACCEPT);
966
967 gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
968 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
969 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); /* centering */
970
971 return dialog;
972 }
973
974 /**
975 * util_info_dialog:
976 * @title: The title of the message to show.
977 * @text: The text of the message to show.
978 * @button_text: The text of the button which will close the messagebox.
979 * @modal: Whether or not the messagebox should be modal.
980 * @button_action: Code to execute on when the messagebox is closed, or %NULL.
981 * @action_data: Optional opaque data to pass to @button_action.
982 *
983 * Displays a message box.
984 *
985 * Return value: A GTK widget handle for the message box.
986 **/
987 GtkWidget *
988 util_info_dialog(const gchar * title, const gchar * text,
989 const gchar * button_text, gboolean modal,
990 GCallback button_action, gpointer action_data)
991 {
992 GtkWidget *dialog;
993 GtkWidget *dialog_vbox, *dialog_hbox, *dialog_bbox;
994 GtkWidget *dialog_bbox_b1;
995 GtkWidget *dialog_textlabel;
996 GtkWidget *dialog_icon;
997
998 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
999 gtk_window_set_type_hint( GTK_WINDOW(dialog) , GDK_WINDOW_TYPE_HINT_DIALOG );
1000 gtk_window_set_modal( GTK_WINDOW(dialog) , modal );
1001 gtk_window_set_title( GTK_WINDOW(dialog) , title );
1002 gtk_container_set_border_width( GTK_CONTAINER(dialog) , 10 );
1003
1004 dialog_vbox = gtk_vbox_new( FALSE , 0 );
1005 dialog_hbox = gtk_hbox_new( FALSE , 0 );
1006
1007 /* icon */
1008 dialog_icon = gtk_image_new_from_stock( GTK_STOCK_DIALOG_INFO , GTK_ICON_SIZE_DIALOG );
1009 gtk_box_pack_start( GTK_BOX(dialog_hbox) , dialog_icon , FALSE , FALSE , 2 );
1010
1011 /* label */
1012 dialog_textlabel = gtk_label_new( text );
1013 /* gtk_label_set_selectable( GTK_LABEL(dialog_textlabel) , TRUE ); */
1014 gtk_box_pack_start( GTK_BOX(dialog_hbox) , dialog_textlabel , TRUE , TRUE , 2 );
1015
1016 gtk_box_pack_start( GTK_BOX(dialog_vbox) , dialog_hbox , FALSE , FALSE , 2 );
1017 gtk_box_pack_start( GTK_BOX(dialog_vbox) , gtk_hseparator_new() , FALSE , FALSE , 4 );
1018
1019 dialog_bbox = gtk_hbutton_box_new();
1020 gtk_button_box_set_layout( GTK_BUTTON_BOX(dialog_bbox) , GTK_BUTTONBOX_END );
1021 dialog_bbox_b1 = gtk_button_new_with_label( button_text );
1022 g_signal_connect_swapped( G_OBJECT(dialog_bbox_b1) , "clicked" ,
1023 G_CALLBACK(gtk_widget_destroy) , dialog );
1024 if ( button_action )
1025 g_signal_connect( G_OBJECT(dialog_bbox_b1) , "clicked" ,
1026 button_action , action_data );
1027
1028 gtk_container_add( GTK_CONTAINER(dialog_bbox) , dialog_bbox_b1 );
1029 gtk_box_pack_start( GTK_BOX(dialog_vbox) , dialog_bbox , FALSE , FALSE , 0 );
1030
1031 gtk_container_add( GTK_CONTAINER(dialog) , dialog_vbox );
1032
1033 GTK_WIDGET_SET_FLAGS( dialog_bbox_b1 , GTK_CAN_DEFAULT);
1034 gtk_widget_grab_default( dialog_bbox_b1 );
1035
1036 gtk_widget_show_all(dialog);
1037
1038 return dialog;
1039 }
1040
1041
1042 /**
1043 * util_get_localdir:
1044 *
1045 * Returns a string with the full path of Audacious local datadir (where config files are placed).
1046 * It's useful in order to put in the right place custom config files for audacious plugins.
1047 *
1048 * Return value: a string with full path of Audacious local datadir (should be freed after use)
1049 **/
1050 gchar*
1051 util_get_localdir(void)
1052 {
1053 gchar *datadir;
1054 gchar *tmp;
1055
1056 if ( (tmp = getenv("XDG_CONFIG_HOME")) == NULL )
1057 datadir = g_build_filename( g_get_home_dir() , ".config" , "audacious" , NULL );
1058 else
1059 datadir = g_build_filename( tmp , "audacious" , NULL );
1060
1061 return datadir;
1062 }
1063
1064
1065 gchar *
1066 construct_uri(gchar *string, const gchar *playlist_name) // uri, path and anything else
1067 {
1068 gchar *filename = g_strdup(string);
1069 gchar *tmp, *path;
1070 gchar *uri = NULL;
1071
1072 /* try to translate dos path */
1073 convert_dos_path(filename); /* in place replacement */
1074
1075 // make full path uri here
1076 // case 1: filename is raw full path or uri
1077 if (filename[0] == '/' || strstr(filename, "://")) {
1078 uri = g_filename_to_uri(filename, NULL, NULL);
1079 if(!uri) {
1080 uri = g_strdup(filename);
1081 }
1082 g_free(filename);
1083 }
1084 // case 2: filename is not raw full path nor uri, playlist path is full path
1085 // make full path by replacing last part of playlist path with filename. (using g_build_filename)
1086 else if (playlist_name[0] == '/' || strstr(playlist_name, "://")) {
1087 path = g_filename_from_uri(playlist_name, NULL, NULL);
1088 if (!path) {
1089 path = g_strdup(playlist_name);
1090 }
1091 tmp = strrchr(path, '/'); *tmp = '\0';
1092 tmp = g_build_filename(path, filename, NULL);
1093 g_free(path); g_free(filename);
1094 uri = g_filename_to_uri(tmp, NULL, NULL);
1095 g_free(tmp);
1096 }
1097 // case 3: filename is not raw full path nor uri, playlist path is not full path
1098 // just abort.
1099 else {
1100 g_free(filename);
1101 return NULL;
1102 }
1103
1104 AUDDBG("uri=%s\n", uri);
1105 return uri;
1106 }
1107
1108 /*
1109 * minimize number of realloc's:
1110 * - set N to nearest power of 2 not less then N
1111 * - double it
1112 *
1113 * -- asphyx
1114 *
1115 * XXX: what's so smart about this?? seems wasteful and silly. --nenolod
1116 */
1117 gpointer
1118 smart_realloc(gpointer ptr, gsize *size)
1119 {
1120 *size = (size_t)pow(2, ceil(log(*size) / log(2)) + 1);
1121 if (ptr != NULL) free(ptr);
1122 ptr = malloc(*size);
1123 return ptr;
1124 }
1125
1126 void
1127 make_directory(const gchar * path, mode_t mode)
1128 {
1129 if (g_mkdir_with_parents(path, mode) == 0)
1130 return;
1131
1132 g_printerr(_("Could not create directory (%s): %s\n"), path,
1133 g_strerror(errno));
1134 }