combo.cc

Go to the documentation of this file.
00001 
00007 /*
00008 Copyright (C) 2002 The Exult Team
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #  include <config.h>
00027 #endif
00028 
00029 #include <gdk/gdkkeysyms.h>
00030 #include "studio.h"
00031 #include "combo.h"
00032 #include "exult_constants.h"
00033 #include "exceptions.h"
00034 #include "shapevga.h"
00035 #include "shapefile.h"
00036 #include "ibuf8.h"
00037 #include "objserial.h"
00038 #include "shapegroup.h"
00039 #include "Flex.h"
00040 #include "u7drag.h"
00041 
00042 using std::cout;
00043 using std::cin;
00044 using std::endl;
00045 
00046 const int border = 2;     // Border at bottom, sides of each
00047           //   combo in browser.
00048 const int maxtiles = 32;    // Max. width/height in tiles.
00049 
00050 /*
00051  *  Open combo window (if not already open).
00052  */
00053 
00054 C_EXPORT void on_new_combo1_activate
00055   (
00056   GtkMenuItem     *menuitem,
00057         gpointer         user_data
00058   )
00059   {
00060   ExultStudio *studio = ExultStudio::get_instance();
00061   studio->open_combo_window();
00062   }
00063 void ExultStudio::open_combo_window
00064   (
00065   )
00066   {
00067   if (combowin && combowin->is_visible())
00068     return;     // Already open.
00069   if (!vgafile)
00070     {
00071     EStudio::Alert("'shapes.vga' file isn't present");
00072     return;
00073     }
00074   Shapes_vga_file *svga = (Shapes_vga_file *) vgafile->get_ifile();
00075   delete combowin;    // Delete old (svga may have changed).
00076   combowin = new Combo_editor(svga, palbuf);
00077   combowin->show(true);
00078           // Set edit-mode to pick.
00079   GtkWidget *mitem = glade_xml_get_widget(app_xml, "pick_for_combo1");
00080   gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mitem), TRUE);
00081   }
00082 
00083 /*
00084  *  Save combos.
00085  */
00086 
00087 void ExultStudio::save_combos
00088   (
00089   )
00090   {
00091           // Get file info.
00092   Shape_file_info *combos = files->create("combos.flx");
00093   try {
00094     if (combos)
00095       combos->flush();
00096   } catch (const exult_exception& e) {
00097     EStudio::Alert(e.what());
00098   }
00099   }
00100 
00101 /*
00102  *  Callbacks for "Combo" editor window.
00103  */
00104 C_EXPORT gint on_combo_draw_expose_event
00105   (
00106   GtkWidget *widget,    // The view window.
00107   GdkEventExpose *event,
00108   gpointer data     // ->Shape_chooser.
00109   )
00110   {
00111   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00112     GTK_OBJECT(gtk_widget_get_toplevel(GTK_WIDGET(widget))));
00113   combo->render(&event->area);
00114   return TRUE;
00115   }
00116 
00117 C_EXPORT void
00118 on_combo_remove_clicked                (GtkButton       *button,
00119                                         gpointer         user_data)
00120 {
00121   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00122     GTK_OBJECT(gtk_widget_get_toplevel(GTK_WIDGET(button))));
00123   combo->remove();
00124 }
00125 
00126 C_EXPORT void
00127 on_combo_apply_clicked                 (GtkButton       *button,
00128                                         gpointer         user_data)
00129 {
00130   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00131     GTK_OBJECT(gtk_widget_get_toplevel(GTK_WIDGET(button))));
00132   combo->save();
00133 }
00134 
00135 C_EXPORT void
00136 on_combo_ok_clicked                    (GtkButton       *button,
00137                                         gpointer         user_data)
00138 {
00139   GtkWidget *win = gtk_widget_get_toplevel(GTK_WIDGET(button));
00140   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00141               GTK_OBJECT(win));
00142   combo->save();
00143   gtk_widget_hide(win);
00144 }
00145 
00146 C_EXPORT void
00147 on_combo_locx_changed     (GtkSpinButton *button,
00148            gpointer   user_data)
00149 {
00150   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00151     GTK_OBJECT(gtk_widget_get_toplevel(GTK_WIDGET(button))));
00152   combo->set_position();
00153 }
00154 
00155 C_EXPORT void
00156 on_combo_locy_changed     (GtkSpinButton *button,
00157            gpointer   user_data)
00158 {
00159   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00160     GTK_OBJECT(gtk_widget_get_toplevel(GTK_WIDGET(button))));
00161   combo->set_position();
00162 }
00163 
00164 C_EXPORT void
00165 on_combo_locz_changed     (GtkSpinButton *button,
00166            gpointer   user_data)
00167 {
00168   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00169     GTK_OBJECT(gtk_widget_get_toplevel(GTK_WIDGET(button))));
00170   combo->set_position();
00171 }
00172 
00173 C_EXPORT void
00174 on_combo_order_changed      (GtkSpinButton *button,
00175            gpointer   user_data)
00176 {
00177   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00178     GTK_OBJECT(gtk_widget_get_toplevel(GTK_WIDGET(button))));
00179   combo->set_order();
00180 }
00181 
00182 /*
00183  *  Mouse events in draw area.
00184  */
00185 C_EXPORT gint on_combo_draw_button_press_event
00186   (
00187   GtkWidget *widget,    // The view window.
00188   GdkEventButton *event,
00189   gpointer data     // ->Combo_chooser.
00190   )
00191   {
00192   Combo_editor *combo = (Combo_editor *) gtk_object_get_user_data(
00193     GTK_OBJECT(gtk_widget_get_toplevel(GTK_WIDGET(widget))));
00194   return combo->mouse_press(event);
00195   }
00196 
00197 /*
00198  *  Which member makes the better 'hot-spot' in a combo, where 'better'
00199  *  means lower, then southmost, then eastmost.
00200  *
00201  *  Output: 0 if c0, 1 if c1
00202  */
00203 
00204 int hot_spot_compare
00205   (
00206   Combo_member& c0,
00207   Combo_member& c1
00208   )
00209   {
00210   if (c0.tz < c1.tz)
00211     return 0;
00212   else if (c1.tz < c0.tz)
00213     return 1;
00214   else if (c0.ty > c1.ty)
00215     return 0;
00216   else if (c1.ty > c0.ty)
00217     return 1;
00218   else
00219     return c0.tx >= c1.tx ? 0 : 1;
00220   }
00221 
00222 /*
00223  *  Get footprint of given member.
00224  */
00225 
00226 Rectangle Combo::get_member_footprint
00227   (
00228   int i       // i'th member.
00229   )
00230   {
00231   Combo_member *m = members[i];
00232           // Get tile dims.
00233   Shape_info& info = shapes_file->get_info(m->shapenum);
00234   int xtiles = info.get_3d_xtiles(m->framenum),
00235       ytiles = info.get_3d_ytiles(m->framenum);
00236           // Get tile footprint.
00237   Rectangle box(m->tx - xtiles + 1, m->ty - ytiles + 1, 
00238               xtiles, ytiles);
00239   return box;
00240   }
00241 
00242 /*
00243  *  Create empty combo.
00244  */
00245 
00246 Combo::Combo
00247   (
00248   Shapes_vga_file *svga
00249   ) : shapes_file(svga),
00250       starttx(c_num_tiles), startty(c_num_tiles), hot_index(-1),
00251       tilefoot(0, 0, 0, 0)
00252   {
00253           // Read info. the first time.
00254   shapes_file->read_info(false, true);//+++++BG?
00255   }
00256 
00257 /*
00258  *  Copy another.
00259  */
00260 
00261 Combo::Combo
00262   (
00263   const Combo& c2
00264   ) : shapes_file(c2.shapes_file), starttx(c2.starttx),
00265       startty(c2.startty), 
00266       hot_index(c2.hot_index), name(c2.name), tilefoot(c2.tilefoot)
00267   {
00268   for (std::vector<Combo_member *>::const_iterator it = c2.members.begin();
00269           it != c2.members.end(); ++it)
00270     {
00271     Combo_member *m = *it;
00272     members.push_back(new Combo_member(m->tx, m->ty, m->tz, 
00273           m->shapenum, m->framenum));
00274     }
00275   }
00276 
00277 /*
00278  *  Clean up.
00279  */
00280 
00281 Combo::~Combo
00282   (
00283   )
00284   {
00285   for (std::vector<Combo_member *>::iterator it = members.begin();
00286           it != members.end(); ++it)
00287     delete *it;
00288   }
00289 
00290 /*
00291  *  Add a new object.
00292  */
00293 
00294 void Combo::add
00295   (
00296   int tx, int ty, int tz,   // Location rel. to top-left.
00297   int shnum, int frnum    // Shape.
00298   )
00299   {
00300           // Look for identical shape, pos.
00301   for (std::vector<Combo_member *>::iterator it = members.begin();
00302           it != members.end(); ++it)
00303     {
00304     Combo_member *m = *it;
00305     if (tx == m->tx && ty == m->ty && tz == m->tz &&
00306         shnum == m->shapenum && frnum == m->framenum)
00307       return;   // Don't add same one twice.
00308     }
00309           // Get tile dims.
00310   Shape_info& info = shapes_file->get_info(shnum);
00311   int xtiles = info.get_3d_xtiles(frnum),
00312       ytiles = info.get_3d_ytiles(frnum),
00313       ztiles = info.get_3d_height();
00314           // Get tile footprint.
00315   Rectangle box(tx - xtiles + 1, ty - ytiles + 1, xtiles, ytiles);
00316   if (!members.size())    // First one?
00317     tilefoot = box;   // Init. total footprint.
00318   else
00319     {     // Too far away?
00320     if (tilefoot.x + tilefoot.w - box.x > maxtiles ||
00321         box.x + box.w - tilefoot.x > maxtiles ||
00322         tilefoot.y + tilefoot.h - box.y > maxtiles ||
00323         box.y + box.h - tilefoot.y > maxtiles)
00324       {
00325       EStudio::Alert(
00326         "New object is too far (> 32) from others");
00327       return;
00328       }
00329           // Add to footprint.
00330     tilefoot = tilefoot.add(box);
00331     }
00332   Combo_member *memb = new Combo_member(tx, ty, tz, shnum, frnum);
00333   members.push_back(memb);
00334           // Figure visible top-left tile, with
00335           //   1 to spare.
00336   int vtx = tx - xtiles - 2 - (tz + ztiles + 1)/2, 
00337       vty = ty - ytiles - 2 - (tz + ztiles + 1)/2;
00338   if (vtx < starttx)    // Adjust our starting point.
00339     starttx = vtx;
00340   if (vty < startty)
00341     startty = vty;
00342   if (hot_index == -1 ||    // First one, or better than prev?
00343       hot_spot_compare(*memb, *members[hot_index]) == 0)
00344     hot_index = members.size() - 1;
00345   }
00346 
00347 /*
00348  *  Remove i'th object.
00349  */
00350 
00351 void Combo::remove
00352   (
00353   int i
00354   )
00355   {
00356   if (i < 0 || i >= members.size())
00357     return;
00358           // Get and remove i'th entry.
00359   std::vector<Combo_member *>::iterator it = members.begin() + i;
00360   Combo_member *m = *it;
00361   members.erase(it);
00362   delete m;
00363   hot_index = -1;     // Figure new hot-spot, footprint.
00364   tilefoot = Rectangle(0, 0, 0, 0);
00365   for (std::vector<Combo_member *>::iterator it = members.begin();
00366             it != members.end(); ++it)
00367     {
00368     Combo_member *m = *it;
00369     int index = it - members.begin();
00370     Rectangle box = get_member_footprint(index);
00371     if (hot_index == -1)  // First?
00372       {
00373       hot_index = 0;
00374       tilefoot = box;
00375       }
00376     else
00377       {
00378       if (hot_spot_compare(*m, *members[hot_index]) == 0)
00379         hot_index = index;
00380       tilefoot = tilefoot.add(box);
00381       }
00382     }
00383   }
00384 
00385 /*
00386  *  Paint in a drawing area.
00387  */
00388 
00389 void Combo::draw
00390   (
00391   Shape_draw *draw,
00392   int selected,     // Index of 'selected' item, or -1.
00393   int xoff, int yoff    // Offset within draw.
00394   )
00395   {
00396   int selx = -1000, sely = -1000;
00397   bool selfound = false;
00398   for (std::vector<Combo_member *>::iterator it = members.begin();
00399           it != members.end(); ++it)
00400     {
00401     Combo_member *m = *it;
00402           // Figure pixels up-left for lift.
00403     int lft = m->tz*(c_tilesize/2);
00404           // Figure relative tile.
00405     int mtx = m->tx - starttx,
00406         mty = m->ty - startty;
00407           // Hot spot:
00408     int x = mtx*c_tilesize - lft,
00409         y = mty*c_tilesize - lft;
00410     Shape_frame *shape = shapes_file->get_shape(m->shapenum,
00411                 m->framenum);
00412     if (!shape)
00413       continue;
00414           // But draw_shape uses top-left.
00415     x -= shape->get_xleft();
00416     y -= shape->get_yabove();
00417     x += xoff; y += yoff; // Add offset within area.
00418     draw->draw_shape(shape, x, y);
00419     if (it - members.begin() == selected)
00420       {
00421       selx = x; // Save coords for selected.
00422       sely = y;
00423       selfound = true;
00424       }
00425     }
00426   if (selfound)     // Now put border around selected.
00427     {
00428     Combo_member *m = members[selected];
00429           // FOR NOW, use color #1 ++++++++
00430     draw->draw_shape_outline(m->shapenum, m->framenum,
00431             selx, sely, 1);
00432     }
00433   }
00434 
00435 /*
00436  *  Find last member in list that contains a mouse point.
00437  *
00438  *  Output: Index of member found, or -1 if none.
00439  */
00440 
00441 int Combo::find
00442   (
00443   int mx, int my      // Mouse position in draw area.
00444   )
00445   {
00446   int cnt = members.size();
00447   for (int i = cnt - 1; i >= 0; i--)
00448     {
00449     Combo_member *m = members[i];
00450           // Figure pixels up-left for lift.
00451     int lft = m->tz*(c_tilesize/2);
00452           // Figure relative tile.
00453     int mtx = m->tx - starttx,
00454         mty = m->ty - startty;
00455     int x = mtx*c_tilesize - lft,
00456         y = mty*c_tilesize - lft;
00457     Shape_frame *frame = shapes_file->get_shape(
00458             m->shapenum, m->framenum);
00459     if (frame && frame->has_point(mx - x, my - y))
00460       return i;
00461     }
00462   return -1;
00463   }
00464 
00465 /*
00466  *  Write out.
00467  *
00468  *  Output: Allocated buffer containing result.
00469  */
00470 
00471 unsigned char *Combo::write
00472   (
00473   int& datalen      // Actual length of data in buf. is
00474           //   returned here.
00475   )
00476   {
00477   int namelen = name.length();  // Name length.
00478           // Room for our data + members.
00479   unsigned char *buf = new unsigned char[namelen + 1 + 
00480             7*4 + members.size()*(5*4)];
00481   unsigned char *ptr = buf;
00482   Serial_out out(ptr);
00483   out << name;
00484   out << hot_index << starttx << startty;
00485   out << (short) members.size();  // # members to follow.
00486   for (std::vector<Combo_member *>::const_iterator it = members.begin();
00487           it != members.end(); ++it)
00488     {
00489     Combo_member *m = *it;
00490     out << m->tx << m->ty << m->tz << m->shapenum <<
00491             m->framenum;
00492     }
00493   datalen = ptr - buf;    // Return actual length.
00494   return buf;
00495   }
00496 
00497 /*
00498  *  Read in.
00499  *
00500  *  Output: ->past actual data read.
00501  */
00502 
00503 unsigned char *Combo::read
00504   (
00505   unsigned char *buf,
00506   int bufsize
00507   )
00508   {
00509   unsigned char *ptr = buf;
00510   Serial_in in(ptr);
00511   in << name;
00512   in << hot_index << starttx << startty;
00513   short cnt;
00514   in << cnt;      // # members to follow.
00515   for (int i = 0; i < cnt; i++)
00516     {
00517     short tx, ty, tz, shapenum, framenum;
00518     in << tx << ty << tz << shapenum << framenum;
00519     Combo_member *memb = new Combo_member(tx, ty, tz, 
00520             shapenum, framenum);
00521     members.push_back(memb);
00522     Rectangle box = get_member_footprint(i);
00523     if (i == 0)   // Figure footprint.
00524       tilefoot = box;
00525     else
00526       tilefoot = tilefoot.add(box);
00527     }
00528   return ptr;
00529   }
00530 
00531 /*
00532  *  Set to edit an existing combo.
00533  */
00534 
00535 void Combo_editor::set_combo
00536   (
00537   Combo *newcombo,    // We'll own this.
00538   int findex      // File index.
00539   )
00540   {
00541   delete combo;
00542   combo = newcombo;
00543   file_index = findex;
00544   selected = -1;
00545   ExultStudio::get_instance()->set_entry(
00546       "combo_name", combo->name.c_str(), true);
00547   set_controls();     // No selection now.
00548   render();
00549   }
00550 
00551 /*
00552  *  Create combo editor.
00553  */
00554 
00555 Combo_editor::Combo_editor
00556   (
00557   Shapes_vga_file *svga,    // File containing shapes.
00558   unsigned char *palbuf   // Palette for drawing shapes.
00559   ) : Shape_draw(svga, palbuf, glade_xml_get_widget(
00560     ExultStudio::get_instance()->get_xml(), "combo_draw")),
00561       selected(-1), setting_controls(false), file_index(-1)
00562   {
00563   static bool first = true;
00564   combo = new Combo(svga);
00565   GladeXML *app_xml = ExultStudio::get_instance()->get_xml();
00566   win = glade_xml_get_widget(app_xml, "combo_win");
00567   gtk_object_set_user_data(GTK_OBJECT(win), this);
00568   if (first)      // Indicate the events we want.
00569     {
00570     gtk_widget_set_events(draw, GDK_EXPOSURE_MASK | 
00571       GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
00572         GDK_BUTTON1_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
00573     first = false;
00574     }
00575   set_controls();
00576   }
00577 
00578 /*
00579  *  Clean up.
00580  */
00581 
00582 Combo_editor::~Combo_editor
00583   (
00584   )
00585   {
00586   delete combo;
00587   }
00588 
00589 /*
00590  *  Show/hide.
00591  */
00592 
00593 void Combo_editor::show
00594   (
00595   bool tf
00596   )
00597   {
00598   if (tf)
00599     gtk_widget_show(win);
00600   else
00601     gtk_widget_hide(win);
00602   }
00603 
00604 /*
00605  *  Display.
00606  */
00607 
00608 void Combo_editor::render
00609   (
00610   GdkRectangle *area    // 0 for whole draw area.
00611   )
00612   {
00613   Shape_draw::configure();  // Setup the first time.
00614           // Get dims.
00615   int draww = draw->allocation.width,
00616       drawh = draw->allocation.height;
00617   GdkRectangle all;
00618   if (!area)
00619     {
00620     all.x = all.y = 0;
00621     all.width = draww;
00622     all.height = drawh;
00623     area = &all;
00624     }
00625   gdk_gc_set_clip_rectangle(drawgc, area);
00626   iwin->fill8(255);   // Fill with background color.
00627   combo->draw(this, selected);  // Draw shapes.
00628   Shape_draw::show(area->x, area->y, area->width, area->height);
00629   }
00630 
00631 /*
00632  *  Set controls according to what's selected.
00633  */
00634 
00635 void Combo_editor::set_controls
00636   (
00637   )
00638   {
00639   setting_controls = true;  // Avoid updating.
00640   ExultStudio *studio = ExultStudio::get_instance();
00641   Combo_member *m = combo->get(selected);
00642   if (!m)       // None selected?
00643     {
00644     studio->set_spin("combo_locx", 0, false);
00645     studio->set_spin("combo_locy", 0, false);
00646     studio->set_spin("combo_locz", 0, false);
00647     studio->set_spin("combo_order", 0, false);
00648     studio->set_sensitive("combo_remove", false);
00649     }
00650   else
00651     {
00652     int draww = draw->allocation.width,
00653         drawh = draw->allocation.height;
00654     studio->set_sensitive("combo_locx", true);
00655     studio->set_spin("combo_locx", m->tx - combo->starttx,
00656               0, draww/c_tilesize);
00657     studio->set_sensitive("combo_locy", true);
00658     studio->set_spin("combo_locy", m->ty - combo->startty,
00659               0, drawh/c_tilesize);
00660     studio->set_sensitive("combo_locz", true);
00661     studio->set_spin("combo_locz", m->tz, 0, 15);
00662     studio->set_sensitive("combo_order", true);
00663     studio->set_spin("combo_order", selected, 0,
00664             combo->members.size() - 1);
00665     studio->set_sensitive("combo_remove", true);
00666     }
00667   setting_controls = false;
00668   }
00669 
00670 /*
00671  *  Handle a mouse-press event.
00672  */
00673 
00674 gint Combo_editor::mouse_press
00675   (
00676   GdkEventButton *event
00677   )
00678   {
00679   if (event->button != 1)
00680     return FALSE;   // Handling left-click.
00681           // Get mouse position, draw dims.
00682   int mx = (int) event->x, my = (int) event->y;
00683   selected = combo->find(mx, my); // Find it (or -1 if not found).
00684   set_controls();
00685   render();
00686   return TRUE;
00687   }
00688 
00689 /*
00690  *  Move the selected item within the order.
00691  */
00692 
00693 void Combo_editor::set_order
00694   (
00695   )
00696   {
00697   if (setting_controls || selected < 0)
00698     return;
00699   ExultStudio *studio = ExultStudio::get_instance();
00700   int newi = studio->get_spin("combo_order");
00701   if (selected == newi)
00702     return;     // Already correct.
00703   int dir = newi > selected ? 1 : -1;
00704   while (newi != selected)
00705     {
00706     Combo_member *tmp = combo->members[selected + dir];
00707     combo->members[selected + dir] = combo->members[selected];
00708     combo->members[selected] = tmp;
00709     selected += dir;
00710     }
00711   render();
00712   }
00713 
00714 /*
00715  *  Move the selected item to the desired position in the spin buttons.
00716  */
00717 
00718 void Combo_editor::set_position
00719   (
00720   )
00721   {
00722   Combo_member *m = combo->get(selected);
00723   if (!m || setting_controls)
00724     return;
00725   ExultStudio *studio = ExultStudio::get_instance();
00726   m->tx = combo->starttx + studio->get_spin("combo_locx");
00727   m->ty = combo->startty + studio->get_spin("combo_locy");
00728   m->tz = studio->get_spin("combo_locz");
00729   render();
00730   }
00731 
00732 /*
00733  *  Get # shapes we can display.
00734  */
00735 
00736 int Combo_chooser::get_count
00737   (
00738   )
00739   {
00740   return group ? group->size() : combos.size();
00741   }
00742 
00743 /*
00744  *  Add an object/shape picked from Exult.
00745  */
00746 
00747 void Combo_editor::add
00748   (
00749   unsigned char *data,    // Serialized object.
00750   int datalen
00751   )
00752   {
00753   unsigned long addr;
00754   int tx, ty, tz;
00755   int shape, frame, quality;
00756   std::string name;
00757   if (!Object_in(data, datalen, addr, tx, ty, tz, shape, frame,
00758     quality, name))
00759     {
00760     cout << "Error decoding object" << endl;
00761     return;
00762     }
00763   combo->add(tx, ty, tz, shape, frame);
00764   render();
00765   }
00766 
00767 /*
00768  *  Remove selected.
00769  */
00770 
00771 void Combo_editor::remove
00772   (
00773   )
00774   {
00775   if (selected >= 0)
00776     {
00777     combo->remove(selected);
00778     selected = -1;
00779     set_controls();
00780     render();
00781     }
00782   }
00783 
00784 /*
00785  *  Save combo.
00786  */
00787 
00788 void Combo_editor::save
00789   (
00790   )
00791   {
00792   ExultStudio *studio = ExultStudio::get_instance();
00793           // Get name from field.
00794   combo->name = studio->get_text_entry("combo_name");
00795   Flex_file_info *flex_info = dynamic_cast<Flex_file_info *>
00796         (studio->get_files()->create("combos.flx"));
00797   if (!flex_info)
00798     {
00799     EStudio::Alert("Can't open 'combos.flx'");
00800     return;
00801     }
00802   flex_info->set_modified();
00803   int len;      // Serialize.
00804   unsigned char *newbuf = combo->write(len);
00805           // Update or append file data.
00806   flex_info->set(file_index == -1 ? flex_info->size() : file_index, 
00807           (char *) newbuf, len);
00808   Combo_chooser *browser = dynamic_cast<Combo_chooser *>(
00809             studio->get_browser());
00810   if (browser)      // Browser open?
00811     file_index = browser->add(new Combo(*combo), file_index);
00812   }
00813 
00814 /*
00815  *  Blit onto screen.
00816  */
00817 
00818 void Combo_chooser::show
00819   (
00820   int x, int y, int w, int h  // Area to blit.
00821   )
00822   {
00823   Shape_draw::show(draw->window, x, y, w, h);
00824   if (selected >= 0)    // Show selected.
00825     {
00826     Rectangle b = info[selected].box;
00827           // Draw yellow box.
00828     gdk_draw_rectangle(draw->window, drawgc, FALSE, 
00829               b.x, b.y, b.w, b.h);
00830     }
00831   }
00832 
00833 /*
00834  *  Select an entry.  This should be called after rendering
00835  *  the combo.
00836  */
00837 
00838 void Combo_chooser::select
00839   (
00840   int new_sel
00841   )
00842   {
00843   if (new_sel < 0 || new_sel >= info_cnt)
00844     return;     // Bad value.
00845   selected = new_sel;
00846   enable_controls();
00847   int num = info[selected].num;
00848   Combo *combo = combos[num];
00849           // Remove prev. selection msg.
00850 //  gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00851   char buf[150];      // Show new selection.
00852   g_snprintf(buf, sizeof(buf), "Combo %d", num);
00853   if (combo && !combo->name.empty())
00854     {
00855     int len = strlen(buf);
00856     g_snprintf(buf + len, sizeof(buf) - len, 
00857         ":  '%s'", combo->name.c_str());
00858     }
00859   gtk_statusbar_push(GTK_STATUSBAR(sbar), sbar_sel, buf);
00860   }
00861 
00862 /*
00863  *  Unselect.
00864  */
00865 
00866 void Combo_chooser::unselect
00867   (
00868   bool need_render      // 1 to render and show.
00869   )
00870   {
00871   if (selected >= 0)
00872     {
00873     selected = -1;
00874     if (need_render)
00875       {
00876       render();
00877       show();
00878       }
00879     if (sel_changed)  // Tell client.
00880       (*sel_changed)();
00881     }
00882   enable_controls();    // Enable/disable controls.
00883   char buf[150];      // Show new selection.
00884   if (info_cnt > 0)
00885     {
00886 //    gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00887     g_snprintf(buf, sizeof(buf), "Combos %d to %d",
00888       info[0].num, info[info_cnt - 1].num);
00889     gtk_statusbar_push(GTK_STATUSBAR(sbar), sbar_sel, buf);
00890     }
00891   else
00892     {
00893 //    gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00894     gtk_statusbar_push(GTK_STATUSBAR(sbar), sbar_sel,
00895               "No combos");
00896     }
00897   }
00898 
00899 /*
00900  *  Load/reload from file.
00901  */
00902 
00903 void Combo_chooser::load
00904   (
00905   )
00906   {
00907   int cnt = combos.size();
00908   for (int i = 0; i < cnt; i++) // Delete all the combos.
00909     delete combos[i];
00910   int num_combos = flex_info->size();
00911           // We need 'shapes.vga'.
00912   Shape_file_info *svga_info = 
00913         ExultStudio::get_instance()->get_vgafile();
00914   Shapes_vga_file *svga = svga_info ?
00915       (Shapes_vga_file *) svga_info->get_ifile() : 0;
00916   combos.resize(num_combos);  // Set size of list.
00917   if (!svga)
00918     num_combos = 0;
00919           // Read them all in.
00920   for (int i = 0; i < num_combos; i++)
00921     {
00922     size_t len;
00923     unsigned char *buf = (unsigned char *) flex_info->get(i, len);
00924     Combo *combo = new Combo(svga);
00925     combo->read(buf, len);
00926     combos[i] = combo;  // Store in list.
00927     }
00928   }
00929 
00930 /*
00931  *  Render as many combos as fit in the combo chooser window.
00932  */
00933 
00934 void Combo_chooser::render
00935   (
00936   )
00937   {
00938           // Look for selected frame.
00939   int selcombo = -1, new_selected = -1;
00940   if (selected >= 0)    // Save selection info.
00941     selcombo = info[selected].num;
00942           // Remove "selected" message.
00943   //gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00944   delete [] info;     // Delete old info. list.
00945           // Get drawing area dimensions.
00946   gint winw = draw->allocation.width, winh = draw->allocation.height;
00947           // Provide more than enough room.
00948   info = new Combo_info[256];
00949   info_cnt = 0;     // Count them.
00950           // Clear window first.
00951   iwin->fill8(255);   // Background color.
00952   int index = index0;
00953           // We'll always show 128x128.
00954   const int combow = 128, comboh = 128;
00955   int total_cnt = get_count();
00956   int y = border;
00957           // Show bottom if at least 1/2 vis.
00958   while (index < total_cnt && y + comboh/2 + border <= winh)
00959     {
00960     int x = border;
00961     int cliph = y + comboh <= winh ? comboh : (winh - y);
00962     while (index < total_cnt && x + combow + border <= winw)
00963       {
00964       iwin->set_clip(x, y, combow, cliph);
00965       int combonum = group ? (*group)[index] : index;
00966       combos[combonum]->draw(this, -1, x, y);
00967       iwin->clear_clip();
00968           // Store info. about where drawn.
00969       info[info_cnt].set(combonum, x, y, combow, comboh);
00970       if (combonum == selcombo)
00971             // Found the selected combo.
00972         new_selected = info_cnt;
00973       info_cnt++;
00974       index++;    // Next combo.
00975       x += combow + border;
00976       }
00977     y += comboh + border;
00978     }
00979   if (new_selected == -1)
00980     unselect(false);
00981   else
00982     select(new_selected);
00983   }
00984 
00985 /*
00986  *  Scroll to a new combo.
00987  */
00988 
00989 void Combo_chooser::scroll
00990   (
00991   int newindex      // Abs. index of leftmost to show.
00992   )
00993   {
00994   int total = combos.size();
00995   if (index0 < newindex)  // Going forwards?
00996     index0 = newindex < total ? newindex : total;
00997   else if (index0 > newindex) // Backwards?
00998     index0 = newindex >= 0 ? newindex : 0;
00999   render();
01000   show();
01001   }
01002 
01003 /*
01004  *  Scroll up/down by one row.
01005  */
01006 
01007 void Combo_chooser::scroll
01008   (
01009   bool upwards
01010   )
01011   {
01012   GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(vscroll));
01013   float delta = adj->step_increment;
01014   if (upwards)
01015     delta = -delta;
01016   adj->value += delta;
01017   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
01018   scroll((gint) adj->value);
01019   }
01020 
01021 /*
01022  *  Someone wants the dragged combo.
01023  */
01024 
01025 void Combo_chooser::drag_data_get
01026   (
01027   GtkWidget *widget,    // The view window.
01028   GdkDragContext *context,
01029   GtkSelectionData *seldata,  // Fill this in.
01030   guint info,
01031   guint time,
01032   gpointer data     // ->Shape_chooser.
01033   )
01034   {
01035   cout << "In DRAG_DATA_GET" << endl;
01036   Combo_chooser *chooser = (Combo_chooser *) data;
01037   if (chooser->selected < 0 || info != U7_TARGET_COMBOID)
01038     return;     // Not sure about this.
01039           // Get combo #.
01040   int num = chooser->info[chooser->selected].num;
01041   Combo *combo = chooser->combos[num];
01042           // Get enough memory.
01043   int cnt = combo->members.size();
01044   int buflen = 5*4 + cnt*5*4;
01045 cout << "Buflen = " << buflen << endl;
01046   guchar *buf = new unsigned char[buflen];
01047   guchar *ptr = buf;
01048   U7_combo_data *ents = new U7_combo_data[cnt];
01049           // Get 'hot-spot' member.
01050   Combo_member *hot = combo->members[combo->hot_index];
01051   for (int i = 0; i < cnt; i++)
01052     {
01053     Combo_member *m = combo->members[i];
01054     ents[i].tx = m->tx - hot->tx;
01055     ents[i].ty = m->ty - hot->ty;
01056     ents[i].tz = m->tz - hot->tz;
01057     ents[i].shape = m->shapenum;
01058     ents[i].frame = m->framenum;
01059     }
01060   Rectangle foot = combo->tilefoot;
01061   int len = Store_u7_comboid(buf, foot.w, foot.h,
01062       foot.x + foot.w - 1 - hot->tx,
01063       foot.y + foot.h - 1 - hot->ty, cnt, ents);
01064   assert(len <= buflen);
01065 #ifdef WIN32
01066   windragdata *wdata = (windragdata *)seldata;
01067   wdata->assign(info, len, buf);
01068 #else
01069           // Make us owner of xdndselection.
01070   gtk_selection_owner_set(widget, gdk_atom_intern("XdndSelection", 0),
01071                 time);
01072           // Set data.
01073   gtk_selection_data_set(seldata,
01074       gdk_atom_intern(U7_TARGET_COMBOID_NAME, 0),
01075                                         8, buf, len);
01076 #endif
01077   delete buf;
01078   delete [] ents;
01079   }
01080 
01081 /*
01082  *  Another app. has claimed the selection.
01083  */
01084 
01085 gint Combo_chooser::selection_clear
01086   (
01087   GtkWidget *widget,    // The view window.
01088   GdkEventSelection *event,
01089   gpointer data     // ->Combo_chooser.
01090   )
01091   {
01092 //  Combo_chooser *chooser = (Combo_chooser *) data;
01093   cout << "SELECTION_CLEAR" << endl;
01094   return TRUE;
01095   }
01096 
01097 /*
01098  *  Beginning of a drag.
01099  */
01100 
01101 gint Combo_chooser::drag_begin
01102   (
01103   GtkWidget *widget,    // The view window.
01104   GdkDragContext *context,
01105   gpointer data     // ->Combo_chooser.
01106   )
01107   {
01108   cout << "In DRAG_BEGIN" << endl;
01109   Combo_chooser *chooser = (Combo_chooser *) data;
01110   if (chooser->selected < 0)
01111     return FALSE;   // ++++Display a halt bitmap.
01112           // Get ->combo.
01113   int num = chooser->info[chooser->selected].num;
01114   Combo *combo = chooser->combos[num];
01115           // Show 'hot' member as icon.
01116   Combo_member *hot = combo->members[combo->hot_index];
01117   Shape_frame *shape = combo->shapes_file->get_shape(hot->shapenum,
01118               hot->framenum);
01119   if (shape)
01120     chooser->set_drag_icon(context, shape);
01121   return TRUE;
01122   }
01123 
01124 /*
01125  *  Handle a scrollbar event.
01126  */
01127 
01128 void Combo_chooser::scrolled
01129   (
01130   GtkAdjustment *adj,   // The adjustment.
01131   gpointer data     // ->Combo_chooser.
01132   )
01133   {
01134   Combo_chooser *chooser = (Combo_chooser *) data;
01135   gint newindex = (gint) adj->value;
01136   chooser->scroll(newindex);
01137   }
01138 
01139 /*
01140  *  Callbacks for controls:
01141  */
01142 /*
01143  *  Keystroke in draw-area.
01144  */
01145 static gboolean
01146 on_combo_key_press      (GtkEntry *entry,
01147            GdkEventKey  *event,
01148            gpointer  user_data)
01149 {
01150   Combo_chooser *chooser = (Combo_chooser *) user_data;
01151   switch (event->keyval)
01152     {
01153   case GDK_Delete:
01154     chooser->remove();
01155     return TRUE;
01156     }
01157   return FALSE;     // Let parent handle it.
01158 }
01159 
01160 /*
01161  *  Enable/disable controls after selection changed.
01162  */
01163 
01164 void Combo_chooser::enable_controls
01165   (
01166   )
01167   {
01168   if (selected == -1)   // No selection.
01169     {
01170     if (!group)
01171       {
01172       gtk_widget_set_sensitive(move_down, false);
01173       gtk_widget_set_sensitive(move_up, false);
01174       }
01175     return;
01176     }
01177   if (!group)
01178     {
01179     gtk_widget_set_sensitive(move_down, 
01180         info[selected].num < combos.size() - 1);
01181     gtk_widget_set_sensitive(move_up, 
01182           info[selected].num > 0);
01183     }
01184   }
01185 
01186 static gint Mouse_release(GtkWidget *, GdkEventButton *, gpointer);
01187 
01188 /*
01189  *  Create the list.
01190  */
01191 
01192 Combo_chooser::Combo_chooser
01193   (
01194   Vga_file *i,      // Where they're kept.
01195   Flex_file_info *flinfo,   // Flex-file info.
01196   unsigned char *palbuf,    // Palette, 3*256 bytes (rgb triples).
01197   int w, int h,     // Dimensions.
01198   Shape_group *g      // Filter, or null.
01199   ) : Object_browser(g, flinfo), 
01200     Shape_draw(i, palbuf, gtk_drawing_area_new()),
01201     flex_info(flinfo), index0(0),
01202     info(0), info_cnt(0), sel_changed(0)
01203   {
01204   load();       // Init. from file data.
01205           // Put things in a vert. box.
01206   GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
01207   set_widget(vbox); // This is our "widget"
01208   gtk_widget_show(vbox);
01209   
01210   GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
01211   gtk_widget_show(hbox);
01212   gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
01213   
01214           // A frame looks nice.
01215   GtkWidget *frame = gtk_frame_new(NULL);
01216   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
01217   gtk_widget_show(frame);
01218   gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
01219           // NOTE:  draw is in Shape_draw.
01220           // Indicate the events we want.
01221   gtk_widget_set_events(draw, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
01222     | GDK_BUTTON_RELEASE_MASK
01223     | GDK_POINTER_MOTION_HINT_MASK |
01224     GDK_BUTTON1_MOTION_MASK | GDK_KEY_PRESS_MASK);
01225           // Set "configure" handler.
01226   gtk_signal_connect(GTK_OBJECT(draw), "configure_event",
01227         GTK_SIGNAL_FUNC(configure), this);
01228           // Set "expose" handler.
01229   gtk_signal_connect(GTK_OBJECT(draw), "expose_event",
01230         GTK_SIGNAL_FUNC(expose), this);
01231           // Keystroke.
01232   gtk_signal_connect(GTK_OBJECT(draw), "key-press-event",
01233           GTK_SIGNAL_FUNC (on_combo_key_press),
01234           this);
01235   GTK_WIDGET_SET_FLAGS(draw, GTK_CAN_FOCUS);
01236           // Set mouse click handler.
01237   gtk_signal_connect(GTK_OBJECT(draw), "button_press_event",
01238         GTK_SIGNAL_FUNC(mouse_press), this);
01239   gtk_signal_connect(GTK_OBJECT(draw), "button_release_event",
01240         GTK_SIGNAL_FUNC(Mouse_release), this);
01241           // Mouse motion.
01242   gtk_signal_connect(GTK_OBJECT(draw), "drag_begin",
01243         GTK_SIGNAL_FUNC(drag_begin), this);
01244 #ifdef WIN32
01245 // required to override GTK+ Drag and Drop
01246   gtk_signal_connect(GTK_OBJECT(draw), "motion_notify_event",
01247         GTK_SIGNAL_FUNC(win32_drag_motion), this);
01248 #else
01249   gtk_signal_connect(GTK_OBJECT(draw), "motion_notify_event",
01250         GTK_SIGNAL_FUNC(drag_motion), this);
01251 #endif
01252   gtk_signal_connect (GTK_OBJECT(draw), "drag_data_get",
01253         GTK_SIGNAL_FUNC(drag_data_get), this);
01254   gtk_signal_connect (GTK_OBJECT(draw), "selection_clear_event",
01255         GTK_SIGNAL_FUNC(selection_clear), this);
01256   gtk_container_add (GTK_CONTAINER (frame), draw);
01257   gtk_drawing_area_size(GTK_DRAWING_AREA(draw), w, h);
01258   gtk_widget_show(draw);
01259           // Want a scrollbar for the combos.
01260   GtkObject *combo_adj = gtk_adjustment_new(0, 0, 
01261         combos.size(), 1, 
01262         4, 1.0);
01263   vscroll = gtk_vscrollbar_new(GTK_ADJUSTMENT(combo_adj));
01264           // Update window when it stops.
01265   gtk_range_set_update_policy(GTK_RANGE(vscroll),
01266           GTK_UPDATE_DELAYED);
01267   gtk_box_pack_start(GTK_BOX(hbox), vscroll, FALSE, TRUE, 0);
01268           // Set scrollbar handler.
01269   gtk_signal_connect(GTK_OBJECT(combo_adj), "value_changed",
01270           GTK_SIGNAL_FUNC(scrolled), this);
01271   gtk_widget_show(vscroll);
01272           // At the bottom, status bar:
01273   GtkWidget *hbox1 = gtk_hbox_new(FALSE, 0);
01274   gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 0);
01275   gtk_widget_show(hbox1);
01276           // At left, a status bar.
01277   sbar = gtk_statusbar_new();
01278   sbar_sel = gtk_statusbar_get_context_id(GTK_STATUSBAR(sbar),
01279               "selection");
01280   gtk_box_pack_start(GTK_BOX(hbox1), sbar, TRUE, TRUE, 0);
01281   gtk_widget_show(sbar);
01282           // Add controls to bottom.
01283   gtk_box_pack_start(GTK_BOX(vbox), 
01284     create_controls(find_controls|move_controls), FALSE, FALSE, 0);
01285   }
01286 
01287 /*
01288  *  Delete.
01289  */
01290 
01291 Combo_chooser::~Combo_chooser
01292   (
01293   )
01294   {
01295   gtk_widget_destroy(get_widget());
01296   delete [] info;
01297   int i;
01298   int cnt = combos.size();
01299   for (i = 0; i < cnt; i++) // Delete all the combos.
01300     delete combos[i];
01301   }
01302 
01303 /*
01304  *  Add a new or updated combo.
01305  *
01306  *  Output: Index of entry.
01307  */
01308 
01309 int Combo_chooser::add
01310   (
01311   Combo *newcombo,    // We'll own this.
01312   int index     // Index to replace, or -1 to add new.
01313   )
01314   {
01315   if (index == -1)
01316     {     // New.
01317     combos.push_back(newcombo);
01318     index = combos.size() - 1;  // Index of new entry.
01319     }
01320   else
01321     {
01322     assert(index >= 0 && index < combos.size());
01323     delete combos[index];
01324     combos[index] = newcombo;
01325     }
01326   GtkAdjustment *adj = 
01327       gtk_range_get_adjustment(GTK_RANGE(vscroll));
01328   adj->upper = combos.size();
01329   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
01330   render();
01331   show();
01332   return index;     // Return index.
01333   }
01334 
01335 /*
01336  *  Remove selected entry.
01337  */
01338 
01339 void Combo_chooser::remove
01340   (
01341   )
01342   {
01343   if (selected < 0)
01344     return;
01345   int tnum = info[selected].num;
01346   Combo_editor *combowin = ExultStudio::get_instance()->get_combowin();
01347   if (combowin && combowin->is_visible() && combowin->file_index == tnum)
01348     {
01349     EStudio::Alert("Can't remove the combo you're editing");
01350     return;
01351     }
01352   if (EStudio::Prompt("Okay to remove selected combo?", "Yes", "no")
01353                 != 0)
01354     return;
01355   selected = -1;
01356   Combo *todel = combos[tnum];
01357   delete todel;     // Delete from our list.
01358   combos.erase(combos.begin() + tnum);
01359   flex_info->set_modified();
01360   flex_info->remove(tnum);  // Update flex-file list.
01361   GtkAdjustment *adj =    // Update scrollbar.
01362       gtk_range_get_adjustment(GTK_RANGE(vscroll));
01363   adj->upper = combos.size();
01364   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
01365   render();
01366   show();
01367   }
01368 
01369 /*
01370  *  Bring up editor for selected combo.
01371  */
01372 
01373 void Combo_chooser::edit
01374   (
01375   )
01376   {
01377   if (selected < 0)
01378     return;
01379   Combo_editor *combowin = ExultStudio::get_instance()->get_combowin();
01380   if (combowin && combowin->is_visible())
01381     {
01382     EStudio::Alert("You're already editing a combo");
01383     return;
01384     }
01385   int tnum = info[selected].num;
01386   ExultStudio *studio = ExultStudio::get_instance();
01387   studio->open_combo_window();  // Open it.
01388   Combo_editor *ed = studio->get_combowin();
01389   if (!ed || !ed->is_visible())
01390     return;     // Failed.  Shouldn't happen.
01391   ed->set_combo(new Combo(*(combos[tnum])), tnum);
01392   }
01393 
01394 /*
01395  *  Configure the viewing window.
01396  */
01397 
01398 gint Combo_chooser::configure
01399   (
01400   GtkWidget *widget,    // The draw area.
01401   GdkEventConfigure *event,
01402   gpointer data     // ->Combo_chooser
01403   )
01404   {
01405   Combo_chooser *chooser = (Combo_chooser *) data;
01406   chooser->Shape_draw::configure();
01407   chooser->render();
01408           // Set new scroll amounts.
01409   int w = event->width, h = event->height;
01410   int per_row = (w - border)/(128 + border);
01411   int num_rows = (h - border)/(128 + border);
01412   int page_size = per_row*num_rows;
01413   GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(
01414             chooser->vscroll));
01415   adj->step_increment = per_row;
01416   adj->page_increment = page_size;
01417   adj->page_size = page_size;
01418   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
01419 #if 0 /* ++++++Later */
01420   if (chooser->group)   // Filtering?
01421     chooser->enable_drop(); // Can drop combos here.
01422 #endif
01423   return (TRUE);
01424   }
01425 
01426 /*
01427  *  Handle an expose event.
01428  */
01429 
01430 gint Combo_chooser::expose
01431   (
01432   GtkWidget *widget,    // The view window.
01433   GdkEventExpose *event,
01434   gpointer data     // ->Combo_chooser.
01435   )
01436   {
01437   Combo_chooser *chooser = (Combo_chooser *) data;
01438   chooser->show(event->area.x, event->area.y, event->area.width,
01439               event->area.height);
01440   return (TRUE);
01441   }
01442 
01443 #ifdef WIN32
01444 
01445 /*
01446  *  Dragging in win32.
01447  */
01448 static bool win32_button = false;
01449 
01450 gint Combo_chooser::win32_drag_motion
01451   (
01452   GtkWidget *widget,    // The view window.
01453   GdkEventMotion *event,
01454   gpointer data     // ->Combo_chooser.
01455   )
01456   {
01457     if (win32_button)
01458     {
01459     win32_button = false;
01460 
01461     // prepare the dragged data
01462     windragdata wdata;
01463 
01464     // This call allows us to recycle the data transfer initialization code.
01465     //  It's clumsy, but far easier to maintain.
01466     drag_data_get(NULL, NULL, (GtkSelectionData *) &wdata,
01467       U7_TARGET_COMBOID, 0, data);
01468 
01469     POINT pnt;
01470     GetCursorPos(&pnt);
01471 
01472     LPDROPSOURCE idsrc = (LPDROPSOURCE) new Windropsource(0, 
01473       pnt.x, pnt.y);
01474     LPDATAOBJECT idobj = (LPDATAOBJECT) new Winstudioobj(wdata);
01475     DWORD dndout;
01476 
01477     HRESULT res = DoDragDrop(idobj, idsrc, DROPEFFECT_COPY, &dndout);
01478     if (FAILED(res)) {
01479       g_warning ("Oops! Something is wrong with OLE2 DnD..");
01480     }
01481 
01482     delete idsrc;
01483     idobj->Release(); // Not sure if we really need this. However, it doesn't hurt either.
01484     }
01485 
01486   return true;
01487   };
01488 
01489 #else
01490 gint Combo_chooser::drag_motion
01491   (
01492   GtkWidget *widget,    // The view window.
01493   GdkEventMotion *event,
01494   gpointer data     // ->Shape_chooser.
01495   )
01496   {
01497   Combo_chooser *chooser = (Combo_chooser *) data;
01498   if (!chooser->dragging && chooser->selected >= 0)
01499     chooser->start_drag(U7_TARGET_COMBOID_NAME, 
01500       U7_TARGET_COMBOID, (GdkEvent *) event);
01501   return true;
01502   }
01503 #endif
01504 
01505 /*
01506  *  Handle a mouse button press event.
01507  */
01508 
01509 gint Combo_chooser::mouse_press
01510   (
01511   GtkWidget *widget,    // The view window.
01512   GdkEventButton *event,
01513   gpointer data     // ->Combo_chooser.
01514   )
01515   {
01516   gtk_widget_grab_focus(widget);  // Enables keystrokes.
01517   Combo_chooser *chooser = (Combo_chooser *) data;
01518 
01519     if (event->button == 4) {
01520         chooser->scroll(true);
01521         return(TRUE);
01522     } else if (event->button == 5) {
01523         chooser->scroll(false);
01524         return(TRUE);
01525     }
01526 
01527 
01528 
01529   int old_selected = chooser->selected;
01530   int i;        // Search through entries.
01531   for (i = 0; i < chooser->info_cnt; i++)
01532     if (chooser->info[i].box.has_point(
01533           (int) event->x, (int) event->y))
01534       {   // Found the box?
01535           // Indicate we can drag.
01536 #ifdef WIN32
01537 // Here, we have to override GTK+'s Drag and Drop, which is non-OLE and
01538 // usually stucks outside the program window. I think it's because
01539 // the dragged shape only receives mouse motion events when the new mouse pointer
01540 // position is *still* inside the shape. So if you move the mouse too fast,
01541 // we are stuck.
01542       win32_button = true;
01543 #endif
01544       chooser->selected = i;
01545       chooser->render();
01546       chooser->show();
01547           // Tell client.
01548       if (chooser->sel_changed)
01549         (*chooser->sel_changed)();
01550       break;
01551       }
01552   if (i == chooser->info_cnt && event->button == 1)
01553     chooser->unselect(true);  // Nothing under mouse.
01554   else if (chooser->selected == old_selected && old_selected >= 0)
01555     {     // Same square.  Check for dbl-click.
01556     if (((GdkEvent *) event)->type == GDK_2BUTTON_PRESS)
01557       chooser->edit();
01558     }
01559   if (event->button == 3)
01560     gtk_menu_popup(GTK_MENU(chooser->create_popup()), 
01561         0, 0, 0, 0, event->button, event->time);
01562   return (TRUE);
01563   }
01564 
01565 /*
01566  *  Handle a mouse button-release event in the combo chooser.
01567  */
01568 static gint Mouse_release
01569   (
01570   GtkWidget *widget,    // The view window.
01571   GdkEventButton *event,
01572   gpointer data     // ->Shape_chooser.
01573   )
01574   {
01575   Combo_chooser *chooser = (Combo_chooser *) data;
01576   chooser->mouse_up();
01577   }
01578 
01579 /*
01580  *  Move currently-selected combo up or down.
01581  */
01582 
01583 void Combo_chooser::move
01584   (
01585   bool upwards
01586   )
01587   {
01588   if (selected < 0)
01589     return;     // Shouldn't happen.
01590   int tnum = info[selected].num;
01591   if ((tnum == 0 && upwards) || (tnum == combos.size() - 1 && !upwards))
01592     return;
01593   if (upwards)      // Going to swap tnum & tnum+1.
01594     tnum--;
01595   Combo *tmp = combos[tnum];
01596   combos[tnum] = combos[tnum + 1];
01597   combos[tnum + 1] = tmp;
01598   selected += upwards ? -1 : 1;
01599           // Update editor if open.
01600   Combo_editor *combowin = ExultStudio::get_instance()->get_combowin();
01601   if (combowin && combowin->is_visible())
01602     {
01603     if (combowin->file_index == tnum)
01604       combowin->file_index = tnum + 1;
01605     else if (combowin->file_index == tnum + 1)
01606       combowin->file_index = tnum;
01607     }
01608   flex_info->set_modified();
01609   flex_info->swap(tnum);    // Update flex-file list.
01610   render();
01611   show();
01612   }
01613 
01614 /*
01615  *  Search for an entry.
01616  */
01617 
01618 void Combo_chooser::search
01619   (
01620   const char *srch,   // What to search for.
01621   int dir       // 1 or -1.
01622   )
01623   {
01624   int total = get_count();
01625   if (!total)
01626     return;     // Empty.
01627   ExultStudio *studio = ExultStudio::get_instance();
01628           // Start with selection, or top.
01629   int start = selected >= 0 ? info[selected].num : 0;
01630   int i;
01631   start += dir;
01632   int stop = dir == -1 ? -1 : total;
01633   for (i = start; i != stop; i += dir)
01634     {
01635     int num = group ? (*group)[i] : i;
01636     const char *nm = combos[i]->name.c_str();
01637     if (nm && search_name(nm, srch))
01638       break;    // Found it.
01639     }
01640   if (i == stop)
01641     return;     // Not found.
01642   while (i < index0)    // Above current view?
01643     scroll(true);
01644   while (i >= index0 + info_cnt)  // Below?
01645     scroll(false);
01646   int newsel = i - index0;  // New selection.
01647   if (newsel >= 0 && newsel < info_cnt)
01648     select(newsel);
01649   show();
01650   }
01651 

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