shapelst.cc

Go to the documentation of this file.
00001 
00007 /*
00008 Copyright (C) 1999  Jeffrey S. Freedman
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 #ifdef WIN32
00030 #include "Windrag.h"
00031 #include <windows.h>
00032 #endif
00033 
00034 #include <gtk/gtk.h>
00035 #include <gdk/gdkkeysyms.h>
00036 #ifdef XWIN
00037 #include <gdk/gdkx.h>
00038 #endif
00039 #include <glib.h>
00040 #include <stdlib.h>
00041 #include <sys/stat.h>
00042 #include <unistd.h>
00043 #include "shapelst.h"
00044 #include "shapevga.h"
00045 #include "ibuf8.h"
00046 #include "Flex.h"
00047 #include "u7drag.h"
00048 #include "studio.h"
00049 #include "utils.h"
00050 #include "shapegroup.h"
00051 #include "shapefile.h"
00052 #include "pngio.h"
00053 #include "fontgen.h"
00054 
00055 using std::cout;
00056 using std::endl;
00057 using std::strlen;
00058 using std::string;
00059 using EStudio::Prompt;
00060 using EStudio::Alert;
00061 using EStudio::Add_menu_item;
00062 
00063 std::vector<Editing_file*> Shape_chooser::editing_files;
00064 int Shape_chooser::check_editing_timer = -1;
00065 
00066 /*
00067  *  Here's a description of a file being edited by an external program
00068  *  like Gimp or Photoshop.
00069  */
00070 class Editing_file
00071   {
00072   string vga_basename;    // Name of image file this comes from.
00073   string pathname;    // Full path to file.
00074   time_t mtime;     // Last modification time.
00075   int shapenum, framenum;   // Shape/frame.
00076   int tiles;      // If > 0, #8x8 tiles per row or col.
00077   bool bycolumns;     // If true tile by column first.
00078 public:
00079   friend class Shape_chooser;
00080           // Create for single frame:
00081   Editing_file(const char *vganm, const char *pnm, time_t m, 
00082               int sh, int fr) 
00083     : vga_basename(vganm), pathname(pnm), 
00084       mtime(m), shapenum(sh), framenum(fr), tiles(0)
00085     {  }
00086           // Create tiled:
00087   Editing_file(const char *vganm, const char *pnm, time_t m, int sh,
00088             int ts, bool bycol)
00089     : vga_basename(vganm), pathname(pnm), mtime(m), shapenum(sh),
00090       framenum(0), tiles(ts), bycolumns(bycol)
00091     {  }
00092   };    
00093 
00094 /*
00095  *  Callback for when a shape is dropped on our draw area.
00096  */
00097 
00098 static void Shape_dropped_here
00099   (
00100   int file,     // U7_SHAPE_SHAPES.
00101   int shape,
00102   int frame,
00103   void *udata
00104   )
00105   {
00106   ((Shape_chooser *) udata)->shape_dropped_here(file, shape, frame);
00107   }
00108 
00109 /*
00110  *  Blit onto screen.
00111  */
00112 
00113 void Shape_chooser::show
00114   (
00115   int x, int y, int w, int h  // Area to blit.
00116   )
00117   {
00118   Shape_draw::show(draw->window, x, y, w, h);
00119   if (selected >= 0)    // Show selected.
00120     {
00121     Rectangle b = info[selected].box;
00122           // Draw yellow box.
00123     gdk_draw_rectangle(draw->window, drawgc, FALSE, 
00124               b.x, b.y, b.w, b.h);
00125     }
00126   }
00127 
00128 /*
00129  *  Send selected shape/frame to Exult.
00130  */
00131 
00132 void Shape_chooser::tell_server_shape
00133   (
00134   )
00135   {
00136   int shnum = -1, frnum = 0;
00137   if (selected >= 0)
00138     {
00139     shnum = info[selected].shapenum;
00140     frnum = info[selected].framenum;
00141     }
00142   unsigned char buf[Exult_server::maxlength];
00143   unsigned char *ptr = &buf[0];
00144   Write2(ptr, shnum);
00145   Write2(ptr, frnum);
00146   ExultStudio *studio = ExultStudio::get_instance();
00147   studio->send_to_server(Exult_server::set_edit_shape, buf, ptr - buf);
00148   }
00149 
00150 /*
00151  *  Select an entry.  This should be called after rendering
00152  *  the shape.
00153  */
00154 
00155 void Shape_chooser::select
00156   (
00157   int new_sel
00158   )
00159   {
00160   selected = new_sel;
00161   tell_server_shape();    // Tell Exult.
00162   int shapenum = info[selected].shapenum;
00163           // Update spin-button value, range.
00164   gtk_widget_set_sensitive(fspin, true);
00165   gtk_adjustment_set_value(frame_adj, info[selected].framenum);
00166   int nframes = ifile->get_num_frames(shapenum);
00167   frame_adj->upper = nframes - 1;
00168   gtk_adjustment_changed(frame_adj);
00169   gtk_widget_set_sensitive(fspin, true);
00170           // Remove prev. selection msg.
00171 //  gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00172   char buf[150];      // Show new selection.
00173   g_snprintf(buf, sizeof(buf), "Shape %d (%d frames)",
00174             shapenum, nframes);
00175   ExultStudio *studio = ExultStudio::get_instance();
00176   if (shapes_file && studio->get_shape_name(shapenum))
00177     {
00178     int len = strlen(buf);
00179     g_snprintf(buf + len, sizeof(buf) - len, 
00180         ":  '%s'", studio->get_shape_name(shapenum));
00181     }
00182   gtk_statusbar_push(GTK_STATUSBAR(sbar), sbar_sel, buf);
00183   }
00184 
00185 const int border = 4;     // Border at bottom, sides.
00186 /*
00187  *  Render as many shapes as fit in the shape chooser window.
00188  */
00189 
00190 void Shape_chooser::render
00191   (
00192   )
00193   {
00194   if (frames_mode)
00195     {
00196     render_frames();
00197     return;
00198     }
00199           // Look for selected frame.
00200   int selshape = -1, selframe = -1, new_selected = -1;
00201   if (selected >= 0)    // Save selection info.
00202     {
00203     selshape = info[selected].shapenum;
00204     selframe = info[selected].framenum;
00205     }
00206           // Remove "selected" message.
00207   //gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00208   delete [] info;     // Delete old info. list.
00209           // Get drawing area dimensions.
00210   gint winw = draw->allocation.width, winh = draw->allocation.height;
00211           // Provide more than enough room.
00212   info = new Shape_entry[1024];
00213           // Clear window first.
00214   iwin->fill8(255);   // Set to background_color.
00215   int x = 0;
00216   info_cnt = 0;     // Count them.
00217   int curr_y = 0;
00218   int row_h = 0;
00219   int row = row0;     // Row #.
00220   int total_cnt = get_count();
00221   int index;      // This is shapenum if there's no
00222           //   filter (group).
00223   for (int index = index0; index < total_cnt; index++)
00224     {
00225     int shapenum = group ? (*group)[index] : index;
00226     int framenum = shapenum == selshape ? selframe : framenum0;
00227     Shape_frame *shape = ifile->get_shape(shapenum, framenum);
00228     if(shape)
00229       {
00230       int sh = shape->get_height(),
00231           sw = shape->get_width();
00232       if (sh>row_h)
00233         row_h = sh;
00234           // Check if we've exceeded max width
00235       if (x + sw > winw)
00236         { // Next line.
00237         curr_y += row_h + border;
00238         row_h = sh;
00239         x = 0;
00240         row++;
00241         if (row == row_indices.size())
00242           row_indices.push_back(index);
00243         else if (row < row_indices.size())
00244           row_indices[row] = index;
00245         if (curr_y + 36 >= winh)
00246           break;
00247         }
00248       int sy = curr_y+border; // Get top y-coord.
00249       shape->paint(iwin, x + shape->get_xleft(),
00250             sy + shape->get_yabove());
00251       if (sh > winh)
00252         {
00253         sy += sh - winh;
00254         sh = winh;
00255         }
00256           // Store info. about where drawn.
00257       info[info_cnt].set(index,
00258           shapenum, framenum, x, sy, sw, sh);
00259       if (shapenum == selshape)
00260             // Found the selected shape.
00261         new_selected = info_cnt;
00262       x += sw + border;
00263       info_cnt++;
00264       }
00265     }
00266   nrows = row - row0;   // # rows shown.
00267   nrows += (x > 0);   // Add partial row at end.
00268   if (new_selected == -1)
00269     unselect(false);
00270   else
00271     select(new_selected);
00272   adjust_vscrollbar();    // Set new scroll values.
00273 }
00274 
00275 /*
00276  *  Get maximum shape height for all its frames.
00277  */
00278 
00279 static int Get_max_height
00280   (
00281   Shape *shape
00282   )
00283   {
00284   int cnt = shape->get_num_frames();
00285   int maxh = 0;
00286   for (int i = 0; i < cnt; i++)
00287     {
00288     Shape_frame *frame = shape->get_frame(i);
00289     int ht =  frame ? frame->get_height() : -1;
00290     if (ht > maxh)
00291       maxh = ht;
00292     }
00293   return maxh;
00294   }
00295 
00296 /*
00297  *  Get the x-offset in pixels where a frame will be drawn.
00298  *
00299  *  Output: Offset from left edge of (virtual) drawing area.
00300  */
00301 
00302 static int Get_x_offset
00303   (
00304   Shape *shape,
00305   int framenum
00306   )
00307   {
00308   if (!shape)
00309     return 0;
00310   int nframes = shape->get_num_frames();
00311   if (framenum >= nframes)
00312     framenum = nframes - 1;
00313   int xoff = 0;
00314   for (int i = 0; i < framenum; i++)
00315     xoff += shape->get_frame(i)->get_width() + border;
00316   return xoff;
00317   }
00318 
00319 /*
00320  *  Render one shape per row, showing its frames from left to right.
00321  */
00322 
00323 void Shape_chooser::render_frames
00324   (
00325   )
00326   {
00327           // Get drawing area dimensions.
00328   gint winw = draw->allocation.width, winh = draw->allocation.height;
00329           // Look for selected frame.
00330   int selshape = -1, selframe = -1, new_selected = -1;
00331   if (selected >= 0)    // Save selection info.
00332     {
00333     selshape = info[selected].shapenum;
00334     selframe = info[selected].framenum;
00335     }
00336           // Remove "selected" message.
00337   //gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00338   delete [] info;     // Delete old info. list.
00339   iwin->set_clip(0, 0, winw, winh);
00340           // Provide more than enough room.
00341   info = new Shape_entry[4048]; //++++++Pretty risky.  Use s vec!!!
00342           // Clear window first.
00343   iwin->fill8(255);   // Fill with background color.
00344   info_cnt = 0;     // Count them.
00345   int curr_y = 0;
00346   int row = row0;     // Row #.
00347   int total_cnt = get_count();
00348   int index;      // This is shapenum if there's no
00349           //   filter (group).
00350   for (int index = index0; index < total_cnt; index++)
00351     {
00352     int shapenum = group ? (*group)[index] : index;
00353           // Get all frames.
00354     Shape *shape = ifile->extract_shape(shapenum);
00355     int nframes = shape ? shape->get_num_frames() : 0;
00356     if (!nframes)
00357       continue;
00358     int row_h = Get_max_height(shape);
00359     int x = -hoffset;
00360     int sw, sh;
00361     for (int framenum = 0; framenum < nframes; framenum++,
00362             x += sw + border)
00363       {
00364       if (x >= winw - 1)  // Past right edge?
00365         break;
00366       Shape_frame *frame = shape->get_frame(framenum);
00367       if (!frame)
00368         {
00369         sw = sh = 0;
00370         continue;
00371         }
00372       sh = frame->get_height();
00373       sw = frame->get_width();
00374       if (x < 0 && x + sw < sw/2)
00375         continue;// Skip to left of hoffset.
00376       int sy = curr_y+border; // Get top y-coord.
00377       frame->paint(iwin, x + frame->get_xleft(),
00378             sy + frame->get_yabove());
00379       if (sh > winh)
00380         {
00381         sy += sh - winh;
00382         sh = winh;
00383         }
00384           // Store info. about where drawn.
00385       info[info_cnt].set(index, 
00386           shapenum, framenum, x, sy, sw, sh);
00387       if (shapenum == selshape && framenum == selframe)
00388             // Found the selected shape.
00389         new_selected = info_cnt;
00390       info_cnt++;
00391       }
00392           // Next line.
00393     curr_y += row_h + border;
00394     x = 0;
00395     if (row == row_indices.size())
00396       row_indices.push_back(index);
00397     else if (row < row_indices.size())
00398       row_indices[row] = index;
00399     row++;
00400     if (curr_y + 36 >= winh)
00401       break;
00402     }
00403   int nrows = row - row0;   // # rows shown.
00404   if (new_selected == -1)
00405     unselect(false);
00406   else
00407     select(new_selected);
00408   iwin->clear_clip();
00409   adjust_vscrollbar();    // Set new scroll values.
00410 }
00411 
00412 /*
00413  *  Horizontally scroll so that the selected frame is visible (in frames
00414  *  mode).
00415  */
00416 
00417 void Shape_chooser::scroll_to_frame
00418   (
00419   )
00420   {
00421   if (selected >= 0)    // Save selection info.
00422     {
00423     int selshape = info[selected].shapenum;
00424     int selframe = info[selected].framenum;
00425     Shape *shape = ifile->extract_shape(selshape);
00426     int xoff = Get_x_offset(shape, selframe);
00427     if (xoff < hoffset) // Left of visual area?
00428       hoffset = xoff > border ? xoff - border : 0;
00429     else
00430       {
00431       gint winw = draw->allocation.width;
00432       int sw = shape->get_frame(selframe)->get_width();
00433       if (xoff + sw + border - hoffset > winw)
00434         hoffset = xoff + sw + border - winw;
00435       }
00436     GtkAdjustment *adj = gtk_range_get_adjustment(
00437             GTK_RANGE(hscroll));
00438     gtk_adjustment_set_value(adj, hoffset);
00439     }
00440   }
00441 
00442 /*
00443  *  Find start of next row.
00444  *
00445  *  Output: Index of start of next row, or -1 if given is last.
00446  */
00447 
00448 int Shape_chooser::next_row
00449   (
00450   int start     // Index of row to start at.
00451   )
00452   {
00453   int total_cnt = get_count();
00454   if (frames_mode)    // Easy if 1 shape/row.
00455     return start < total_cnt - 1 ? (start + 1) : -1;
00456   int selshape = -1, selframe = -1;
00457   if (selected >= 0)    // Save selection info.
00458     {
00459     selshape = info[selected].shapenum;
00460     selframe = info[selected].framenum;
00461     }
00462   gint winw = draw->allocation.width;
00463   int index = start;
00464   int x = 0;
00465   while (index < total_cnt)
00466     {
00467     int shapenum = group ? (*group)[index] : index;
00468     int framenum = shapenum == selshape ? selframe : framenum0;
00469     Shape_frame *shape = ifile->get_shape(shapenum, framenum);
00470     if(shape)
00471       {
00472       int sw = shape->get_width();
00473       if (x + sw > winw)
00474         break;  // Done.
00475       x += sw + border;
00476       }
00477     index++;
00478     }
00479   if (index == start)   // Always advance at least 1.
00480     index++;
00481   if (index == total_cnt)
00482     return -1;    // Past end.
00483   return index;
00484   } 
00485 
00486 /*
00487  *  Scroll so a desired index is in view.
00488  */
00489 
00490 void Shape_chooser::goto_index
00491   (
00492   int index     // Desired index.
00493   )
00494   {
00495   int total = get_count();  // Total #entries.
00496   if (!total)
00497     return;     // Empty.
00498   assert (index >= 0 && index < total);
00499           // Get index past what's shown.
00500   int last_index = index0 + (frames_mode ? nrows : info_cnt);
00501   if (index < index0)   // Above current view?
00502     {
00503     do
00504       index0 = row_indices[--row0];
00505     while (index < index0);
00506     }
00507   else if (index >= last_index && last_index > 0)
00508     {     // Below current view.
00509     do
00510       {
00511       if (row0 < row_indices.size() - 1)
00512         index0 = row_indices[++row0];
00513       else
00514         {
00515         int i = next_row(index0);
00516         if (i < 0)  // Past end.  Shouldn't happen.
00517           break;
00518         row_indices.push_back(i);
00519         row0++;
00520         index0 = i;
00521         }
00522       }
00523     while (index0 < index);
00524     if (index != index0)
00525       row0--;   // We passed it.
00526     index0 = row_indices[row0];
00527     info_cnt = 0;
00528     }
00529           // Get to right spot again!
00530   GtkAdjustment *adj = gtk_range_get_adjustment(
00531             GTK_RANGE(vscroll));
00532   if (row0 >= adj->value)   // Beyond apparent end?
00533     adjust_vscrollbar();  // Needs updating.
00534   gtk_adjustment_set_value(adj, row0);
00535   }
00536 
00537 /*
00538  *  Configure the viewing window.
00539  */
00540 
00541 static gint Configure_chooser
00542   (
00543   GtkWidget *widget,    // The drawing area.
00544   GdkEventConfigure *event,
00545   gpointer data     // ->Shape_chooser
00546   )
00547   {
00548   Shape_chooser *chooser = (Shape_chooser *) data;
00549   return chooser->configure(event);
00550   }
00551 gint Shape_chooser::configure
00552   (
00553   GdkEventConfigure *event
00554   )
00555   {
00556   Shape_draw::configure();
00557           // Did the size change?
00558   if (event->width != config_width || event->height != config_height)
00559     {
00560     row_indices.resize(1);  // Start over with row info.
00561     row0 = 0;
00562     info_cnt = 0;
00563     int i0 = index0;  // Get back to where we were.
00564     index0 = 0;
00565     goto_index(i0); // Now goto where we were.
00566     render();   // This also adjusts scrollbar.
00567     adjust_hscrollbar(-1);
00568     config_width = event->width;
00569     config_height = event->height;
00570     }
00571   else
00572     render();   // Same size?  Just render it.
00573           // Set handler for shape dropped here,
00574           //   BUT not more than once.
00575   if (drop_callback != Shape_dropped_here)
00576     enable_drop(Shape_dropped_here, this);
00577   return (TRUE);
00578   }
00579 
00580 /*
00581  *  Handle an expose event.
00582  */
00583 
00584 gint Shape_chooser::expose
00585   (
00586   GtkWidget *widget,    // The view window.
00587   GdkEventExpose *event,
00588   gpointer data     // ->Shape_chooser.
00589   )
00590   {
00591   Shape_chooser *chooser = (Shape_chooser *) data;
00592   chooser->show(event->area.x, event->area.y, event->area.width,
00593               event->area.height);
00594   return (TRUE);
00595   }
00596 
00597 /*
00598  *  Handle a mouse drag event.
00599  */
00600 
00601 #ifdef WIN32
00602 
00603 static bool win32_button = false;
00604 
00605 gint Shape_chooser::win32_drag_motion
00606   (
00607   GtkWidget *widget,    // The view window.
00608   GdkEventMotion *event,
00609   gpointer data     // ->Shape_chooser.
00610   )
00611   {
00612     if (win32_button)
00613     {
00614     win32_button = false;
00615 
00616     // prepare the dragged data
00617     windragdata wdata;
00618 
00619     // This call allows us to recycle the data transfer initialization code.
00620     //  It's clumsy, but far easier to maintain.
00621     drag_data_get(NULL, NULL, (GtkSelectionData *) &wdata,
00622       U7_TARGET_SHAPEID, 0, data);
00623 
00624     POINT pnt;
00625     GetCursorPos(&pnt);
00626 
00627     LPDROPSOURCE idsrc = (LPDROPSOURCE) new Windropsource(0, 
00628       pnt.x, pnt.y);
00629     LPDATAOBJECT idobj = (LPDATAOBJECT) new Winstudioobj(wdata);
00630     DWORD dndout;
00631 
00632     HRESULT res = DoDragDrop(idobj, idsrc, DROPEFFECT_COPY, &dndout);
00633     if (FAILED(res)) {
00634       g_warning ("Oops! Something is wrong with OLE2 DnD..");
00635     }
00636 
00637     delete idsrc;
00638     idobj->Release(); // Not sure if we really need this. However, it doesn't hurt either.
00639     }
00640 
00641   return true;
00642   };
00643 #else
00644 gint Shape_chooser::drag_motion
00645   (
00646   GtkWidget *widget,    // The view window.
00647   GdkEventMotion *event,
00648   gpointer data     // ->Shape_chooser.
00649   )
00650   {
00651   Shape_chooser *chooser = (Shape_chooser *) data;
00652   if (!chooser->dragging && chooser->selected >= 0)
00653     chooser->start_drag(U7_TARGET_SHAPEID_NAME, 
00654       U7_TARGET_SHAPEID, (GdkEvent *) event);
00655   return true;
00656   }
00657 #endif
00658 
00659 /*
00660  *  Handle a mouse button-press event.
00661  */
00662 gint Shape_chooser::mouse_press
00663   (
00664   GtkWidget *widget,    // The view window.
00665   GdkEventButton *event
00666   )
00667   {
00668   gtk_widget_grab_focus(widget);
00669 
00670   if (event->button == 4) {
00671           if (row0 > 0)
00672                 scroll_vertical(row0-1);
00673           return(TRUE);
00674       } else if (event->button == 5) {
00675           scroll_vertical(row0+1);
00676           return(TRUE);
00677   }
00678 
00679   int old_selected = selected;
00680   int i;        // Search through entries.
00681   for (i = 0; i < info_cnt; i++)
00682     if (info[i].box.has_point(
00683           (int) event->x, (int) event->y))
00684       {   // Found the box?
00685           // Indicate we can drag.
00686 #ifdef WIN32
00687 // Here, we have to override GTK+'s Drag and Drop, which is non-OLE and
00688 // usually stucks outside the program window. I think it's because
00689 // the dragged shape only receives mouse motion events when the new mouse pointer
00690 // position is *still* inside the shape. So if you move the mouse too fast,
00691 // we are stuck.
00692       win32_button = true;
00693 #endif
00694       selected = i;
00695       render();
00696       show();
00697           // Tell client.
00698       if (sel_changed)
00699         (*sel_changed)();
00700       break;
00701       }
00702   if (i == info_cnt && event->button == 1)
00703     unselect(true);   // No selection.
00704   else if (selected == old_selected && old_selected >= 0)
00705     {     // Same square.  Check for dbl-click.
00706     if (((GdkEvent *) event)->type == GDK_2BUTTON_PRESS)
00707       edit_shape_info();
00708     }
00709   if (event->button == 3)
00710     gtk_menu_popup(GTK_MENU(create_popup()), 
00711         0, 0, 0, 0, event->button, event->time);
00712   return (TRUE);
00713   }
00714 
00715 /*
00716  *  Handle mouse button press/release events.
00717  */
00718 static gint Mouse_press
00719   (
00720   GtkWidget *widget,    // The view window.
00721   GdkEventButton *event,
00722   gpointer data     // ->Shape_chooser.
00723   )
00724   {
00725   Shape_chooser *chooser = (Shape_chooser *) data;
00726   return chooser->mouse_press(widget, event);
00727   }
00728 static gint Mouse_release
00729   (
00730   GtkWidget *widget,    // The view window.
00731   GdkEventButton *event,
00732   gpointer data     // ->Shape_chooser.
00733   )
00734   {
00735   Shape_chooser *chooser = (Shape_chooser *) data;
00736   chooser->mouse_up();
00737   }
00738 
00739 /*
00740  *  Keystroke in draw-area.
00741  */
00742 C_EXPORT gboolean
00743 on_draw_key_press     (GtkEntry *entry,
00744            GdkEventKey  *event,
00745            gpointer  user_data)
00746 {
00747   Shape_chooser *chooser = (Shape_chooser *) user_data;
00748   switch (event->keyval)
00749     {
00750   case GDK_Delete:
00751     chooser->del_frame();
00752     return TRUE;
00753   case GDK_Insert:
00754     chooser->new_frame();
00755     return TRUE;
00756     }
00757   return FALSE;     // Let parent handle it.
00758 }
00759 
00760 const unsigned char transp = 255;
00761 
00762 /*
00763  *  Export the currently selected frame as a .png file.
00764  *
00765  *  Output: Modification time of file written, or 0 if error.
00766  */
00767 
00768 time_t Shape_chooser::export_png
00769   (
00770   const char *fname   // File to write out.
00771   )
00772   {
00773   ExultStudio *studio = ExultStudio::get_instance();
00774   int shnum = info[selected].shapenum,
00775       frnum = info[selected].framenum;
00776   Shape_frame *frame = ifile->get_shape(shnum, frnum);
00777   int w = frame->get_width(), h = frame->get_height();
00778   Image_buffer8 img(w, h);  // Render into a buffer.
00779   img.fill8(transp);    // Fill with transparent pixel.
00780   frame->paint(&img, frame->get_xleft(), frame->get_yabove());
00781   int xoff = 0, yoff = 0;
00782   if (frame->is_rle())
00783     {
00784     xoff = -frame->get_xright();
00785     yoff = -frame->get_ybelow();
00786     }
00787   return export_png(fname, img, xoff, yoff);
00788   }
00789 
00790 /*
00791  *  Convert a GDK color map to a 3*256 byte RGB palette.
00792  */
00793 
00794 static void Get_rgb_palette
00795   (
00796   GdkRgbCmap *palette,
00797   unsigned char *buf    // 768 bytes (3*256).
00798   )
00799   {
00800   for (int i = 0; i < 256; i++)
00801     {
00802     buf[3*i] = (palette->colors[i]>>16)&0xff;
00803     buf[3*i + 1] = (palette->colors[i]>>8)&0xff;
00804     buf[3*i + 2] = palette->colors[i]&0xff;
00805     }
00806   }
00807 
00808 /*
00809  *  Export an image as a .png file.
00810  *
00811  *  Output: Modification time of file written, or 0 if error.
00812  */
00813 
00814 time_t Shape_chooser::export_png
00815   (
00816   const char *fname,    // File to write out.
00817   Image_buffer8& img,   // Image.
00818   int xoff, int yoff    // Offset (from bottom-right).
00819   )
00820   {
00821   unsigned char pal[3*256]; // Set up palette.
00822   Get_rgb_palette(palette, pal);
00823   int w = img.get_width(), h = img.get_height();
00824   struct stat fs;     // Write out to the .png.
00825           // (Rotate transp. pixel to 0 for the
00826           //   Gimp's sake.)
00827   if (!Export_png8(fname, transp, w, h, w, xoff, yoff, img.get_bits(),
00828           &pal[0], 256, true) ||
00829       stat(fname, &fs) != 0)
00830     {
00831     Alert("Error creating '%s'", fname);
00832     return 0;
00833     }
00834   return fs.st_mtime;
00835   }
00836 
00837 /*
00838  *  Export the current shape (which better be 8x8 flat) as a tiled PNG.
00839  *
00840  *  Output: modification time of file, or 0 if error.
00841  */
00842 
00843 time_t Shape_chooser::export_tiled_png
00844   (
00845   const char *fname,    // File to write out.
00846   int tiles,      // If #0, write all frames as tiles,
00847           //   this many in each row (col).
00848   bool bycols     // Write tiles columns-first.
00849   )
00850   {
00851   assert (selected >= 0);
00852   int shnum = info[selected].shapenum;
00853   ExultStudio *studio = ExultStudio::get_instance();
00854           // Low shape in 'shapes.vga'?
00855   assert (shnum < 0x96 && file_info == studio->get_vgafile());
00856   Shape *shape = ifile->extract_shape(shnum);
00857   assert(shape != 0);
00858   cout << "Writing " << fname << " tiled" 
00859     << (bycols ? ", by cols" : ", by rows") << " first" << endl;
00860   int nframes = shape->get_num_frames();
00861           // Figure #tiles in other dim.
00862   int dim1_cnt = (nframes + tiles - 1)/tiles;
00863   int w, h;
00864   if (bycols)
00865     { h = tiles*8; w = dim1_cnt*8; }
00866   else
00867     { w = tiles*8; h = dim1_cnt*8; }
00868   Image_buffer8 img(w, h);
00869   img.fill8(transp);    // Fill with transparent pixel.
00870   for (int f = 0; f < nframes; f++)
00871     {
00872     Shape_frame *frame = shape->get_frame(f);
00873     if (!frame)
00874       continue; // We'll just leave empty ones blank.
00875     if (frame->is_rle() || frame->get_width() != 8 ||
00876           frame->get_height() != 8)
00877       {
00878       Alert("Can only tile 8x8 flat shapes");
00879       return 0;
00880       }
00881     int x, y;
00882     if (bycols)
00883       { y = f%tiles; x = f/tiles; }
00884     else
00885       { x = f%tiles; y = f/tiles; }
00886     frame->paint(&img, x*8 + frame->get_xleft(), 
00887             y*8 + frame->get_yabove());
00888     }
00889           // Write out to the .png.
00890   return export_png(fname, img, 0, 0);
00891   }
00892 
00893 /*
00894  *  Bring up the shape-info editor for the selected shape.
00895  */
00896 
00897 void Shape_chooser::edit_shape_info
00898   (
00899   )
00900   {
00901   ExultStudio *studio = ExultStudio::get_instance();
00902   int shnum = info[selected].shapenum,
00903       frnum = info[selected].framenum;
00904   Shape_info *info = 0;
00905   char *name = 0;
00906   if (shapes_file)
00907     {     // Read info. the first time.
00908     shapes_file->read_info(false, true);//+++++BG?
00909     info = &shapes_file->get_info(shnum);
00910     name = studio->get_shape_name(shnum);
00911     }
00912   studio->open_shape_window(shnum, frnum, file_info, name, info);
00913   }
00914 
00915 /*
00916  *  Bring up the user's image-editor for the selected shape.
00917  */
00918 
00919 void Shape_chooser::edit_shape
00920   (
00921   int tiles,      // If #0, write all frames as tiles,
00922           //   this many in each row (col).
00923   bool bycols     // Write tiles columns-first.
00924   )
00925   {
00926   ExultStudio *studio = ExultStudio::get_instance();
00927   int shnum = info[selected].shapenum,
00928       frnum = info[selected].framenum;
00929   string filestr("<GAME>"); // Set up filename.
00930   filestr += "/itmp";   // "Image tmp" directory.
00931   U7mkdir(filestr.c_str(), 0755); // Create if not already there.
00932           // Lookup <GAME>.
00933   filestr = get_system_path(filestr);
00934   char *ext;
00935   if (!tiles)     // Create name from file,shape,frame.
00936     ext = g_strdup_printf("/%s.s%d_f%d.png",
00937         file_info->get_basename(), shnum, frnum);
00938   else        // Tiled.
00939     ext = g_strdup_printf("/%s.s%d_%c%d.png",
00940         file_info->get_basename(), shnum,
00941         (bycols ? 'c' : 'r'), tiles);
00942   filestr += ext;
00943   g_free(ext);
00944   const char *fname = filestr.c_str();
00945   cout << "Writing image '" << fname << "'" << endl;
00946   time_t mtime;
00947   if (!tiles)     // One frame?
00948     {
00949     mtime = export_png(fname);
00950     if (!mtime)
00951       return;
00952           // Store info. about file.
00953     editing_files.push_back(new Editing_file(
00954         file_info->get_basename(), fname, mtime, shnum, frnum));
00955     }
00956   else
00957     {
00958     mtime = export_tiled_png(fname, tiles, bycols);
00959     if (!mtime)
00960       return;
00961     editing_files.push_back(new Editing_file(
00962       file_info->get_basename(), fname, mtime, shnum,
00963             tiles, bycols));
00964     }
00965   string cmd(studio->get_image_editor());
00966   cmd += ' ';
00967   cmd += fname;
00968   cmd += " &";      // Background. 
00969 #ifndef WIN32
00970   int ret = system(cmd.c_str());
00971   if (ret == 127 || ret == -1)
00972     Alert("Can't launch '%s'", studio->get_image_editor());
00973 #else
00974   PROCESS_INFORMATION pi;
00975   STARTUPINFO   si;
00976   std::memset (&si, 0, sizeof(si));
00977   si.cb = sizeof(si);
00978   int ret = CreateProcess (NULL, const_cast<char *>(cmd.c_str()), 
00979       NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
00980   if (!ret)
00981     Alert("Can't launch '%s'", studio->get_image_editor());
00982 #endif
00983   if (check_editing_timer == -1)  // Monitor files every 6 seconds.
00984     check_editing_timer = gtk_timeout_add(6000,
00985         Shape_chooser::check_editing_files_cb, 0L);
00986   }
00987 
00988 /*
00989  *  Check the list of files being edited externally, and read in any that
00990  *  have changed.
00991  *
00992  *  Output: 1 always.
00993  */
00994 
00995 gint Shape_chooser::check_editing_files_cb
00996   (
00997   gpointer
00998   )
00999   {
01000   ExultStudio *studio = ExultStudio::get_instance();
01001           // Is focus in main window?
01002   if (studio->has_focus())
01003     check_editing_files();
01004   return 1;     // Keep repeating.
01005   }
01006           // This one doesn't check focus.
01007 gint Shape_chooser::check_editing_files
01008   (
01009   )
01010   {
01011   bool modified = false;
01012   for (std::vector<Editing_file*>::iterator it = editing_files.begin();
01013         it != editing_files.end(); it++)
01014     {
01015     Editing_file *ed = *it;
01016     struct stat fs;   // Check mod. time of file.
01017     if (stat(ed->pathname.c_str(), &fs) != 0)
01018       {   // Gone?
01019       delete ed;
01020       it = editing_files.erase(it);
01021       continue;
01022       }
01023     if (fs.st_mtime <= ed->mtime)
01024       continue; // Still not changed.
01025     ed->mtime = fs.st_mtime;
01026     read_back_edited(ed);
01027     modified = true;  // Did one.
01028     }
01029   if (modified)     // Repaint if modified.
01030     {
01031     ExultStudio *studio = ExultStudio::get_instance();
01032     Object_browser *browser = studio->get_browser();
01033     if (browser)
01034       {   // Repaint main window.
01035       browser->render();
01036       browser->show();
01037       }
01038     studio->update_group_windows(0);
01039     }
01040   return 1;     // Continue timeouts.
01041   }
01042 
01043 /*
01044  *  Find the closest color in a palette to a given one.
01045  *
01046  *  Output: 0-254, whichever has closest color.
01047  */
01048 
01049 static int Find_closest_color
01050   (
01051   unsigned char *pal,   // 3*255 bytes.
01052   int r, int g, int b   // Color to match.
01053   )
01054   {
01055   int best_index = -1;
01056   long best_distance = 0xfffffff;
01057           // But don't search rotating colors.
01058   for (int i = 0; i < 0xe0; i++)
01059     {     // Get deltas.
01060     long dr = r - pal[3*i], dg = g - pal[3*i + 1], 
01061               db = b - pal[3*i + 2];
01062           // Figure distance-squared.
01063     long dist = dr*dr + dg*dg + db*db;
01064     if (dist < best_distance)
01065       {   // Better than prev?
01066       best_index = i;
01067       best_distance = dist;
01068       }
01069     }
01070   return best_index;
01071   }
01072 
01073 /*
01074  *  Convert an 8-bit image in one palette to another.
01075  */
01076 
01077 static void Convert_indexed_image
01078   (
01079   unsigned char *pixels,    // Pixels.
01080   int count,      // # pixels.
01081   unsigned char *oldpal,    // Palette pixels currently uses.
01082   int oldpalsize,     // Size of old palette.
01083   unsigned char *newpal   // Palette (255 bytes) to convert to.
01084   )
01085   {
01086   if (memcmp(oldpal, newpal, oldpalsize) == 0)
01087     return;     // Old palette matches new.
01088   int map[256];     // Set up old->new map.
01089   int i;
01090   for (i = 0; i < 256; i++) // Set to 'unknown'.
01091     map[i] = -1;
01092           // Go through pixels.
01093   for (i = 0; i < count; i++)
01094     {
01095     unsigned char pix = *pixels;
01096     if (map[pix] == -1) // New one?
01097       map[pix] = Find_closest_color(newpal, oldpal[3*pix], 
01098           oldpal[3*pix+1], oldpal[3*pix+2]);
01099     *pixels++ = map[pix];
01100     }
01101   }
01102 
01103 /*
01104  *  Import a PNG file into a given shape,frame.
01105  */
01106 
01107 static void Import_png
01108   (
01109   const char *fname,    // Filename.
01110   Shape_file_info *finfo,   // What we're updating.
01111   unsigned char *pal,   // 3*255 bytes game palette.
01112   int shapenum, int framenum  // Shape, frame to update
01113   )
01114   {
01115   ExultStudio *studio = ExultStudio::get_instance();
01116   Vga_file *ifile = finfo->get_ifile();
01117   if (!ifile)
01118     return;     // Shouldn't happen.
01119   Shape *shape = ifile->extract_shape(shapenum);
01120   if (!shape)
01121     return;
01122   int w, h, rowsize, xoff, yoff, palsize;
01123   unsigned char *pixels, *oldpal;
01124           // Import, with 255 = transp. index.
01125   if (!Import_png8(fname, 255, w, h, rowsize, xoff, yoff,
01126             pixels, oldpal, palsize))
01127     return;     // Just return if error, for now.
01128           // Convert to game palette.
01129   Convert_indexed_image(pixels, h*rowsize, oldpal, palsize, pal);
01130   delete oldpal;
01131           // Low shape in 'shapes.vga'?
01132   bool flat = shapenum < 0x96 && finfo == studio->get_vgafile();
01133   int xleft, yabove;
01134   if (flat)
01135     {
01136     xleft = yabove = 8;
01137     if (w != 8 || h != 8 || rowsize != 8)
01138       {
01139       char *msg = g_strdup_printf(
01140         "Shape %d must be 8x8", shapenum);
01141       studio->prompt(msg, "Continue");
01142       g_free(msg);
01143       delete pixels;
01144       return;
01145       }
01146     }
01147   else        // RLE. xoff,yoff are neg. from bottom.
01148     {
01149     xleft = w + xoff - 1;
01150     yabove = h + yoff - 1;
01151     }
01152   shape->set_frame(new Shape_frame(pixels,
01153       w, h, xleft, yabove, !flat), framenum);
01154   delete pixels;
01155   finfo->set_modified();
01156   }
01157 
01158 /*
01159  *  Import a tiled PNG file into a given shape's frames.
01160  */
01161 
01162 static void Import_png_tiles
01163   (
01164   const char *fname,    // Filename.
01165   Shape_file_info *finfo,   // What we're updating.
01166   unsigned char *pal,   // 3*255 bytes game palette.
01167   int shapenum,
01168   int tiles,      // #tiles per row/col.
01169   bool bycols     // Write tiles columns-first.
01170   )
01171   {
01172   ExultStudio *studio = ExultStudio::get_instance();
01173   Vga_file *ifile = finfo->get_ifile();
01174   if (!ifile)
01175     return;     // Shouldn't happen.
01176   Shape *shape = ifile->extract_shape(shapenum);
01177   if (!shape)
01178     return;
01179   int nframes = shape->get_num_frames();
01180   cout << "Reading " << fname << " tiled" 
01181     << (bycols ? ", by cols" : ", by rows") << " first" << endl;
01182           // Figure #tiles in other dim.
01183   int dim0_cnt = tiles;
01184   int dim1_cnt = (nframes + dim0_cnt - 1)/dim0_cnt;
01185   int needw, needh;   // Figure min. image dims.
01186   if (bycols)
01187     { needh = dim0_cnt*8; needw = dim1_cnt*8; }
01188   else
01189     { needw = dim0_cnt*8; needh = dim1_cnt*8; }
01190   int w, h, rowsize, xoff, yoff, palsize;
01191   unsigned char *pixels, *oldpal;
01192           // Import, with 255 = transp. index.
01193   if (!Import_png8(fname, 255, w, h, rowsize, xoff, yoff,
01194             pixels, oldpal, palsize))
01195     {
01196     Alert("Error reading '%s'", fname);
01197     return;
01198     }
01199           // Convert to game palette.
01200   Convert_indexed_image(pixels, h*rowsize, oldpal, palsize, pal);
01201   delete oldpal;
01202   if (w < needw || h < needh)
01203     {
01204     Alert("File '%s' image is too small.  %dx%d required.",
01205             fname, needw, needh);
01206     return;
01207     }
01208   for (int frnum = 0; frnum < nframes; frnum++)
01209     {
01210     int x, y;
01211     if (bycols)
01212       { y = frnum%dim0_cnt; x = frnum/dim0_cnt; }
01213     else
01214       { x = frnum%dim0_cnt; y = frnum/dim0_cnt; }
01215     unsigned char *src = pixels + w*8*y + 8*x;
01216     unsigned char buf[8*8]; // Move tile to buffer.
01217     unsigned char *ptr = &buf[0];
01218     for (int row = 0; row < 8; row++)
01219       {   // Write it out.
01220       memcpy(ptr, src, 8);
01221       ptr += 8;
01222       src += w;
01223       }
01224     shape->set_frame(new Shape_frame(&buf[0], 8, 8, 8, 8, false), 
01225                   frnum);
01226     }
01227   delete pixels;
01228   finfo->set_modified();
01229   }
01230 
01231 /*
01232  *  Read in a shape that was changed by an external program (i.e., Gimp).
01233  */
01234 
01235 void Shape_chooser::read_back_edited
01236   (
01237   Editing_file *ed
01238   )
01239   {
01240   ExultStudio *studio = ExultStudio::get_instance();
01241   Shape_file_info *finfo = studio->open_shape_file(
01242             ed->vga_basename.c_str());
01243   if (!finfo)
01244     return;
01245   unsigned char pal[3*256]; // Convert to 0-255 RGB's.
01246   unsigned char *palbuf = studio->get_palbuf();
01247   for (int i = 0; i < 3*256; i++)
01248     pal[i] = palbuf[i]*4;
01249   if (!ed->tiles)
01250     Import_png(ed->pathname.c_str(), finfo, pal,
01251         ed->shapenum, ed->framenum);
01252   else
01253     Import_png_tiles(ed->pathname.c_str(), finfo, pal,
01254         ed->shapenum, ed->tiles, ed->bycolumns);
01255   }
01256 
01257 /*
01258  *  Delete all the files being edited after doing a final check.
01259  */
01260 
01261 void Shape_chooser::clear_editing_files
01262   (
01263   )
01264   {
01265   check_editing_files();    // Import any that changed.
01266   while (!editing_files.empty())
01267     {
01268     Editing_file *ed = editing_files.back();
01269     editing_files.pop_back();
01270     unlink(ed->pathname.c_str());
01271     delete ed;
01272     }
01273   if (check_editing_timer != -1)
01274     gtk_timeout_remove(check_editing_timer);
01275   check_editing_timer = -1;
01276   }
01277 
01278 /*
01279  *  Export current frame.
01280  */
01281 
01282 void Shape_chooser::export_frame
01283   (
01284   char *fname,
01285   gpointer user_data
01286   )
01287   {
01288   Shape_chooser *ed = (Shape_chooser *) user_data;
01289   if (U7exists(fname))
01290     {
01291     char *msg = g_strdup_printf(
01292       "'%s' already exists.  Overwrite?", fname);
01293     int answer = Prompt(msg, "Yes", "No");
01294     g_free(msg);
01295     if (answer != 0)
01296       return;
01297     }
01298   if (ed->selected < 0)
01299     return;     // Shouldn't happen.
01300   ed->export_png(fname);
01301   }
01302 
01303 /*
01304  *  Import current frame.
01305  */
01306 
01307 void Shape_chooser::import_frame
01308   (
01309   char *fname,
01310   gpointer user_data
01311   )
01312   {
01313   Shape_chooser *ed = (Shape_chooser *) user_data;
01314   if (ed->selected < 0)
01315     return;     // Shouldn't happen.
01316   int shnum = ed->info[ed->selected].shapenum,
01317       frnum = ed->info[ed->selected].framenum;
01318   unsigned char pal[3*256]; // Get current palette.
01319   Get_rgb_palette(ed->palette, pal);
01320   Import_png(fname, ed->file_info, pal, shnum, frnum);
01321   ed->render();
01322   ed->show();
01323   ExultStudio *studio = ExultStudio::get_instance();
01324   studio->update_group_windows(0);
01325   }
01326 
01327 /*
01328  *  Add a frame.
01329  */
01330 
01331 void Shape_chooser::new_frame
01332   (
01333   )
01334   {
01335   if (selected < 0)
01336     return;
01337   int shnum = info[selected].shapenum,
01338       frnum = info[selected].framenum;
01339   Vga_file *ifile = file_info->get_ifile();
01340           // Read entire shape.
01341   Shape *shape = ifile->extract_shape(shnum);
01342   if (!shape ||     // Shouldn't happen.
01343           // We'll insert AFTER frnum.
01344       frnum > shape->get_num_frames())
01345     return;
01346           // Low shape in 'shapes.vga'?
01347   ExultStudio *studio = ExultStudio::get_instance();
01348   bool flat = shnum < 0x96 && file_info == studio->get_vgafile();
01349   int w = 0, h = 0;
01350   int xleft, yabove;
01351   if (flat)
01352     w = h = xleft = yabove = 8;
01353   else        // Find largest frame.
01354     {
01355     int cnt = shape->get_num_frames();
01356     for (int i = 0; i < cnt; i++)
01357       {
01358       int ht = shape->get_frame(i)->get_height();
01359       if (ht > h)
01360         h = ht;
01361       int wd = shape->get_frame(i)->get_width();
01362       if (wd > w)
01363         w = wd;
01364       }
01365     if (h == 0)
01366       h = 8;
01367     if (w == 0)
01368       w = 8;
01369     xleft = w - 1;
01370     yabove = h - 1;
01371     }
01372   Image_buffer8 img(w, h);
01373   img.fill8(1);     // Just use color #1.
01374   if (w > 2 && h > 2)
01375     img.fill8(2, w - 2, h - 2, 1, 1);
01376   Shape_frame *frame = new Shape_frame(img.get_bits(),
01377       w, h, xleft, yabove, !flat);
01378   shape->add_frame(frame, frnum + 1);
01379   file_info->set_modified();
01380   Object_browser *browser = studio->get_browser();
01381   if (browser)
01382     {     // Repaint main window.
01383     browser->render();
01384     browser->show();
01385     }
01386   studio->update_group_windows(0);
01387   }
01388 
01389 /*
01390  *  Callback for new-shape 'okay'.
01391  */
01392 C_EXPORT void
01393 on_new_shape_okay_clicked              (GtkButton       *button,
01394                                         gpointer         user_data)
01395 {
01396   GtkWidget *win = gtk_widget_get_toplevel(GTK_WIDGET(button));
01397   Shape_chooser *chooser = (Shape_chooser *)
01398         gtk_object_get_user_data(GTK_OBJECT(win));
01399   chooser->create_new_shape();
01400   gtk_widget_hide(win);
01401 }
01402           // Toggled 'From font' button:
01403 C_EXPORT void on_new_shape_font_toggled
01404   (
01405   GtkToggleButton *btn,
01406         gpointer user_data
01407   )
01408   {
01409   bool on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn));
01410   GtkWidget *win = gtk_widget_get_toplevel(GTK_WIDGET(btn));
01411   Shape_chooser *chooser = (Shape_chooser *)
01412         gtk_object_get_user_data(GTK_OBJECT(win));
01413   chooser->from_font_toggled(on);
01414   }
01415 C_EXPORT gboolean on_new_shape_font_color_draw_expose_event
01416   (
01417   GtkWidget *widget,    // The draw area.
01418   GdkEventExpose *event,
01419   gpointer data
01420   )
01421   {
01422   ExultStudio *studio = ExultStudio::get_instance();
01423   int index = studio->get_spin("new_shape_font_color");
01424   Shape_chooser *ed = (Shape_chooser *) 
01425         gtk_object_get_user_data(GTK_OBJECT(widget));
01426   guint32 color = ed->get_color(index);
01427   GdkGC *gc = (GdkGC *) 
01428       gtk_object_get_data(GTK_OBJECT(widget), "color_gc");
01429   if (!gc)
01430     {
01431     gc = gdk_gc_new(widget->window);
01432     gtk_object_set_data(GTK_OBJECT(widget), "color_gc", gc);
01433     }
01434   gdk_rgb_gc_set_foreground(gc, color);
01435   gdk_draw_rectangle(widget->window, gc, TRUE, event->area.x, 
01436       event->area.y, event->area.width, event->area.height);
01437   return (TRUE);
01438   }
01439 C_EXPORT void on_new_shape_font_color_changed
01440   (
01441   GtkSpinButton *button,
01442   gpointer user_data
01443   )
01444   {
01445   ExultStudio *studio = ExultStudio::get_instance();
01446           // Show new color.
01447   GtkWidget *draw = glade_xml_get_widget(studio->get_xml(), 
01448             "new_shape_font_color_draw");
01449   GdkRectangle area = {0, 0, draw->allocation.width, 
01450             draw->allocation.height};
01451   gtk_widget_draw(draw, &area);
01452   }
01453 
01454 /*
01455  *  Font file was selected.
01456  */
01457 
01458 static void font_file_chosen
01459   (
01460   const char *fname,
01461   gpointer user_data
01462   )
01463   {
01464   ExultStudio *studio = ExultStudio::get_instance();
01465   studio->set_entry("new_shape_font_name", fname);
01466   studio->set_spin("new_shape_nframes", 128);
01467   }
01468 
01469 /*
01470  *  'From font' toggled in 'New shape' dialog.
01471  */
01472 
01473 void Shape_chooser::from_font_toggled
01474   (
01475   bool on
01476   )
01477   {
01478   ExultStudio *studio = ExultStudio::get_instance();
01479   studio->set_sensitive("new_shape_font_name", on);
01480   if (!on)
01481     return;
01482   studio->set_sensitive("new_shape_font_color", true);
01483   studio->set_sensitive("new_shape_font_height", true);
01484   GtkFileSelection *fsel = Create_file_selection(
01485         "Choose font file", font_file_chosen, 0L);
01486   gtk_widget_show(GTK_WIDGET(fsel));
01487   }
01488 
01489 /*
01490  *  Add a new shape.
01491  */
01492 
01493 void Shape_chooser::new_shape
01494   (
01495   )
01496   {
01497   ExultStudio *studio = ExultStudio::get_instance();
01498   GladeXML *xml = studio->get_xml();
01499   GtkWidget *win = glade_xml_get_widget(xml, "new_shape_window");
01500   gtk_window_set_modal(GTK_WINDOW(win), true);
01501   gtk_object_set_user_data(GTK_OBJECT(win), this);
01502           // Get current selection.
01503   int shnum = selected >= 0 ? info[selected].shapenum : 0,
01504       frnum = selected >= 0 ? info[selected].framenum : 0;
01505   GtkWidget *spin = glade_xml_get_widget(xml, "new_shape_num");
01506   GtkAdjustment *adj = gtk_spin_button_get_adjustment(
01507             GTK_SPIN_BUTTON(spin));
01508   adj->upper = 2047;    // Just a big number.
01509   gtk_adjustment_changed(adj);
01510   Vga_file *ifile = file_info->get_ifile();
01511   int shstart;      // Find an unused shape.
01512   for (shstart = shnum; shstart <= adj->upper; shstart++)
01513     if (shstart >= ifile->get_num_shapes() ||
01514         !ifile->get_num_frames(shstart))
01515       break;    
01516   if (shstart > adj->upper)
01517     {
01518     for (shstart = shnum - 1; shstart >= 0; shstart--)
01519       if (!ifile->get_num_frames(shstart))
01520         break;
01521     if (shstart < 0)
01522       shstart = shnum;
01523     }
01524   gtk_adjustment_set_value(adj, shstart);
01525   spin = glade_xml_get_widget(xml, "new_shape_nframes");
01526   adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(spin));
01527   bool flat = shnum < 0x96 && file_info == studio->get_vgafile();
01528   if (flat)
01529     adj->upper = 31;
01530   else
01531     adj->upper = 255;
01532   gtk_adjustment_changed(adj);
01533   spin = glade_xml_get_widget(xml, "new_shape_font_height");
01534   studio->set_sensitive("new_shape_font_height", false);
01535   adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(spin));
01536   adj->lower = 4;
01537   adj->upper = 64;
01538   gtk_adjustment_changed(adj);
01539   spin = glade_xml_get_widget(xml, "new_shape_font_color");
01540   studio->set_sensitive("new_shape_font_color", false);
01541   adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(spin));
01542   adj->lower = 0;
01543   adj->upper = 255;
01544   gtk_adjustment_changed(adj);
01545           // Unset 'From font:'.
01546   studio->set_toggle("new_shape_font", false);
01547 #ifndef HAVE_FREETYPE2      /* No freetype?  No fonts.  */
01548   studio->set_sensitive("new_shape_font", false);
01549 #endif
01550           // Store our pointer in color drawer.
01551   GtkWidget *draw = glade_xml_get_widget(xml, 
01552             "new_shape_font_color_draw");
01553   gtk_object_set_user_data(GTK_OBJECT(draw), this);
01554   gtk_widget_show(win);
01555   }
01556 
01557 /*
01558  *  Add a new shape after the user has clicked 'Okay' in the new-shape
01559  *  dialog.
01560  */
01561 
01562 void Shape_chooser::create_new_shape
01563   (
01564   )
01565   {
01566   ExultStudio *studio = ExultStudio::get_instance();
01567   int shnum = studio->get_spin("new_shape_num");
01568   int nframes = studio->get_spin("new_shape_nframes");
01569   if (nframes <= 0)
01570     nframes = 1;
01571   else if (nframes > 256)
01572     nframes = 256;
01573   Vga_file *ifile = file_info->get_ifile();
01574   if (shnum < ifile->get_num_shapes() && ifile->get_num_frames(shnum))
01575     {
01576     if (Prompt("Replace existing shape?", "Yes", "No") != 0)
01577       return;
01578     }
01579   Shape *shape = ifile->new_shape(shnum);
01580   if (!shape)
01581     {
01582     Alert("Can't create shape %d", shnum);
01583     return;
01584     }
01585           // Create frames.
01586   bool flat = shnum < 0x96 && file_info == studio->get_vgafile();
01587   bool use_font = false;
01588 #ifdef HAVE_FREETYPE2
01589           // Want to create from a font?
01590   use_font = studio->get_toggle("new_shape_font");
01591   const char *fontname = studio->get_text_entry("new_shape_font_name");
01592   use_font = use_font && (fontname != 0) && *fontname != 0;
01593   if (use_font)
01594     {
01595     if (flat)
01596       {
01597       Alert("Can't load font into a 'flat' shape");
01598       return;
01599       }
01600     int ht = studio->get_spin("new_shape_font_height");
01601     int fg = studio->get_spin("new_shape_font_color");
01602     if (!Gen_font_shape(shape, fontname, nframes,
01603           // Use transparent color for bgnd.
01604             ht, fg, 255))
01605       Alert("Error loading font file '%s'", fontname);
01606     }
01607 #endif
01608   if (!use_font)
01609     {
01610     int w = 8, h = 8;
01611     int xleft = flat ? 8 : w - 1;
01612     int yabove = flat ? 8 : h - 1;
01613     Image_buffer8 img(w, h);
01614     img.fill8(1);   // Just use color #1.
01615     img.fill8(2, w - 2, h - 2, 1, 1);
01616           // Include some transparency.
01617     img.fill8(255, w/2, h/2, w/4, h/4);
01618     for (int i = 0; i < nframes; i++)
01619       shape->add_frame(new Shape_frame(img.get_bits(),
01620         w, h, xleft, yabove, !flat), i);
01621     }
01622   file_info->set_modified();
01623   Object_browser *browser = studio->get_browser();
01624   if (browser)
01625     {     // Repaint main window.
01626     browser->render();
01627     browser->show();
01628     }
01629   studio->update_group_windows(0);
01630   }
01631 
01632 /*
01633  *  Delete a frame, and the shape itself if this is its last.
01634  */
01635 
01636 void Shape_chooser::del_frame
01637   (
01638   )
01639   {
01640   if (selected < 0)
01641     return;
01642   int shnum = info[selected].shapenum,
01643       frnum = info[selected].framenum;
01644   Vga_file *ifile = file_info->get_ifile();
01645           // Read entire shape.
01646   Shape *shape = ifile->extract_shape(shnum);
01647   if (!shape ||     // Shouldn't happen.
01648       frnum > shape->get_num_frames() - 1)
01649     return;
01650           // 1-shape file & last frame?
01651   if (!ifile->is_flex() && shape->get_num_frames() == 1)
01652     return;
01653   shape->del_frame(frnum);
01654   file_info->set_modified();
01655   ExultStudio *studio = ExultStudio::get_instance();
01656   Object_browser *browser = studio->get_browser();
01657   if (browser)
01658     {     // Repaint main window.
01659     browser->render();
01660     browser->show();
01661     }
01662   studio->update_group_windows(0);
01663   }
01664 
01665 /*
01666  *  Someone wants the dragged shape.
01667  */
01668 
01669 void Shape_chooser::drag_data_get
01670   (
01671   GtkWidget *widget,    // The view window.
01672   GdkDragContext *context,
01673   GtkSelectionData *seldata,  // Fill this in.
01674   guint info,
01675   guint time,
01676   gpointer data     // ->Shape_chooser.
01677   )
01678   {
01679   cout << "In DRAG_DATA_GET" << endl;
01680   Shape_chooser *chooser = (Shape_chooser *) data;
01681   if (chooser->selected < 0 || info != U7_TARGET_SHAPEID)
01682     return;     // Not sure about this.
01683   guchar buf[30];
01684   int file = chooser->ifile->get_u7drag_type();
01685   if (file == U7_SHAPE_UNK)
01686     U7_SHAPE_SHAPES;  // Just assume it's shapes.vga.
01687   Shape_entry& shinfo = chooser->info[chooser->selected];
01688   int len = Store_u7_shapeid(buf, file, shinfo.shapenum, 
01689               shinfo.framenum);
01690   cout << "Setting selection data (" << shinfo.shapenum <<
01691       '/' << shinfo.framenum << ')' << endl;
01692 #ifdef WIN32
01693   windragdata *wdata = (windragdata *)seldata;
01694   wdata->assign(info, len, buf);
01695 #else
01696           // Make us owner of xdndselection.
01697   gtk_selection_owner_set(widget, gdk_atom_intern("XdndSelection", 0),
01698                 time);
01699           // Set data.
01700   gtk_selection_data_set(seldata,
01701       gdk_atom_intern(U7_TARGET_SHAPEID_NAME, 0),
01702                                         8, buf, len);
01703 #endif
01704   }
01705 
01706 /*
01707  *  Another app. has claimed the selection.
01708  */
01709 
01710 gint Shape_chooser::selection_clear
01711   (
01712   GtkWidget *widget,    // The view window.
01713   GdkEventSelection *event,
01714   gpointer data     // ->Shape_chooser.
01715   )
01716   {
01717 //  Shape_chooser *chooser = (Shape_chooser *) data;
01718   cout << "SELECTION_CLEAR" << endl;
01719   return TRUE;
01720   }
01721 
01722 /*
01723  *  Beginning of a drag.
01724  */
01725 
01726 gint Shape_chooser::drag_begin
01727   (
01728   GtkWidget *widget,    // The view window.
01729   GdkDragContext *context,
01730   gpointer data     // ->Shape_chooser.
01731   )
01732   {
01733   cout << "In DRAG_BEGIN" << endl;
01734   Shape_chooser *chooser = (Shape_chooser *) data;
01735   if (chooser->selected < 0)
01736     return FALSE;   // ++++Display a halt bitmap.
01737           // Get ->shape.
01738   Shape_entry& shinfo = chooser->info[chooser->selected];
01739   Shape_frame *shape = chooser->ifile->get_shape(shinfo.shapenum, 
01740               shinfo.framenum);
01741   if (!shape)
01742     return FALSE;
01743   chooser->set_drag_icon(context, shape); // Set icon for dragging.
01744   return TRUE;
01745   }
01746 
01747 /*
01748  *  Scroll to a new shape/frame.
01749  */
01750 
01751 void Shape_chooser::scroll_vertical
01752   (
01753   int newindex      // Abs. index of row to show.
01754   )
01755   {
01756           // Already know where this is?
01757   if (newindex < row_indices.size())
01758     {
01759     index0 = row_indices[newindex];
01760     row0 = newindex;
01761     }
01762   else
01763     {     // Start with last known row.
01764     row0 = row_indices.size() - 1;
01765     int index = row_indices[row0];
01766     while (row0 < newindex && (index = next_row(index)) >= 0)
01767       {
01768       index0 = index;
01769       row0++;
01770       row_indices.push_back(index);
01771       }
01772     }
01773   int total = get_count();
01774   render();
01775   show();
01776   }
01777 
01778 /*
01779  *  Adjust vertical scroll amounts.
01780  */
01781 
01782 void Shape_chooser::adjust_vscrollbar
01783   (
01784   )
01785   { 
01786   GtkAdjustment *adj = gtk_range_get_adjustment(
01787             GTK_RANGE(vscroll));
01788   int known_rows = row_indices.size() - 1;
01789   float num_per_row = known_rows > 0 ? 
01790     ((float) row_indices[known_rows])/known_rows : 1;
01791           // This may change for the group.
01792   adj->upper = 1 + get_count()/num_per_row;
01793   adj->step_increment = 1;
01794   adj->page_increment = nrows;
01795   adj->page_size = nrows;
01796   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
01797   }
01798 
01799 /*
01800  *  Adjust horizontal scroll amounts.
01801  */
01802 
01803 void Shape_chooser::adjust_hscrollbar
01804   (
01805   int newmax      // New max., or -1 to leave alone.
01806   )
01807   { 
01808   GtkAdjustment *adj = gtk_range_get_adjustment(
01809             GTK_RANGE(hscroll));
01810   if (newmax > 0)
01811     adj->upper = newmax;
01812   adj->page_increment = draw->allocation.width;
01813   adj->page_size = draw->allocation.width;
01814   if (adj->page_size > adj->upper)
01815     adj->upper = adj->page_size;
01816   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
01817   }
01818 
01819 /*
01820  *  Handle a scrollbar event.
01821  */
01822 
01823 void Shape_chooser::vscrolled   // For vertical scrollbar.
01824   (
01825   GtkAdjustment *adj,   // The adjustment.
01826   gpointer data     // ->Shape_chooser.
01827   )
01828   {
01829   Shape_chooser *chooser = (Shape_chooser *) data;
01830 cout << "Scrolled to " << adj->value << '\n';
01831   gint newindex = (gint) adj->value;
01832   chooser->scroll_vertical(newindex);
01833   }
01834 void Shape_chooser::hscrolled   // For horizontal scrollbar.
01835   (
01836   GtkAdjustment *adj,   // The adjustment.
01837   gpointer data     // ->Shape_chooser.
01838   )
01839   {
01840   Shape_chooser *chooser = (Shape_chooser *) data;
01841   chooser->hoffset = (gint) adj->value;
01842   chooser->render_frames();
01843   chooser->show();
01844   }
01845 
01846 /*
01847  *  Handle a change to the 'frame' spin button.
01848  */
01849 
01850 void Shape_chooser::frame_changed
01851   (
01852   GtkAdjustment *adj,   // The adjustment.
01853   gpointer data     // ->Shape_chooser.
01854   )
01855   {
01856   Shape_chooser *chooser = (Shape_chooser *) data;
01857   gint newframe = (gint) adj->value;
01858   if (chooser->selected >= 0)
01859     {
01860     Shape_entry& shinfo = chooser->info[chooser->selected];
01861     int nframes = chooser->ifile->get_num_frames(shinfo.shapenum);
01862     if (newframe >= nframes)  // Just checking
01863       return;
01864     shinfo.framenum = newframe;
01865     if (chooser->frames_mode) // Get sel. frame in view.
01866       chooser->scroll_to_frame();
01867     chooser->render();
01868     chooser->show();
01869     }
01870   }
01871 
01872 /*
01873  *  'All frames' toggled.
01874  */
01875 
01876 void Shape_chooser::all_frames_toggled
01877   (
01878   GtkToggleButton *btn,
01879         gpointer data
01880   )
01881   {
01882   Shape_chooser *chooser = (Shape_chooser *) data;
01883   bool on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn));
01884   chooser->frames_mode = on;
01885   if (on)       // Frame => show horiz. scrollbar.
01886     gtk_widget_show(chooser->hscroll);
01887   else
01888     gtk_widget_hide(chooser->hscroll);
01889   chooser->row_indices.resize(1); // Start over with row info.
01890   chooser->row0 = 0;
01891   chooser->info_cnt = 0;
01892           // Make selection visible.
01893   int indx = chooser->selected >= 0 ? 
01894     chooser->info[chooser->selected].index : chooser->index0;
01895   chooser->index0 = 0;
01896   chooser->goto_index(indx);
01897 //  chooser->render();
01898 //  chooser->show();
01899   }
01900 
01901 /*
01902  *  Handle popup menu items.
01903  */
01904 
01905 void Shape_chooser::on_shapes_popup_info_activate
01906   (
01907   GtkMenuItem *item,
01908   gpointer udata
01909   )
01910   {
01911   ((Shape_chooser *) udata)->edit_shape_info();
01912   }
01913 
01914 void Shape_chooser::on_shapes_popup_edit_activate
01915   (
01916   GtkMenuItem *item,
01917   gpointer udata
01918   )
01919   {
01920   ((Shape_chooser *) udata)->edit_shape();
01921   }
01922 
01923 void Shape_chooser::on_shapes_popup_edtiles_activate
01924   (
01925   GtkMenuItem *item,
01926   gpointer udata
01927   )
01928   {
01929   Shape_chooser *ch = (Shape_chooser *) udata;
01930   if (ch->selected < 0)
01931     return;     // Shouldn't happen.
01932   ExultStudio *studio = ExultStudio::get_instance();
01933   GladeXML *xml = studio->get_xml();
01934   GtkWidget *win = glade_xml_get_widget(xml, "export_tiles_window");
01935   gtk_window_set_modal(GTK_WINDOW(win), true);
01936   gtk_object_set_user_data(GTK_OBJECT(win), ch);
01937           // Get current selection.
01938   int shnum = ch->info[ch->selected].shapenum;
01939   Vga_file *ifile = ch->file_info->get_ifile();
01940   int nframes = ifile->get_num_frames(shnum);
01941   GtkWidget *spin = glade_xml_get_widget(xml, "export_tiles_count");
01942   GtkAdjustment *adj = gtk_spin_button_get_adjustment(
01943             GTK_SPIN_BUTTON(spin));
01944   adj->lower = 1;
01945   adj->upper = nframes;
01946   gtk_adjustment_changed(adj);
01947   gtk_widget_show(win);
01948   }
01949 
01950 static void on_shapes_popup_import
01951   (
01952   GtkMenuItem *item,
01953   gpointer udata
01954   )
01955   {
01956   GtkFileSelection *fsel = Create_file_selection(
01957     "Import frame from a .png file", 
01958       (File_sel_okay_fun) Shape_chooser::import_frame, 
01959               udata);
01960   gtk_widget_show(GTK_WIDGET(fsel));
01961   }
01962 static void on_shapes_popup_export
01963   (
01964   GtkMenuItem *item,
01965   gpointer udata
01966   )
01967   {
01968   GtkFileSelection *fsel = Create_file_selection(
01969     "Export frame to a .png file",
01970       (File_sel_okay_fun) Shape_chooser::export_frame, 
01971               udata);
01972   gtk_widget_show(GTK_WIDGET(fsel));
01973   }
01974 static void on_shapes_popup_new_frame
01975   (
01976   GtkMenuItem *item,
01977   gpointer udata
01978   )
01979   {
01980   ((Shape_chooser *) udata)->new_frame();
01981   }
01982 static void on_shapes_popup_new_shape
01983   (
01984   GtkMenuItem *item,
01985   gpointer udata
01986   )
01987   {
01988   ((Shape_chooser *) udata)->new_shape();
01989   }
01990 
01991 /*
01992  *  Callback for edit-tiles 'okay'.
01993  */
01994 C_EXPORT void
01995 on_export_tiles_okay_clicked           (GtkButton       *button,
01996                                         gpointer         user_data)
01997 {
01998   GtkWidget *win = gtk_widget_get_toplevel(GTK_WIDGET(button));
01999   Shape_chooser *chooser = (Shape_chooser *)
02000         gtk_object_get_user_data(GTK_OBJECT(win));
02001   ExultStudio *studio = ExultStudio::get_instance();
02002   int tiles = studio->get_spin("export_tiles_count");
02003   bool bycol = studio->get_toggle("tiled_by_columns");
02004   chooser->edit_shape(tiles, bycol);
02005   gtk_widget_hide(win);
02006 }
02007 
02008 /*
02009  *  Handle a shape dropped on our draw area.
02010  */
02011 
02012 void Shape_chooser::shape_dropped_here
02013   (
02014   int file,     // U7_SHAPE_SHAPES.
02015   int shape,
02016   int frame
02017   )
02018   {
02019           // Got to be from same file type.
02020   if (ifile->get_u7drag_type() == file && group != 0)
02021     {     // Add to group.
02022     group->add(shape);
02023           // Update all windows for this group.
02024     ExultStudio::get_instance()->update_group_windows(group);
02025     }
02026   }
02027 
02028 /*
02029  *  Get # shapes we can display.
02030  */
02031 
02032 int Shape_chooser::get_count
02033   (
02034   )
02035   {
02036   return group ? group->size() : ifile->get_num_shapes();
02037   }
02038 
02039 /*
02040  *  Search for an entry.
02041  */
02042 
02043 void Shape_chooser::search
02044   (
02045   const char *srch,   // What to search for.
02046   int dir       // 1 or -1.
02047   )
02048   {
02049   if (!shapes_file)   // Not 'shapes.vga'.
02050     return;     // In future, maybe find shape #?
02051   int total = get_count();
02052   if (!total)
02053     return;     // Empty.
02054   ExultStudio *studio = ExultStudio::get_instance();
02055           // Start with selection, or top.
02056   int start = selected >= 0 ? info[selected].index : index0;
02057   int i;
02058   start += dir;
02059   int stop = dir == -1 ? -1 : total;
02060   for (i = start; i != stop; i += dir)
02061     {
02062     int shnum = group ? (*group)[i] : i;
02063     char *nm = studio->get_shape_name(shnum);
02064     if (nm && search_name(nm, srch))
02065       break;    // Found it.
02066     }
02067   if (i == stop)
02068     return;     // Not found.
02069   goto_index(i);
02070   int newsel;     // Get new selection index.
02071   if (!frames_mode)   // Easy if showing 1 shape/spot.
02072     newsel = i - row_indices[row0];
02073   else
02074     {
02075     int shnum = group ? (*group)[i] : i;
02076     for (newsel = 0; newsel < info_cnt &&
02077         info[newsel].shapenum != shnum; newsel++)
02078       ;
02079     }
02080   if (newsel >= 0 && newsel < info_cnt)
02081     select(newsel);
02082   show();
02083   }
02084 
02085 /*
02086  *  Locate shape on game map.
02087  */
02088 
02089 void Shape_chooser::locate
02090   (
02091   bool upwards
02092   )
02093   {
02094   if (selected < 0)
02095     return;     // Shouldn't happen.
02096   unsigned char data[Exult_server::maxlength];
02097   unsigned char *ptr = &data[0];
02098   Write2(ptr, info[selected].shapenum);
02099   *ptr++ = upwards ? 1 : 0;
02100   ExultStudio *studio = ExultStudio::get_instance();
02101   studio->send_to_server(
02102       Exult_server::locate_shape, data, ptr - data);
02103   }
02104 
02105 /*
02106  *  Set up popup menu for shape browser.
02107  */
02108 
02109 GtkWidget *Shape_chooser::create_popup
02110   (
02111   )
02112   {
02113   ExultStudio *studio = ExultStudio::get_instance();
02114   Object_browser::create_popup(); // Create popup with groups, files.
02115   if (selected >= 0)    // Add editing choices.
02116     {
02117     Add_menu_item(popup, "Info...",
02118       GTK_SIGNAL_FUNC(on_shapes_popup_info_activate), this);
02119     if (studio->get_image_editor())
02120       {
02121       Add_menu_item(popup, "Edit...",
02122         GTK_SIGNAL_FUNC(on_shapes_popup_edit_activate),
02123                  this);
02124       if (info[selected].shapenum < 0x96 && 
02125           file_info == studio->get_vgafile())
02126         Add_menu_item(popup, "Edit tiled...",
02127             GTK_SIGNAL_FUNC(
02128             on_shapes_popup_edtiles_activate), this);
02129       }
02130           // Separator.
02131     Add_menu_item(popup);
02132           // Add/del.
02133     Add_menu_item(popup, "New frame",
02134       GTK_SIGNAL_FUNC(on_shapes_popup_new_frame), this);
02135           // Export/import.
02136     Add_menu_item(popup, "Export frame...",
02137       GTK_SIGNAL_FUNC(on_shapes_popup_export), this);
02138     Add_menu_item(popup, "Import frame...",
02139       GTK_SIGNAL_FUNC(on_shapes_popup_import), this);
02140     }
02141   if (ifile->is_flex())   // Multiple-shapes file (.vga)?
02142     {
02143           // Separator.
02144     Add_menu_item(popup);
02145     Add_menu_item(popup, "New shape",
02146       GTK_SIGNAL_FUNC(on_shapes_popup_new_shape), this);
02147     }
02148   return popup;
02149   }
02150 
02151 /*
02152  *  Create the list.
02153  */
02154 
02155 Shape_chooser::Shape_chooser
02156   (
02157   Vga_file *i,      // Where they're kept.
02158   unsigned char *palbuf,    // Palette, 3*256 bytes (rgb triples).
02159   int w, int h,     // Dimensions.
02160   Shape_group *g,
02161   Shape_file_info *fi
02162   ) : Object_browser(g, fi),
02163     Shape_draw(i, palbuf, gtk_drawing_area_new()),
02164     shapes_file(0), framenum0(0),
02165     info(0), info_cnt(0), row0(0), nrows(0),
02166     sel_changed(0), frames_mode(false), hoffset(0)
02167   {
02168   row_indices.reserve(40);
02169   row_indices.push_back(0); // First row is 0.
02170           // Put things in a vert. box.
02171   GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
02172   set_widget(vbox); // This is our "widget"
02173   gtk_widget_show(vbox);
02174   
02175   GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
02176   gtk_widget_show(hbox);
02177   gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
02178 
02179           // A frame looks nice.
02180   GtkWidget *frame = gtk_frame_new(NULL);
02181   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
02182   gtk_widget_show(frame);
02183   gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
02184           // NOTE:  draw is in Shape_draw.
02185           // Indicate the events we want.
02186   gtk_widget_set_events(draw, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
02187     | GDK_BUTTON_RELEASE_MASK
02188     | GDK_POINTER_MOTION_HINT_MASK |
02189     GDK_BUTTON1_MOTION_MASK | GDK_KEY_PRESS_MASK);
02190           // Set "configure" handler.
02191   gtk_signal_connect(GTK_OBJECT(draw), "configure_event",
02192         GTK_SIGNAL_FUNC(Configure_chooser), this);
02193           // Set "expose" handler.
02194   gtk_signal_connect(GTK_OBJECT(draw), "expose_event",
02195         GTK_SIGNAL_FUNC(expose), this);
02196           // Keystroke.
02197   gtk_signal_connect(GTK_OBJECT(draw), "key-press-event",
02198           GTK_SIGNAL_FUNC (on_draw_key_press),
02199           this);
02200   GTK_WIDGET_SET_FLAGS(draw, GTK_CAN_FOCUS);
02201           // Set mouse click handler.
02202   gtk_signal_connect(GTK_OBJECT(draw), "button_press_event",
02203         GTK_SIGNAL_FUNC(Mouse_press), this);
02204   gtk_signal_connect(GTK_OBJECT(draw), "button_release_event",
02205         GTK_SIGNAL_FUNC(Mouse_release), this);
02206           // Mouse motion.
02207   gtk_signal_connect(GTK_OBJECT(draw), "drag_begin",
02208         GTK_SIGNAL_FUNC(drag_begin), this);
02209 #ifdef WIN32
02210 // required to override GTK+ Drag and Drop
02211   gtk_signal_connect(GTK_OBJECT(draw), "motion_notify_event",
02212         GTK_SIGNAL_FUNC(win32_drag_motion), this);
02213 #else
02214   gtk_signal_connect(GTK_OBJECT(draw), "motion_notify_event",
02215         GTK_SIGNAL_FUNC(drag_motion), this);
02216 #endif
02217 //  gtk_signal_connect(GTK_OBJECT(draw), "motion_notify_event",
02218 //        GTK_SIGNAL_FUNC(Mouse_drag_motion), this);
02219   gtk_signal_connect (GTK_OBJECT(draw), "drag_data_get",
02220         GTK_SIGNAL_FUNC(drag_data_get), this);
02221   gtk_signal_connect (GTK_OBJECT(draw), "selection_clear_event",
02222         GTK_SIGNAL_FUNC(selection_clear), this);
02223   gtk_container_add (GTK_CONTAINER (frame), draw);
02224   gtk_drawing_area_size(GTK_DRAWING_AREA(draw), w, h);
02225   gtk_widget_show(draw);
02226           // Want vert. scrollbar for the shapes.
02227   GtkObject *shape_adj = gtk_adjustment_new(0, 0, 
02228         get_count()/4, 1, 1, 1);
02229   vscroll = gtk_vscrollbar_new(GTK_ADJUSTMENT(shape_adj));
02230           // Update window when it stops.
02231   gtk_range_set_update_policy(GTK_RANGE(vscroll),
02232           GTK_UPDATE_DELAYED);
02233   gtk_box_pack_start(GTK_BOX(hbox), vscroll, FALSE, TRUE, 0);
02234           // Set scrollbar handler.
02235   gtk_signal_connect(GTK_OBJECT(shape_adj), "value_changed",
02236           GTK_SIGNAL_FUNC(vscrolled), this);
02237   gtk_widget_show(vscroll);
02238           // Horizontal scrollbar.
02239   shape_adj = gtk_adjustment_new(0, 0, 1600, 8, 16, 16);
02240   hscroll = gtk_hscrollbar_new(GTK_ADJUSTMENT(shape_adj));
02241           // Update window when it stops.
02242   gtk_range_set_update_policy(GTK_RANGE(hscroll),
02243           GTK_UPDATE_DELAYED);
02244   gtk_box_pack_start(GTK_BOX(vbox), hscroll, FALSE, TRUE, 0);
02245           // Set scrollbar handler.
02246   gtk_signal_connect(GTK_OBJECT(shape_adj), "value_changed",
02247           GTK_SIGNAL_FUNC(hscrolled), this);
02248 //++++  gtk_widget_hide(hscroll); // Only shown in 'frames' mode.
02249           // At the bottom, status bar & frame:
02250   GtkWidget *hbox1 = gtk_hbox_new(FALSE, 0);
02251   gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 0);
02252   gtk_widget_show(hbox1);
02253           // At left, a status bar.
02254   sbar = gtk_statusbar_new();
02255   sbar_sel = gtk_statusbar_get_context_id(GTK_STATUSBAR(sbar),
02256               "selection");
02257   gtk_box_pack_start(GTK_BOX(hbox1), sbar, TRUE, TRUE, 0);
02258   gtk_widget_show(sbar);
02259   GtkWidget *label = gtk_label_new("Frame:");
02260   gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 4);
02261   gtk_widget_show(label);
02262           // A spin button for frame#.
02263   frame_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 
02264         16, 1, 
02265         4, 1.0));
02266   fspin = gtk_spin_button_new(GTK_ADJUSTMENT(frame_adj), 
02267                   1, 0);
02268   gtk_signal_connect(GTK_OBJECT(frame_adj), "value_changed",
02269           GTK_SIGNAL_FUNC(frame_changed), this);
02270   gtk_box_pack_start(GTK_BOX(hbox1), fspin, FALSE, FALSE, 0);
02271   gtk_widget_show(fspin);
02272           // A toggle for 'All Frames'.
02273   GtkWidget *allframes = gtk_toggle_button_new_with_label("Frames");
02274   gtk_box_pack_start(GTK_BOX(hbox1), allframes, FALSE, FALSE, 4);
02275   gtk_widget_show(allframes);
02276   gtk_signal_connect(GTK_OBJECT(allframes), "toggled",
02277         GTK_SIGNAL_FUNC(all_frames_toggled), this);
02278           // Add search controls to bottom.
02279   gtk_box_pack_start(GTK_BOX(vbox), 
02280     create_controls(find_controls | locate_controls),
02281             FALSE, FALSE, 0);
02282   }
02283 
02284 /*
02285  *  Delete.
02286  */
02287 
02288 Shape_chooser::~Shape_chooser
02289   (
02290   )
02291   {
02292   gtk_widget_destroy(get_widget());
02293   delete [] info;
02294   }
02295   
02296 /*
02297  *  Unselect.
02298  */
02299 
02300 void Shape_chooser::unselect
02301   (
02302   bool need_render      // 1 to render and show.
02303   )
02304   {
02305   if (selected >= 0)
02306     {
02307     selected = -1;
02308           // Update spin button for frame #.
02309     gtk_adjustment_set_value(frame_adj, 0);
02310     gtk_widget_set_sensitive(fspin, false);
02311     if (need_render)
02312       {
02313       render();
02314       show();
02315       }
02316     if (sel_changed)  // Tell client.
02317       (*sel_changed)();
02318     }
02319   char buf[150];      // Show new selection.
02320   if (info_cnt > 0)
02321     {
02322 //    gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
02323     g_snprintf(buf, sizeof(buf), "Shapes %d to %d",
02324       info[0].shapenum, info[info_cnt - 1].shapenum);
02325     gtk_statusbar_push(GTK_STATUSBAR(sbar), sbar_sel, buf);
02326     }
02327   }
02328 
02329 

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