1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * GTK native dialog implementation.
12 *
13 * See LICENSE.txt for copyright information.
14 */
15
16 #include <gtk/gtk.h>
17
18 #include "allegro5/allegro.h"
19 #include "allegro5/allegro_native_dialog.h"
20 #include "allegro5/internal/aintern_native_dialog.h"
21 #include "gtk_dialog.h"
22 #include "gtk_xgtk.h"
23
24 typedef struct {
25 ALLEGRO_DISPLAY *display;
26 ALLEGRO_NATIVE_DIALOG *dialog;
27 } GTK_FILE_DIALOG_MESSAGE;
28
29 /* [nd_gtk thread] */
create_gtk_file_dialog(gpointer data)30 static gboolean create_gtk_file_dialog(gpointer data)
31 {
32 GTK_FILE_DIALOG_MESSAGE *msg = data;
33 ALLEGRO_DISPLAY *display = msg->display;
34 ALLEGRO_NATIVE_DIALOG *fd = msg->dialog;
35 bool save = fd->flags & ALLEGRO_FILECHOOSER_SAVE;
36 bool folder = fd->flags & ALLEGRO_FILECHOOSER_FOLDER;
37 gint result;
38
39 GtkWidget *window;
40
41 window =
42 gtk_file_chooser_dialog_new(al_cstr(fd->title),
43 NULL,
44 save ? GTK_FILE_CHOOSER_ACTION_SAVE : folder ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : GTK_FILE_CHOOSER_ACTION_OPEN,
45 "_Cancel", GTK_RESPONSE_CANCEL,
46 save ? "_Save" : "_Open", GTK_RESPONSE_ACCEPT, NULL);
47
48 _al_gtk_make_transient(display, window);
49
50 if (save) {
51 gtk_file_chooser_set_do_overwrite_confirmation
52 (GTK_FILE_CHOOSER(window), true);
53 }
54
55 if (fd->fc_initial_path) {
56 bool is_dir;
57 bool exists;
58 const char *path = al_path_cstr(fd->fc_initial_path, ALLEGRO_NATIVE_PATH_SEP);
59
60 if (al_filename_exists(path)) {
61 exists = true;
62 ALLEGRO_FS_ENTRY *fs = al_create_fs_entry(path);
63 is_dir = al_get_fs_entry_mode(fs) & ALLEGRO_FILEMODE_ISDIR;
64 al_destroy_fs_entry(fs);
65 }
66 else {
67 exists = false;
68 is_dir = false;
69 }
70
71 if (is_dir) {
72 gtk_file_chooser_set_current_folder
73 (GTK_FILE_CHOOSER(window),
74 al_path_cstr(fd->fc_initial_path, ALLEGRO_NATIVE_PATH_SEP));
75 }
76 else if (exists) {
77 gtk_file_chooser_set_filename
78 (GTK_FILE_CHOOSER(window),
79 al_path_cstr(fd->fc_initial_path, ALLEGRO_NATIVE_PATH_SEP));
80 }
81 else {
82 ALLEGRO_PATH *dir_path = al_clone_path(fd->fc_initial_path);
83 if (dir_path) {
84 al_set_path_filename(dir_path, NULL);
85 gtk_file_chooser_set_current_folder
86 (GTK_FILE_CHOOSER(window),
87 al_path_cstr(dir_path, ALLEGRO_NATIVE_PATH_SEP));
88 if (save) {
89 gtk_file_chooser_set_current_name
90 (GTK_FILE_CHOOSER(window),
91 al_get_path_filename(fd->fc_initial_path));
92 }
93 al_destroy_path(dir_path);
94 }
95 }
96 }
97
98 if (fd->flags & ALLEGRO_FILECHOOSER_MULTIPLE)
99 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(window), true);
100
101 /* FIXME: Move all this filter parsing stuff into a common file. */
102 if (al_ustr_size(fd->fc_patterns) > 0) {
103 GtkFileFilter* filter = gtk_file_filter_new();
104 int start = 0;
105 int end = 0;
106 bool is_mime_type = false;
107 while (true) {
108 int32_t c = al_ustr_get(fd->fc_patterns, end);
109 if (c < 0 || c == ';') {
110 if (end - start > 0) {
111 ALLEGRO_USTR* pattern = al_ustr_dup_substr(fd->fc_patterns, start, end);
112 if (is_mime_type) {
113 gtk_file_filter_add_mime_type(filter, al_cstr(pattern));
114 }
115 else {
116 gtk_file_filter_add_pattern(filter, al_cstr(pattern));
117 }
118 al_ustr_free(pattern);
119 }
120 start = end + 1;
121 is_mime_type = false;
122 }
123 if (c == '/')
124 is_mime_type = true;
125 if (c < 0)
126 break;
127 end += al_utf8_width(c);
128 }
129
130 gtk_file_filter_set_name(filter, "All supported files");
131 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(window), filter);
132 }
133
134 result = gtk_dialog_run(GTK_DIALOG(window));
135 if (result == GTK_RESPONSE_ACCEPT) {
136 GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(window));
137 int i;
138 GSList* iter;
139
140 fd->fc_path_count = g_slist_length(filenames);
141 fd->fc_paths = al_malloc(fd->fc_path_count * sizeof(void *));
142 for (i = 0, iter = filenames; i < (int)fd->fc_path_count; i++, iter = g_slist_next(iter)) {
143 fd->fc_paths[i] = al_create_path((const char*)iter->data);
144 g_free(iter->data);
145 }
146 g_slist_free(filenames);
147 }
148
149 gtk_widget_destroy(window);
150
151 ASSERT(fd->async_queue);
152 g_async_queue_push(fd->async_queue, ACK_CLOSED);
153
154 return FALSE;
155 }
156
157 /* [user thread] */
_al_show_native_file_dialog(ALLEGRO_DISPLAY * display,ALLEGRO_NATIVE_DIALOG * fd)158 bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display,
159 ALLEGRO_NATIVE_DIALOG *fd)
160 {
161 GTK_FILE_DIALOG_MESSAGE msg;
162
163 if (!_al_gtk_ensure_thread())
164 return false;
165
166 fd->async_queue = g_async_queue_new();
167
168 msg.display = display;
169 msg.dialog = fd;
170 g_timeout_add(0, create_gtk_file_dialog, &msg);
171
172 /* Wait for a signal that the window is closed. */
173 while (g_async_queue_pop(fd->async_queue) != ACK_CLOSED)
174 ;
175 g_async_queue_unref(fd->async_queue);
176 fd->async_queue = NULL;
177 return true;
178 }
179
180 /* vim: set sts=3 sw=3 et: */
181