Crossfire Client, Trunk  R18666
/home/leaf/crossfire/client/trunk/gtk-v2/src/create_char.c
Go to the documentation of this file.
00001 const char * const rcsid_gtk2_create_char_c =
00002     "$Id: create_char.c 12988 2010-04-27 04:04:46Z kbulgrien $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2010,2011 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire@metalforge.org
00023 */
00024 
00030 #ifdef HAVE_CONFIG_H
00031 #  include <config.h>
00032 #endif
00033 
00034 #include <gtk/gtk.h>
00035 #include <glade/glade.h>
00036 #include <ctype.h>
00037 
00038 #include "client.h"
00039 
00040 #include "image.h"
00041 
00042 #include "main.h"
00043 #include "gtk2proto.h"
00044 #include "metaserver.h"
00045 
00046 /* This corresponds to the number of opt_.. fields in the
00047  * create_character_window.  In theory, the vbox could be resized
00048  * (or add a sub box), and adjust accordingly,
00049  * but it is unlikely that number of optional choices is going
00050  * to rapidly grow, and making it static makes some things much
00051  * easier.
00052  * Instead of having two different sets of fields named different,
00053  * we just have one set and split it in half, for race and
00054  * class.  This is done so that boxes don't need to be moved
00055  * around - imagine cas where one race has options and
00056  * another doesn't, but the currently selected class does -
00057  * as player chooses different races, that race choice will
00058  * come and go, but we want the class box to remain and to keep
00059  * its same value.
00060  */
00061 #define NUM_OPT_FIELDS  6
00062 #define RACE_OPT_START  0
00063 #define CLASS_OPT_START NUM_OPT_FIELDS/2
00064 #define RACE_OPT_END    CLASS_OPT_START - 1
00065 #define CLASS_OPT_ENG   NUM_OPT_FIELDS - 1
00066 
00067 
00068 /* These are in the create_character_window */
00069 static GtkWidget *spinbutton_cc[NUM_NEW_CHAR_STATS], *label_rs[NUM_NEW_CHAR_STATS],
00070     *label_cs[NUM_NEW_CHAR_STATS], *label_tot[NUM_NEW_CHAR_STATS],
00071     *label_cc_unspent, *textview_rs_desc, *label_cc_desc, *label_cc_status_update,
00072     *button_cc_cancel, *button_cc_done, *create_character_window, *combobox_rs,
00073     *combobox_cs, *textview_cs_desc, *entry_new_character_name, *button_choose_starting_map,
00074     *opt_label[NUM_OPT_FIELDS], *opt_combobox[NUM_OPT_FIELDS];
00075 
00076 static GtkTextMark *text_mark_cs, *text_mark_rs;
00077 
00078 /* These are in the choose starting map window */
00079 static GtkWidget *choose_starting_map_window,
00080     *button_csm_done, *button_csm_cancel, *combobox_starting_map;
00081 
00082 GtkTextBuffer *textbuf_starting_map;
00083 
00084 static int has_init=0, negative_stat=0;
00085 
00086 #define STARTING_MAP_PANE   0
00087 Info_Pane create_char_pane[1];
00088 
00089 #define WINDOW_NONE             0
00090 #define WINDOW_CREATE_CHARACTER 1
00091 #define WINDOW_CHOOSE_MAP       2
00092 
00106 static void show_window(int window)
00107 {
00108     switch (window) {
00109 
00110     case WINDOW_NONE:
00111         gtk_widget_hide(create_character_window);
00112         gtk_widget_hide(choose_starting_map_window);
00113         break;
00114 
00115     case WINDOW_CREATE_CHARACTER:
00116         gtk_widget_show(create_character_window);
00117         gtk_widget_hide(choose_starting_map_window);
00118         break;
00119 
00120     case WINDOW_CHOOSE_MAP:
00121         gtk_widget_hide(create_character_window);
00122         gtk_widget_show(choose_starting_map_window);
00123         break;
00124     }
00125 }
00126 
00127 
00137 static void create_character_set_sensitive(int sensitive)
00138 {
00139     int i;
00140 
00141     gtk_widget_set_sensitive(button_cc_done, sensitive);
00142     gtk_widget_set_sensitive(button_choose_starting_map, sensitive);
00143     gtk_widget_set_sensitive(entry_new_character_name, sensitive);
00144     gtk_widget_set_sensitive(combobox_rs, sensitive);
00145     gtk_widget_set_sensitive(combobox_cs, sensitive);
00146     /* Note we do not change status of cancel button - let
00147      * the player cancel out of the window if they want -
00148      * no harm in doing so.
00149      */
00150 
00151     for (i=0; i<NUM_NEW_CHAR_STATS; i++)
00152         gtk_widget_set_sensitive(spinbutton_cc[i], sensitive);
00153 
00154     /* If we do not have any starting maps, no reason to show
00155      * that button to the player.
00156      */
00157     if (starting_map_number)
00158         gtk_widget_show(button_choose_starting_map);
00159     else
00160         gtk_widget_hide(button_choose_starting_map);
00161 }
00162 
00169 void create_character_window_show()
00170 {
00171     int reset_needed = 0;
00172 
00173     /* If we don't have race/class/stat_point values, get them now.
00174      * those values are reset if we switch between servers, so there
00175      * should never be any danger of them being wrong.
00176      * In theory, if one of these is true, all of them should be true
00177      * because it shouldn't be possible to get in a case where we have
00178      * gotten race info but not class.
00179      */
00180     if (!races) {
00181         cs_print_string(csocket.fd, "requestinfo race_list");
00182         reset_needed = 1;
00183     }
00184     if (!classes) {
00185         cs_print_string(csocket.fd, "requestinfo class_list");
00186         reset_needed = 1;
00187     }
00188     if (!stat_points) {
00189         cs_print_string(csocket.fd, "requestinfo newcharinfo");
00190         reset_needed = 1;
00191     }
00192     /* In this case, we are getting copies of some of the data
00193      * from the server - we need to discard any data we currently
00194      * have then (mainly, clear out things like the pulldown list
00195      * for classes
00196      */
00197     if (reset_needed) {
00198         gtk_label_set_text(GTK_LABEL(label_cc_status_update),
00199                            "Getting race & class information from the server");
00200         create_character_set_sensitive(FALSE);
00201     }
00202 
00203     /* This will be set true once we get all the data */
00204     gtk_widget_show(create_character_window);
00205 }
00206 
00212 void create_character_window_hide()
00213 {
00214     show_window(WINDOW_NONE);
00215 }
00216 
00223 static void update_all_stats()
00224 {
00225     int i, stat_points_used=0, statval, tmp;
00226     const gchar *tval;
00227     char buf[MAX_BUF];
00228 
00229     negative_stat = 0;
00230     for (i=0; i<NUM_NEW_CHAR_STATS; i++) {
00231 
00232         tmp = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinbutton_cc[i]));
00233         stat_points_used += tmp;
00234         statval = tmp;
00235 
00236         /* We presume the label value is correct here - it should
00237          * be - this is easier than tracking down the corresponding
00238          * race/class structure and getting the value there.
00239          */
00240         tval = gtk_label_get_text(GTK_LABEL(label_cs[i]));
00241         statval += atoi(tval);
00242 
00243         tval = gtk_label_get_text(GTK_LABEL(label_rs[i]));
00244         statval += atoi(tval);
00245 
00246         /* Might it be good to draw negative stats in red?  Rather
00247          * than hardcode that, it should be a style
00248          */
00249         if (statval < 0) negative_stat = 1;
00250 
00251         sprintf(buf, "%d", statval);
00252         gtk_label_set_text(GTK_LABEL(label_tot[i]), buf);
00253     }
00254 
00255     tmp = stat_points - stat_points_used;
00256     sprintf(buf,"%d", tmp);
00257     gtk_label_set_text(GTK_LABEL(label_cc_unspent), buf);
00258 
00259     /* Display some warning messages - we could try and display all the
00260      * different warnings at once, but one at a time should be good enough.
00261      * perhaps the done button should be inactivated if there are errors.
00262      */
00263     if (tmp < 0) {
00264         gtk_label_set_text(GTK_LABEL(label_cc_status_update), 
00265                            "You have used more than your allotted total attribute points");
00266     } else if (negative_stat) {
00267         gtk_label_set_text(GTK_LABEL(label_cc_status_update), 
00268                    "Negative attributes are not allowed - adjust your selections before finishing");
00269 
00270     } else {
00271         gtk_label_set_text(GTK_LABEL(label_cc_status_update), "Waiting for player selections");
00272     }
00273     
00274 
00275 }
00276 
00277 static void send_create_player_to_server()
00278 {
00279     const gchar *char_name;
00280     int i, on_choice, tmp;
00281     SockList sl;
00282     char buf[MAX_BUF];
00283     uint8 sockbuf[MAX_BUF];
00284 
00285     char_name = gtk_entry_get_text(GTK_ENTRY(entry_new_character_name));
00286 
00287     SockList_Init(&sl, sockbuf);
00288     SockList_AddString(&sl, "createplayer ");
00289     SockList_AddChar(&sl, strlen(char_name));
00290     SockList_AddString(&sl, char_name);
00291     SockList_AddChar(&sl, strlen(account_password));
00292 
00293     SockList_AddString(&sl, account_password);
00294 
00295     /* The client should never be popping up the new client creation
00296      * window unless the server supports loginmethod >= 2, so
00297      * we do not have any check here for that, but these
00298      * attributes are only valid for loginmethod >= 2
00299      */
00300     i = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox_rs));
00301     snprintf(buf, MAX_BUF, "race %s", races[i].arch_name);
00302     SockList_AddChar(&sl, strlen(buf)+1);
00303     SockList_AddString(&sl, buf);
00304     SockList_AddChar(&sl, 0);
00305 
00306     /* From a practical standpoint, the server should never send
00307      * race/class choices unless it also supports the receipt of
00308      * those.  So no special checks are needed here.
00309      */
00310     for (on_choice = 0; on_choice < races[i].num_rc_choice; on_choice++) {
00311         int j;
00312 
00313         j = gtk_combo_box_get_active(GTK_COMBO_BOX(opt_combobox[on_choice + RACE_OPT_START]));
00314 
00315         snprintf(buf, MAX_BUF, "choice %s %s", races[i].rc_choice[on_choice].choice_name,
00316                  races[i].rc_choice[on_choice].value_arch[j]);
00317 
00318         SockList_AddChar(&sl, strlen(buf)+1);
00319         SockList_AddString(&sl, buf);
00320         SockList_AddChar(&sl, 0);
00321     }
00322 
00323 
00324     i = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox_cs));
00325     snprintf(buf, MAX_BUF, "class %s", classes[i].arch_name);
00326     SockList_AddChar(&sl, strlen(buf)+1);
00327     SockList_AddString(&sl, buf);
00328     SockList_AddChar(&sl, 0);
00329 
00330     for (on_choice = 0; on_choice < classes[i].num_rc_choice; on_choice++) {
00331         int j;
00332 
00333         j = gtk_combo_box_get_active(GTK_COMBO_BOX(opt_combobox[on_choice + CLASS_OPT_START]));
00334 
00335         snprintf(buf, MAX_BUF, "choice %s %s", classes[i].rc_choice[on_choice].choice_name,
00336                  classes[i].rc_choice[on_choice].value_arch[j]);
00337 
00338         SockList_AddChar(&sl, strlen(buf)+1);
00339         SockList_AddString(&sl, buf);
00340         SockList_AddChar(&sl, 0);
00341     }
00342 
00343     /* Its possible that the server does not provide a choice of
00344      * starting maps - if that is the case, then we will never
00345      * display the starting map window.  So check for that here.
00346      */
00347     if (starting_map_number) {
00348         i = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox_starting_map));
00349         if (i != -1) {
00350             snprintf(buf, MAX_BUF, "starting_map %s", starting_map_info[i].arch_name);
00351             SockList_AddChar(&sl, strlen(buf)+1);
00352             SockList_AddString(&sl, buf);
00353             SockList_AddChar(&sl, 0);
00354         }
00355     }
00356 
00357     for (i=0; i<NUM_NEW_CHAR_STATS; i++) {
00358         tmp = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinbutton_cc[i]));
00359         snprintf(buf, MAX_BUF, "%s %d", stat_mapping[i].widget_suffix, tmp);
00360         SockList_AddChar(&sl, strlen(buf)+1);
00361         SockList_AddString(&sl, buf);
00362         SockList_AddChar(&sl, 0);
00363     }
00364 
00365     SockList_Send(&sl, csocket.fd);
00366 
00367 }
00368 
00369 
00377 void
00378 on_button_cc_cancel(GtkButton *button, gpointer user_data)
00379 {
00380     show_window(WINDOW_NONE);
00381     choose_char_window_show();
00382 }
00383 
00384 
00392 void
00393 on_button_choose_starting_map(GtkButton *button, gpointer user_data)
00394 {
00395     show_window(WINDOW_CHOOSE_MAP);
00396 }
00397 
00398 
00409 static int character_data_ok()
00410 {
00411     const gchar *char_name;
00412     int i, stat_points_used=0, tmp[NUM_NEW_CHAR_STATS], negative_stat=0;
00413     SockList sl;
00414     char buf[MAX_BUF];
00415     uint8 sockbuf[MAX_BUF];
00416 
00417     char_name = gtk_entry_get_text(GTK_ENTRY(entry_new_character_name));
00418 
00419     if (!char_name || char_name[0] == 0) {
00420         gtk_label_set_text(GTK_LABEL(label_cc_status_update),
00421                            "You must enter a character name");
00422         show_window(WINDOW_CREATE_CHARACTER);
00423         return FALSE;
00424     }
00425 
00426     /* We get the stat values here - we also total up how many
00427      * points are used.  If everything checks out, we will
00428      * need these stat values later.
00429      */
00430     for (i=0; i<NUM_NEW_CHAR_STATS; i++) {
00431         tmp[i] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinbutton_cc[i]));
00432         stat_points_used += tmp[i];
00433     }
00434 
00435     if (stat_points_used > stat_points) {
00436         gtk_label_set_text(GTK_LABEL(label_cc_status_update), 
00437                            "You have used more than your allotted total attribute points");
00438         show_window(WINDOW_CREATE_CHARACTER);
00439         return FALSE;
00440     }
00441     /* negative_stat is a global to this file.  update_all_stats()
00442      * sets it/clears it - rather than doing that work again, just
00443      * re-use that value.
00444      */
00445     if (negative_stat) {
00446         gtk_label_set_text(GTK_LABEL(label_cc_status_update), 
00447                    "Negative attributes are not allowed - adjust your selections before finishing");
00448         show_window(WINDOW_CREATE_CHARACTER); 
00449         return FALSE;
00450     }
00451 
00452     /* No message is normally displayed for this - the player is
00453      * always going to get this case when starting out, but if
00454      * they hit done, we want to warn them that they have points
00455      * left to spend, since at present time there is no way to spend
00456      * these points later.
00457      */
00458     if (stat_points_used < stat_points) {
00459         GtkWidget *dialog;
00460         int result;
00461 
00462         dialog =
00463             gtk_message_dialog_new(GTK_WINDOW(create_character_window), 
00464                                    GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
00465                                    GTK_BUTTONS_YES_NO,
00466                                    "%s\n%s\n%s", 
00467                                    "You have not spent all your attribute points.",
00468                                    "You will be unable to spend these later.",
00469                                    "Create character anyways?");
00470         result = gtk_dialog_run(GTK_DIALOG(dialog));
00471         gtk_widget_destroy(dialog);
00472         if (result == GTK_RESPONSE_NO) {
00473             show_window(WINDOW_CREATE_CHARACTER); 
00474             return FALSE;
00475         }
00476         /* Otherwise, fall through below */
00477     }
00478 
00479     /* Check to see starting map - note that start_map_number could
00480      * be zero, which means that the server does not have a choice,
00481      * and thus we don't have to get anything from the player.
00482      * Is throwing a dialog box up here perhaps overkill?
00483      */
00484     i = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox_starting_map));
00485     if (starting_map_number && i == -1) {
00486         GtkWidget *dialog;
00487         int result;
00488 
00489         show_window(WINDOW_CHOOSE_MAP);
00490         dialog =
00491             gtk_message_dialog_new(GTK_WINDOW(choose_starting_map_window),
00492                                    GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING,
00493                                    GTK_BUTTONS_OK,
00494                                    "You must choose a starting map before you can start playing");
00495         result = gtk_dialog_run(GTK_DIALOG(dialog));
00496         gtk_widget_destroy(dialog);
00497         return FALSE;
00498     }
00499     /* Everything checks out OK */
00500     return TRUE;
00501 }
00502 
00512 void
00513 on_button_cc_done(GtkButton *button, gpointer user_data)
00514 {
00515     if (character_data_ok()) {
00516         /* If we get here, everything checks out - now we have to
00517          * send the data to the server.
00518          */
00519         gtk_label_set_text(GTK_LABEL(label_cc_status_update), 
00520                        "Sending new character information to server");
00521         show_window(WINDOW_CREATE_CHARACTER);
00522         send_create_player_to_server();
00523     }
00524 }
00525 
00530 void
00531 on_spinbutton_cc (GtkSpinButton *spinbutton, gpointer user_data) {
00532 
00533     update_all_stats();
00534 }
00535 
00548 void
00549 on_combobox_rcs_changed(GtkComboBox *box, gpointer user_data)
00550 {
00551     int active_entry, i, opt_start, opt_end;
00552     GtkWidget **label_stat;
00553     Race_Class_Info *rc;
00554     char buf[256];
00555 
00556     active_entry = gtk_combo_box_get_active(box);
00557 
00558     /* I don't think this can ever happen - if we get here,
00559      * something should be active.
00560      */
00561     if (active_entry == -1)
00562         return;
00563 
00564     /* since we are using a list store, and we are not re-arranging the order,
00565      * the entry number should match our array number.
00566      */
00567     if (box == GTK_COMBO_BOX(combobox_cs)) {
00568         gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview_cs_desc)),
00569                                  classes[active_entry].description,
00570                                  strlen(classes[active_entry].description));
00571         gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(textview_cs_desc),
00572                                      text_mark_cs, 0.0, TRUE, 0.0, 0.0);
00573 
00574         rc = &classes[active_entry];
00575         label_stat = label_cs;
00576         opt_start = CLASS_OPT_START;
00577 
00578     } else if (box == GTK_COMBO_BOX(combobox_rs)) {
00579         gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview_rs_desc)),
00580                                  races[active_entry].description,
00581                                  strlen(races[active_entry].description));
00582         gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(textview_rs_desc),
00583                                      text_mark_rs, 0.0, TRUE, 0.0, 0.0);
00584         rc = &races[active_entry];
00585         label_stat = label_rs;
00586         opt_start = RACE_OPT_START;
00587     } else {
00588         LOG(LOG_ERROR, "gtk-v2/src/create_char.c:on_combobox_rcs_changed", 
00589             "Passed in combobox does not match any combobox");
00590         return;
00591     }
00592 
00593     for (i=0; i < rc->num_rc_choice; i++) {
00594         int j;
00595         GtkTreeModel *store;
00596         GtkTreeIter iter;
00597 
00598         if (i == (NUM_OPT_FIELDS/2)) {
00599             LOG(LOG_ERROR, "gtk-v2/src/create_char.c:on_combobox_rcs_changed",
00600                 "Number of racial option exceeds allocated amount (%d > %d)",
00601                 i, NUM_OPT_FIELDS/2);
00602             break;
00603         }
00604         /* Set up the races combobox */
00605         store = gtk_combo_box_get_model(GTK_COMBO_BOX(opt_combobox[i + opt_start]));
00606         gtk_list_store_clear(GTK_LIST_STORE(store));
00607 
00608         for (j=0; j<rc->rc_choice[i].num_values; j++) {
00609             gtk_list_store_append(GTK_LIST_STORE(store), &iter);
00610             gtk_list_store_set(GTK_LIST_STORE(store), &iter, 0, rc->rc_choice[i].value_desc[j], -1);
00611         }
00612         gtk_combo_box_set_active(GTK_COMBO_BOX(opt_combobox[i+opt_start]), 0);
00613 
00614         gtk_label_set(GTK_LABEL(opt_label[i+opt_start]), rc->rc_choice[i].choice_desc);
00615         gtk_widget_show(opt_label[i+opt_start]);
00616         gtk_widget_show(opt_combobox[i+opt_start]);
00617         /* No signals are connected - the value of the combo
00618          * box will be when we send the data to the server.
00619          */
00620     }
00621 
00622     /* Hide any unused fields */
00623     for ( ; i < (NUM_OPT_FIELDS/2); i++) {
00624         gtk_widget_hide(opt_label[i + opt_start]);
00625         gtk_widget_hide(opt_combobox[i + opt_start]);
00626     }
00627             
00628 
00629     /* label_stat now points at the array of stats to update, and rc points
00630      * at either the race or class to get values from.
00631      */
00632     for (i=0; i < NUM_NEW_CHAR_STATS; i++) {
00633         sprintf(buf, "%+d", rc->stat_adj[stat_mapping[i].rc_offset]);
00634         gtk_label_set_text(GTK_LABEL(label_stat[i]), buf);
00635     }
00636     update_all_stats();
00637 }
00638 
00646 void new_char_window_update_info()
00647 {
00648     char buf[256];
00649     GtkListStore *store;
00650     GtkTreeIter iter;
00651     GtkCellRenderer *renderer;
00652     int i;
00653 
00654     /* We could do the update as we get the data, but it shouldn't take
00655      * too long to get all the data, and simpler to just do one update
00656      */
00657     if (!stat_points || num_races != used_races || num_classes != used_classes)
00658         return;
00659 
00660     gtk_label_set_text(GTK_LABEL(label_cc_status_update), "Waiting for player selections");
00661 
00662     sprintf(buf,"%d", stat_points);
00663     gtk_label_set_text(GTK_LABEL(label_cc_unspent), buf);
00664 
00665     /* Set up the races combobox */
00666     store = gtk_list_store_new(1, G_TYPE_STRING);
00667 
00668     for (i=0; i<num_races; i++) {
00669         gtk_list_store_append(store, &iter);
00670         gtk_list_store_set(store, &iter, 0, races[i].public_name, -1);
00671     }
00672 
00673     gtk_combo_box_set_model(GTK_COMBO_BOX(combobox_rs), GTK_TREE_MODEL(store));
00674     gtk_cell_layout_clear(GTK_CELL_LAYOUT(combobox_rs));
00675 
00676     renderer = gtk_cell_renderer_text_new();
00677     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox_rs), renderer, FALSE);
00678     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox_rs), renderer,
00679                                    "text", 0, NULL);
00680 
00681     g_signal_connect ((gpointer) combobox_rs,  "changed",
00682                       G_CALLBACK (on_combobox_rcs_changed), NULL);
00683 
00684     gtk_combo_box_set_active(GTK_COMBO_BOX(combobox_rs), 0);
00685     /* Set up the classes combobox */
00686     store = gtk_list_store_new(1, G_TYPE_STRING);
00687 
00688     for (i=0; i<num_classes; i++) {
00689         gtk_list_store_append(store, &iter);
00690         gtk_list_store_set(store, &iter, 0, classes[i].public_name, -1);
00691 
00692     }
00693 
00694     gtk_combo_box_set_model(GTK_COMBO_BOX(combobox_cs), GTK_TREE_MODEL(store));
00695     gtk_cell_layout_clear(GTK_CELL_LAYOUT(combobox_cs));
00696 
00697     renderer = gtk_cell_renderer_text_new();
00698     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox_cs), renderer, FALSE);
00699     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox_cs), renderer,
00700                                    "text", 0, NULL);
00701     g_signal_connect ((gpointer) combobox_cs,  "changed",
00702                       G_CALLBACK (on_combobox_rcs_changed), NULL);
00703     gtk_combo_box_set_active(GTK_COMBO_BOX(combobox_cs), 0);
00704 
00705     /* Reset to minimum/maximum values for the spinbutton.
00706      */
00707     for (i=0; i<NUM_NEW_CHAR_STATS; i++) {
00708         /* Reset any stat values - just makes more sense, but also
00709          * possible that starting value set in the glade file may
00710          * be outside of this range.
00711          */
00712         gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton_cc[i]), stat_min);
00713         gtk_spin_button_set_range(GTK_SPIN_BUTTON(spinbutton_cc[i]), stat_min,
00714                                   stat_maximum);
00715     }
00716 
00717     create_character_set_sensitive(TRUE);
00718 }
00719 
00720 /******************************************************************************
00721  * This section is related to the starting map window.
00722  *****************************************************************************/
00723 
00732 void
00733 on_button_csm_cancel(GtkButton *button, gpointer user_data)
00734 {
00735     show_window(WINDOW_CREATE_CHARACTER);
00736 }
00737 
00738 
00739 void
00740 on_combobox_starting_map_changed(GtkComboBox *box, gpointer user_data)
00741 {
00742     int active_entry, i;
00743     GtkWidget **label_stat;
00744     Race_Class_Info *rc;
00745     char buf[256];
00746 
00747     active_entry = gtk_combo_box_get_active(box);
00748 
00749     /* I don't think this can ever happen - if we get here,
00750      * something should be active.
00751      */
00752     if (active_entry == -1)
00753         return;
00754 
00755     /* since we are using a list store, and we are not re-arranging the order,
00756      * the entry number should match our array number.
00757      */
00758     gtk_text_buffer_set_text(textbuf_starting_map, "", 0);
00759     add_marked_text_to_pane(&create_char_pane[STARTING_MAP_PANE],
00760                             starting_map_info[active_entry].description, 0, 0, 0);
00761 
00762 }
00767 void starting_map_update_info()
00768 {
00769     char buf[256];
00770     GtkListStore *store;
00771     GtkTreeIter iter;
00772     GtkCellRenderer *renderer;
00773     int i;
00774 
00775     /* Set up the races combobox */
00776     store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
00777 
00778     for (i=0; i<=starting_map_number; i++) {
00779         gtk_list_store_append(store, &iter);
00780         gtk_list_store_set(store, &iter, 0, starting_map_info[i].public_name, -1);
00781         gtk_list_store_set(store, &iter, 1, starting_map_info[i].arch_name, -1);
00782     }
00783 
00784     gtk_combo_box_set_model(GTK_COMBO_BOX(combobox_starting_map), GTK_TREE_MODEL(store));
00785     gtk_cell_layout_clear(GTK_CELL_LAYOUT(combobox_starting_map));
00786 
00787     renderer = gtk_cell_renderer_text_new();
00788     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox_starting_map), renderer, FALSE);
00789     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox_starting_map), renderer,
00790                                    "text", 0, NULL);
00791 
00792     g_signal_connect ((gpointer) combobox_starting_map,  "changed",
00793                       G_CALLBACK (on_combobox_starting_map_changed), NULL);
00794 
00795     gtk_combo_box_set_active(GTK_COMBO_BOX(combobox_starting_map), -1);
00796 
00797     /* If we get called, we presume we have data to show, so activate button */
00798     gtk_widget_show(button_choose_starting_map);
00799 
00800 }
00801 
00802 
00806 void init_create_character_window()
00807 {
00808     GladeXML *xml_tree;
00809     char tmpbuf[80];
00810     int i;
00811     GtkTextIter iter;
00812     GtkCellRenderer *renderer;
00813 
00814     if (has_init) return;
00815     has_init=1;
00816 
00817     create_character_window = glade_xml_get_widget(dialog_xml, "create_character_window");
00818     gtk_window_set_transient_for(GTK_WINDOW(create_character_window), GTK_WINDOW(window_root));
00819 
00820     xml_tree = glade_get_widget_tree(GTK_WIDGET(create_character_window));
00821 
00822     button_cc_done = glade_xml_get_widget(dialog_xml,"button_cc_done");
00823     button_cc_cancel = glade_xml_get_widget(dialog_xml,"button_cc_cancel");
00824     button_choose_starting_map = glade_xml_get_widget(dialog_xml,"button_choose_starting_map");
00825     label_cc_status_update = glade_xml_get_widget(dialog_xml,"label_cc_status_update");
00826     label_cc_desc = glade_xml_get_widget(dialog_xml,"label_cc_desc");
00827     label_cc_unspent = glade_xml_get_widget(dialog_xml,"label_cc_unspent");
00828     combobox_rs = glade_xml_get_widget(dialog_xml,"combobox_rs");
00829     combobox_cs = glade_xml_get_widget(dialog_xml,"combobox_cs");
00830     entry_new_character_name = glade_xml_get_widget(dialog_xml,"cc_entry_new_character_name");
00831 
00832     textview_rs_desc = glade_xml_get_widget(dialog_xml,"textview_rs_desc");
00833     text_mark_rs = gtk_text_mark_new("rs_start", TRUE);
00834     gtk_text_buffer_get_start_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview_rs_desc)),
00835                                    &iter);
00836     gtk_text_buffer_add_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview_rs_desc)),
00837                              text_mark_rs, &iter);
00838 
00839     textview_cs_desc = glade_xml_get_widget(dialog_xml,"textview_cs_desc");
00840     text_mark_cs = gtk_text_mark_new("cs_start", TRUE);
00841     gtk_text_buffer_get_start_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview_cs_desc)),
00842                                    &iter);
00843     gtk_text_buffer_add_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview_cs_desc)),
00844                              text_mark_cs, &iter);
00845 
00846     for (i=0; i<NUM_NEW_CHAR_STATS; i++) {
00847         snprintf(tmpbuf, 80, "spinbutton_cc_%s", stat_mapping[i].widget_suffix);
00848         spinbutton_cc[i] = glade_xml_get_widget(dialog_xml, tmpbuf);
00849 
00850         g_signal_connect ((gpointer) spinbutton_cc[i], "value-changed",
00851                           G_CALLBACK (on_spinbutton_cc), (void*)i);
00852 
00853         snprintf(tmpbuf, 80, "label_rs_%s", stat_mapping[i].widget_suffix);
00854         label_rs[i] = glade_xml_get_widget(dialog_xml, tmpbuf);
00855 
00856         snprintf(tmpbuf, 80, "label_cs_%s", stat_mapping[i].widget_suffix);
00857         label_cs[i] = glade_xml_get_widget(dialog_xml, tmpbuf);
00858 
00859         snprintf(tmpbuf, 80, "label_tot_%s", stat_mapping[i].widget_suffix);
00860         label_tot[i] = glade_xml_get_widget(dialog_xml, tmpbuf);
00861     }
00862 
00863     /* Note that in the glade file, the numbering starts at 1 */
00864     for (i=0; i < NUM_OPT_FIELDS; i++ ) {
00865         GtkListStore *store;
00866 
00867         snprintf(tmpbuf, 80, "opt_label%d", i+1);
00868         opt_label[i] = glade_xml_get_widget(dialog_xml, tmpbuf);
00869 
00870         snprintf(tmpbuf, 80, "opt_combobox%d", i+1);
00871         opt_combobox[i] = glade_xml_get_widget(dialog_xml, tmpbuf);
00872 
00873         gtk_cell_layout_clear(GTK_CELL_LAYOUT(opt_combobox[i]));
00874         renderer = gtk_cell_renderer_text_new();
00875         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(opt_combobox[i]), renderer, FALSE);
00876         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(opt_combobox[i]), renderer,
00877                                    "text", 0, NULL);
00878         store = gtk_list_store_new(1, G_TYPE_STRING);
00879         gtk_combo_box_set_model(GTK_COMBO_BOX(opt_combobox[i]), GTK_TREE_MODEL(store));
00880 
00881     }
00882 
00883     g_signal_connect ((gpointer) button_cc_done, "clicked",
00884                       G_CALLBACK (on_button_cc_done), NULL);
00885     g_signal_connect ((gpointer) button_cc_cancel, "clicked",
00886                       G_CALLBACK (on_button_cc_cancel), NULL);
00887     g_signal_connect ((gpointer) button_choose_starting_map, "clicked",
00888                       G_CALLBACK (on_button_choose_starting_map), NULL);
00889 
00890     /* For starting map window */
00891     choose_starting_map_window =  glade_xml_get_widget(dialog_xml, "choose_starting_map_window");
00892 
00893     gtk_window_set_transient_for(GTK_WINDOW(choose_starting_map_window), GTK_WINDOW(window_root));
00894     xml_tree = glade_get_widget_tree(GTK_WIDGET(create_character_window));
00895 
00896     create_char_pane[STARTING_MAP_PANE].textview = glade_xml_get_widget(dialog_xml,"textview_starting_map");
00897     textbuf_starting_map = gtk_text_view_get_buffer(
00898         GTK_TEXT_VIEW(create_char_pane[STARTING_MAP_PANE].textview));
00899     add_tags_to_textbuffer(&create_char_pane[STARTING_MAP_PANE], textbuf_starting_map);
00900     add_style_to_textbuffer(&create_char_pane[STARTING_MAP_PANE], NULL);
00901 
00902     gtk_text_buffer_get_end_iter(create_char_pane[STARTING_MAP_PANE].textbuffer, &iter);
00903     create_char_pane[STARTING_MAP_PANE].textmark = gtk_text_buffer_create_mark(
00904         create_char_pane[STARTING_MAP_PANE].textbuffer, NULL, &iter, FALSE);
00905 
00906     button_csm_done = glade_xml_get_widget(dialog_xml,"button_csm_done");
00907     button_csm_cancel = glade_xml_get_widget(dialog_xml,"button_csm_cancel");
00908     combobox_starting_map = glade_xml_get_widget(dialog_xml,"combobox_starting_map");
00909 
00910     g_signal_connect ((gpointer) button_csm_done, "clicked",
00911                       G_CALLBACK (on_button_cc_done), NULL);
00912     g_signal_connect ((gpointer) button_csm_cancel, "clicked",
00913                       G_CALLBACK (on_button_csm_cancel), NULL);
00914 
00915 }
00916