File_gump.cc

Go to the documentation of this file.
00001 /*
00002  *  Copyright (C) 2000-2002  The Exult Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (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 #ifndef ALPHA_LINUX_CXX
00024 #  include <cstring>
00025 #endif 
00026 
00027 #include "SDL_events.h"
00028 
00029 #include "Audio.h"
00030 #include "Configuration.h"
00031 #include "File_gump.h"
00032 #include "exult.h"
00033 #include "game.h"
00034 #include "gamewin.h"
00035 #include "Gump_button.h"
00036 #include "mouse.h"
00037 #include "Yesno_gump.h"
00038 
00039 #ifndef UNDER_CE
00040 using std::cout;
00041 using std::endl;
00042 using std::memmove;
00043 using std::string;
00044 using std::strlen;
00045 using std::strncpy;
00046 #endif
00047 
00048 /*
00049  *  Statics:
00050  */
00051 short File_gump::btn_rows[2] = {143, 156};
00052 short File_gump::btn_cols[3] = {94, 163, 232};
00053 short File_gump::textx = 237, File_gump::texty = 14,
00054       File_gump::texth = 13;
00055 
00056 
00057 /*
00058  *  Load or save button.
00059  */
00060 class Load_save_button : public Gump_button
00061 {
00062 public:
00063   Load_save_button(Gump *par, int px, int py, int shapenum)
00064     : Gump_button(par, shapenum, px, py)
00065     {  }
00066           // What to do when 'clicked':
00067   virtual void activate();
00068 };
00069 
00070 /*
00071  *  Quit button.
00072  */
00073 class Quit_button : public Gump_button
00074 {
00075 public:
00076   Quit_button(Gump *par, int px, int py)
00077     : Gump_button(par, 
00078       game->get_shape("gumps/quitbtn"), px, py)
00079     {  }
00080           // What to do when 'clicked':
00081   virtual void activate();
00082 };
00083 
00084 /*
00085  *  Sound 'toggle' buttons.
00086  */
00087 class Sound_button : public Gump_button
00088 {
00089 public:
00090   Sound_button(Gump *par, int px, int py, int shapenum,
00091                 bool enabled)
00092     : Gump_button(par, shapenum, px, py)
00093     { pushed = enabled; }
00094           // What to do when 'clicked':
00095   virtual void activate();
00096 };
00097 
00098 /*
00099  *  Clicked a 'load' or 'save' button.
00100  */
00101 
00102 void Load_save_button::activate
00103   (
00104   )
00105 {
00106   if (get_shapenum() == game->get_shape("gumps/loadbtn"))
00107     ((File_gump *) parent)->load();
00108   else
00109     ((File_gump *) parent)->save();
00110 }
00111 
00112 /*
00113  *  Clicked on 'quit'.
00114  */
00115 
00116 void Quit_button::activate
00117   (
00118   )
00119 {
00120   ((File_gump *) parent)->quit();
00121 }
00122 
00123 /*
00124  *  Clicked on one of the sound options.
00125  */
00126 
00127 void Sound_button::activate
00128   (
00129   )
00130 {
00131   pushed = ((File_gump *) parent)->toggle_option(this);
00132   parent->paint();
00133 }
00134 
00135 
00136 /*
00137  *  An editable text field:
00138  */
00139 class Gump_text : public Gump_widget
00140 {
00141   char *text;     // Holds text, 0-delimited.
00142   int max_size;     // Size (max) of text.
00143   int length;     // Current # chars.
00144   int textx, texty;   // Where to show text rel. to parent.
00145   int cursor;     // Index of char. cursor is before.
00146 public:
00147   Gump_text(Gump *par, int shnum, int px, int py, int maxsz,
00148             int tx, int ty)
00149     : Gump_widget(par, shnum, px, py), text(new char[maxsz + 1]),
00150       max_size(maxsz), length(0), textx(x + tx), texty(y + ty),
00151       cursor(0)
00152     {
00153     text[0] = text[maxsz] = 0;
00154     Shape_frame *shape = 
00155         ShapeID(shnum, 0, SF_GUMPS_VGA).get_shape();
00156           // Want text coords. rel. to parent.
00157     textx -= shape->get_xleft();
00158     texty -= shape->get_yabove();
00159     }
00160   ~Gump_text()
00161     { delete [] text; }
00162   int get_length()
00163     { return length; }
00164   char *get_text()
00165     { return text; }
00166   void set_text(char *newtxt) // Set text.
00167     {
00168       strncpy(text, newtxt ? newtxt : "", max_size);
00169       length = strlen(text);
00170     }
00171   int get_cursor()
00172     { return cursor; }
00173   void set_cursor(int pos)  // Set cursor (safely).
00174     {
00175       if (pos >= 0 && pos <= length)
00176       {
00177         cursor = pos;
00178         refresh();
00179       }
00180     }
00181   void paint();     // Paint.
00182           // Handle mouse click.
00183   int mouse_clicked(int mx, int my);
00184   void insert(int chr);   // Insert a character.
00185   int delete_left();    // Delete char. to left of cursor.
00186   int delete_right();   // Delete char. to right of cursor.
00187   void lose_focus();
00188 
00189 protected:
00190   void refresh()
00191     {
00192       paint();
00193     }
00194 };
00195 
00196 /*
00197  *  Paint text field.
00198  */
00199 
00200 void Gump_text::paint
00201   (
00202   )
00203   {
00204   paint_shape(parent->get_x() + x, parent->get_y() + y);
00205           // Show text.
00206   sman->paint_text(2, text, parent->get_x() + textx,
00207             parent->get_y() + texty);
00208   if (get_framenum())     // Focused?  Show cursor.
00209     gwin->get_win()->fill8(0, 1, sman->get_text_height(2),
00210       parent->get_x() + textx +
00211           sman->get_text_width(2, text, cursor),
00212         parent->get_y() + texty + 1);
00213   gwin->set_painted();
00214   }
00215 
00216 /*
00217  *  Handle click on text object.
00218  *
00219  *  Output: 1 if point is within text object, else 0.
00220  */
00221 
00222 int Gump_text::mouse_clicked
00223   (
00224   int mx, int my      // Mouse position on screen.
00225   )
00226   {
00227   if (!on_widget(mx, my))   // Not in our area?
00228     return (0);
00229   mx -= textx + parent->get_x();  // Get pt. rel. to text area.
00230   if (!get_framenum())    // Gaining focus?
00231     {
00232     set_frame(1);   // We have focus now.
00233     cursor = 0;   // Put cursor at start.
00234     }
00235   else
00236     {
00237     for (cursor = 0; cursor <= length; cursor++)
00238       if (sman->get_text_width(2, text, cursor) > mx)
00239         {
00240         if (cursor > 0)
00241           cursor--;
00242         break;
00243         }
00244     if (cursor > length)
00245       cursor--; // Passed the end.
00246     }
00247   return (1);
00248   }
00249 
00250 /*
00251  *  Insert a character at the cursor.
00252  */
00253 
00254 void Gump_text::insert
00255   (
00256   int chr
00257   )
00258   {
00259   if (!get_framenum() || length == max_size)
00260     return;     // Can't.
00261   if (cursor < length)    // Open up space.
00262     memmove(text + cursor + 1, text + cursor, length - cursor);
00263   text[cursor++] = chr;   // Store, and increment cursor.
00264   length++;
00265   text[length] = 0;
00266   refresh();
00267   }
00268 
00269 /*
00270  *  Delete the character to the left of the cursor.
00271  *
00272  *  Output: 1 if successful.
00273  */
00274 
00275 int Gump_text::delete_left
00276   (
00277   )
00278   {
00279   if (!get_framenum() || !cursor)   // Can't do it.
00280     return (0);
00281   if (cursor < length)    // Shift text left.
00282     memmove(text + cursor - 1, text + cursor, length - cursor);
00283   text[--length] = 0;   // 0-delimit.
00284   cursor--;
00285   refresh();
00286   return (1);
00287   }
00288 
00289 /*
00290  *  Delete char. to right of cursor.
00291  *
00292  *  Output: 1 if successful.
00293  */
00294 
00295 int Gump_text::delete_right
00296   (
00297   )
00298   {
00299   if (!get_framenum() || cursor == length)
00300     return (0);   // Past end of text.
00301   cursor++;     // Move right.
00302   return (delete_left());   // Delete what was passed.
00303   }
00304 
00305 /*
00306  *  Lose focus.
00307  */
00308 
00309 void Gump_text::lose_focus
00310   (
00311   )
00312   {
00313   set_frame(0);
00314   refresh();
00315   }
00316 
00317 /*
00318  *  Create the load/save box.
00319  */
00320 
00321 File_gump::File_gump
00322   (
00323   ) : Modal_gump(0, game->get_shape("gumps/fileio")),
00324     pushed_text(0), focus(0), restored(0)
00325 {
00326   set_object_area(Rectangle(0,0,0,0), 8, 150);
00327 
00328   size_t i;
00329   int ty = texty;
00330   for (i = 0; i < sizeof(names)/sizeof(names[0]); i++, ty += texth)
00331   {
00332     names[i] = new Gump_text(this, 
00333       game->get_shape("gumps/fntext"), 
00334               textx, ty, 30, 12, 2);
00335     names[i]->set_text(gwin->get_save_name(i));
00336   }
00337           // First row of buttons:
00338   buttons[0] = buttons[1] = 0;  // No load/save until name chosen.
00339   buttons[2] = new Quit_button(this, btn_cols[2], btn_rows[0]);
00340           // 2nd row.
00341   buttons[3] = new Sound_button(this, btn_cols[0], btn_rows[1], 
00342       game->get_shape("gumps/musicbtn"),
00343             Audio::get_ptr()->is_music_enabled());
00344   buttons[4] = new Sound_button(this, btn_cols[1], btn_rows[1],
00345       game->get_shape("gumps/speechbtn"),
00346             Audio::get_ptr()->is_speech_enabled());
00347   buttons[5] = new Sound_button(this, btn_cols[2], btn_rows[1],
00348       game->get_shape("gumps/soundbtn"),
00349             Audio::get_ptr()->are_effects_enabled());
00350 }
00351 
00352 /*
00353  *  Delete the load/save box.
00354  */
00355 
00356 File_gump::~File_gump
00357   (
00358   )
00359 {
00360   size_t i;
00361   for (i = 0; i < sizeof(names)/sizeof(names[0]); i++)
00362     delete names[i];
00363   for (i = 0; i < sizeof(buttons)/sizeof(buttons[0]); i++)
00364     delete buttons[i];
00365 }
00366 
00367 /*
00368  *  Get the index of one of the text fields (savegame #).
00369  *
00370  *  Output: Index, or -1 if not found.
00371  */
00372 
00373 int File_gump::get_save_index
00374   (
00375   Gump_text *txt
00376   )
00377 {
00378   for (size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++)
00379     if (names[i] == txt)
00380       return (i);
00381   return (-1);
00382 }
00383 
00384 /*
00385  *  Remove text focus.
00386  */
00387 
00388 void File_gump::remove_focus
00389   (
00390   )
00391   {
00392   if (!focus)
00393     return;
00394   focus->lose_focus();
00395   focus = 0;
00396   delete buttons[0];    // Remove load/save buttons.
00397   buttons[0] = 0;
00398   delete buttons[1];
00399   buttons[1] = 0;
00400   paint();
00401   }
00402 
00403 /*
00404  *  'Load' clicked.
00405  */
00406 
00407 void File_gump::load
00408   (
00409   )
00410 {
00411   if (!focus ||     // This would contain the name.
00412       !focus->get_length())
00413     return;
00414   int num = get_save_index(focus);// Which one is it?
00415   if (num == -1)
00416     return;     // Shouldn't ever happen.
00417   if (!Yesno_gump::ask(
00418       "Okay to load over your current game?"))
00419     return;
00420   gwin->restore_gamedat(num); // Aborts if unsuccessful.
00421   gwin->read();     // And read the files in.
00422   done = 1;
00423   restored = 1;
00424 }
00425 
00426 /*
00427  *  'Save' clicked.
00428  */
00429 
00430 void File_gump::save
00431   (
00432   )
00433 {
00434   if (!focus ||       // This would contain the name.
00435       !focus->get_length())
00436     return;
00437   int num = get_save_index(focus);// Which one is it?
00438   if (num == -1)
00439     return;     // Shouldn't ever happen.
00440   if (*gwin->get_save_name(num))  // Already a game in this slot?
00441     if (!Yesno_gump::ask(
00442       "Okay to write over existing saved game?"))
00443       return;
00444   gwin->write();    // First flush to 'gamedat'.
00445   gwin->save_gamedat(num, focus->get_text());
00446   cout << "Saved game #" << num << " successfully." << endl;
00447   remove_focus();
00448 }
00449 
00450 /*
00451  *  'Quit' clicked.
00452  */
00453 
00454 void File_gump::quit
00455   (
00456   )
00457 {
00458   if (!Yesno_gump::ask("Do you really want to quit?"))
00459     return;
00460   quitting_time = QUIT_TIME_YES;
00461   done = 1;
00462 }
00463 
00464 /*
00465  *  One of the option toggle buttons was pressed.
00466  *
00467  *  Output: New state of option (0 or 1).
00468  */
00469 
00470 int File_gump::toggle_option
00471   (
00472   Gump_button *btn    // Button that was clicked.
00473   )
00474 {
00475   if (btn == buttons[3])    // Music?
00476   {
00477     bool music = !Audio::get_ptr()->is_music_enabled();
00478     Audio::get_ptr()->set_music_enabled(music);
00479     if (!music)   // Stop what's playing.
00480       Audio::get_ptr()->stop_music();
00481     string s = music ? "yes" : "no";
00482           // Write option out.
00483     config->set("config/audio/midi/enabled", s, true);
00484     return music ? 1 : 0;
00485   }
00486   if (btn == buttons[4])    // Speech?
00487   {
00488     bool speech = !Audio::get_ptr()->is_speech_enabled();
00489     Audio::get_ptr()->set_speech_enabled(speech);
00490     string s = speech ? "yes" : "no";
00491           // Write option out.
00492     config->set("config/audio/speech/enabled", s, true);
00493     return speech ? 1 : 0;
00494   }
00495   if (btn == buttons[5])    // Sound effects?
00496   {
00497     bool effects = !Audio::get_ptr()->are_effects_enabled();
00498     Audio::get_ptr()->set_effects_enabled(effects);
00499     if (!effects)   // Off?  Stop what's playing.
00500       Audio::get_ptr()->stop_sound_effects();
00501     string s = effects ? "yes" : "no";
00502           // Write option out.
00503     config->set("config/audio/effects/enabled", s, true);
00504     return effects ? 1 : 0;
00505   }
00506   return false;     // Shouldn't get here.
00507 }
00508 
00509 /*
00510  *  Paint on screen.
00511  */
00512 
00513 void File_gump::paint
00514   (
00515   )
00516 {
00517   Gump::paint();      // Paint background
00518           // Paint text objects.
00519   size_t i;
00520   for (i = 0; i < sizeof(names)/sizeof(names[0]); i++)
00521     if (names[i])
00522       names[i]->paint();
00523   for (i = 0; i < sizeof(buttons)/sizeof(buttons[0]); i++)
00524     if (buttons[i])
00525       buttons[i]->paint();
00526 }
00527 
00528 /*
00529  *  Handle mouse-down events.
00530  */
00531 
00532 void File_gump::mouse_down
00533   (
00534   int mx, int my      // Position in window.
00535   )
00536 {
00537   pushed = 0;
00538   pushed_text = 0;
00539           // First try checkmark.
00540   Gump_button *btn = Gump::on_button(mx, my);
00541   if (btn)
00542     pushed = btn;
00543   else        // Try buttons at bottom.
00544     for (size_t i = 0; i < sizeof(buttons)/sizeof(buttons[0]); i++)
00545       if (buttons[i] && buttons[i]->on_button(mx, my))
00546       {
00547         pushed = buttons[i];
00548         break;
00549       }
00550   if (pushed)     // On a button?
00551   {
00552     pushed->push();
00553     return;
00554   }
00555           // See if on text field.
00556   for (size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++)
00557     if (names[i]->on_widget(mx, my))
00558     {
00559       pushed_text = names[i];
00560       break;
00561     }
00562 }
00563 
00564 /*
00565  *  Handle mouse-up events.
00566  */
00567 
00568 void File_gump::mouse_up
00569   (
00570   int mx, int my      // Position in window.
00571   )
00572 {
00573   if (pushed)     // Pushing a button?
00574   {
00575     pushed->unpush();
00576     if (pushed->on_button(mx, my))
00577       pushed->activate();
00578     pushed = 0;
00579   }
00580   if (!pushed_text)
00581     return;
00582           // Let text field handle it.
00583   if (!pushed_text->mouse_clicked(mx, my) ||
00584       pushed_text == focus) // Same field already selected?
00585   {
00586     pushed_text->paint();
00587     pushed_text = 0;
00588     return;
00589   }
00590   if (focus)      // Another had focus.
00591   {
00592     focus->set_text(gwin->get_save_name(get_save_index(focus)));
00593     focus->lose_focus();
00594   }
00595   focus = pushed_text;    // Switch focus to new field.
00596   pushed_text = 0;
00597   if (focus->get_length())  // Need load/save buttons?
00598   {
00599     if (!buttons[0])
00600       buttons[0] = new Load_save_button(this,
00601           btn_cols[0], btn_rows[0], game->get_shape("gumps/loadbtn"));
00602     if (!buttons[1])
00603       buttons[1] = new Load_save_button(this,
00604           btn_cols[1], btn_rows[0], game->get_shape("gumps/savebtn"));
00605   }
00606   else if (!focus->get_length())
00607   {     // No name yet.
00608     delete buttons[0];
00609     delete buttons[1];
00610     buttons[0] = buttons[1] = 0;
00611   }
00612   paint();      // Repaint.
00613   gwin->set_painted();
00614 }
00615 
00616 /*
00617  *  Handle character that was typed.
00618  */
00619 
00620 void File_gump::text_input(int chr, int unicode)
00621 {
00622   if (!focus)     // Text field?
00623     return;
00624   switch (chr)
00625     {
00626   case SDLK_RETURN:   // If only 'Save', do it.
00627     if (!buttons[0] && buttons[1])
00628       {
00629       buttons[1]->push();
00630       gwin->show(1);
00631       buttons[1]->unpush();
00632       gwin->show(1);
00633       buttons[1]->activate();
00634       }
00635     break;
00636   case SDLK_BACKSPACE:
00637     if (focus->delete_left())
00638     {   // Can't restore now.
00639       delete buttons[0];
00640       buttons[0] = 0;
00641     }
00642     if (!focus->get_length())
00643     {   // Last char.?
00644       delete buttons[0];
00645       delete buttons[1];
00646       buttons[0] = buttons[1] = 0;
00647       paint();
00648     }
00649     return;
00650   case SDLK_DELETE:
00651     if (focus->delete_right())
00652     {   // Can't restore now.
00653       delete buttons[0];
00654       buttons[0] = 0;
00655     }
00656     if (!focus->get_length())
00657     {   // Last char.?
00658       delete buttons[0];
00659       delete buttons[1];
00660       buttons[0] = buttons[1] = 0;
00661       paint();
00662     }
00663     return;
00664   case SDLK_LEFT:
00665     focus->set_cursor(focus->get_cursor() - 1);
00666     return;
00667   case SDLK_RIGHT:
00668     focus->set_cursor(focus->get_cursor() + 1);
00669     return;
00670   case SDLK_HOME:
00671     focus->set_cursor(0);
00672     return;
00673   case SDLK_END:
00674     focus->set_cursor(focus->get_length());
00675     return;
00676   }
00677 
00678   if ((unicode & 0xFF80) == 0 )
00679     chr = unicode & 0x7F;
00680   else
00681     chr = 0;
00682   
00683   if (chr < ' ')
00684     return;     // Ignore other special chars.
00685   if (chr < 256 && isascii(chr))
00686   {
00687     int old_length = focus->get_length();
00688     focus->insert(chr);
00689           // Added first character?  Need 
00690           //   'Save' button.
00691     if (!old_length && focus->get_length() && !buttons[1])
00692     {
00693       buttons[1] = new Load_save_button(this,
00694           btn_cols[1], btn_rows[0], game->get_shape("gumps/savebtn"));
00695       buttons[1]->paint();
00696     }
00697     if (buttons[0])   // Can't load now.
00698     {
00699       delete buttons[0];
00700       buttons[0] = 0;
00701       paint();
00702     }
00703     gwin->set_painted();
00704   }
00705 }

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