studio.cc

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2000-2001 The Exult Team
00003 
00004 This program is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU General Public License
00006 as published by the Free Software Foundation; either version 2
00007 of the License, or (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 */
00018 
00019 #ifdef HAVE_CONFIG_H
00020 #  include <config.h>
00021 #endif
00022 
00023 #ifdef HAVE_SYS_TYPES_H
00024 #include <sys/types.h>
00025 #endif
00026 #include <dirent.h>
00027 #include <gtk/gtk.h>
00028 #ifdef XWIN
00029 #include <gdk/gdkx.h>
00030 #endif
00031 #include <glib.h>
00032 #include <unistd.h>
00033 #include <errno.h>
00034 
00035 #include <cstdio>     /* These are for sockets. */
00036 #ifdef WIN32
00037 #include <windows.h>
00038 #include <ole2.h>
00039 #include "servewin32.h"
00040 #else
00041 #include <sys/socket.h>
00042 #include <sys/un.h>
00043 #endif
00044 #include <fcntl.h>
00045 #include <cstdarg>
00046 
00047 #include "shapelst.h"
00048 #include "shapevga.h"
00049 #include "Flex.h"
00050 #include "studio.h"
00051 #include "utils.h"
00052 #include "u7drag.h"
00053 #include "shapegroup.h"
00054 #include "shapefile.h"
00055 #include "locator.h"
00056 #include "combo.h"
00057 #include "Configuration.h"
00058 #include "objserial.h"
00059 #include "exceptions.h"
00060 #include "logo.xpm"
00061 #include "fnames.h"
00062 #include "execbox.h"
00063 
00064 using std::cerr;
00065 using std::cout;
00066 using std::endl;
00067 using std::string;
00068 using std::vector;
00069 using std::ofstream;
00070 
00071 ExultStudio *ExultStudio::self = 0;
00072 Configuration *config = 0;
00073 const std::string c_empty_string; // Used by config. library.
00074 
00075           // Mode menu items:
00076 static char *mode_names[4] = {"move1", "paint1", "paint_with_chunks1", 
00077               "pick_for_combo1"};
00078 
00079 enum ExultFileTypes {
00080   ShapeArchive =1,
00081   ChunkArchive,
00082   PaletteFile,
00083   FlexArchive,
00084   ComboArchive
00085 };
00086 
00087 typedef struct _FileTreeItem FileTreeItem;
00088 struct _FileTreeItem
00089 {
00090   const gchar    *label;
00091   ExultFileTypes datatype;
00092   FileTreeItem   *children;
00093 };
00094 
00095 /* columns */
00096 enum
00097 {
00098   FOLDER_COLUMN = 0,
00099   FILE_COLUMN,
00100   DATA_COLUMN,
00101   NUM_COLUMNS
00102 };
00103 
00104 
00105 static void Filelist_selection(GtkTreeView *treeview, GtkTreePath *path)
00106 {
00107   int type = -1;
00108   char *text;
00109   GtkTreeIter iter;
00110   GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
00111 
00112   gtk_tree_model_get_iter(model, &iter, path);
00113   gtk_tree_model_get(model, &iter, FILE_COLUMN, &text, DATA_COLUMN, 
00114                 &type,-1);
00115   printf("%s %d\n",text,type);
00116   
00117   ExultStudio *studio = ExultStudio::get_instance();
00118   switch(type) {
00119   case ShapeArchive:
00120     studio->set_browser("Shape Browser", 
00121           studio->create_browser(text));
00122     break;
00123   case ComboArchive:
00124     studio->set_browser("Combo Browser", 
00125           studio->create_browser(text));
00126     break;
00127   case ChunkArchive:
00128     studio->set_browser("Chunk Browser", 
00129           studio->create_browser(text));
00130     break;
00131   case PaletteFile:
00132     studio->set_browser("Palette Browser", 
00133           studio->create_browser(text));
00134     break;
00135   default:
00136     break;
00137   }
00138   g_free(text);
00139 }                                     
00140 
00141 C_EXPORT void on_filelist_tree_cursor_changed(GtkTreeView *treeview)
00142 {
00143   GtkTreePath *path;
00144   GtkTreeViewColumn *col;
00145 
00146   gtk_tree_view_get_cursor(treeview, &path, &col);
00147   Filelist_selection(treeview, path);
00148 }
00149 
00150 C_EXPORT void
00151 on_open_game_activate                  (GtkMenuItem     *menuitem,
00152                                         gpointer         user_data)
00153 {
00154   ExultStudio::get_instance()->choose_game_path();
00155 }
00156 
00157 C_EXPORT void
00158 on_new_game_activate                   (GtkMenuItem     *menuitem,
00159                                         gpointer         user_data)
00160 {
00161   ExultStudio::get_instance()->new_game();
00162 }
00163 
00164 C_EXPORT void
00165 on_connect_activate                    (GtkMenuItem     *menuitem,
00166                                         gpointer         user_data)
00167 {
00168   ExultStudio::get_instance()->connect_to_server();
00169 }
00170 
00171 C_EXPORT void
00172 on_save_all1_activate                  (GtkMenuItem     *menuitem,
00173                                         gpointer         user_data)
00174 {
00175   ExultStudio::get_instance()->save_all();
00176 }
00177 
00178 C_EXPORT void
00179 on_new_shapes_file_activate             (GtkMenuItem     *menuitem,
00180                                         gpointer         user_data)
00181 {
00182   ExultStudio::get_instance()->new_shape_file(false);
00183 }
00184 
00185 C_EXPORT void
00186 on_new_shape_file_activate             (GtkMenuItem     *menuitem,
00187                                         gpointer         user_data)
00188 {
00189   ExultStudio::get_instance()->new_shape_file(true);
00190 }
00191 
00192 C_EXPORT void
00193 on_save_map_menu_activate              (GtkMenuItem     *menuitem,
00194                                         gpointer         user_data)
00195 {
00196   ExultStudio::get_instance()->write_map();
00197 }
00198 
00199 C_EXPORT void
00200 on_read_map_menu_activate              (GtkMenuItem     *menuitem,
00201                                         gpointer         user_data)
00202 {
00203   ExultStudio::get_instance()->read_map();
00204 }
00205 
00206 C_EXPORT void
00207 on_save_shape_info1_activate           (GtkMenuItem     *menuitem,
00208                                         gpointer         user_data)
00209 {
00210   ExultStudio::get_instance()->write_shape_info();
00211 }
00212 
00213 C_EXPORT void
00214 on_reload_usecode_menu_activate        (GtkMenuItem     *menuitem,
00215                                         gpointer         user_data)
00216 {
00217   ExultStudio::get_instance()->reload_usecode();
00218 }
00219 
00220 C_EXPORT void
00221 on_compile_usecode_menu_activate       (GtkMenuItem     *menuitem,
00222                                         gpointer         user_data)
00223 {
00224   ExultStudio::get_instance()->compile();
00225 }
00226 
00227 C_EXPORT void
00228 on_save_groups1_activate               (GtkMenuItem     *menuitem,
00229                                         gpointer         user_data)
00230 {
00231   ExultStudio::get_instance()->save_groups();
00232 }
00233 
00234 C_EXPORT void
00235 on_save_combos1_activate               (GtkMenuItem     *menuitem,
00236                                         gpointer         user_data)
00237 {
00238   ExultStudio::get_instance()->save_combos();
00239 }
00240 
00241 C_EXPORT void
00242 on_preferences_activate                (GtkMenuItem     *menuitem,
00243                                         gpointer         user_data)
00244 {
00245   ExultStudio::get_instance()->open_preferences();
00246 }
00247 
00248 C_EXPORT void
00249 on_cut1_activate                       (GtkMenuItem     *menuitem,
00250                                         gpointer         user_data)
00251 {
00252   unsigned char z = 0;
00253   ExultStudio::get_instance()->send_to_server(Exult_server::cut,
00254               &z, 1);
00255 }
00256 
00257 C_EXPORT void
00258 on_copy1_activate                      (GtkMenuItem     *menuitem,
00259                                         gpointer         user_data)
00260 {
00261   unsigned char o = 1;
00262   ExultStudio::get_instance()->send_to_server(Exult_server::cut,
00263               &o, 1);
00264 }
00265 
00266 C_EXPORT void
00267 on_paste1_activate                       (GtkMenuItem     *menuitem,
00268                                         gpointer         user_data)
00269 {
00270   ExultStudio::get_instance()->send_to_server(Exult_server::paste);
00271 }
00272 
00273 C_EXPORT void
00274 on_properties1_activate                (GtkMenuItem     *menuitem,
00275                                         gpointer         user_data)
00276 {
00277   unsigned char o = 0;    // 0=npc/egg properties.
00278   ExultStudio::get_instance()->send_to_server(
00279         Exult_server::edit_selected, &o, 1);
00280 }
00281 
00282 C_EXPORT void
00283 on_basic_properties1_activate          (GtkMenuItem     *menuitem,
00284                                         gpointer         user_data)
00285 {
00286   unsigned char o = 1;    // 1=basic object properties.
00287   ExultStudio::get_instance()->send_to_server(
00288         Exult_server::edit_selected, &o, 1);
00289 }
00290 
00291 C_EXPORT void
00292 on_move1_activate                (GtkMenuItem     *menuitem,
00293                                         gpointer         user_data)
00294 {
00295           // NOTE:  modes are defined in cheat.h.
00296   if (GTK_CHECK_MENU_ITEM(menuitem)->active)
00297     ExultStudio::get_instance()->set_edit_mode(0);
00298 }
00299 
00300 C_EXPORT void
00301 on_paint1_activate                 (GtkMenuItem     *menuitem,
00302                                         gpointer         user_data)
00303 {
00304   if (GTK_CHECK_MENU_ITEM(menuitem)->active)
00305     ExultStudio::get_instance()->set_edit_mode(1);
00306 }
00307 
00308 C_EXPORT void
00309 on_paint_with_chunks1_activate         (GtkMenuItem     *menuitem,
00310                                         gpointer         user_data)
00311 {
00312   if (GTK_CHECK_MENU_ITEM(menuitem)->active)
00313     ExultStudio::get_instance()->set_edit_mode(2);
00314 }
00315 
00316 C_EXPORT void
00317 on_pick_for_combo1_activate            (GtkMenuItem     *menuitem,
00318                                         gpointer         user_data)
00319 {
00320   if (GTK_CHECK_MENU_ITEM(menuitem)->active)
00321     ExultStudio::get_instance()->set_edit_mode(3);
00322 }
00323 
00324 C_EXPORT void
00325 on_unused_shapes1_activate             (GtkMenuItem     *menuitem,
00326                                         gpointer         user_data)
00327 {
00328   if (EStudio::Prompt(
00329     "Finding unused shapes may take several minutes\nProceed?",
00330           "Yes", "No") != 0)
00331     return;
00332   ExultStudio::get_instance()->send_to_server(
00333             Exult_server::unused_shapes);
00334 }
00335 
00336 C_EXPORT void
00337 on_play_button_clicked      (GtkToggleButton *button,
00338            gpointer   user_data)
00339 {
00340   ExultStudio::get_instance()->set_play(
00341         gtk_toggle_button_get_active(button));
00342 }
00343 
00344 C_EXPORT void
00345 on_tile_grid_button_toggled   (GtkToggleButton *button,
00346            gpointer   user_data)
00347 {
00348   ExultStudio::get_instance()->set_tile_grid(
00349         gtk_toggle_button_get_active(button));
00350 }
00351 
00352 C_EXPORT void
00353 on_edit_lift_spin_changed   (GtkSpinButton *button,
00354            gpointer   user_data)
00355 {
00356   ExultStudio::get_instance()->set_edit_lift(
00357         gtk_spin_button_get_value_as_int(button));
00358 }
00359 
00360 C_EXPORT void
00361 on_hide_lift_spin_changed   (GtkSpinButton *button,
00362            gpointer   user_data)
00363 {
00364   ExultStudio::get_instance()->set_hide_lift(
00365         gtk_spin_button_get_value_as_int(button));
00366 }
00367 
00368 C_EXPORT void
00369 on_edit_terrain_button_toggled    (GtkToggleButton *button,
00370            gpointer   user_data)
00371 {
00372   ExultStudio::get_instance()->set_edit_terrain(
00373         gtk_toggle_button_get_active(button));
00374 }
00375 
00376 void on_choose_directory               (gchar *dir,
00377           gpointer  user_data)
00378 {
00379   ExultStudio::get_instance()->set_game_path(dir);
00380 }
00381 
00382 /*
00383  *  Configure main window.
00384  */
00385 C_EXPORT gint on_main_window_configure_event
00386   (
00387   GtkWidget *widget,    // The view window.
00388   GdkEventConfigure *event,
00389   gpointer data
00390   )
00391   {
00392   ExultStudio *studio = ExultStudio::get_instance();
00393           // Configure "Hide lift" spin range.
00394   studio->set_spin("hide_lift_spin", 
00395         studio->get_spin("hide_lift_spin"), 1, 16);
00396   return FALSE;
00397   }
00398 
00399 /*
00400  *  Main window's close button.
00401  */
00402 C_EXPORT gboolean on_main_window_delete_event
00403   (
00404   GtkWidget *widget,
00405   GdkEvent *event,
00406   gpointer user_data
00407   )
00408   {
00409   if (!ExultStudio::get_instance()->okay_to_close())
00410     return TRUE;    // Can't quit.
00411   return FALSE;
00412   }
00413 C_EXPORT void on_main_window_destroy_event
00414   (
00415   GtkWidget *widget,
00416   gpointer data
00417   )
00418   {
00419   gtk_main_quit();
00420   }
00421 /*
00422  *  "Exit" in main window.
00423  */
00424 C_EXPORT void
00425 on_main_window_quit                    (GtkMenuItem     *menuitem,
00426                                         gpointer         user_data)
00427 {
00428   if (ExultStudio::get_instance()->okay_to_close())
00429     gtk_main_quit();
00430 }
00431 /*
00432  *  Main window got focus.
00433  */
00434 C_EXPORT gboolean on_main_window_focus_in_event
00435   (
00436   GtkWidget *widget,
00437   GdkEventFocus *event,
00438   gpointer user_data
00439   )
00440   {
00441   Shape_chooser::check_editing_files();
00442   return FALSE;
00443   }
00444 
00445 
00446 /*
00447  *  Set up everything.
00448  */
00449 
00450 ExultStudio::ExultStudio(int argc, char **argv): files(0), curfile(0), 
00451   names(0), glade_path(0), shape_info_modified(false),
00452   shape_names_modified(false),
00453   vgafile(0), facefile(0), eggwin(0), 
00454   server_socket(-1), server_input_tag(-1), 
00455   static_path(0), image_editor(0), default_game(0), background_color(0),
00456   browser(0), palbuf(0), egg_monster_draw(0), 
00457   egg_ctx(0),
00458   waiting_for_server(0), npcwin(0), npc_draw(0), npc_face_draw(0),
00459   npc_ctx(0), npc_status_id(0),
00460   objwin(0), obj_draw(0), shapewin(0), shape_draw(0),
00461   equipwin(0), locwin(0), combowin(0), compilewin(0), compile_box(0)
00462 {
00463   // Initialize the various subsystems
00464   self = this;
00465   gtk_init( &argc, &argv );
00466   gdk_rgb_init();
00467   glade_init();
00468   config = new Configuration;
00469   config->read_config_file(USER_CONFIGURATION_FILE);
00470           // Get options.
00471   const char *xmldir = 0;   // Default:  Look here for .glade.
00472   const char *game = 0;   // Game to look up in .exult.cfg.
00473   static char *optstring = "g:x:d:";
00474   extern int optind, opterr, optopt;
00475   extern char *optarg;
00476   opterr = 0;     // Don't let getopt() print errs.
00477   int optchr;
00478   while ((optchr = getopt(argc, argv, optstring)) != -1)
00479     switch (optchr)
00480       {
00481     case 'g':   // Game.  Replaces use of -d, -x.
00482       game = optarg;
00483       break;
00484     case 'x':   // XML (.glade) directory.
00485       xmldir = optarg;
00486       break;
00487       }
00488   string dirstr, datastr;
00489   if (!xmldir)
00490     {
00491     config->value("config/disk/data_path", datastr, EXULT_DATADIR);
00492     xmldir = datastr.c_str();
00493     }
00494   string defgame;     // Default game name.
00495   config->value("config/estudio/default_game", defgame, "blackgate");
00496   default_game = g_strdup(defgame.c_str());
00497   if (!game)
00498     game = default_game;
00499   config->set("config/estudio/default_game", defgame, true);
00500   char path[256];     // Set up paths.
00501   if(xmldir)
00502     strcpy(path, xmldir);
00503   else if(U7exists(EXULT_DATADIR"/exult_studio.glade"))
00504     strcpy(path,EXULT_DATADIR);
00505   else
00506     strcpy(path,".");
00507   strcat(path, "/exult_studio.glade");
00508   // Load the Glade interface
00509   app_xml = glade_xml_new(path, NULL, NULL);
00510   app = glade_xml_get_widget( app_xml, "main_window" );
00511   glade_path = g_strdup(path);
00512 
00513   // More setting up...
00514           // Connect signals automagically.
00515   glade_xml_signal_autoconnect(app_xml);
00516   int w, h;     // Get main window dims.
00517   config->value("config/estudio/main/width", w, 0);
00518   config->value("config/estudio/main/height", h, 0);
00519   if (w > 0 && h > 0)
00520 //++++Used to work  gtk_window_set_default_size(GTK_WINDOW(app), w, h);
00521     gtk_window_resize(GTK_WINDOW(app), w, h);
00522   gtk_widget_show( app );
00523           // Background color for shape browser.
00524   int bcolor;
00525   config->value("config/estudio/background_color", bcolor, 0);
00526   background_color = bcolor;
00527   if (game)     // Game given?
00528     {
00529     string d("config/disk/game/");
00530     d += game; 
00531     string gd = d + "/path";
00532     config->value(gd.c_str(), dirstr, "");
00533     if (dirstr == "")
00534       {
00535       cerr << "Game '" << game << 
00536         "' path not found in config. file" << endl;
00537       exit(1);
00538       }
00539     string pd = d + "/patch"; // Look up patch dir. too.
00540     string ptchstr;
00541     config->value(pd.c_str(), ptchstr, "");
00542     set_game_path(dirstr.c_str(), ptchstr == "" ? 0 
00543             : ptchstr.c_str());
00544     }
00545   string iedit;     // Get image-editor command.
00546   config->value("config/estudio/image_editor", iedit, "gimp-remote -n");
00547   image_editor = g_strdup(iedit.c_str());
00548   config->set("config/estudio/image_editor", iedit, true);
00549 #ifdef WIN32
00550     OleInitialize(NULL);
00551 #endif
00552           // Init. 'Mode' menu, since Glade
00553           //   doesn't seem to do it right.
00554   GSList *group = NULL;
00555   for (int i = 0; i < sizeof(mode_names)/sizeof(mode_names[0]); i++)
00556     {
00557     GtkWidget *item = glade_xml_get_widget(app_xml, mode_names[i]);
00558     gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item),
00559                 group);
00560     group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(item));
00561     gtk_check_menu_item_set_active(
00562         GTK_CHECK_MENU_ITEM(item), i == 0);
00563     }
00564 }
00565 
00566 ExultStudio::~ExultStudio()
00567 {
00568 #ifdef WIN32
00569     OleUninitialize();
00570 #endif
00571           // Store main window size.
00572   int w = app->allocation.width, h = app->allocation.height;
00573           // Finish up external edits.
00574   Shape_chooser::clear_editing_files();
00575   config->set("config/estudio/main/width", w, true);
00576   config->set("config/estudio/main/height", h, true);
00577   int num_names = names.size();
00578   for (int i = 0; i < num_names; i++)
00579     delete names[i];
00580   names.resize(0);
00581   g_free(glade_path);
00582   delete files;
00583   files = 0;
00584   delete [] palbuf;
00585   palbuf = 0;
00586   if (objwin)
00587     gtk_widget_destroy(objwin);
00588   delete obj_draw;
00589   if (eggwin)
00590     gtk_widget_destroy(eggwin);
00591   delete egg_monster_draw;
00592   eggwin = 0;
00593   if (npcwin)
00594     gtk_widget_destroy(npcwin);
00595   delete npc_draw;
00596   npcwin = 0;
00597   if (shapewin)
00598     gtk_widget_destroy(shapewin);
00599   delete shape_draw;
00600   shapewin = 0;
00601   if (equipwin)
00602     gtk_widget_destroy(equipwin);
00603   equipwin = 0;
00604   if (compilewin)
00605     gtk_widget_destroy(compilewin);
00606   compilewin = 0;
00607   delete compile_box;
00608   compile_box = 0;
00609   if (locwin)
00610     delete locwin;
00611   if (combowin)
00612     delete combowin;
00613   locwin = 0;
00614   g_object_unref( G_OBJECT( app_xml ) );
00615 #ifndef WIN32
00616   if (server_input_tag >= 0)
00617     gdk_input_remove(server_input_tag);
00618   if (server_socket >= 0)
00619     close(server_socket);
00620 #else
00621   if (server_input_tag >= 0)
00622     gtk_timeout_remove(server_input_tag);
00623   if (server_socket >= 0)
00624     Exult_server::disconnect_from_server();
00625 #endif
00626   g_free(static_path);
00627   g_free(image_editor);
00628   g_free(default_game);
00629   self = 0;
00630   delete config;
00631   config = 0;
00632 }
00633 
00634 /*
00635  *  Okay to close game?
00636  */
00637 
00638 bool ExultStudio::okay_to_close
00639   (
00640   )
00641   {
00642   if (need_to_save())
00643     {
00644     int choice = prompt("File(s) modified.  Save?",
00645             "Yes", "No", "Cancel");
00646     if (choice == 2)  // Cancel?
00647       return false;
00648     if (choice == 0)
00649       save_all(); // Write out changes.
00650     }
00651   return true;
00652   }
00653 
00654 /*
00655  *  See if given window has focus (experimental).
00656  */
00657 
00658 inline bool Window_has_focus(GtkWindow *win)
00659   {
00660   return (win->focus_widget != 0 &&
00661     GTK_WIDGET_HAS_FOCUS(win->focus_widget));
00662   }
00663 
00664 /*
00665  *  Do we have focus?  Currently only checks main window and group
00666  *  windows.
00667  */
00668 
00669 bool ExultStudio::has_focus
00670   (
00671   )
00672   {
00673   if (Window_has_focus(GTK_WINDOW(app)))
00674     return true;    // Main window.
00675           // Group windows:
00676   vector<GtkWindow*>::const_iterator it;
00677   for (it = group_windows.begin(); it != group_windows.end(); ++it)
00678     if (Window_has_focus(*it))
00679       return true;
00680   return false;
00681   }
00682 
00683 /*
00684  *  Set browser area.  NOTE:  obj may be null.
00685  */
00686 void ExultStudio::set_browser(const char *name, Object_browser *obj)
00687 {
00688   GtkWidget *browser_frame = glade_xml_get_widget(app_xml, 
00689               "browser_frame" );
00690   GtkWidget *browser_box = glade_xml_get_widget(app_xml, "browser_box" );
00691 //+++Now owned by Shape_file_info.  delete browser;
00692   if (browser)
00693     gtk_container_remove(GTK_CONTAINER(browser_box),
00694               browser->get_widget());
00695   browser = obj;
00696   
00697   gtk_frame_set_label( GTK_FRAME( browser_frame ), name );
00698   gtk_widget_show( browser_box );
00699   if (browser)
00700     gtk_box_pack_start(GTK_BOX(browser_box), 
00701           browser->get_widget(), TRUE, TRUE, 0);
00702 }
00703 
00704 Object_browser *ExultStudio::create_browser(const char *fname)
00705 {
00706   curfile = open_shape_file(fname);
00707   if (!curfile)
00708     return 0;
00709   Object_browser *chooser = curfile->get_browser(vgafile, palbuf);
00710   setup_groups();     // Set up 'groups' page.
00711   return chooser;
00712 }
00713 
00714 /*
00715  *  Get current groups.
00716  */
00717 
00718 Shape_group_file *ExultStudio::get_cur_groups
00719   (
00720   )
00721   {
00722   return curfile ? curfile->get_groups() : 0;
00723   }
00724 
00725 /*
00726  *  Test for a directory 'slash' or colon.
00727  */
00728 
00729 inline bool Is_dir_marker
00730   (
00731   char c
00732   )
00733   {
00734   return (c == '/' || c == '\\' || c == ':');
00735   }
00736 
00737 /*
00738  *  New game directory was chosen.
00739  */
00740 
00741 void on_choose_new_game_dir
00742   (
00743   const char *dir,
00744   gpointer udata      // ->studio.
00745   )
00746   {
00747   ((ExultStudio *) udata)->create_new_game(dir);
00748   }
00749 void ExultStudio::create_new_game
00750   (
00751   const char *dir     // Directory for new game.
00752   )
00753   {
00754           // Take basename as game name.
00755   const char *eptr = dir + strlen(dir) - 1;
00756   while (eptr >= dir && Is_dir_marker(*eptr))
00757     eptr--;
00758   eptr++;
00759   const char *ptr = eptr - 1;
00760   for ( ; ptr >= dir; ptr--)
00761     if (Is_dir_marker(*ptr))
00762       {
00763       ptr++;
00764       break;
00765       }
00766   if (ptr < dir || eptr - ptr <= 0)
00767     {
00768     EStudio::Alert("Can't find base game name in '%s'", dir);
00769     return;
00770     }
00771   string gamestr(ptr, eptr - ptr);
00772   string dirstr(dir);
00773   string static_path = dirstr + "/static";
00774   if (U7exists(static_path))
00775     {
00776     string msg("Directory '");
00777     msg += static_path;
00778     msg += "' already exists.\n";
00779     msg += "Files within may be overwritten.\n";
00780     msg += "Proceed?";
00781     if (prompt(msg.c_str(), "Yes", "No") != 0)
00782       return;
00783     }
00784   U7mkdir(dir, 0755);   // Create "game", "game/static",
00785           //   "game/patch".
00786   U7mkdir(static_path.c_str(), 0755);
00787   string patch_path = dirstr + "/patch";
00788   U7mkdir(patch_path.c_str(), 0755);
00789           // Set .exult.cfg.
00790   string d("config/disk/game/");
00791   string gameconfig = d + gamestr;
00792   d = gameconfig + "/path";
00793   config->set(d.c_str(), dirstr, false);
00794   d = gameconfig + "/patch";
00795   config->set(d.c_str(), patch_path, false);
00796   d = gameconfig + "/editing";  // We are editing.
00797   config->set(d.c_str(), "yes", true);
00798   string esdir;     // Get dir. for new files.
00799   config->value("config/disk/data_path", esdir, EXULT_DATADIR);
00800   esdir += "/estudio/new";
00801   DIR *dirrd = opendir(esdir.c_str());
00802   if (!dirrd)
00803     EStudio::Alert("'%s' for initial data files not found",
00804                 esdir.c_str());
00805   else
00806     {
00807     struct dirent *entry;
00808     while(entry=readdir(dirrd)) 
00809       {
00810       char *fname = entry->d_name;
00811       int flen = strlen(fname);
00812           // Ignore case of extension.
00813       if(!strcmp(fname, ".") || !strcmp(fname,".."))
00814         continue;
00815       string src = esdir + '/' + fname;
00816       string dest = static_path + '/' + fname;
00817       try {
00818         U7copy(src.c_str(), dest.c_str());
00819       } catch (exult_exception& e) {
00820         EStudio::Alert(e.what());
00821       }
00822       }
00823     closedir(dirrd);
00824     }
00825   set_game_path(dir);   // Open as current game.
00826   write_shape_info(true);   // Create initial .dat files.
00827   }
00828 
00829 /*
00830  *  Prompt for a 'new game' directory.
00831  */
00832 
00833 void ExultStudio::new_game()
00834 {
00835   if (!okay_to_close())   // Okay to close prev. game?
00836     return;
00837   GtkFileSelection *fsel = Create_file_selection(
00838       "Choose new game directory",
00839       on_choose_new_game_dir, this);
00840   gtk_widget_show(GTK_WIDGET(fsel));
00841 }
00842 
00843 /*
00844  *  Choose game directory.
00845  */
00846 void ExultStudio::choose_game_path()
00847 {
00848   if (!okay_to_close())   // Okay to close prev. game?
00849     return;
00850   size_t  bufsize=128;
00851   char * cwd(new char[bufsize]);
00852   while(!getcwd(cwd,bufsize))
00853     {
00854     if(errno==ERANGE)
00855       {
00856       bufsize+=128;
00857       delete [] cwd;
00858       cwd=new char[bufsize];
00859       }
00860     else
00861       {
00862       // Ooops. getcwd() has failed
00863       delete [] cwd;  // Prevent leakage
00864       return;
00865       }
00866     }
00867   GtkFileSelection *fsel = Create_file_selection(
00868           "Select game directory",
00869       (File_sel_okay_fun) on_choose_directory, 0L);
00870   gtk_file_selection_set_filename(fsel, cwd);
00871   gtk_widget_show(GTK_WIDGET(fsel));
00872   delete [] cwd;  // Prevent leakage
00873 }
00874 
00875 
00876 
00877 /*
00878  *  Note: Patchpath may be NULL,in which case gamepath/patch is used.
00879  */
00880 void ExultStudio::set_game_path(const char *gamepath, const char *patchpath)
00881 {
00882           // Finish up external edits.
00883   Shape_chooser::clear_editing_files();
00884           // Set top-level path.
00885   add_system_path("<GAME>", gamepath);
00886   if (static_path)
00887     g_free(static_path);
00888           // Set up path to static.
00889   static_path = g_strdup_printf("%s/static", gamepath);
00890   add_system_path("<STATIC>", static_path);
00891   char *patch_path = patchpath ? g_strdup(patchpath) :
00892           g_strdup_printf("%s/patch", gamepath);
00893   add_system_path("<PATCH>", patch_path);
00894   if (!U7exists(patch_path))  // Create patch if not there.
00895     U7mkdir(patch_path, 0755);
00896   g_free(patch_path);
00897           // Clear file cache!
00898   U7FileManager::get_ptr()->reset();
00899   delete palbuf;      // Delete old.
00900   string palname("<PATCH>/"); // 1st look in patch for palettes.
00901   palname += "palettes.flx";
00902   size_t len;
00903   if (!U7exists(palname.c_str()))
00904     (palname = "<STATIC>/") += "palettes.flx";
00905   if (!U7exists(palname.c_str()))
00906     {     // No palette file, so create fake.
00907     palbuf = new unsigned char[3*256];  // How about all white?
00908     memset(palbuf, (63<<16)|(63<<8)|63, 3*256);
00909     }
00910   else
00911     {
00912     U7object pal(palname.c_str(), 0);
00913           // this may throw an exception
00914     palbuf = (unsigned char *) pal.retrieve(len);
00915     }
00916           // Set background color.
00917   palbuf[3*255] = (background_color>>18)&0x3f;
00918   palbuf[3*255 + 1] = (background_color>>10)&0x3f;
00919   palbuf[3*255 + 2] = (background_color>>2)&0x3f;
00920           // Delete old names.
00921   int num_names = names.size();
00922   for (int i = 0; i < num_names; i++)
00923     delete names[i];
00924   names.resize(0);
00925   delete files;     // Close old shape files.
00926   browser = 0;      // This was owned by 'files'.
00927   files = new Shape_file_set();
00928   vgafile = open_shape_file("shapes.vga");
00929   facefile = open_shape_file("faces.vga");
00930           // Read in shape names.
00931   string txtname("<PATCH>/text.flx");
00932   if (!U7exists(txtname.c_str()))
00933     txtname = "<STATIC>/text.flx";
00934   if (U7exists(txtname.c_str()))
00935     {
00936     Flex *items = new Flex(txtname.c_str());
00937     int num_names = items->number_of_objects();
00938     names.resize(num_names);
00939     int i;
00940     for (i = 0; i < num_names; i++)
00941       names[i] = items->retrieve(i, len);
00942     delete items;
00943     }
00944   setup_file_list();    // Set up file-list window.
00945   set_browser("", 0);   // No browser.
00946   connect_to_server();    // Connect to server with 'gamedat'.
00947 }
00948 
00949 void add_to_tree(GtkTreeStore *model, const char *folderName, const char *files, ExultFileTypes file_type)
00950 {
00951   struct dirent *entry;
00952   GtkTreeIter iter;
00953   
00954   // First, we add the folder
00955   gtk_tree_store_append(model, &iter, NULL);
00956   gtk_tree_store_set(model, &iter,
00957     FOLDER_COLUMN, folderName,
00958     FILE_COLUMN, NULL,
00959     DATA_COLUMN, -1,
00960     -1);
00961   
00962   // Scan the files which are separated by commas
00963   GtkTreeIter child_iter;
00964   char *startpos = (char *)files;
00965   int adding_children = 1;
00966   do {
00967     char *pattern;
00968     char *commapos = strstr(startpos, ",");
00969     if(commapos==0) {
00970       pattern = strdup(startpos);
00971       adding_children = 0;
00972     } else {
00973       pattern = g_strndup(startpos, commapos-startpos);
00974       startpos = commapos+1;
00975     }
00976     
00977     string spath("<STATIC>"), ppath("<PATCH>");
00978           spath = get_system_path(spath);
00979           ppath = get_system_path(ppath);
00980           char *ext = strstr(pattern,"*");
00981     if(!ext)
00982       ext = pattern;
00983     else
00984       ext++;
00985     DIR *dir = opendir(ppath.c_str());// Get names from 'patch' first.
00986     if (dir) {
00987       while(entry=readdir(dir)) {
00988         char *fname = entry->d_name;
00989                           int flen = strlen(fname);
00990                                         // Ignore case of extension.
00991                           if(!strcmp(fname,".")||!strcmp(fname,"..") ||strcasecmp(fname + flen - strlen(ext), ext) != 0)
00992                                   continue;
00993         gtk_tree_store_append (model, &child_iter, &iter);
00994         gtk_tree_store_set (model, &child_iter,
00995                 FOLDER_COLUMN, NULL,
00996                 FILE_COLUMN, fname,
00997           DATA_COLUMN, file_type,
00998                 -1);
00999                           
01000                   }
01001                   closedir(dir);
01002           }
01003     dir = opendir(spath.c_str());   // Now go through 'static'.
01004           if (dir) {
01005                   while(entry=readdir(dir)) {
01006                           char *fname = entry->d_name;
01007                           int flen = strlen(fname);
01008                                         // Ignore case of extension.
01009                           if(!strcmp(fname,".")||!strcmp(fname,"..")||strcasecmp(fname + flen - strlen(ext), ext) != 0)
01010                                   continue;
01011                                         // See if also in 'patch'.
01012                           string fullpath(ppath);
01013                           fullpath += "/";
01014                           fullpath += fname;
01015                           if (U7exists(fullpath))
01016                                   continue;
01017         gtk_tree_store_append (model, &child_iter, &iter);
01018         gtk_tree_store_set (model, &child_iter,
01019                 FOLDER_COLUMN, NULL,
01020                 FILE_COLUMN, fname,
01021           DATA_COLUMN, file_type,
01022                 -1);
01023                   }
01024                   closedir(dir);
01025           }
01026 
01027     free(pattern);
01028   } while (adding_children);
01029 }
01030 
01031 /*
01032  *  Set up the file list to the left of the main window.
01033  */
01034 
01035 void ExultStudio::setup_file_list() {
01036   GtkWidget *file_list = glade_xml_get_widget( app_xml, "file_list" );
01037   
01038     /* create tree store */
01039   GtkTreeModel *oldmod = gtk_tree_view_get_model(
01040             GTK_TREE_VIEW(file_list));
01041     GtkTreeStore *model;
01042   if (oldmod)
01043     model = GTK_TREE_STORE(oldmod);
01044   else        // Create the first time.
01045     {
01046     model = gtk_tree_store_new(
01047       NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
01048     gtk_tree_view_set_model(GTK_TREE_VIEW(file_list), 
01049               GTK_TREE_MODEL(model));
01050     g_object_unref(model);
01051     gint col_offset;
01052       GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
01053     GtkTreeViewColumn *column;
01054     
01055     /* column for folder names */
01056     g_object_set (renderer, "xalign", 0.0, NULL);
01057     col_offset = gtk_tree_view_insert_column_with_attributes(
01058           GTK_TREE_VIEW(file_list),
01059           -1, "Folders",
01060           renderer, "text",
01061           FOLDER_COLUMN, NULL);
01062     column = gtk_tree_view_get_column(GTK_TREE_VIEW(file_list), 
01063               col_offset - 1);
01064     gtk_tree_view_column_set_clickable(
01065           GTK_TREE_VIEW_COLUMN(column), TRUE);
01066     /* column for file names */
01067     col_offset = gtk_tree_view_insert_column_with_attributes(
01068           GTK_TREE_VIEW(file_list),
01069           -1, "Files",
01070           renderer, "text",
01071           FILE_COLUMN, NULL);
01072     column = gtk_tree_view_get_column(GTK_TREE_VIEW(file_list), 
01073               col_offset - 1);
01074     gtk_tree_view_column_set_clickable(
01075           GTK_TREE_VIEW_COLUMN(column), TRUE);
01076     }
01077   gtk_tree_store_clear(model);
01078   add_to_tree(model, "Shape Files", "*.vga,*.shp,combos.flx", ShapeArchive);
01079   add_to_tree(model, "Map Files", "u7chunks", ChunkArchive);
01080   add_to_tree(model, "Palette Files", "*.pal,palettes.flx", PaletteFile);
01081   
01082 #if 0 /* Skip this until we can do something with these files. */
01083   add_to_tree(model, "FLEX Files", "*.flx", FlexArchive);
01084 #endif
01085           // Expand all entries.
01086   gtk_tree_view_expand_all(GTK_TREE_VIEW(file_list));
01087   gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(file_list), TRUE);
01088   gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(file_list)), GTK_SELECTION_SINGLE);
01089 }
01090 
01091 /*
01092  *  Save all the files we've changed:
01093  *    Object browser.
01094  *    Group files.
01095  *    Shape files.
01096  *    Shape info (tfa.dat, weapons.dat, etc.)
01097  *    Game map.
01098  */
01099 
01100 void ExultStudio::save_all
01101   (
01102   )
01103   {
01104   save_groups();      // Group files.
01105   if (files)
01106     {     // Get back any changed images.
01107     Shape_chooser::check_editing_files();
01108     files->flush();   // Write out the .vga files.
01109     }
01110   write_shape_info();
01111   write_map();
01112   }
01113 
01114 /*
01115  *  Any files need saving?
01116  */
01117 
01118 bool ExultStudio::need_to_save
01119   (
01120   )
01121   {
01122   if (groups_modified())
01123     return true;
01124   if (files && files->is_modified())
01125     return true;
01126   if (shape_info_modified || shape_names_modified)
01127     return true;
01128           // Ask Exult about the map.
01129   if (Send_data(server_socket, Exult_server::info) != -1)
01130     {     // Should get immediate answer.
01131     unsigned char data[Exult_server::maxlength];
01132     Exult_server::Msg_type id;
01133     Exult_server::wait_for_response(server_socket, 100);
01134     int len = Exult_server::Receive_data(server_socket, 
01135             id, data, sizeof(data));
01136     unsigned char *ptr = &data[0];
01137     int vers, edlift, hdlift, edmode;
01138     bool editing, grid, mod;
01139     if (id == Exult_server::info &&
01140         Game_info_in(data, len, vers, edlift, hdlift, 
01141             editing, grid, mod, edmode) &&
01142         mod == true)
01143       return true;
01144     }
01145   return false;
01146   }
01147 
01148 /*
01149  *  Write out map.
01150  */
01151 
01152 void ExultStudio::write_map
01153   (
01154   )
01155   {
01156   send_to_server(Exult_server::write_map);
01157   }
01158 
01159 /*
01160  *  Read in map.
01161  */
01162 
01163 void ExultStudio::read_map
01164   (
01165   )
01166   {
01167   send_to_server(Exult_server::read_map);
01168   }
01169 
01170 /*
01171  *  Write out shape info. and text.
01172  */
01173 
01174 void ExultStudio::write_shape_info
01175   (
01176   bool force      // If set, always write.
01177   )
01178   {
01179   if ((force || shape_info_modified) && vgafile)
01180     {
01181     Shapes_vga_file *svga = 
01182         (Shapes_vga_file *) vgafile->get_ifile();
01183           // Make sure data's been read in.
01184     svga->read_info(false, true);//+++++BG?
01185     svga->write_info(false);//++++BG?
01186     }
01187   shape_info_modified = false;
01188   if (force || shape_names_modified)
01189     {
01190     shape_names_modified = false;
01191     int cnt = names.size();
01192     std::ofstream out;
01193     U7open(out, "<PATCH>/text.flx");
01194     Flex_writer writer(out, "Text created by ExultStudio", cnt);
01195     for (int i = 0; i < cnt; i++)
01196       {
01197       char *str = names[i];
01198       if (str)
01199         out << str;
01200       out.put((char) 0);  // 0-delimit.
01201       writer.mark_section_done();
01202       }
01203     if (!writer.close())
01204       EStudio::Alert("Error writing 'text.flx'");
01205     }
01206   }
01207 
01208 /*
01209  *  Reload usecode.
01210  */
01211 
01212 void ExultStudio::reload_usecode
01213   (
01214   )
01215   {
01216   send_to_server(Exult_server::reload_usecode);
01217   }
01218 
01219 /*
01220  *  Tell Exult to start/stop playing.
01221  */
01222 
01223 void ExultStudio::set_play
01224   (
01225   gboolean play     // True to play, false to edit.
01226   )
01227   {
01228   unsigned char data[Exult_server::maxlength];
01229   unsigned char *ptr = &data[0];
01230   Write2(ptr, play ? 0 : 1);  // Map_edit = !play.
01231   send_to_server(Exult_server::map_editing_mode, data, ptr - data);
01232   }
01233 
01234 /*
01235  *  Tell Exult to show or not show the tile grid.
01236  */
01237 
01238 void ExultStudio::set_tile_grid
01239   (
01240   gboolean grid     // True to show.
01241   )
01242   {
01243   unsigned char data[Exult_server::maxlength];
01244   unsigned char *ptr = &data[0];
01245   Write2(ptr, grid ? 1 : 0);  // Map_edit = !play.
01246   send_to_server(Exult_server::tile_grid, data, ptr - data);
01247   }
01248 
01249 /*
01250  *  Tell Exult to edit at a given lift.
01251  */
01252 
01253 void ExultStudio::set_edit_lift
01254   (
01255   int lift
01256   )
01257   {
01258   unsigned char data[Exult_server::maxlength];
01259   unsigned char *ptr = &data[0];
01260   Write2(ptr, lift);
01261   send_to_server(Exult_server::edit_lift, data, ptr - data);
01262   }
01263 
01264 /*
01265  *  Tell Exult to hide objects at or above a given lift.
01266  */
01267 
01268 void ExultStudio::set_hide_lift
01269   (
01270   int lift
01271   )
01272   {
01273   unsigned char data[Exult_server::maxlength];
01274   unsigned char *ptr = &data[0];
01275   Write2(ptr, lift);
01276   send_to_server(Exult_server::hide_lift, data, ptr - data);
01277   }
01278 
01279 /*
01280  *  Tell Exult to enter/leave 'terrain-edit' mode.
01281  */
01282 
01283 void ExultStudio::set_edit_terrain
01284   (
01285   gboolean terrain    // True/false
01286   )
01287   {
01288   unsigned char data[Exult_server::maxlength];
01289   unsigned char *ptr = &data[0];
01290   Write2(ptr, terrain ? 1 : 0); // NOTE:  Pass -1 to abort.  But I
01291           //   haven't got an interface yet.
01292   send_to_server(Exult_server::terrain_editing_mode, data, ptr - data);
01293   if (!terrain)
01294     {     // Turning it off.
01295     if (browser)
01296       browser->end_terrain_editing();
01297           // FOR NOW, skip_lift is reset.
01298     set_spin("hide_lift_spin", 16, true);
01299     }
01300   else        // Disable "Hide lift".
01301     set_sensitive("hide_lift_spin", false);
01302           // Set edit-mode to paint.
01303   GtkWidget *mitem = glade_xml_get_widget(app_xml, 
01304             terrain ? "paint1" : "move1");
01305   gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mitem), TRUE);
01306   }
01307 
01308 void ExultStudio::set_edit_mode
01309   (
01310   int md        // 0-2 (drag, paint, pick.
01311   )
01312   {
01313   unsigned char data[Exult_server::maxlength];
01314   unsigned char *ptr = &data[0];
01315   Write2(ptr, md);
01316           //   haven't got an interface yet.
01317   send_to_server(Exult_server::set_edit_mode, data, ptr - data);
01318   }
01319 
01320 /*
01321  *  Insert 0-delimited text into an edit box.
01322  */
01323 
01324 static void Insert_text
01325   (
01326   GtkEditable *ed,
01327   const char *text,
01328   int& pos      // Where to insert.  Updated.
01329   )
01330   {
01331   gtk_editable_insert_text(ed, text, strlen(text), &pos);
01332   }
01333 
01334 /*
01335  *  Show unused shape list received from exult.
01336  */
01337 
01338 void ExultStudio::show_unused_shapes
01339   (
01340   unsigned char *data,    // Bits set for unused shapes.
01341   int datalen     // #bytes.
01342   )
01343   {
01344   int nshapes = datalen*8;
01345   GtkTextView *text = GTK_TEXT_VIEW(glade_xml_get_widget(app_xml, "msg_text"));
01346   GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
01347   gtk_text_buffer_set_text(buffer, "", 0);  // Clear out old text
01348   set_visible("msg_win", TRUE); // Show message window.
01349   int pos = 0;
01350   GtkEditable *ed = GTK_EDITABLE(text);
01351   Insert_text(ed,
01352     "The following shapes were not found.\n", pos);
01353   Insert_text(ed,
01354     "Note that some may be created by usecode (script)\n\n", pos);
01355   for (int i = 0x96; i < nshapes; i++)  // Ignore flats (<0x96).
01356     if (!(data[i/8]&(1<<(i%8))))
01357       {
01358       const char *nm = get_shape_name(i);
01359       char *msg = g_strdup_printf("  Shape %4d:    %s\n",
01360               i, nm ? nm : "");
01361       Insert_text(ed, msg, pos);
01362       g_free(msg);
01363       }
01364   // FIXME: gtk_text_set_point(text, 0);  // Scroll back to top.
01365   }
01366 
01367 
01368 /*
01369  *  Open a shape (or chunks) file in 'patch' or 'static' directory.
01370  *
01371  *  Output: ->file info (may already have existed), or 0 if error.
01372  */
01373 
01374 Shape_file_info *ExultStudio::open_shape_file
01375   (
01376   const char *basename    // Base name of file to open.
01377   )
01378   {
01379   Shape_file_info *info = files->create(basename);
01380   if (!info)
01381     EStudio::Alert("'%s' not found in static or patch directories",
01382               basename);
01383   return info;
01384   }
01385 
01386 /*
01387  *  Prompt for a new shape file name.
01388  */
01389 
01390 void ExultStudio::new_shape_file
01391   (
01392   bool single     // Not a FLEX file.
01393   )
01394   {
01395   GtkFileSelection *fsel = Create_file_selection(
01396     single ? "Write new .shp file" : "Write new .vga file",
01397       (File_sel_okay_fun) create_shape_file,
01398               (gpointer) single);
01399 //  This doesn't work very well in GTK 1.2.  Try again later.
01400 //  gtk_file_selection_complete(fsel, single ? "*.shp" : "*.vga");
01401   if (is_system_path_defined("<PATCH>"))
01402     {     // Default to 'patch'.
01403     string patch = get_system_path("<PATCH>/");
01404     gtk_file_selection_set_filename(fsel, patch.c_str());
01405     }
01406   gtk_widget_show(GTK_WIDGET(fsel));
01407   }
01408 
01409 /*
01410  *  Create a new shape/shapes file.
01411  */
01412 
01413 void ExultStudio::create_shape_file
01414   (
01415   char *pathname,     // Full path.
01416   gpointer udata      // 1 if NOT a FLEX file.
01417   )
01418   {
01419   bool oneshape = (bool) udata;
01420   Shape *shape = 0;
01421   if (oneshape)     // Single-shape?
01422     {     // Create one here.
01423     const int w = 8, h = 8;
01424     unsigned char pixels[w*h];  // Create an 8x8 shape.
01425     memset(&pixels[0], 1, w*h); // Just use color #1.
01426     shape = new Shape(new Shape_frame(&pixels[0],
01427         w, h, w - 1, h - 1, true));
01428     }
01429   try {       // Write file.
01430     if (oneshape)
01431       Image_file_info::write_file(pathname, &shape, 1, true);
01432     else
01433       Image_file_info::write_file(pathname, 0, 0, false);
01434   }
01435   catch (const exult_exception& e) {
01436     EStudio::Alert(e.what());
01437   }
01438   ExultStudio *studio = ExultStudio::get_instance();
01439   studio->setup_file_list();  // Rescan list of shape files.
01440   }
01441 
01442 /*
01443  *  Get value of a toggle button (false if not found).
01444  */
01445 
01446 bool ExultStudio::get_toggle
01447   (
01448   char *name
01449   )
01450   {
01451   GtkWidget *btn = glade_xml_get_widget(app_xml, name);
01452   return btn ? gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn)) : -1;
01453   }
01454 
01455 /*
01456  *  Find and set a toggle/checkbox button.
01457  */
01458 
01459 void ExultStudio::set_toggle
01460   (
01461   char *name,
01462   bool val
01463   )
01464   {
01465   GtkWidget *btn = glade_xml_get_widget(app_xml, name);
01466   if (btn)
01467     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btn), val);
01468   }
01469 
01470 /*
01471  *  Get an 8-bit set of flags from a group of toggles.
01472  */
01473 
01474 unsigned char ExultStudio::get_bit_toggles
01475   (
01476   char **names,     // Names for bit 0, 1, 2,...
01477   int num       // # of names/bits.
01478   )
01479   {
01480   unsigned char bits = 0;
01481   for (int i = 0; i < num; i++)
01482     bits |= (get_toggle(names[i]) ? 1 : 0) << i;
01483   return bits;
01484   }
01485 
01486 /*
01487  *  Set a group of toggles based on a sequential (0, 1, 2...) set of bit
01488  *  flags.
01489  */
01490 
01491 void ExultStudio::set_bit_toggles
01492   (
01493   char **names,     // Names for bit 0, 1, 2,...
01494   int num,      // # of names/bits.
01495   unsigned char bits
01496   )
01497   {
01498   for (int i = 0; i < num; i++)
01499     set_toggle(names[i], (bits&(1<<i)) != 0);
01500   }
01501 
01502 /*
01503  *  Get value of option-menu button (-1 if unsuccessful).
01504  */
01505 
01506 int ExultStudio::get_optmenu
01507   (
01508   char *name
01509   )
01510   {
01511   GtkWidget *btn = glade_xml_get_widget(app_xml, name);
01512   if (!btn)
01513     return -1;
01514   GtkWidget *menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(btn));
01515   GtkWidget *active = gtk_menu_get_active(GTK_MENU(menu));
01516   return g_list_index(GTK_MENU_SHELL(menu)->children, active);
01517   }
01518 
01519 /*
01520  *  Find and set an option-menu button.
01521  */
01522 
01523 void ExultStudio::set_optmenu
01524   (
01525   char *name,
01526   int val
01527   )
01528   {
01529   GtkWidget *btn = glade_xml_get_widget(app_xml, name);
01530   if (btn)
01531     gtk_option_menu_set_history(GTK_OPTION_MENU(btn), val);
01532   }
01533 
01534 /*
01535  *  Get value of spin button (-1 if unsuccessful).
01536  */
01537 
01538 int ExultStudio::get_spin
01539   (
01540   char *name
01541   )
01542   {
01543   GtkWidget *btn = glade_xml_get_widget(app_xml, name);
01544   return btn ? gtk_spin_button_get_value_as_int(
01545             GTK_SPIN_BUTTON(btn)) : -1;
01546   }
01547 
01548 /*
01549  *  Find and set a spin button.
01550  */
01551 
01552 void ExultStudio::set_spin
01553   (
01554   char *name,
01555   int val,
01556   bool sensitive
01557   )
01558   {
01559   GtkWidget *btn = glade_xml_get_widget(app_xml, name);
01560   if (btn)
01561     {
01562     gtk_spin_button_set_value(GTK_SPIN_BUTTON(btn), val);
01563     gtk_widget_set_sensitive(btn, sensitive);
01564     }
01565   }
01566 
01567 /*
01568  *  Find and set a spin button, along with its range.
01569  */
01570 
01571 void ExultStudio::set_spin
01572   (
01573   char *name,
01574   int val,
01575   int low, int high   // Range.
01576   )
01577   {
01578   GtkWidget *btn = glade_xml_get_widget(app_xml, name);
01579   if (btn)
01580     {
01581     gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(btn),
01582       GTK_ADJUSTMENT(
01583       gtk_adjustment_new (0, low, high, 1, 10, 10)));
01584     gtk_spin_button_set_value(GTK_SPIN_BUTTON(btn), val);
01585     }
01586   }
01587 
01588 /*
01589  *  Get number from a text field.
01590  *
01591  *  Output: Number, or -1 if not found.
01592  */
01593 
01594 int ExultStudio::get_num_entry
01595   (
01596   char *name
01597   )
01598   {
01599   GtkWidget *field = glade_xml_get_widget(app_xml, name);
01600   if (!field)
01601     return -1;
01602   const gchar *txt = gtk_entry_get_text(GTK_ENTRY(field));
01603   if (!txt)
01604     return -1;
01605   while (*txt == ' ')
01606     txt++;      // Skip space.
01607   if (txt[0] == '0' && txt[1] == 'x')
01608     return (int) strtoul(txt + 2, 0, 16); // Hex.
01609   else
01610     return atoi(txt);
01611   }
01612 
01613 /*
01614  *  Get text from a text field.
01615  *
01616  *  Output: ->text, or null if not found.
01617  */
01618 
01619 const gchar *ExultStudio::get_text_entry
01620   (
01621   char *name
01622   )
01623   {
01624   GtkWidget *field = glade_xml_get_widget(app_xml, name);
01625   if (!field)
01626     return 0;
01627   return gtk_entry_get_text(GTK_ENTRY(field));
01628   }
01629 
01630 /*
01631  *  Find and set a text field to a number.
01632  */
01633 
01634 void ExultStudio::set_entry
01635   (
01636   char *name,
01637   int val,
01638   bool hex,
01639   bool sensitive
01640   )
01641   {
01642   GtkWidget *field = glade_xml_get_widget(app_xml, name);
01643   if (field)
01644     {
01645     char *txt = hex ? g_strdup_printf("0x%x", val)
01646         : g_strdup_printf("%d", val);
01647     gtk_entry_set_text(GTK_ENTRY(field), txt);
01648     g_free(txt);
01649     gtk_widget_set_sensitive(field, sensitive);
01650     }
01651   }
01652 
01653 /*
01654  *  Set text field.
01655  */
01656 
01657 void ExultStudio::set_entry
01658   (
01659   char *name,
01660   const char *val,
01661   bool sensitive
01662   )
01663   {
01664   GtkWidget *field = glade_xml_get_widget(app_xml, name);
01665   if (field)
01666     {
01667     gtk_entry_set_text(GTK_ENTRY(field), val);
01668     gtk_widget_set_sensitive(field, sensitive);
01669     }
01670   }
01671 
01672 /*
01673  *  Set statusbar.
01674  *  Output: Msg. ID, or 0.
01675  */
01676 
01677 guint ExultStudio::set_statusbar
01678   (
01679   char *name,
01680   int context,
01681   char *msg
01682   )
01683   {
01684   GtkWidget *sbar = glade_xml_get_widget(app_xml, name);
01685   if (sbar)
01686     return gtk_statusbar_push(GTK_STATUSBAR(sbar), context, msg);
01687   else
01688     return 0;
01689   }
01690 
01691 /*
01692  *  Pop last message that was set.
01693  */
01694 
01695 void ExultStudio::remove_statusbar
01696   (
01697   char *name,
01698   int context,
01699   guint msgid
01700   )
01701   {
01702   if (msgid == 0)
01703     return;
01704   GtkWidget *sbar = glade_xml_get_widget(app_xml, name);
01705   if (sbar)
01706     return gtk_statusbar_remove(GTK_STATUSBAR(sbar),context,msgid);
01707   }
01708 
01709 /*
01710  *  Set a button's text.
01711  */
01712 
01713 void ExultStudio::set_button
01714   (
01715   char *name,
01716   const char *text
01717   )
01718   {
01719   GtkWidget *btn = glade_xml_get_widget(app_xml, name);
01720   GtkLabel *label = GTK_LABEL(GTK_BIN(btn)->child);
01721   gtk_label_set_text(label, text);
01722   }
01723 
01724 /*
01725  *  Show/hide a widget.
01726  */
01727 
01728 void ExultStudio::set_visible
01729   (
01730   char *name,
01731   bool vis
01732   )
01733   {
01734   GtkWidget *widg = glade_xml_get_widget(app_xml, name);
01735   if (widg)
01736     {
01737     if (vis)
01738       gtk_widget_show(widg);
01739     else
01740       gtk_widget_hide(widg);
01741     }
01742   }
01743 
01744 /*
01745  *  Enable/disable a widget.
01746  */
01747 
01748 void ExultStudio::set_sensitive
01749   (
01750   char *name,
01751   bool tf
01752   )
01753   {
01754   GtkWidget *widg = glade_xml_get_widget(app_xml, name);
01755   if (widg)
01756     gtk_widget_set_sensitive(widg, tf);
01757   }
01758 
01759 /*
01760  *  Handle 'prompt' dialogs:
01761  */
01762 static int prompt_choice = 0;   // Gets prompt choice.
01763 
01764 C_EXPORT void
01765 on_prompt3_yes_clicked      (GtkToggleButton *button,
01766            gpointer   user_data)
01767 {
01768   prompt_choice = 0;
01769 }
01770 C_EXPORT void
01771 on_prompt3_no_clicked     (GtkToggleButton *button,
01772            gpointer   user_data)
01773 {
01774   prompt_choice = 1;
01775 }
01776 C_EXPORT void
01777 on_prompt3_cancel_clicked   (GtkToggleButton *button,
01778            gpointer   user_data)
01779 {
01780   prompt_choice = 2;
01781 }
01782 
01783 
01784 /*
01785  *  Prompt for one of two/three answers.
01786  *
01787  *  Output: 0 for 1st choice, 1 for 2nd, 2 for 3rd.
01788  */
01789 
01790 int ExultStudio::prompt
01791   (
01792   const char *msg,    // Question to ask.
01793   const char *choice0,    // 1st choice.
01794   const char *choice1,    // 2nd choice, or NULL.
01795   const char *choice2   // 3rd choice, or NULL.
01796   )
01797   {
01798   static GdkPixmap *logo_pixmap = NULL;
01799   static GdkBitmap *logo_mask = NULL;
01800   if (!logo_pixmap)   // First time?
01801     {
01802     logo_pixmap = gdk_pixmap_create_from_xpm_d(app->window, 
01803         &logo_mask, NULL, logo_xpm);
01804     GtkWidget *pix = gtk_pixmap_new(logo_pixmap, logo_mask);
01805     gtk_widget_show(pix);
01806     GtkWidget *hbox = glade_xml_get_widget(app_xml, 
01807               "prompt3_hbox");
01808     gtk_box_pack_start(GTK_BOX(hbox), pix, FALSE, FALSE, 12);
01809           // Make logo show to left.
01810     gtk_box_reorder_child(GTK_BOX(hbox), pix, 0);
01811     }
01812   GtkWidget *dlg = glade_xml_get_widget(app_xml, "prompt3_dialog");
01813   gtk_label_set_text(
01814     GTK_LABEL(glade_xml_get_widget(app_xml, "prompt3_label")),
01815                 msg);
01816   set_button("prompt3_yes", choice0);
01817   if (choice1)
01818     {
01819     set_button("prompt3_no", choice1);
01820     set_visible("prompt3_no", true);
01821     }
01822   else
01823     set_visible("prompt3_no", false);
01824   if (choice2)      // 3rd choice?  Show btn if so.
01825     {
01826     set_button("prompt3_cancel", choice2);
01827     set_visible("prompt3_cancel", true);
01828     }
01829   else
01830     set_visible("prompt3_cancel", false);
01831   prompt_choice = -1;
01832   gtk_window_set_modal(GTK_WINDOW(dlg), true);
01833   gtk_widget_show(dlg);   // Should be modal.
01834   while (prompt_choice == -1) // Spin.
01835     gtk_main_iteration(); // (Blocks).
01836   gtk_widget_hide(dlg);
01837   assert(prompt_choice >= 0 && prompt_choice <= 2);
01838   return prompt_choice;
01839   }
01840 
01841 namespace EStudio {
01842 
01843 /*
01844  *  Same as ExultStudio::prompt, but as a routine.
01845  */
01846 int Prompt
01847   (
01848   const char *msg,    // Question to ask.
01849   const char *choice0,    // 1st choice.
01850   const char *choice1,    // 2nd choice, or NULL.
01851   const char *choice2   // 3rd choice, or NULL.
01852   )
01853   {
01854   return ExultStudio::get_instance()->prompt(msg, choice0, choice1,
01855               choice2);
01856   }
01857 
01858 /*
01859  *  Just print a message (usually an error).
01860  */
01861 void Alert
01862   (
01863   const char *msg,    // May be in printf format.
01864   ...
01865   )
01866   {
01867   std::va_list args;
01868   va_start(args, msg);
01869   char *fullmsg = g_strdup_vprintf(msg, args);
01870   Prompt(fullmsg, "Okay");
01871   g_free(fullmsg);
01872   }
01873 
01874 /*
01875  *  Add a menu item to a menu.
01876  *
01877  *  Output: Menu item.
01878  */
01879 
01880 GtkWidget *Add_menu_item
01881   (
01882   GtkWidget *menu,    // Menu to add to.
01883   const char *label,    // What to put.  NULL for separator.
01884   GtkSignalFunc func,   // Handle menu choice.
01885   gpointer func_data    // Data passed to func().
01886   )
01887   {
01888   GtkWidget *mitem = label ? gtk_menu_item_new_with_label(label) :
01889         gtk_menu_item_new();
01890   gtk_widget_show(mitem);
01891   gtk_menu_append(GTK_MENU(menu), mitem);
01892   if (!label)     // Want separator?
01893     gtk_widget_set_sensitive(mitem, FALSE);
01894   if (func)     // Function?
01895     gtk_signal_connect(GTK_OBJECT(mitem), "activate",
01896         GTK_SIGNAL_FUNC(func), func_data);
01897   return mitem;
01898   }
01899 
01900 /*
01901  *  Create an arrow button.
01902  */
01903 
01904 GtkWidget *Create_arrow_button
01905   (
01906   GtkArrowType dir,   // Direction.
01907   GtkSignalFunc clicked,    // Call this when clicked.
01908   gpointer func_data    // Passed to 'clicked'.
01909   )
01910   {
01911   GtkWidget *btn = gtk_button_new();
01912   gtk_widget_show(btn);
01913   GTK_WIDGET_SET_FLAGS(btn, GTK_CAN_DEFAULT);
01914   GtkWidget *arrow = gtk_arrow_new(dir, GTK_SHADOW_OUT);
01915   gtk_widget_show(arrow);
01916   gtk_container_add(GTK_CONTAINER(btn), arrow);
01917   gtk_signal_connect(GTK_OBJECT(btn), "clicked", clicked, func_data);
01918   return btn;
01919   }
01920 
01921 } // namespace EStudio
01922 
01923 /*
01924  *  'Preferences' window events.
01925  */
01926 
01927 C_EXPORT void
01928 on_prefs_cancel_clicked     (GtkButton *button,
01929            gpointer   user_data)
01930 {
01931   gtk_widget_hide(gtk_widget_get_toplevel(GTK_WIDGET(button)));
01932 }
01933 C_EXPORT void
01934 on_prefs_apply_clicked      (GtkButton *button,
01935            gpointer   user_data)
01936 {
01937   ExultStudio::get_instance()->save_preferences();
01938 }
01939 C_EXPORT void
01940 on_prefs_okay_clicked     (GtkButton *button,
01941            gpointer   user_data)
01942 {
01943   ExultStudio::get_instance()->save_preferences();
01944   gtk_widget_hide(gtk_widget_get_toplevel(GTK_WIDGET(button)));
01945 }
01946 
01947 /*
01948  *  'Okay' was hit in the color selector.
01949  */
01950 
01951 void ExultStudio::background_color_okay
01952   (
01953   GtkWidget *dlg,
01954   gpointer data
01955   )
01956   {
01957   GtkColorSelectionDialog *colorsel = GTK_COLOR_SELECTION_DIALOG(dlg);
01958   gdouble rgb[3];
01959   gtk_color_selection_get_color(
01960       GTK_COLOR_SELECTION(colorsel->colorsel), rgb);
01961   unsigned char r = (unsigned char) (rgb[0]*256),
01962           g = (unsigned char) (rgb[1]*256),
01963           b = (unsigned char) (rgb[2]*256);
01964   ExultStudio *studio = ExultStudio::get_instance();
01965   studio->background_color = (r<<16) + (g<<8) + b;
01966           // Show new color.
01967   GtkWidget *backgrnd = glade_xml_get_widget(studio->app_xml, 
01968               "prefs_background");
01969   gtk_object_set_user_data(GTK_OBJECT(backgrnd), 
01970           (gpointer) studio->background_color);
01971   GdkRectangle area = {0, 0, backgrnd->allocation.width, 
01972           backgrnd->allocation.height};
01973   gtk_widget_draw(backgrnd, &area);
01974   gtk_widget_destroy(dlg);
01975   }
01976 
01977 C_EXPORT void
01978 on_prefs_background_choose_clicked  (GtkButton *button,
01979            gpointer   user_data)
01980 {
01981   GtkColorSelectionDialog *colorsel = GTK_COLOR_SELECTION_DIALOG(
01982     gtk_color_selection_dialog_new("Background color"));
01983   gtk_window_set_modal(GTK_WINDOW(colorsel), true);
01984           // Set mouse click handler.
01985   gtk_signal_connect_object(GTK_OBJECT(colorsel->ok_button), "clicked",
01986     GTK_SIGNAL_FUNC(ExultStudio::background_color_okay), 
01987             GTK_OBJECT(colorsel));
01988   gtk_signal_connect_object(GTK_OBJECT(colorsel->cancel_button), 
01989     "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
01990             GTK_OBJECT(colorsel));
01991           // Set delete handler.
01992   gtk_signal_connect(GTK_OBJECT(colorsel), "delete_event",
01993         GTK_SIGNAL_FUNC(gtk_false), 0L);
01994           // Get color.
01995   guint32 c = ExultStudio::get_instance()->get_background_color();
01996   gdouble rgb[3];
01997   rgb[0] = ((double) ((c>>16)&0xff))/256;
01998   rgb[1] = ((double) ((c>>8)&0xff))/256;
01999   rgb[2] = ((double) ((c>>0)&0xff))/256;
02000   gtk_color_selection_set_color(GTK_COLOR_SELECTION(colorsel->colorsel),
02001                 rgb);
02002   gtk_widget_show(GTK_WIDGET(colorsel));
02003 }
02004           // Background color area exposed.
02005 C_EXPORT gboolean on_prefs_background_expose_event
02006   (
02007   GtkWidget *widget,    // The draw area.
02008   GdkEventExpose *event,
02009   gpointer data
02010   )
02011   {
02012   guint32 color = (guint32) gtk_object_get_user_data(GTK_OBJECT(widget));
02013   GdkGC *gc = (GdkGC *) 
02014       gtk_object_get_data(GTK_OBJECT(widget), "color_gc");
02015   if (!gc)
02016     {
02017     gc = gdk_gc_new(widget->window);
02018     gtk_object_set_data(GTK_OBJECT(widget), "color_gc", gc);
02019     }
02020   gdk_rgb_gc_set_foreground(gc, color);
02021   gdk_draw_rectangle(widget->window, gc, TRUE, event->area.x, 
02022       event->area.y, event->area.width, event->area.height);
02023   return (TRUE);
02024   }
02025 
02026           // X at top of window.
02027 C_EXPORT gboolean on_prefs_window_delete_event
02028   (
02029   GtkWidget *widget,
02030   GdkEvent *event,
02031   gpointer user_data
02032   )
02033   {
02034   gtk_widget_hide(widget);
02035   return TRUE;
02036   }
02037 
02038 /*
02039  *  Open preferences window.
02040  */
02041 
02042 void ExultStudio::open_preferences
02043   (
02044   )
02045   {
02046   set_entry("prefs_image_editor", image_editor ? image_editor : "");
02047   set_entry("prefs_default_game", default_game ? default_game : "");
02048   GtkWidget *backgrnd = glade_xml_get_widget(app_xml, 
02049               "prefs_background");
02050   gtk_object_set_user_data(GTK_OBJECT(backgrnd), 
02051             (gpointer) background_color);
02052   GtkWidget *win = glade_xml_get_widget(app_xml, "prefs_window");
02053   gtk_widget_show(win);
02054   }
02055 
02056 /*
02057  *  Save preferences.
02058  */
02059 
02060 void ExultStudio::save_preferences
02061   (
02062   )
02063   {
02064   const char *text = get_text_entry("prefs_image_editor");
02065   g_free(image_editor);
02066   image_editor = g_strdup(text);
02067   config->set("config/estudio/image_editor", image_editor, true);
02068   text = get_text_entry("prefs_default_game");
02069   g_free(default_game);
02070   default_game = g_strdup(text);
02071   config->set("config/estudio/default_game", default_game, true);
02072   GtkWidget *backgrnd = glade_xml_get_widget(app_xml, 
02073               "prefs_background");
02074   background_color = (guint32) gtk_object_get_user_data(
02075             GTK_OBJECT(backgrnd));
02076   config->set("config/estudio/background_color", background_color, true);
02077           // Set background color.
02078   palbuf[3*255] = (background_color>>18)&0x3f;
02079   palbuf[3*255 + 1] = (background_color>>10)&0x3f;
02080   palbuf[3*255 + 2] = (background_color>>2)&0x3f;
02081   if (browser)      // Repaint browser.
02082     browser->set_background_color(background_color);
02083   }
02084 
02085 
02086 /*
02087  *  Main routine.
02088  */
02089 void ExultStudio::run()
02090 {
02091   gtk_main();
02092 }
02093 
02094 /*
02095  *  This is called every few seconds to try to reconnect to Exult.
02096  */
02097 
02098 static gint Reconnect
02099   (
02100   gpointer data     // ->ExultStudio.
02101   )
02102   {
02103   ExultStudio *studio = (ExultStudio *) data;
02104   if (studio->connect_to_server())
02105     return 0;   // Cancel timer.  We succeeded.
02106   else
02107     return 1;
02108   }
02109 
02110 /*
02111  *  Send message to server.
02112  *
02113  *  Output: false if error sending (reported).
02114  */
02115 
02116 bool ExultStudio::send_to_server
02117   (
02118   Exult_server::Msg_type id,
02119   unsigned char *data, 
02120   int datalen
02121   )
02122   {
02123   if (Send_data(server_socket, id, data, datalen) == -1)
02124     {
02125     cerr << "Error sending to server" << endl;
02126     return false;
02127     }
02128   return true;
02129   }
02130 
02131 /*
02132  *  Input from server is available.
02133  */
02134 
02135 #ifndef WIN32
02136 static void Read_from_server
02137   (
02138   gpointer data,      // ->ExultStudio.
02139   gint socket,
02140   GdkInputCondition condition
02141   )
02142   {
02143   ExultStudio *studio = (ExultStudio *) data;
02144   studio->read_from_server();
02145   }
02146 #else
02147 static gint Read_from_server
02148   (
02149   gpointer data     // ->ExultStudio.
02150   )
02151   {
02152   ExultStudio *studio = (ExultStudio *) data;
02153   studio->read_from_server();
02154   return TRUE;
02155   }
02156 #endif
02157 
02158 gint Do_Drop_Callback(gpointer data);
02159 
02160 void ExultStudio::read_from_server
02161   (
02162   )
02163   {
02164 #ifdef WIN32
02165   // Nothing
02166   int len = Exult_server::peek_pipe();
02167 
02168   //Do_Drop_Callback(&len);
02169 
02170   if (len == -1)  {
02171     cout << "Disconnected from server" << endl;
02172     gtk_timeout_remove(server_input_tag);
02173     Exult_server::disconnect_from_server();
02174     server_input_tag = -1;
02175     server_socket = -1;
02176         // Try again every 4 secs.
02177     gtk_timeout_add(4000, Reconnect, this);
02178 
02179     return;
02180   }
02181   if (len < 1) return;
02182 #endif
02183   unsigned char data[Exult_server::maxlength];
02184   Exult_server::Msg_type id;
02185   int datalen = Exult_server::Receive_data(server_socket, id, data,
02186               sizeof(data));
02187   if (datalen < 0)
02188     {
02189     cout << "Error reading from server" << endl;
02190     if (server_socket == -1)// Socket closed?
02191       {
02192 #ifndef WIN32
02193       gdk_input_remove(server_input_tag);
02194 #else
02195       gtk_timeout_remove(server_input_tag);
02196       Exult_server::disconnect_from_server();
02197 #endif
02198       server_input_tag = -1;
02199           // Try again every 4 secs.
02200       gtk_timeout_add(4000, Reconnect, this);
02201       }
02202     return;
02203     }
02204   cout << "Read " << datalen << " bytes from server" << endl;
02205   cout << "ID = " << (int) id << endl;
02206   switch (id)
02207     {
02208   case Exult_server::obj:
02209     open_obj_window(data, datalen);
02210     break;
02211   case Exult_server::egg:
02212     open_egg_window(data, datalen);
02213     break;
02214   case Exult_server::npc:
02215     open_npc_window(data, datalen);
02216     break;
02217   case Exult_server::user_responded:
02218   case Exult_server::cancel:
02219   case Exult_server::locate_terrain:
02220   case Exult_server::swap_terrain:
02221   case Exult_server::insert_terrain:
02222   case Exult_server::delete_terrain:
02223   case Exult_server::locate_shape:
02224   case Exult_server::game_pos:
02225     if (waiting_for_server) // Send msg. to callback.
02226       {
02227       waiting_for_server(id, data, datalen, waiting_client);
02228       waiting_for_server = 0;
02229       waiting_client = 0;
02230       }
02231     else if (browser)
02232       browser->server_response((int) id, data, datalen);
02233     break;
02234   case Exult_server::info:
02235     info_received(data, datalen);
02236     break;
02237   case Exult_server::view_pos:
02238     if (locwin)
02239       locwin->view_changed(data, datalen);
02240     break;
02241   case Exult_server::combo_pick:
02242     open_combo_window();  // Open if necessary.
02243     if (combowin)
02244       combowin->add(data, datalen);
02245     break;
02246   case Exult_server::unused_shapes:
02247     show_unused_shapes(data, datalen);
02248     break;
02249   case Exult_server::select_status:
02250     set_edit_menu(data[0] != 0, data[1] != 0);
02251     break;
02252   case Exult_server::usecode_debugging:
02253     std::cerr << "Warning: got a usecode debugging message! (ignored)"
02254           << std::endl;
02255     break;
02256   }
02257   }
02258 
02259 /*
02260  *  Try to connect to the Exult game.
02261  *
02262  *  Output: True if succeeded.
02263  */
02264 bool ExultStudio::connect_to_server
02265   (
02266   )
02267   {
02268   if (!static_path)
02269     return false;   // No place to go.
02270 #ifndef WIN32
02271   if (server_socket >= 0)   // Close existing socket.
02272     {
02273     close(server_socket);
02274     gdk_input_remove(server_input_tag);
02275     }
02276   server_socket = server_input_tag = -1;
02277   struct sockaddr_un addr;
02278   addr.sun_family = AF_UNIX;
02279 
02280   char *home = getenv("HOME");
02281   addr.sun_path[0] = 0;
02282   if (home)     // Use $HOME/.exult/exultserver
02283     {     //   if possible.
02284     strcpy(addr.sun_path, home);
02285     strcat(addr.sun_path, "/.exult/exultserver");
02286     if (!U7exists(addr.sun_path))
02287       addr.sun_path[0] = 0;
02288     }
02289   if (!addr.sun_path[0])    // Default to game/gamedat.
02290     {
02291           strcpy(addr.sun_path, static_path);
02292           char *pstatic = strrchr(addr.sun_path, '/');
02293           if (pstatic && !pstatic[1])     // End of path?
02294                   {
02295                   pstatic[0] = 0;
02296                   pstatic = strrchr(addr.sun_path, '/');
02297                   }
02298           if (!pstatic)
02299                   {
02300                   cout << "Can't find gamedat for socket" << endl;
02301                   return false;
02302                   }
02303           strcpy(pstatic + 1, "gamedat/exultserver");
02304     }
02305   server_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
02306   if (server_socket < 0)
02307     {
02308     perror("Failed to open map-editor socket");
02309     return false;
02310     }
02311           // Set to be non-blocking.
02312 //  fcntl(server_socket, F_SETFL, 
02313 //        fcntl(server_socket, F_GETFL) | O_NONBLOCK);
02314   cout << "Trying to connect to server at '" << addr.sun_path << "'"
02315               << endl;
02316   if (connect(server_socket, (struct sockaddr *) &addr, 
02317           sizeof(addr.sun_family) + strlen(addr.sun_path)) == -1)
02318     {
02319     perror("Socket connect");
02320     close(server_socket);
02321     server_socket = -1;
02322     return false;
02323     }
02324   server_input_tag = gdk_input_add(server_socket,
02325       GDK_INPUT_READ, Read_from_server, this);
02326 #else
02327     // Close existing socket.
02328   if (server_socket != -1) Exult_server::disconnect_from_server();
02329   if (server_input_tag != -1) gtk_timeout_remove(server_input_tag);
02330   server_socket = server_input_tag = -1;
02331 
02332   if (Exult_server::try_connect_to_server(static_path) > 0)
02333     server_input_tag = gtk_timeout_add(50, Read_from_server, this);
02334   else
02335     return false;
02336 #endif
02337   cout << "Connected to server" << endl;
02338   send_to_server(Exult_server::info); // Request version, etc.
02339   set_edit_menu(false, false);  // For now, init. edit menu.
02340   return true;
02341   }
02342 /*
02343  *  'Info' message received.
02344  */
02345 
02346 void ExultStudio::info_received
02347   (
02348   unsigned char *data,    // Message from Exult.
02349   int len
02350   )
02351   {
02352   int vers, edlift, hdlift, edmode;
02353   bool editing, grid, mod;
02354   Game_info_in(data, len, vers, edlift, hdlift, 
02355           editing, grid, mod, edmode);
02356   if (vers != Exult_server::version)
02357     {     // Wrong version of Exult.
02358     EStudio::Alert("Expected ExultServer version %d, but got %d",
02359         Exult_server::version, vers);
02360 #ifndef WIN32
02361     close(server_socket);
02362     gdk_input_remove(server_input_tag);
02363 #else
02364     Exult_server::disconnect_from_server();
02365     gtk_timeout_remove(server_input_tag);
02366 #endif
02367     server_socket = server_input_tag = -1;
02368     return;
02369     }
02370           // Set controls to what Exult thinks.
02371   set_spin("edit_lift_spin", edlift);
02372   set_spin("hide_lift_spin", hdlift);
02373   set_toggle("play_button", !editing);
02374   set_toggle("tile_grid_button", grid);
02375   if (edmode >= 0 && edmode < sizeof(mode_names)/sizeof(mode_names[0]))
02376     {
02377     GtkWidget *mitem = glade_xml_get_widget(app_xml, 
02378               mode_names[edmode]);
02379 
02380     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mitem),
02381                   TRUE);
02382     }
02383   }
02384 
02385 /*
02386  *  Set edit menu entries depending on whether there's a selection or
02387  *  clipboard available.
02388  */
02389 
02390 void ExultStudio::set_edit_menu
02391   (
02392   bool sel,     // Selection available.
02393   bool clip     // Clipboard isn't empty.
02394   )
02395   {
02396   set_sensitive("cut1", sel);
02397   set_sensitive("copy1", sel);
02398   set_sensitive("paste1", clip);
02399   set_sensitive("properties1", sel);
02400   set_sensitive("basic_properties1", sel);
02401   }
02402 

Generated on Mon Jul 9 14:42:48 2007 for ExultEngine by  doxygen 1.5.1