Missed an important bugfix from the beta2 NEWS. Oh well.
[freeciv.git] / tools / mpgui_gtk2.c
blobff98a26ebef10a6078d08730a54a5af136b8b424
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdlib.h>
20 #include <gtk/gtk.h>
22 /* utility */
23 #include "fciconv.h"
24 #include "fcintl.h"
25 #include "log.h"
26 #include "mem.h"
27 #include "netintf.h"
28 #include "registry.h"
29 #include "shared.h"
31 /* common */
32 #include "version.h"
34 /* modinst */
35 #include "download.h"
36 #include "mpcmdline.h"
37 #include "mpdb.h"
39 #include "modinst.h"
41 static GtkWidget *statusbar;
42 static GtkWidget *progressbar;
43 static GtkWidget *main_list;
44 static GtkListStore *main_store;
45 static GtkWidget *URL_input;
46 static gboolean downloading = FALSE;
48 struct fcmp_params fcmp = {
49 .list_url = MODPACK_LIST_URL,
50 .inst_prefix = NULL,
51 .autoinstall = NULL
54 static gboolean quit_dialog_callback(void);
56 #define ML_COL_NAME 0
57 #define ML_COL_VER 1
58 #define ML_COL_INST 2
59 #define ML_COL_TYPE 3
60 #define ML_COL_SUBTYPE 4
61 #define ML_COL_LIC 5
62 #define ML_COL_URL 6
64 #define ML_COL_COUNT 7
66 #define ML_TYPE 7
67 #define ML_NOTES 8
68 #define ML_STORE_SIZE 9
70 /****************************************************************
71 freeciv-modpack quit
72 ****************************************************************/
73 static void modinst_quit(void)
75 save_install_info_lists(&fcmp);
77 registry_module_close();
78 free_nls();
80 exit(EXIT_SUCCESS);
83 /****************************************************************
84 This is the response callback for the dialog with the message:
85 Are you sure you want to quit?
86 ****************************************************************/
87 static void quit_dialog_response(GtkWidget *dialog, gint response)
89 gtk_widget_destroy(dialog);
90 if (response == GTK_RESPONSE_YES) {
91 modinst_quit();
95 /****************************************************************
96 Popups the quit dialog if
97 ****************************************************************/
98 static gboolean quit_dialog_callback(void)
100 if (downloading) {
101 /* Download in progress. Confirm quit from user. */
102 static GtkWidget *dialog;
104 if (!dialog) {
105 dialog = gtk_message_dialog_new(NULL,
107 GTK_MESSAGE_WARNING,
108 GTK_BUTTONS_YES_NO,
109 _("Are you sure you want to quit?"));
111 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
113 g_signal_connect(dialog, "response",
114 G_CALLBACK(quit_dialog_response), NULL);
115 g_signal_connect(dialog, "destroy",
116 G_CALLBACK(gtk_widget_destroyed), &dialog);
119 gtk_window_present(GTK_WINDOW(dialog));
121 } else {
122 /* User loses no work by quitting, so let's not annoy him/her
123 * with confirmation dialog. */
124 modinst_quit();
127 /* Stop emission of event. */
128 return TRUE;
131 /**************************************************************************
132 Progress indications from downloader
133 **************************************************************************/
134 static void msg_callback(const char *msg)
136 gtk_label_set_text(GTK_LABEL(statusbar), msg);
139 /**************************************************************************
140 Progress indications from downloader
141 **************************************************************************/
142 static void pbar_callback(int downloaded, int max)
144 /* Control file already downloaded */
145 double fraction = (double) downloaded / (max);
147 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), fraction);
151 struct msg_data {
152 const char *msg;
155 /**************************************************************************
156 Main thread handling of message sent from downloader thread.
157 **************************************************************************/
158 static gboolean msg_main_thread(gpointer user_data)
160 struct msg_data *data = (struct msg_data *)user_data;
162 msg_callback(data->msg);
164 FC_FREE(data);
166 return FALSE;
169 /**************************************************************************
170 Downloader thread message callback.
171 **************************************************************************/
172 static void msg_dl_thread(const char *msg)
174 struct msg_data *data = fc_malloc(sizeof(*data));
176 data->msg = msg;
178 gdk_threads_add_idle(msg_main_thread, data);
181 struct pbar_data {
182 int current;
183 int max;
186 /**************************************************************************
187 Main thread handling of progressbar update sent from downloader thread.
188 **************************************************************************/
189 static gboolean pbar_main_thread(gpointer user_data)
191 struct pbar_data *data = (struct pbar_data *)user_data;
193 pbar_callback(data->current, data->max);
195 FC_FREE(data);
197 return FALSE;
200 /**************************************************************************
201 Downloader thread progress bar callback.
202 **************************************************************************/
203 static void pbar_dl_thread(int current, int max)
205 struct pbar_data *data = fc_malloc(sizeof(*data));
207 data->current = current;
208 data->max = max;
210 gdk_threads_add_idle(pbar_main_thread, data);
213 /**************************************************************************
214 Main thread handling of versionlist update requested by downloader thread
215 **************************************************************************/
216 static gboolean versionlist_update_main_thread(gpointer user_data)
218 GtkTreeIter iter;
220 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(main_store), &iter)) {
221 do {
222 const char *name_str;
223 int type_int;
224 const char *new_inst;
225 enum modpack_type type;
227 gtk_tree_model_get(GTK_TREE_MODEL(main_store), &iter,
228 ML_COL_NAME, &name_str,
229 ML_TYPE, &type_int,
230 -1);
232 type = type_int;
234 new_inst = get_installed_version(name_str, type);
236 if (new_inst == NULL) {
237 new_inst = _("Not installed");
240 gtk_list_store_set(main_store, &iter,
241 ML_COL_INST, new_inst,
242 -1);
244 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(main_store), &iter));
247 return FALSE;
250 /**************************************************************************
251 Downloader thread requests versionlist update.
252 **************************************************************************/
253 static void versionlist_update_dl_thread(void)
255 gdk_threads_add_idle(versionlist_update_main_thread, NULL);
259 /**************************************************************************
260 Entry point for downloader thread
261 **************************************************************************/
262 static gpointer download_thread(gpointer data)
264 const char *errmsg;
266 errmsg = download_modpack(data, &fcmp, msg_dl_thread, pbar_dl_thread);
268 if (errmsg == NULL) {
269 msg_dl_thread(_("Ready"));
270 } else {
271 msg_dl_thread(errmsg);
274 free(data);
276 versionlist_update_dl_thread();
278 downloading = FALSE;
280 return NULL;
283 /**************************************************************************
284 Download modpack, display error message dialogs
285 **************************************************************************/
286 static void gui_download_modpack(const char *URL)
288 GThread *downloader;
289 char *URLbuf;
291 if (downloading) {
292 gtk_label_set_text(GTK_LABEL(statusbar),
293 _("Another download already active"));
294 return;
297 downloading = TRUE;
299 URLbuf = fc_malloc(strlen(URL) + 1);
301 strcpy(URLbuf, URL);
303 downloader = g_thread_create(download_thread, URLbuf, FALSE, NULL);
304 if (downloader == NULL) {
305 gtk_label_set_text(GTK_LABEL(statusbar),
306 _("Failed to start downloader"));
307 free(URLbuf);
311 /**************************************************************************
312 Install modpack button clicked
313 **************************************************************************/
314 static void install_clicked(GtkWidget *w, gpointer data)
316 GtkEntry *URL_in = data;
317 const char *URL = gtk_entry_get_text(URL_in);
319 gui_download_modpack(URL);
322 /**************************************************************************
323 URL entered
324 **************************************************************************/
325 static void URL_return(GtkEntry *w, gpointer data)
327 const char *URL;
329 URL = gtk_entry_get_text(w);
330 gui_download_modpack(URL);
333 /**************************************************************************
334 Callback for getting main list row tooltip
335 **************************************************************************/
336 static gboolean query_main_list_tooltip_cb(GtkWidget *widget,
337 gint x, gint y,
338 gboolean keyboard_tip,
339 GtkTooltip *tooltip,
340 gpointer data)
342 GtkTreeIter iter;
343 GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
344 GtkTreeModel *model;
345 const char *notes;
347 if (!gtk_tree_view_get_tooltip_context(tree_view, &x, &y,
348 keyboard_tip,
349 &model, NULL, &iter)) {
350 return FALSE;
353 gtk_tree_model_get(model, &iter,
354 ML_NOTES, &notes,
355 -1);
357 if (notes != NULL) {
358 gtk_tooltip_set_markup(tooltip, notes);
360 return TRUE;
363 return FALSE;
366 /**************************************************************************
367 Build main modpack list view
368 **************************************************************************/
369 static void setup_modpack_list(const char *name, const char *URL,
370 const char *version, const char *license,
371 enum modpack_type type, const char *subtype,
372 const char *notes)
374 GtkTreeIter iter;
375 const char *type_str;
376 const char *lic_str;
377 const char *inst_str;
379 if (modpack_type_is_valid(type)) {
380 type_str = _(modpack_type_name(type));
381 } else {
382 /* TRANS: Unknown modpack type */
383 type_str = _("?");
386 if (license != NULL) {
387 lic_str = license;
388 } else {
389 /* TRANS: License of modpack is not known */
390 lic_str = Q_("?license:Unknown");
393 inst_str = get_installed_version(name, type);
394 if (inst_str == NULL) {
395 inst_str = _("Not installed");
398 gtk_list_store_append(main_store, &iter);
399 gtk_list_store_set(main_store, &iter,
400 ML_COL_NAME, name,
401 ML_COL_VER, version,
402 ML_COL_INST, inst_str,
403 ML_COL_TYPE, type_str,
404 ML_COL_SUBTYPE, subtype,
405 ML_COL_LIC, lic_str,
406 ML_COL_URL, URL,
407 ML_TYPE, type,
408 ML_NOTES, notes,
409 -1);
412 /**************************************************************************
413 Callback called when entry from main modpack list selected
414 **************************************************************************/
415 static void select_from_list(GtkTreeSelection *select, gpointer data)
417 GtkTreeModel *model;
418 GtkTreeIter it;
419 const char *URL;
421 if (!gtk_tree_selection_get_selected(select, &model, &it)) {
422 return;
425 gtk_tree_model_get(model, &it, ML_COL_URL, &URL, -1);
427 gtk_entry_set_text(GTK_ENTRY(URL_input), URL);
430 /**************************************************************************
431 Build widgets
432 **************************************************************************/
433 static void modinst_setup_widgets(GtkWidget *toplevel)
435 GtkWidget *mbox, *Ubox;
436 GtkWidget *version_label;
437 GtkWidget *install_button, *install_label;
438 GtkWidget *URL_label;
439 GtkCellRenderer *renderer;
440 GtkTreeSelection *selection;
441 const char *errmsg;
442 char verbuf[2048];
443 const char *rev_ver = fc_svn_revision();
445 mbox = gtk_vbox_new(FALSE, 4);
447 if (rev_ver == NULL) {
448 rev_ver = fc_git_revision();
450 if (rev_ver == NULL) {
451 fc_snprintf(verbuf, sizeof(verbuf), "%s%s", word_version(), VERSION_STRING);
452 } else {
453 fc_snprintf(verbuf, sizeof(verbuf), _("%s%s\ncommit: %s"),
454 word_version(), VERSION_STRING, rev_ver);
456 } else {
457 fc_snprintf(verbuf, sizeof(verbuf), "%s%s (%s)", word_version(), VERSION_STRING,
458 rev_ver);
461 version_label = gtk_label_new(verbuf);
463 main_list = gtk_tree_view_new();
464 renderer = gtk_cell_renderer_text_new();
465 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list),
466 ML_COL_NAME,
467 _("Name"), renderer, "text", 0,
468 NULL);
469 renderer = gtk_cell_renderer_text_new();
470 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list),
471 ML_COL_VER,
472 _("Version"), renderer, "text", 1,
473 NULL);
474 renderer = gtk_cell_renderer_text_new();
475 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list),
476 ML_COL_INST,
477 _("Installed"), renderer, "text", 2,
478 NULL);
479 renderer = gtk_cell_renderer_text_new();
480 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list),
481 ML_COL_TYPE,
482 Q_("?modpack:Type"),
483 renderer, "text", 3,
484 NULL);
485 renderer = gtk_cell_renderer_text_new();
486 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list),
487 ML_COL_SUBTYPE,
488 _("Subtype"),
489 renderer, "text", 4,
490 NULL);
491 renderer = gtk_cell_renderer_text_new();
492 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list),
493 ML_COL_LIC,
494 /* TRANS: noun */
495 _("License"), renderer, "text", 5,
496 NULL);
497 renderer = gtk_cell_renderer_text_new();
498 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list),
499 ML_COL_URL,
500 _("URL"), renderer, "text", 6,
501 NULL);
502 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_list));
503 g_signal_connect(selection, "changed", G_CALLBACK(select_from_list), NULL);
505 install_button = gtk_button_new();
506 install_label = gtk_label_new(_("Install modpack"));
507 gtk_label_set_mnemonic_widget(GTK_LABEL(install_label), install_button);
508 g_object_set_data(G_OBJECT(install_button), "label", install_label);
509 gtk_container_add(GTK_CONTAINER(install_button), install_label);
511 Ubox = gtk_hbox_new(FALSE, 4);
512 URL_label = gtk_label_new_with_mnemonic(_("Modpack URL"));
514 URL_input = gtk_entry_new();
515 gtk_entry_set_width_chars(GTK_ENTRY(URL_input),
516 strlen(EXAMPLE_URL));
517 gtk_entry_set_text(GTK_ENTRY(URL_input), DEFAULT_URL_START);
518 g_signal_connect(URL_input, "activate",
519 G_CALLBACK(URL_return), NULL);
521 g_signal_connect(install_button, "clicked",
522 G_CALLBACK(install_clicked), URL_input);
524 gtk_box_pack_start(GTK_BOX(Ubox), URL_label, TRUE, TRUE, 0);
525 gtk_box_pack_end(GTK_BOX(Ubox), URL_input, TRUE, TRUE, 0);
527 progressbar = gtk_progress_bar_new();
529 statusbar = gtk_label_new(_("Select modpack to install"));
531 gtk_box_pack_start(GTK_BOX(mbox), version_label, TRUE, TRUE, 0);
532 gtk_box_pack_start(GTK_BOX(mbox), main_list, TRUE, TRUE, 0);
533 gtk_box_pack_start(GTK_BOX(mbox), Ubox, TRUE, TRUE, 0);
534 gtk_box_pack_start(GTK_BOX(mbox), install_button, TRUE, TRUE, 0);
535 gtk_box_pack_start(GTK_BOX(mbox), progressbar, TRUE, TRUE, 0);
536 gtk_box_pack_end(GTK_BOX(mbox), statusbar, TRUE, TRUE, 0);
538 gtk_container_add(GTK_CONTAINER(toplevel), mbox);
540 main_store = gtk_list_store_new((ML_STORE_SIZE), G_TYPE_STRING, G_TYPE_STRING,
541 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
542 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT,
543 G_TYPE_STRING);
544 errmsg = download_modpack_list(&fcmp, setup_modpack_list, msg_callback);
545 gtk_tree_view_set_model(GTK_TREE_VIEW(main_list), GTK_TREE_MODEL(main_store));
547 g_object_set(main_list, "has-tooltip", TRUE, NULL);
548 g_signal_connect(main_list, "query-tooltip",
549 G_CALLBACK(query_main_list_tooltip_cb), NULL);
551 g_object_unref(main_store);
553 if (errmsg != NULL) {
554 gtk_label_set_text(GTK_LABEL(statusbar), errmsg);
558 /**************************************************************************
559 Entry point of the freeciv-modpack program
560 **************************************************************************/
561 int main(int argc, char *argv[])
563 GtkWidget *toplevel;
564 int loglevel = LOG_NORMAL;
565 int ui_options;
567 init_nls();
568 init_character_encodings(FC_DEFAULT_DATA_ENCODING, FALSE);
569 registry_module_init();
571 fc_init_network();
573 g_thread_init(NULL);
575 log_init(NULL, loglevel, NULL, NULL, -1);
577 /* This modifies argv! */
578 ui_options = fcmp_parse_cmdline(argc, argv);
580 if (ui_options != -1) {
582 load_install_info_lists(&fcmp);
584 /* Process GTK arguments */
585 gtk_init(&ui_options, &argv);
587 toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
589 gtk_widget_realize(toplevel);
590 gtk_widget_set_name(toplevel, "Freeciv-modpack");
591 gtk_window_set_title(GTK_WINDOW(toplevel),
592 _("Freeciv modpack installer (gtk2)"));
594 /* Keep the icon of the executable on Windows */
595 #ifndef WIN32_NATIVE
597 /* Unlike main client, this only works if installed. Ignore any
598 * errors loading the icon. */
599 GError *err;
600 (void) gtk_window_set_icon_from_file(GTK_WINDOW(toplevel), MPICON_PATH,
601 &err);
603 #endif /* WIN32_NATIVE */
605 g_signal_connect(toplevel, "delete_event",
606 G_CALLBACK(quit_dialog_callback), NULL);
608 modinst_setup_widgets(toplevel);
610 gtk_widget_show_all(toplevel);
612 if (fcmp.autoinstall != NULL) {
613 gui_download_modpack(fcmp.autoinstall);
616 gtk_main();
618 save_install_info_lists(&fcmp);
621 log_close();
623 return EXIT_SUCCESS;