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)
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 ***********************************************************************/
15 #include <fc_config.h>
23 #include "fc_cmdline.h"
34 #include "mpcmdline.h"
39 static GtkWidget
*statusbar
;
40 static GtkWidget
*progressbar
;
41 static GtkWidget
*main_list
;
42 static GtkListStore
*main_store
;
43 static GtkWidget
*URL_input
;
44 static gboolean downloading
= FALSE
;
46 struct fcmp_params fcmp
= {
47 .list_url
= MODPACK_LIST_URL
,
52 static gboolean
quit_dialog_callback(void);
58 #define ML_COL_SUBTYPE 4
62 #define ML_COL_COUNT 7
66 #define ML_STORE_SIZE 9
68 /****************************************************************
70 ****************************************************************/
71 static void modinst_quit(void)
73 save_install_info_lists(&fcmp
);
76 cmdline_option_values_free();
81 /****************************************************************
82 This is the response callback for the dialog with the message:
83 Are you sure you want to quit?
84 ****************************************************************/
85 static void quit_dialog_response(GtkWidget
*dialog
, gint response
)
87 gtk_widget_destroy(dialog
);
88 if (response
== GTK_RESPONSE_YES
) {
93 /****************************************************************
94 Popups the quit dialog if
95 ****************************************************************/
96 static gboolean
quit_dialog_callback(void)
99 /* Download in progress. Confirm quit from user. */
100 static GtkWidget
*dialog
;
103 dialog
= gtk_message_dialog_new(NULL
,
107 _("Modpack installation in progress.\nAre you sure you want to quit?"));
109 gtk_window_set_position(GTK_WINDOW(dialog
), GTK_WIN_POS_MOUSE
);
111 g_signal_connect(dialog
, "response",
112 G_CALLBACK(quit_dialog_response
), NULL
);
113 g_signal_connect(dialog
, "destroy",
114 G_CALLBACK(gtk_widget_destroyed
), &dialog
);
117 gtk_window_present(GTK_WINDOW(dialog
));
120 /* User loses no work by quitting, so let's not annoy him/her
121 * with confirmation dialog. */
125 /* Stop emission of event. */
129 /**************************************************************************
130 Progress indications from downloader
131 **************************************************************************/
132 static void msg_callback(const char *msg
)
134 log_verbose("%s", msg
);
135 gtk_label_set_text(GTK_LABEL(statusbar
), msg
);
138 /**************************************************************************
139 Progress indications from downloader
140 **************************************************************************/
141 static void pbar_callback(int downloaded
, int max
)
143 /* Control file already downloaded */
144 double fraction
= (double) downloaded
/ (max
);
146 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar
), fraction
);
153 /**************************************************************************
154 Main thread handling of message sent from downloader thread.
155 **************************************************************************/
156 static gboolean
msg_main_thread(gpointer user_data
)
158 struct msg_data
*data
= (struct msg_data
*)user_data
;
160 msg_callback(data
->msg
);
164 return G_SOURCE_REMOVE
;
167 /**************************************************************************
168 Downloader thread message callback.
169 **************************************************************************/
170 static void msg_dl_thread(const char *msg
)
172 struct msg_data
*data
= fc_malloc(sizeof(*data
));
176 gdk_threads_add_idle(msg_main_thread
, data
);
184 /**************************************************************************
185 Main thread handling of progressbar update sent from downloader thread.
186 **************************************************************************/
187 static gboolean
pbar_main_thread(gpointer user_data
)
189 struct pbar_data
*data
= (struct pbar_data
*)user_data
;
191 pbar_callback(data
->current
, data
->max
);
195 return G_SOURCE_REMOVE
;
198 /**************************************************************************
199 Downloader thread progress bar callback.
200 **************************************************************************/
201 static void pbar_dl_thread(int current
, int max
)
203 struct pbar_data
*data
= fc_malloc(sizeof(*data
));
205 data
->current
= current
;
208 gdk_threads_add_idle(pbar_main_thread
, data
);
211 /**************************************************************************
212 Main thread handling of versionlist update requested by downloader thread
213 **************************************************************************/
214 static gboolean
versionlist_update_main_thread(gpointer user_data
)
218 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(main_store
), &iter
)) {
220 const char *name_str
;
222 const char *new_inst
;
223 enum modpack_type type
;
225 gtk_tree_model_get(GTK_TREE_MODEL(main_store
), &iter
,
226 ML_COL_NAME
, &name_str
,
232 new_inst
= get_installed_version(name_str
, type
);
234 if (new_inst
== NULL
) {
235 new_inst
= _("Not installed");
238 gtk_list_store_set(main_store
, &iter
,
239 ML_COL_INST
, new_inst
,
242 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(main_store
), &iter
));
245 return G_SOURCE_REMOVE
;
248 /**************************************************************************
249 Downloader thread requests versionlist update.
250 **************************************************************************/
251 static void versionlist_update_dl_thread(void)
253 gdk_threads_add_idle(versionlist_update_main_thread
, NULL
);
256 /**************************************************************************
257 Entry point for downloader thread
258 **************************************************************************/
259 static gpointer
download_thread(gpointer data
)
263 errmsg
= download_modpack(data
, &fcmp
, msg_dl_thread
, pbar_dl_thread
);
265 if (errmsg
== NULL
) {
266 msg_dl_thread(_("Ready"));
268 msg_dl_thread(errmsg
);
273 versionlist_update_dl_thread();
280 /**************************************************************************
281 Download modpack, display error message dialogs
282 **************************************************************************/
283 static void gui_download_modpack(const char *URL
)
289 gtk_label_set_text(GTK_LABEL(statusbar
),
290 _("Another download already active"));
296 URLbuf
= fc_malloc(strlen(URL
) + 1);
300 downloader
= g_thread_new("Downloader", download_thread
, URLbuf
);
301 if (downloader
== NULL
) {
302 gtk_label_set_text(GTK_LABEL(statusbar
),
303 _("Failed to start downloader"));
306 g_thread_unref(downloader
);
310 /**************************************************************************
311 Install modpack button clicked
312 **************************************************************************/
313 static void install_clicked(GtkWidget
*w
, gpointer data
)
315 GtkEntry
*URL_in
= data
;
316 const char *URL
= gtk_entry_get_text(URL_in
);
318 gui_download_modpack(URL
);
321 /**************************************************************************
323 **************************************************************************/
324 static void URL_return(GtkEntry
*w
, gpointer data
)
328 URL
= gtk_entry_get_text(w
);
329 gui_download_modpack(URL
);
332 /**************************************************************************
333 Callback for getting main list row tooltip
334 **************************************************************************/
335 static gboolean
query_main_list_tooltip_cb(GtkWidget
*widget
,
337 gboolean keyboard_tip
,
342 GtkTreeView
*tree_view
= GTK_TREE_VIEW(widget
);
346 if (!gtk_tree_view_get_tooltip_context(tree_view
, &x
, &y
,
348 &model
, NULL
, &iter
)) {
352 gtk_tree_model_get(model
, &iter
,
357 gtk_tooltip_set_markup(tooltip
, notes
);
365 /**************************************************************************
366 Build main modpack list view
367 **************************************************************************/
368 static void setup_modpack_list(const char *name
, const char *URL
,
369 const char *version
, const char *license
,
370 enum modpack_type type
, const char *subtype
,
374 const char *type_str
;
376 const char *inst_str
;
378 if (modpack_type_is_valid(type
)) {
379 type_str
= _(modpack_type_name(type
));
381 /* TRANS: Unknown modpack type */
385 if (license
!= NULL
) {
388 /* TRANS: License of modpack is not known */
389 lic_str
= Q_("?license:Unknown");
392 inst_str
= get_installed_version(name
, type
);
393 if (inst_str
== NULL
) {
394 inst_str
= _("Not installed");
397 gtk_list_store_append(main_store
, &iter
);
398 gtk_list_store_set(main_store
, &iter
,
401 ML_COL_INST
, inst_str
,
402 ML_COL_TYPE
, type_str
,
403 ML_COL_SUBTYPE
, subtype
,
411 /**************************************************************************
412 Callback called when entry from main modpack list selected
413 **************************************************************************/
414 static void select_from_list(GtkTreeSelection
*select
, gpointer data
)
420 if (!gtk_tree_selection_get_selected(select
, &model
, &it
)) {
424 gtk_tree_model_get(model
, &it
, ML_COL_URL
, &URL
, -1);
426 gtk_entry_set_text(GTK_ENTRY(URL_input
), URL
);
429 /**************************************************************************
431 **************************************************************************/
432 static void modinst_setup_widgets(GtkWidget
*toplevel
)
434 GtkWidget
*mbox
, *Ubox
;
435 GtkWidget
*version_label
;
436 GtkWidget
*install_button
, *install_label
;
437 GtkWidget
*URL_label
;
438 GtkCellRenderer
*renderer
;
439 GtkTreeSelection
*selection
;
442 const char *rev_ver
= fc_svn_revision();
444 mbox
= gtk_grid_new();
445 gtk_orientable_set_orientation(GTK_ORIENTABLE(mbox
),
446 GTK_ORIENTATION_VERTICAL
);
447 gtk_grid_set_row_spacing(GTK_GRID(mbox
), 4);
449 if (rev_ver
== NULL
) {
450 rev_ver
= fc_git_revision();
452 if (rev_ver
== NULL
) {
453 fc_snprintf(verbuf
, sizeof(verbuf
), "%s%s", word_version(), VERSION_STRING
);
455 fc_snprintf(verbuf
, sizeof(verbuf
), _("%s%s\ncommit: %s"),
456 word_version(), VERSION_STRING
, rev_ver
);
459 fc_snprintf(verbuf
, sizeof(verbuf
), "%s%s (%s)", word_version(), VERSION_STRING
,
463 version_label
= gtk_label_new(verbuf
);
465 main_list
= gtk_tree_view_new();
466 renderer
= gtk_cell_renderer_text_new();
467 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list
),
469 _("Name"), renderer
, "text", 0,
471 renderer
= gtk_cell_renderer_text_new();
472 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list
),
474 _("Version"), renderer
, "text", 1,
476 renderer
= gtk_cell_renderer_text_new();
477 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list
),
479 _("Installed"), renderer
, "text", 2,
481 renderer
= gtk_cell_renderer_text_new();
482 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list
),
487 renderer
= gtk_cell_renderer_text_new();
488 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list
),
493 renderer
= gtk_cell_renderer_text_new();
494 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list
),
497 _("License"), renderer
, "text", 5,
499 renderer
= gtk_cell_renderer_text_new();
500 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(main_list
),
502 _("URL"), renderer
, "text", 6,
504 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(main_list
));
505 g_signal_connect(selection
, "changed", G_CALLBACK(select_from_list
), NULL
);
507 install_button
= gtk_button_new();
508 install_label
= gtk_label_new(_("Install modpack"));
509 gtk_label_set_mnemonic_widget(GTK_LABEL(install_label
), install_button
);
510 g_object_set_data(G_OBJECT(install_button
), "label", install_label
);
511 gtk_container_add(GTK_CONTAINER(install_button
), install_label
);
513 Ubox
= gtk_grid_new();
514 gtk_widget_set_halign(Ubox
, GTK_ALIGN_CENTER
);
515 gtk_grid_set_column_spacing(GTK_GRID(Ubox
), 4);
516 URL_label
= gtk_label_new_with_mnemonic(_("Modpack URL"));
518 URL_input
= gtk_entry_new();
519 gtk_entry_set_width_chars(GTK_ENTRY(URL_input
),
520 strlen(EXAMPLE_URL
));
521 gtk_entry_set_text(GTK_ENTRY(URL_input
), DEFAULT_URL_START
);
522 g_signal_connect(URL_input
, "activate",
523 G_CALLBACK(URL_return
), NULL
);
525 g_signal_connect(install_button
, "clicked",
526 G_CALLBACK(install_clicked
), URL_input
);
528 gtk_container_add(GTK_CONTAINER(Ubox
), URL_label
);
529 gtk_container_add(GTK_CONTAINER(Ubox
), URL_input
);
531 progressbar
= gtk_progress_bar_new();
533 statusbar
= gtk_label_new(_("Select modpack to install"));
535 gtk_widget_set_hexpand(main_list
, TRUE
);
536 gtk_widget_set_vexpand(main_list
, TRUE
);
538 gtk_container_add(GTK_CONTAINER(mbox
), version_label
);
539 gtk_container_add(GTK_CONTAINER(mbox
), main_list
);
540 gtk_container_add(GTK_CONTAINER(mbox
), Ubox
);
541 gtk_container_add(GTK_CONTAINER(mbox
), install_button
);
542 gtk_container_add(GTK_CONTAINER(mbox
), progressbar
);
543 gtk_container_add(GTK_CONTAINER(mbox
), statusbar
);
545 gtk_container_add(GTK_CONTAINER(toplevel
), mbox
);
547 main_store
= gtk_list_store_new((ML_STORE_SIZE
), G_TYPE_STRING
, G_TYPE_STRING
,
548 G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
,
549 G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_INT
,
551 errmsg
= download_modpack_list(&fcmp
, setup_modpack_list
, msg_callback
);
552 gtk_tree_view_set_model(GTK_TREE_VIEW(main_list
), GTK_TREE_MODEL(main_store
));
554 g_object_set(main_list
, "has-tooltip", TRUE
, NULL
);
555 g_signal_connect(main_list
, "query-tooltip",
556 G_CALLBACK(query_main_list_tooltip_cb
), NULL
);
558 g_object_unref(main_store
);
560 if (errmsg
!= NULL
) {
561 gtk_label_set_text(GTK_LABEL(statusbar
), errmsg
);
565 /**************************************************************************
566 Entry point of the freeciv-modpack program
567 **************************************************************************/
568 int main(int argc
, char *argv
[])
575 /* This modifies argv! */
576 ui_options
= fcmp_parse_cmdline(argc
, argv
);
578 if (ui_options
!= -1) {
581 for (i
= 1; i
<= ui_options
; i
++) {
582 if (is_option("--help", argv
[i
])) {
584 _("This modpack installer accepts the standard Gtk command-line options\n"
585 "after '--'. See the Gtk documentation.\n\n"));
587 /* TRANS: No full stop after the URL, could cause confusion. */
588 fc_fprintf(stderr
, _("Report bugs at %s\n"), BUG_URL
);
595 if (ui_options
!= -1) {
597 load_install_info_lists(&fcmp
);
599 /* Process GTK arguments */
600 gtk_init(&ui_options
, &argv
);
602 toplevel
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
604 gtk_widget_realize(toplevel
);
605 gtk_widget_set_name(toplevel
, "Freeciv-modpack");
606 gtk_window_set_title(GTK_WINDOW(toplevel
),
607 _("Freeciv modpack installer (gtk3)"));
609 /* Keep the icon of the executable on Windows */
612 /* Unlike main client, this only works if installed. Ignore any
613 * errors loading the icon. */
615 (void) gtk_window_set_icon_from_file(GTK_WINDOW(toplevel
), MPICON_PATH
,
618 #endif /* WIN32_NATIVE */
620 g_signal_connect(toplevel
, "delete_event",
621 G_CALLBACK(quit_dialog_callback
), NULL
);
623 modinst_setup_widgets(toplevel
);
625 gtk_widget_show_all(toplevel
);
627 if (fcmp
.autoinstall
!= NULL
) {
628 gui_download_modpack(fcmp
.autoinstall
);
633 save_install_info_lists(&fcmp
);
637 cmdline_option_values_free();