chunklst.cc

Go to the documentation of this file.
00001 
00007 /*
00008 Copyright (C) 2001 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 #ifdef WIN32
00030 #include "Windrag.h"
00031 #endif
00032 
00033 #include <gtk/gtk.h>
00034 #ifdef XWIN
00035 #include <gdk/gdkx.h>
00036 #endif
00037 #include <glib.h>
00038 #include "chunklst.h"
00039 #include "vgafile.h"
00040 #include "ibuf8.h"
00041 #include "Flex.h"
00042 #include "u7drag.h"
00043 #include "exult_constants.h"
00044 #include "studio.h"
00045 #include "utils.h"
00046 #include "shapegroup.h"
00047 
00048 #include <iosfwd>
00049 
00050 using std::cout;
00051 using std::endl;
00052 using std::strlen;
00053 using EStudio::Add_menu_item;
00054 using EStudio::Create_arrow_button;
00055 
00056 const int border = 2;     // Border at bottom, sides of each
00057           //   chunk.
00058 
00059 /*
00060  *  Blit onto screen.
00061  */
00062 
00063 void Chunk_chooser::show
00064   (
00065   int x, int y, int w, int h  // Area to blit.
00066   )
00067   {
00068   Shape_draw::show(draw->window, x, y, w, h);
00069   if (selected >= 0)    // Show selected.
00070     {
00071     Rectangle b = info[selected].box;
00072           // Draw yellow box.
00073     gdk_draw_rectangle(draw->window, drawgc, FALSE, 
00074               b.x, b.y, b.w, b.h);
00075     }
00076   }
00077 
00078 /*
00079  *  Send selected chunk# to Exult.
00080  */
00081 
00082 void Chunk_chooser::tell_server
00083   (
00084   )
00085   {
00086   if (selected < 0)
00087     return;
00088   unsigned char buf[Exult_server::maxlength];
00089   unsigned char *ptr = &buf[0];
00090   Write2(ptr, info[selected].num);
00091   ExultStudio *studio = ExultStudio::get_instance();
00092   studio->send_to_server(Exult_server::set_edit_chunknum, 
00093               buf, ptr - buf);
00094   }
00095 
00096 /*
00097  *  Select an entry.  This should be called after rendering
00098  *  the chunk.
00099  */
00100 
00101 void Chunk_chooser::select
00102   (
00103   int new_sel
00104   )
00105   {
00106   if (new_sel < 0 || new_sel >= info_cnt)
00107     return;     // Bad value.
00108   selected = new_sel;
00109   tell_server();      // Tell Exult.
00110   enable_controls();
00111   int chunknum = info[selected].num;
00112           // Remove prev. selection msg.
00113 //  gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00114   char buf[150];      // Show new selection.
00115   g_snprintf(buf, sizeof(buf), "Chunk %d", chunknum);
00116   gtk_statusbar_push(GTK_STATUSBAR(sbar), sbar_sel, buf);
00117   }
00118 
00119 /*
00120  *  Render as many chunks as fit in the chunk chooser window.
00121  */
00122 
00123 void Chunk_chooser::render
00124   (
00125   )
00126   {
00127           // Look for selected frame.
00128   int selchunk = -1, new_selected = -1;
00129   if (selected >= 0)    // Save selection info.
00130     selchunk = info[selected].num;
00131           // Remove "selected" message.
00132   //gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00133   delete [] info;     // Delete old info. list.
00134           // Get drawing area dimensions.
00135   gint winw = draw->allocation.width, winh = draw->allocation.height;
00136           // Provide more than enough room.
00137   info = new Chunk_info[256];
00138   info_cnt = 0;     // Count them.
00139           // Clear window first.
00140   iwin->fill8(255);   // Background color.
00141   int index = index0;
00142           // 16x16 tiles, each 8x8 pixels.
00143   const int chunkw = 128, chunkh = 128;
00144   int total_cnt = get_count();
00145   int y = border;
00146           // Show bottom if at least 1/2 vis.
00147   while (index < total_cnt && y + chunkh/2 + border <= winh)
00148     {
00149     int x = border;
00150     int cliph = y + chunkh <= winh ? chunkh : (winh - y);
00151     while (index < total_cnt && x + chunkw + border <= winw)
00152       {
00153       iwin->set_clip(x, y, chunkw, cliph);
00154       int chunknum = group ? (*group)[index] : index;
00155       render_chunk(chunknum, x, y);
00156       iwin->clear_clip();
00157           // Store info. about where drawn.
00158       info[info_cnt].set(chunknum, x, y, chunkw, chunkh);
00159       if (chunknum == selchunk)
00160             // Found the selected chunk.
00161         new_selected = info_cnt;
00162       info_cnt++;
00163       index++;    // Next chunk.
00164       x += chunkw + border;
00165       }
00166     y += chunkh + border;
00167     }
00168   if (new_selected == -1)
00169     unselect(false);
00170   else
00171     select(new_selected);
00172   }
00173 
00174 /*
00175  *  Read in desired chunk if not already read.
00176  *
00177  *  Output: ->chunk, stored in chunklist.
00178  */
00179 
00180 unsigned char *Chunk_chooser::get_chunk
00181   (
00182   int chunknum
00183   )
00184   {
00185   unsigned char *data = chunklist[chunknum];
00186   if (data)
00187     return data;    // Already have it.
00188           // Get from server.
00189   unsigned char buf[Exult_server::maxlength];
00190   unsigned char *ptr = &buf[0];
00191   unsigned char *newptr = &buf[0];
00192   Write2(ptr, chunknum);
00193   ExultStudio *studio = ExultStudio::get_instance();
00194   int server_socket = studio->get_server_socket();
00195   Exult_server::Msg_type id;  // Expect immediate answer.
00196   int datalen;
00197   if (!studio->send_to_server(Exult_server::send_terrain, 
00198               buf, ptr - buf) ||
00199     !Exult_server::wait_for_response(server_socket, 100) ||
00200     (datalen = Exult_server::Receive_data(server_socket, 
00201             id, buf, sizeof(buf))) == -1 ||
00202     id != Exult_server::send_terrain ||
00203     Read2(newptr) != chunknum)
00204     {     // No server?  Get from file.
00205     data = new unsigned char[512];
00206     chunklist[chunknum] = data;
00207     chunkfile.seekg(chunknum*512);
00208     chunkfile.read(reinterpret_cast<char *>(data), 512);
00209     if (!chunkfile.good())
00210       {
00211       memset(data, 0, 512);
00212       cout << "Error reading chunk file" << endl;
00213       }
00214     }
00215   else
00216     set_chunk(buf, datalen);
00217   return chunklist[chunknum];
00218   }
00219 
00220 /*
00221  *  Update #chunks.
00222  */
00223 
00224 void Chunk_chooser::update_num_chunks
00225   (
00226   int new_num_chunks
00227   )
00228   {
00229   num_chunks = new_num_chunks;
00230   GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(vscroll));
00231   adj->upper = num_chunks;
00232   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
00233   }
00234 
00235 /*
00236  *  Set chunk with data from 'Exult'.
00237  *
00238  *  NOTE:  Don't call 'show()' or 'render()' here, since this gets called
00239  *    from 'render()'.
00240  */
00241 
00242 void Chunk_chooser::set_chunk
00243   (
00244   unsigned char *data,    // Message from server.
00245   int datalen
00246   )
00247   {
00248   int tnum = Read2(data);   // First the terrain #.
00249   int new_num_chunks = Read2(data); // Always sends total.
00250   datalen -= 4;
00251   if (datalen != 512)
00252     {
00253     cout << "Set_chunk:  Wrong data length" << endl;
00254     return;
00255     }
00256   if (tnum < 0 || tnum >= new_num_chunks)
00257     {
00258     cout << "Set_chunk:  Bad terrain # (" << tnum <<
00259             ") received" << endl;
00260     return;
00261     }
00262   if (new_num_chunks != num_chunks)
00263     {     // Update total #.
00264     if (new_num_chunks > num_chunks)
00265       chunklist.resize(new_num_chunks);
00266     update_num_chunks(new_num_chunks);
00267     }
00268   unsigned char *chunk = chunklist[tnum];
00269   if (!chunk)     // Not read yet?
00270     chunk = chunklist[tnum] = new unsigned char[512];
00271   memcpy(chunk, data, 512); // Copy it in.
00272   }
00273 
00274 /*
00275  *  Render one chunk.
00276  */
00277 
00278 void Chunk_chooser::render_chunk
00279   (
00280   int chunknum,     // # to render.
00281   int xoff, int yoff    // Where to draw it in iwin.
00282   )
00283   {
00284   unsigned char *data = get_chunk(chunknum);
00285   int y = c_tilesize;
00286   for (int ty = 0; ty < c_tiles_per_chunk; ty++, y += c_tilesize)
00287     {
00288     int x = c_tilesize;
00289     for (int tx = 0; tx < c_tiles_per_chunk; tx++,
00290               x += c_tilesize)
00291       {
00292       unsigned char l = *data++;
00293       unsigned char h = *data++;
00294       int shapenum = l + 256*(h&0x3);
00295       int framenum = h >> 2;
00296       Shape_frame *s = ifile->get_shape(shapenum, framenum);
00297       if (s)
00298         s->paint(iwin, xoff + x - 1, yoff + y -1);
00299       }
00300     }
00301   }
00302   
00303 /*
00304  *  Get # shapes we can display.
00305  */
00306 
00307 int Chunk_chooser::get_count
00308   (
00309   )
00310   {
00311   return group ? group->size() : num_chunks;
00312   }
00313 
00314 /*
00315  *  Configure the viewing window.
00316  */
00317 
00318 gint Chunk_chooser::configure
00319   (
00320   GtkWidget *widget,    // The drawing area.
00321   GdkEventConfigure *event,
00322   gpointer data     // ->Chunk_chooser
00323   )
00324   {
00325   Chunk_chooser *chooser = (Chunk_chooser *) data;
00326   chooser->Shape_draw::configure();
00327   chooser->render();
00328           // Set new scroll amounts.
00329   int w = event->width, h = event->height;
00330   int per_row = (w - border)/(128 + border);
00331   int num_rows = (h - border)/(128 + border);
00332   int page_size = per_row*num_rows;
00333   GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(
00334             chooser->vscroll));
00335   adj->step_increment = per_row;
00336   adj->page_increment = page_size;
00337   adj->page_size = page_size;
00338   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
00339   if (chooser->group)   // Filtering?
00340     chooser->enable_drop(); // Can drop chunks here.
00341 
00342   return (TRUE);
00343   }
00344 
00345 
00346 /*
00347  *  Handle an expose event.
00348  */
00349 
00350 gint Chunk_chooser::expose
00351   (
00352   GtkWidget *widget,    // The view window.
00353   GdkEventExpose *event,
00354   gpointer data     // ->Chunk_chooser.
00355   )
00356   {
00357   Chunk_chooser *chooser = (Chunk_chooser *) data;
00358   chooser->show(event->area.x, event->area.y, event->area.width,
00359               event->area.height);
00360   return (TRUE);
00361   }
00362 
00363 /*
00364  *  Handle a mouse button press event.
00365  */
00366 
00367 #ifdef WIN32
00368 
00369 static bool win32_button = false;
00370 
00371 gint Chunk_chooser::win32_drag_motion
00372   (
00373   GtkWidget *widget,    // The view window.
00374   GdkEventMotion *event,
00375   gpointer data     // ->Chunk_chooser.
00376   )
00377   {
00378     if (win32_button)
00379     {
00380     win32_button = false;
00381 
00382     // prepare the dragged data
00383     windragdata wdata;
00384 
00385     // This call allows us to recycle the data transfer initialization code.
00386     //  It's clumsy, but far easier to maintain.
00387     drag_data_get(NULL, NULL, (GtkSelectionData *) &wdata,
00388       U7_TARGET_CHUNKID, 0, data);
00389 
00390     POINT pnt;
00391     GetCursorPos(&pnt);
00392 
00393     LPDROPSOURCE idsrc = (LPDROPSOURCE) new Windropsource(0, 
00394       pnt.x, pnt.y);
00395     LPDATAOBJECT idobj = (LPDATAOBJECT) new Winstudioobj(wdata);
00396     DWORD dndout;
00397 
00398     HRESULT res = DoDragDrop(idobj, idsrc, DROPEFFECT_COPY, &dndout);
00399     if (FAILED(res)) {
00400       g_warning ("Oops! Something is wrong with OLE2 DnD..");
00401     }
00402 
00403     delete idsrc;
00404     idobj->Release(); // Not sure if we really need this. However, it doesn't hurt either.
00405     }
00406 
00407   return true;
00408   };
00409 
00410 #else
00411 gint Chunk_chooser::drag_motion
00412   (
00413   GtkWidget *widget,    // The view window.
00414   GdkEventMotion *event,
00415   gpointer data     // ->Shape_chooser.
00416   )
00417   {
00418   Chunk_chooser *chooser = (Chunk_chooser *) data;
00419   if (!chooser->dragging && chooser->selected >= 0)
00420     chooser->start_drag(U7_TARGET_CHUNKID_NAME, 
00421       U7_TARGET_CHUNKID, (GdkEvent *) event);
00422   return true;
00423   }
00424 #endif
00425 
00426 gint Chunk_chooser::mouse_press
00427   (
00428   GtkWidget *widget,    // The view window.
00429   GdkEventButton *event,
00430   gpointer data     // ->Chunk_chooser.
00431   )
00432   {
00433   Chunk_chooser *chooser = (Chunk_chooser *) data;
00434 
00435     if (event->button == 4) {
00436         chooser->scroll(true);
00437         return(TRUE);
00438     } else if (event->button == 5) {
00439         chooser->scroll(false);
00440         return(TRUE);
00441     }
00442 
00443   int old_selected = chooser->selected;
00444   int i;        // Search through entries.
00445   for (i = 0; i < chooser->info_cnt; i++)
00446     if (chooser->info[i].box.has_point(
00447           (int) event->x, (int) event->y))
00448       {   // Found the box?
00449 //      if (i == old_selected)
00450 //        return TRUE;
00451           // Indicate we can dra.
00452 #ifdef WIN32
00453 // Here, we have to override GTK+'s Drag and Drop, which is non-OLE and
00454 // usually stucks outside the program window. I think it's because
00455 // the dragged shape only receives mouse motion events when the new mouse pointer
00456 // position is *still* inside the shape. So if you move the mouse too fast,
00457 // we are stuck.
00458       win32_button = true;
00459 #endif
00460 
00461       chooser->selected = i;
00462       chooser->locate_cx = chooser->locate_cy = -1;
00463       chooser->render();
00464       chooser->show();
00465           // Tell client.
00466       if (chooser->sel_changed)
00467         (*chooser->sel_changed)();
00468       break;
00469       }
00470   if (i == chooser->info_cnt && event->button == 1)
00471     chooser->unselect(true);// Nothing under mouse.
00472   else if (event->button == 3)
00473     gtk_menu_popup(GTK_MENU(chooser->create_popup()), 0, 0, 0, 0, 
00474           event->button, event->time);
00475   return (TRUE);
00476   }
00477 
00478 /*
00479  *  Handle a mouse button-release event.
00480  */
00481 static gint Mouse_release
00482   (
00483   GtkWidget *widget,    // The view window.
00484   GdkEventButton *event,
00485   gpointer data     // ->Shape_chooser.
00486   )
00487   {
00488   Chunk_chooser *chooser = (Chunk_chooser *) data;
00489   chooser->mouse_up();
00490   }
00491 
00492 /*
00493  *  Someone wants the dragged chunk.
00494  */
00495 
00496 void Chunk_chooser::drag_data_get
00497   (
00498   GtkWidget *widget,    // The view window.
00499   GdkDragContext *context,
00500   GtkSelectionData *seldata,  // Fill this in.
00501   guint info,
00502   guint time,
00503   gpointer data     // ->Chunk_chooser.
00504   )
00505   {
00506   cout << "In DRAG_DATA_GET" << endl;
00507   Chunk_chooser *chooser = (Chunk_chooser *) data;
00508   if (chooser->selected < 0 || info != U7_TARGET_CHUNKID)
00509     return;     // Not sure about this.
00510   guchar buf[30];
00511   Chunk_info& shinfo = chooser->info[chooser->selected];
00512   int len = Store_u7_chunkid(buf, shinfo.num);
00513   cout << "Setting selection data (" << shinfo.num << ')' << endl;
00514 #ifdef WIN32
00515   windragdata *wdata = (windragdata *)seldata;
00516   wdata->assign(info, len, buf);
00517 #else
00518           // Make us owner of xdndselection.
00519   gtk_selection_owner_set(widget, gdk_atom_intern("XdndSelection", 0),
00520                 time);
00521           // Set data.
00522   gtk_selection_data_set(seldata,
00523       gdk_atom_intern(U7_TARGET_CHUNKID_NAME, 0),
00524                                         8, buf, len);
00525 #endif
00526   }
00527 
00528 /*
00529  *  Another app. has claimed the selection.
00530  */
00531 
00532 gint Chunk_chooser::selection_clear
00533   (
00534   GtkWidget *widget,    // The view window.
00535   GdkEventSelection *event,
00536   gpointer data     // ->Chunk_chooser.
00537   )
00538   {
00539 //  Chunk_chooser *chooser = (Chunk_chooser *) data;
00540   cout << "SELECTION_CLEAR" << endl;
00541   return TRUE;
00542   }
00543 
00544 /*
00545  *  Beginning of a drag.
00546  */
00547 
00548 gint Chunk_chooser::drag_begin
00549   (
00550   GtkWidget *widget,    // The view window.
00551   GdkDragContext *context,
00552   gpointer data     // ->Chunk_chooser.
00553   )
00554   {
00555   cout << "In DRAG_BEGIN" << endl;
00556   Chunk_chooser *chooser = (Chunk_chooser *) data;
00557   if (chooser->selected < 0)
00558     return FALSE;   // ++++Display a halt bitmap.
00559 #if 0
00560           // Get ->chunk.
00561   Chunk_info& shinfo = chooser->info[chooser->selected];
00562   Chunk_frame *chunk = chooser->ifile->get_chunk(shinfo.chunknum, 
00563               shinfo.framenum);
00564   if (!chunk)
00565     return FALSE;
00566   int w = chunk->get_width(), h = chunk->get_height(),
00567     xright = chunk->get_xright(), ybelow = chunk->get_ybelow();
00568   Image_buffer8 tbuf(w, h); // Create buffer to render to.
00569   tbuf.fill8(0xff);   // Fill with 'transparent' pixel.
00570   unsigned char *tbits = tbuf.get_bits();
00571   chunk->paint(&tbuf, w - 1 - xright, h - 1 - ybelow);
00572           // Put chunk on a pixmap.
00573   GdkPixmap *pixmap = gdk_pixmap_new(widget->window, w, h, -1);
00574   gdk_draw_indexed_image(pixmap, chooser->drawgc, 0, 0, w, h,
00575       GDK_RGB_DITHER_NORMAL, tbits,
00576       tbuf.get_line_width(), chooser->palette);
00577   int mask_stride = (w + 7)/8;  // Round up to nearest byte.
00578   char *mdata = new char[mask_stride*h];
00579   for (int y = 0; y < h; y++) // Do each row.
00580           // Do each byte.
00581     for (int b = 0; b < mask_stride; b++)
00582       {
00583       char bits = 0;
00584       unsigned char *vals = tbits + y*w + b*8;
00585       for (int i = 0; i < 8; i++)
00586         if (vals[i] != 0xff)
00587           bits |= (1<<i);
00588       mdata[y*mask_stride + b] = bits;
00589       }
00590   GdkBitmap *mask = gdk_bitmap_create_from_data(widget->window,
00591               mdata, w, h);
00592   delete mdata;
00593           // This will be the chunk dragged.
00594   gtk_drag_set_icon_pixmap(context,
00595       gdk_window_get_colormap(widget->window), pixmap, mask,
00596           w - 2 - xright, h - 2 - ybelow);
00597   gdk_pixmap_unref(pixmap);
00598   gdk_bitmap_unref(mask);
00599 #endif
00600   return TRUE;
00601   }
00602 
00603 /*
00604  *  Chunk was dropped here.
00605  */
00606 
00607 void Chunk_chooser::drag_data_received
00608   (
00609   GtkWidget *widget,
00610   GdkDragContext *context,
00611   gint x,
00612   gint y,
00613   GtkSelectionData *seldata,
00614   guint info,
00615   guint time,
00616   gpointer udata      // Should point to Shape_draw.
00617   )
00618   {
00619   Chunk_chooser *chooser = (Chunk_chooser *) udata;
00620   cout << "Chunk drag_data_received" << endl;
00621   if (seldata->type == gdk_atom_intern(U7_TARGET_CHUNKID_NAME, 0) &&
00622       seldata->format == 8 && seldata->length > 0)
00623     {
00624     int cnum;
00625     Get_u7_chunkid(seldata->data, cnum);
00626     chooser->group->add(cnum);
00627     chooser->render();
00628 //    chooser->adjust_scrollbar(); ++++++Probably need to do this.
00629     }
00630   }
00631 
00632 /*
00633  *  Set to accept drops from drag-n-drop of a chunk.
00634  */
00635 
00636 void Chunk_chooser::enable_drop
00637   (
00638   )
00639   {
00640   if (drop_enabled)   // More than once causes warning.
00641     return;
00642   drop_enabled = true;
00643   gtk_widget_realize(draw);//???????
00644 #ifndef WIN32
00645   GtkTargetEntry tents[1];
00646   tents[0].target = U7_TARGET_CHUNKID_NAME;
00647   tents[0].flags = 0;
00648   tents[0].info = U7_TARGET_CHUNKID;
00649   gtk_drag_dest_set(draw, GTK_DEST_DEFAULT_ALL, tents, 1,
00650       (GdkDragAction) (GDK_ACTION_COPY | GDK_ACTION_MOVE));
00651 
00652   gtk_signal_connect(GTK_OBJECT(draw), "drag_data_received",
00653         GTK_SIGNAL_FUNC(drag_data_received), this);
00654 #endif
00655   }
00656 
00657 /*
00658  *  Scroll to a new chunk/frame.
00659  */
00660 
00661 void Chunk_chooser::scroll
00662   (
00663   int newindex      // Abs. index of leftmost to show.
00664   )
00665   {
00666   int total = get_count();
00667   if (index0 < newindex)  // Going forwards?
00668     index0 = newindex < total ? newindex : total;
00669   else if (index0 > newindex) // Backwards?
00670     index0 = newindex >= 0 ? newindex : 0;
00671   render();
00672   show();
00673   }
00674 
00675 /*
00676  *  Scroll up/down by one row.
00677  */
00678 
00679 void Chunk_chooser::scroll
00680   (
00681   bool upwards
00682   )
00683   {
00684   GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(vscroll));
00685   float delta = adj->step_increment;
00686   if (upwards)
00687     delta = -delta;
00688   adj->value += delta;
00689   gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
00690   scroll((gint) adj->value);
00691   }
00692 
00693 /*
00694  *  Handle a scrollbar event.
00695  */
00696 
00697 void Chunk_chooser::scrolled
00698   (
00699   GtkAdjustment *adj,   // The adjustment.
00700   gpointer data     // ->Chunk_chooser.
00701   )
00702   {
00703   Chunk_chooser *chooser = (Chunk_chooser *) data;
00704 cout << "Scrolled to " << adj->value << '\n';
00705   gint newindex = (gint) adj->value;
00706   chooser->scroll(newindex);
00707   }
00708 
00709 /*
00710  *  Enable/disable controls after selection changed.
00711  */
00712 
00713 void Chunk_chooser::enable_controls
00714   (
00715   )
00716   {
00717   if (selected == -1)   // No selection.
00718     {
00719     gtk_widget_set_sensitive(loc_down, false);
00720     gtk_widget_set_sensitive(loc_up, false);
00721     if (!group)
00722       {
00723       gtk_widget_set_sensitive(move_down, false);
00724       gtk_widget_set_sensitive(move_up, false);
00725       }
00726     return;
00727     }
00728   gtk_widget_set_sensitive(loc_down, true);
00729   gtk_widget_set_sensitive(loc_up, true);
00730   if (!group)
00731     {
00732     gtk_widget_set_sensitive(move_down, 
00733           info[selected].num < num_chunks - 1);
00734     gtk_widget_set_sensitive(move_up, 
00735           info[selected].num > 0);
00736     }
00737   }
00738 
00739 /*
00740  *  Handle popup menu items.
00741  */
00742 
00743 static void on_insert_empty
00744   (
00745   GtkMenuItem *item,
00746   gpointer udata
00747   )
00748   {
00749   Chunk_chooser *chooser = (Chunk_chooser *) udata;
00750   chooser->insert(false);
00751   }
00752 
00753 static void on_insert_dup
00754   (
00755   GtkMenuItem *item,
00756   gpointer udata
00757   )
00758   {
00759   Chunk_chooser *chooser = (Chunk_chooser *) udata;
00760   chooser->insert(true);
00761   }
00762 static void on_delete
00763   (
00764   GtkMenuItem *item,
00765   gpointer udata
00766   )
00767   {
00768   Chunk_chooser *chooser = (Chunk_chooser *) udata;
00769   chooser->del();
00770   }
00771 
00772 /*
00773  *  Set up popup menu.
00774  */
00775 
00776 GtkWidget *Chunk_chooser::create_popup
00777   (
00778   )
00779   {
00780   ExultStudio *studio = ExultStudio::get_instance();
00781   Object_browser::create_popup(); // Create popup with groups, files.
00782   if (group != 0)     // Filtering?  Skip the rest.
00783     return popup;
00784   GtkWidget *mitem = Add_menu_item(popup, "New...");
00785   GtkWidget *new_menu = gtk_menu_new();
00786   gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), new_menu);
00787   Add_menu_item(new_menu, "Empty", GTK_SIGNAL_FUNC(on_insert_empty), 
00788                   this);
00789   if (selected >= 0)
00790     {
00791     Add_menu_item(new_menu, "Duplicate", 
00792           GTK_SIGNAL_FUNC(on_insert_dup), this);
00793     Add_menu_item(popup, "Delete",
00794           GTK_SIGNAL_FUNC(on_delete), this);
00795     }
00796   return popup;
00797   }
00798 
00799 /*
00800  *  Create the list.
00801  */
00802 
00803 Chunk_chooser::Chunk_chooser
00804   (
00805   Vga_file *i,      // Where they're kept.
00806   std::istream& cfile,    // Chunks file (512bytes/entry).
00807   unsigned char *palbuf,    // Palette, 3*256 bytes (rgb triples).
00808   int w, int h,     // Dimensions.
00809   Shape_group *g      // Filter, or null.
00810   ) : Object_browser(g), Shape_draw(i, palbuf, gtk_drawing_area_new()),
00811     chunkfile(cfile), 
00812     info(0), info_cnt(0), sel_changed(0),
00813     locate_cx(-1), locate_cy(-1), drop_enabled(false), to_del(-1)
00814   {
00815   chunkfile.seekg(0, std::ios::end);  // Figure total #chunks.
00816   num_chunks = chunkfile.tellg()/(c_tiles_per_chunk*c_tiles_per_chunk*2);
00817   chunklist.resize(num_chunks); // Init. list of ->'s to chunks.
00818           // Put things in a vert. box.
00819   GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
00820   set_widget(vbox); // This is our "widget"
00821   gtk_widget_show(vbox);
00822   
00823   GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
00824   gtk_widget_show(hbox);
00825   gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
00826   
00827           // A frame looks nice.
00828   GtkWidget *frame = gtk_frame_new(NULL);
00829   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
00830   gtk_widget_show(frame);
00831   gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
00832           // NOTE:  draw is in Shape_draw.
00833           // Indicate the events we want.
00834   gtk_widget_set_events(draw, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
00835     | GDK_BUTTON_RELEASE_MASK
00836     | GDK_POINTER_MOTION_HINT_MASK |
00837     GDK_BUTTON1_MOTION_MASK);
00838           // Set "configure" handler.
00839   gtk_signal_connect(GTK_OBJECT(draw), "configure_event",
00840         GTK_SIGNAL_FUNC(configure), this);
00841           // Set "expose" handler.
00842   gtk_signal_connect(GTK_OBJECT(draw), "expose_event",
00843         GTK_SIGNAL_FUNC(expose), this);
00844           // Set mouse click handler.
00845   gtk_signal_connect(GTK_OBJECT(draw), "button_press_event",
00846         GTK_SIGNAL_FUNC(mouse_press), this);
00847   gtk_signal_connect(GTK_OBJECT(draw), "button_release_event",
00848         GTK_SIGNAL_FUNC(Mouse_release), this);
00849           // Mouse motion.
00850   gtk_signal_connect(GTK_OBJECT(draw), "drag_begin",
00851         GTK_SIGNAL_FUNC(drag_begin), this);
00852 #ifdef WIN32
00853 // required to override GTK+ Drag and Drop
00854   gtk_signal_connect(GTK_OBJECT(draw), "motion_notify_event",
00855         GTK_SIGNAL_FUNC(win32_drag_motion), this);
00856 #else
00857   gtk_signal_connect(GTK_OBJECT(draw), "motion_notify_event",
00858         GTK_SIGNAL_FUNC(drag_motion), this);
00859 #endif
00860   gtk_signal_connect (GTK_OBJECT(draw), "drag_data_get",
00861         GTK_SIGNAL_FUNC(drag_data_get), this);
00862   gtk_signal_connect (GTK_OBJECT(draw), "selection_clear_event",
00863         GTK_SIGNAL_FUNC(selection_clear), this);
00864   gtk_container_add (GTK_CONTAINER (frame), draw);
00865   gtk_drawing_area_size(GTK_DRAWING_AREA(draw), w, h);
00866   gtk_widget_show(draw);
00867           // Want a scrollbar for the chunks.
00868   GtkObject *chunk_adj = gtk_adjustment_new(0, 0, 
00869         num_chunks, 1, 
00870         4, 1.0);
00871   vscroll = gtk_vscrollbar_new(GTK_ADJUSTMENT(chunk_adj));
00872           // Update window when it stops.
00873   gtk_range_set_update_policy(GTK_RANGE(vscroll),
00874           GTK_UPDATE_DELAYED);
00875   gtk_box_pack_start(GTK_BOX(hbox), vscroll, FALSE, TRUE, 0);
00876           // Set scrollbar handler.
00877   gtk_signal_connect(GTK_OBJECT(chunk_adj), "value_changed",
00878           GTK_SIGNAL_FUNC(scrolled), this);
00879   gtk_widget_show(vscroll);
00880           // At the bottom, status bar:
00881   GtkWidget *hbox1 = gtk_hbox_new(FALSE, 0);
00882   gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 0);
00883   gtk_widget_show(hbox1);
00884           // At left, a status bar.
00885   sbar = gtk_statusbar_new();
00886   sbar_sel = gtk_statusbar_get_context_id(GTK_STATUSBAR(sbar),
00887               "selection");
00888   gtk_box_pack_start(GTK_BOX(hbox1), sbar, TRUE, TRUE, 0);
00889   gtk_widget_show(sbar);
00890           // Add locate/move controls to bottom.
00891   gtk_box_pack_start(GTK_BOX(vbox), 
00892     create_controls(locate_controls|(!group ? move_controls : 0)),
00893               FALSE, FALSE, 0);
00894   }
00895 
00896 /*
00897  *  Delete.
00898  */
00899 
00900 Chunk_chooser::~Chunk_chooser
00901   (
00902   )
00903   {
00904   gtk_widget_destroy(get_widget());
00905   delete [] info;
00906   int i;
00907   for (i = 0; i < num_chunks; i++)// Delete all the chunks.
00908     delete chunklist[i];
00909   }
00910 
00911 /*
00912  *  Handle response from server.
00913  *
00914  *  Output: true if handled here.
00915  */
00916 
00917 bool Chunk_chooser::server_response
00918   (
00919   int id,
00920   unsigned char *data,
00921   int datalen
00922   )
00923   {
00924   switch ((Exult_server::Msg_type) id)
00925     {
00926   case Exult_server::locate_terrain:
00927     locate_response(data, datalen);
00928     return true;
00929   case Exult_server::insert_terrain:
00930     insert_response(data, datalen);
00931     return true;
00932   case Exult_server::delete_terrain:
00933     delete_response(data, datalen);
00934     return true;
00935   case Exult_server::swap_terrain:
00936     swap_response(data, datalen);
00937     return true;
00938   case Exult_server::send_terrain:
00939     set_chunk(data, datalen);
00940     render();
00941     show();
00942     return true;
00943   default:
00944     return false;
00945     }
00946   }
00947 
00948 /*
00949  *  Done with terrain editing.
00950  */
00951 
00952 void Chunk_chooser::end_terrain_editing
00953   (
00954   )
00955   {
00956           // Clear out cache of chunks.
00957   for (int i = 0; i < num_chunks; i++)
00958     {
00959     delete chunklist[i];
00960     chunklist[i] = 0;
00961     }
00962   render();
00963   show();
00964   }
00965 
00966 /*
00967  *  Unselect.
00968  */
00969 
00970 void Chunk_chooser::unselect
00971   (
00972   bool need_render      // 1 to render and show.
00973   )
00974   {
00975   if (selected >= 0)
00976     {
00977     selected = -1;
00978     locate_cx = locate_cy = -1;
00979     if (need_render)
00980       {
00981       render();
00982       show();
00983       }
00984     if (sel_changed)  // Tell client.
00985       (*sel_changed)();
00986     }
00987   enable_controls();    // Enable/disable controls.
00988   char buf[150];      // Show new selection.
00989   if (info_cnt > 0)
00990     {
00991 //    gtk_statusbar_pop(GTK_STATUSBAR(sbar), sbar_sel);
00992     g_snprintf(buf, sizeof(buf), "Chunks %d to %d",
00993       info[0].num, info[info_cnt - 1].num);
00994     gtk_statusbar_push(GTK_STATUSBAR(sbar), sbar_sel, buf);
00995     }
00996   }
00997 
00998 
00999 /*
01000  *  Locate terrain on game map.
01001  */
01002 
01003 void Chunk_chooser::locate
01004   (
01005   int dir       // 1=downwards, -1=upwards, 0=from top.
01006   )
01007   {
01008   if (selected < 0)
01009     return;     // Shouldn't happen.
01010   bool upwards = false;
01011   unsigned char data[Exult_server::maxlength];
01012   unsigned char *ptr = &data[0];
01013   int tnum = info[selected].num;  // Terrain #.
01014   int cx = locate_cx, cy = locate_cy;
01015   if (dir == 0)
01016     {
01017     cx = cy = -1;
01018     }
01019   else if (dir == -1)
01020     upwards = true;
01021   Write2(ptr, tnum);
01022   Write2(ptr, cx);    // Current chunk, or -1.
01023   Write2(ptr, cy);
01024   *ptr++ = upwards ? 1 : 0;
01025   ExultStudio *studio = ExultStudio::get_instance();
01026   if (!studio->send_to_server(
01027       Exult_server::locate_terrain, data, ptr - data))
01028     to_del = -1;    // In case we're deleting.
01029   }
01030 
01031 void Chunk_chooser::locate
01032   (
01033   bool upwards
01034   )
01035   {
01036   locate(upwards ? -1 : 1);
01037   }
01038 
01039 /*
01040  *  Response from server to a 'locate'.
01041  */
01042 
01043 void Chunk_chooser::locate_response
01044   (
01045   unsigned char *data,
01046   int datalen
01047   )
01048   {
01049   unsigned char *ptr = data;
01050   int tnum = Read2(ptr);
01051   if (selected < 0 || tnum != info[selected].num)
01052     {
01053     to_del = -1;
01054     return;     // Not the current selection.
01055     }
01056   short cx = (short) Read2(ptr);  // Get chunk found.
01057   short cy = (short) Read2(ptr);
01058   ptr++;        // Skip upwards flag.
01059   if (!*ptr)
01060     {
01061     if (to_del >= 0 && to_del == tnum)
01062       {
01063       unsigned char data[Exult_server::maxlength];
01064       unsigned char *ptr = &data[0];
01065       Write2(ptr, tnum);
01066       ExultStudio *studio = ExultStudio::get_instance();
01067       studio->send_to_server(Exult_server::delete_terrain, 
01068               data, ptr - data);
01069       }
01070     else
01071       EStudio::Alert("Terrain %d not found.", tnum);
01072     }
01073   else
01074     {
01075     locate_cx = cx;   // Save new chunk.
01076     locate_cy = cy;
01077     if (to_del >= 0)
01078       EStudio::Alert("Terrain %d is still in use", tnum);
01079     }
01080   to_del = -1;
01081   }
01082 
01083 /*
01084  *  Insert a new chunk terrain into the list.
01085  */
01086 
01087 void Chunk_chooser::insert
01088   (
01089   bool dup
01090   )
01091   {
01092   if (dup && selected < 0)
01093     return;     // Shouldn't happen.
01094   unsigned char data[Exult_server::maxlength];
01095   unsigned char *ptr = &data[0];
01096   int tnum = selected >= 0 ? info[selected].num : -1;
01097   Write2(ptr, tnum);
01098   *ptr++ = dup ? 1 : 0;
01099   ExultStudio *studio = ExultStudio::get_instance();
01100   studio->send_to_server(
01101       Exult_server::insert_terrain, data, ptr - data);
01102   }
01103 
01104 /*
01105  *  Delete currently selected chunk if it's not being used.
01106  */
01107 
01108 void Chunk_chooser::del
01109   (
01110   )
01111   {
01112   if (selected < 0)
01113     return; 
01114   to_del = info[selected].num;
01115   locate(0);      // See if it exists.
01116   }
01117 
01118 /*
01119  *  Response from server to an 'insert'.
01120  */
01121 
01122 void Chunk_chooser::insert_response
01123   (
01124   unsigned char *data,
01125   int datalen
01126   )
01127   {
01128   unsigned char *ptr = data;
01129   int tnum = (short) Read2(ptr);
01130   bool dup = *ptr++ ? true : false;
01131   bool okay = *ptr ? true : false;
01132   if (!*ptr)
01133     EStudio::Alert("Terrain insert failed.");
01134   else
01135     {     // Insert in our list.
01136     unsigned char *data = new unsigned char[512];
01137     if (dup && tnum >= 0 && tnum < num_chunks && chunklist[tnum])
01138       memcpy(data, chunklist[tnum], 512);
01139     else
01140       memset(data, 0, 512);
01141     if (tnum >= 0 && tnum < num_chunks - 1)
01142       chunklist.insert(chunklist.begin() + tnum + 1, data);
01143     else      // If -1, append to end.
01144       chunklist.push_back(data);
01145     update_num_chunks(num_chunks + 1);
01146     render();
01147     show();
01148     }
01149   }
01150 
01151 /*
01152  *  Response from server to an 'delete'.
01153  */
01154 
01155 void Chunk_chooser::delete_response
01156   (
01157   unsigned char *data,
01158   int datalen
01159   )
01160   {
01161   unsigned char *ptr = data;
01162   int tnum = (short) Read2(ptr);
01163   bool okay = *ptr ? true : false;
01164   if (!*ptr)
01165     EStudio::Alert("Terrain delete failed.");
01166   else
01167     {     // Remove from our list.
01168     delete chunklist[tnum];
01169     chunklist.erase(chunklist.begin() + tnum);
01170     update_num_chunks(num_chunks - 1);
01171     render();
01172     show();
01173     }
01174   }
01175 
01176 /*
01177  *  Move currently-selected chunk up or down.
01178  */
01179 
01180 void Chunk_chooser::move
01181   (
01182   bool upwards
01183   )
01184   {
01185   if (selected < 0)
01186     return;     // Shouldn't happen.
01187   unsigned char data[Exult_server::maxlength];
01188   unsigned char *ptr = &data[0];
01189   int tnum = info[selected].num;
01190   if ((tnum == 0 && upwards) || (tnum == num_chunks - 1 && !upwards))
01191     return;
01192   if (upwards)      // Going to swap tnum & tnum+1.
01193     tnum--;
01194   Write2(ptr, tnum);
01195   ExultStudio *studio = ExultStudio::get_instance();
01196   studio->send_to_server(
01197       Exult_server::swap_terrain, data, ptr - data);
01198   }
01199 
01200 /*
01201  *  Response from server to a 'swap'.
01202  */
01203 
01204 void Chunk_chooser::swap_response
01205   (
01206   unsigned char *data,
01207   int datalen
01208   )
01209   {
01210   unsigned char *ptr = data;
01211   int tnum = (short) Read2(ptr);
01212   bool okay = *ptr ? true : false;
01213   if (!*ptr)
01214     cout << "Terrain insert failed." << endl;
01215   else if (tnum >= 0 && tnum < num_chunks - 1)
01216     {
01217     unsigned char *tmp = get_chunk(tnum);
01218     chunklist[tnum] = get_chunk(tnum + 1);
01219     chunklist[tnum + 1] = tmp;
01220     if (selected >= 0)  // Update selected.
01221       {
01222       if (info[selected].num == tnum)
01223         { // Moving downwards.
01224         if (selected >= info_cnt - 1)
01225           scroll(false);
01226         select(selected + 1);
01227         }
01228       else if (info[selected].num == tnum + 1)
01229         {
01230         if (selected <= 0)
01231           scroll(true);
01232         select(selected - 1);
01233         }
01234       }
01235     render();
01236     show();
01237     }
01238   }

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