1 // CoMET - The Crimson Fields Map Editing Tool
2 // Copyright (C) 2002-2007 Jens Granseuer
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18 
19 ////////////////////////////////////////////////////////////////////////
20 // edwindow.cpp
21 ////////////////////////////////////////////////////////////////////////
22 
23 #include "edwindow.h"
24 #include "extwindow.h"
25 #include "extwindow2.h"
26 #include "gamewindow.h"
27 #include "filewindow.h"
28 #include "gfxwidget.h"
29 #include "fileio.h"
30 #include "strutil.h"
31 #include "main.h"
32 
33 ////////////////////////////////////////////////////////////////////////
34 // NAME       : EdWindow::EdWindow
35 // DESCRIPTION: Create the editing window.
36 // PARAMETERS : mapdir - directory containing the map files
37 //              view   - pointer to the view to use
38 // RETURNS    : -
39 ////////////////////////////////////////////////////////////////////////
40 
EdWindow(const string & mapdir,View * view)41 EdWindow::EdWindow( const string &mapdir, View *view ) :
42           Window( view->x, view->y, view->w, view->h, 0, view ),
43           mission(NULL),
44           panel( view->w - DEFAULT_TILE_WIDTH * 4 - DEFAULT_SLIDER_SIZE - 20,
45                  view->y, DEFAULT_TILE_WIDTH * 4 + DEFAULT_SLIDER_SIZE + 20, view->h ),
46           mv( this,
47               Rect( DEFAULT_SLIDER_SIZE, 0,
48               w - panel.w - DEFAULT_SLIDER_SIZE, h - DEFAULT_SLIDER_SIZE),
49               MV_DISABLE|MV_DISABLE_FOG ),
50           mode(ED_MODE_VIEW), selected(-1), mapdir(mapdir) {
51 
52   unsigned short height = MIN( DEFAULT_TILE_HEIGHT * 6 + 4, h / 3 - 10 );
53   unit_wd = new UnitWidget( L_ID_UNITS, panel.x + 5,
54                             panel.y + panel.h - 6 - height,
55                             panel.w - 10, height, 0, NULL, this, NULL );
56   unit_wd->SetHook( this );
57 
58   tile_wd = new TileWidget( L_ID_TILES, unit_wd->x,
59                             panel.y + sfont->Height() + 15,
60                             unit_wd->w, panel.h - height - sfont->Height() - 30,
61                             0, NULL, this, NULL );
62   tile_wd->SetHook( this );
63 
64   ud_wd = new SliderWidget( S_ID_VERT, 0, 0, DEFAULT_SLIDER_SIZE, mv.Height(),
65                             0, 0, 0, 1, WIDGET_VSCROLL, NULL, this );
66   ud_wd->SetHook( this );
67   lr_wd = new SliderWidget( S_ID_HORIZ, DEFAULT_SLIDER_SIZE, h - DEFAULT_SLIDER_SIZE,
68                             mv.Width(), DEFAULT_SLIDER_SIZE,
69                             0, 0, 0, 1, WIDGET_HSCROLL, NULL, this );
70   lr_wd->SetHook( this );
71 }
72 
73 ////////////////////////////////////////////////////////////////////////
74 // NAME       : EdWindow::~EdWindow
75 // DESCRIPTION: Delete the editing window.
76 // PARAMETERS : -
77 // RETURNS    : -
78 ////////////////////////////////////////////////////////////////////////
79 
~EdWindow(void)80 EdWindow::~EdWindow( void ) {
81   delete mission;
82 }
83 
84 ////////////////////////////////////////////////////////////////////////
85 // NAME       : EdWindow::Draw
86 // DESCRIPTION: Draw the editing window.
87 // PARAMETERS : -
88 // RETURNS    : -
89 ////////////////////////////////////////////////////////////////////////
90 
Draw(void)91 void EdWindow::Draw( void ) {
92   mv.Draw();
93   DrawBack( panel );
94   DrawBox( panel, BOX_RAISED );
95 
96   Widget *wd = widgets;
97   while ( wd ) {
98     if ( !wd->Hidden() ) wd->Draw();
99     wd = wd->next;
100   }
101 
102   PrintCursorPos();
103 }
104 
105 ////////////////////////////////////////////////////////////////////////
106 // NAME       : EdWindow::PrintCursorPos
107 // DESCRIPTION: Print current cursor position to the panel.
108 // PARAMETERS : -
109 // RETURNS    : -
110 ////////////////////////////////////////////////////////////////////////
111 
PrintCursorPos(void)112 void EdWindow::PrintCursorPos( void ) {
113   Rect coord( panel.x + 5, panel.y + 5, panel.w - 10, sfont->Height() );
114   char posbuf[16];
115   DrawBack( coord );
116 
117   if ( mission ) {
118     sprintf( posbuf, "X/Y: %d/%d", mv.Cursor().x, mv.Cursor().y );
119     sfont->Write( posbuf, this, coord.x, coord.y );
120 
121     MapObject *obj = mission->GetMap().GetMapObject( mv.Cursor() );
122     if ( obj && obj->Name() ) {
123       string name = StringUtil::strprintf( "(%s)", obj->Name() );
124       sfont->WriteEllipsis( name.c_str(), this,
125              coord.x + sfont->TextWidth( posbuf ) + 5, coord.y, coord );
126     }
127   }
128 }
129 
130 ////////////////////////////////////////////////////////////////////////
131 // NAME       : EdWindow::HandleEvent
132 // DESCRIPTION: React on user input.
133 // PARAMETERS : event - event received by the event handler
134 // RETURNS    : GUI status
135 ////////////////////////////////////////////////////////////////////////
136 
HandleEvent(const SDL_Event & event)137 GUI_Status EdWindow::HandleEvent( const SDL_Event &event ) {
138 
139   if ( !mission ) {
140     if ( event.type == SDL_KEYDOWN )
141       ShowContextMenu( Point(-1, -1) );
142     else if ( event.type == SDL_MOUSEBUTTONDOWN )
143       ShowContextMenu( Point(event.button.x - x, event.button.y - y) );
144 
145     return GUI_OK;
146   }
147 
148   GUI_Status rc = Window::HandleEvent( event );
149   if ( rc == GUI_OK ) {
150 
151     // check for keyboard commands
152     if ( event.type == SDL_KEYDOWN ) {
153 
154       switch ( event.key.keysym.sym ) {
155       case SDLK_KP1: case SDLK_KP2: case SDLK_KP3:
156       case SDLK_KP4: case SDLK_KP6: case SDLK_KP7:
157       case SDLK_KP8: case SDLK_KP9:
158       case SDLK_LEFT: case SDLK_RIGHT: case SDLK_UP: case SDLK_DOWN:
159         MoveCursor( event.key.keysym.sym );
160         break;
161       case SDLK_SPACE:      // equivalent to left mouse button
162         LeftMouseButton( mv.Cursor() );
163         break;
164       case SDLK_q:
165         Quit();
166         break;
167       case SDLK_ESCAPE:
168         tile_wd->Select( -1 );
169         unit_wd->Select( -1 );
170         break;
171       case SDLK_RETURN:
172         ShowContextMenu( mv.Cursor() );
173 
174       case SDLK_u: {    // edit unit
175         Unit *u = mv.GetMap()->GetUnit( mv.Cursor() );
176         if ( u ) {
177           EdUnitWindow *euw = new EdUnitWindow( *u, *mission, view );
178           euw->SetMapView( mv );
179         }
180         break; }
181       case SDLK_b: {    // edit building
182         Building *b = mv.GetMap()->GetBuilding( mv.Cursor() );
183         if ( b ) new EdBuildingWindow( *b, *mission, view );
184         break; }
185 
186       default:
187         break;
188       }
189 
190     } else if ( event.type == SDL_MOUSEBUTTONDOWN ) {
191       Point pos;
192       bool validhex = !mv.Pixel2Hex( event.button.x - x, event.button.y - y, pos );
193 
194       if ( event.button.button == SDL_BUTTON_LEFT ) {
195         if ( validhex ) LeftMouseButton( pos );
196       } else if ( event.button.button == SDL_BUTTON_RIGHT ) {
197          ShowContextMenu( validhex ? pos : Point(-1,-1) );
198       }
199 
200     } else if ( event.type == SDL_MOUSEMOTION ) {
201       // handle just like a mouse button if LMB is down and
202       // user is terraforming
203       if ( (mode == ED_MODE_TERRAIN) &&
204            (event.motion.state & SDL_BUTTON( SDL_BUTTON_LEFT )) &&
205            !ud_wd->Clicked() && !lr_wd->Clicked() ) {
206         Point pos;
207         if ( !mv.Pixel2Hex( event.motion.x, event.motion.y, pos ) )
208           LeftMouseButton( pos );
209       }
210     }
211 
212   }
213   return rc;
214 }
215 
216 ////////////////////////////////////////////////////////////////////////
217 // NAME       : EdWindow::MoveCursor
218 // DESCRIPTION: Move the cursor in reaction to a key event.
219 // PARAMETERS : key - the key code used to give the order
220 // RETURNS    : -
221 ////////////////////////////////////////////////////////////////////////
222 
MoveCursor(int key)223 void EdWindow::MoveCursor( int key ) {
224   Direction dir;
225 
226   switch ( key ) {
227   case SDLK_KP1:  dir = SOUTHWEST; break;
228   case SDLK_DOWN:
229   case SDLK_KP2:  dir = SOUTH;     break;
230   case SDLK_KP3:  dir = SOUTHEAST; break;
231   case SDLK_KP7:  dir = NORTHWEST; break;
232   case SDLK_UP:
233   case SDLK_KP8:  dir = NORTH;     break;
234   case SDLK_KP9:  dir = NORTHEAST; break;
235   case SDLK_LEFT:
236   case SDLK_KP4:  dir = WEST;      break;
237   case SDLK_RIGHT:
238   case SDLK_KP6:  dir = EAST;      break;
239   default: return;
240   }
241 
242   Point dest;
243   if ( !mv.GetMap()->Dir2Hex( mv.Cursor(), dir, dest ) ) SetCursor( dest );
244 }
245 
246 ////////////////////////////////////////////////////////////////////////
247 // NAME       : EdWindow::SetCursor
248 // DESCRIPTION: Set the cursor to a new hex on the map. Contrary to the
249 //              low-level function in MapView this updates the display
250 //              at the old and new position if necessary.
251 // PARAMETERS : cursor - new cursor position
252 // RETURNS    : -
253 ////////////////////////////////////////////////////////////////////////
254 
SetCursor(const Point & cursor)255 void EdWindow::SetCursor( const Point &cursor ) {
256   Rect upd;
257 
258   if ( mv.CursorEnabled() ) {
259     upd = mv.SetCursor( Point(-1,-1) );  // disable cursor for hex update
260     Show( upd );                         // update previous cursor position
261   }
262 
263   upd = mv.SetCursor( cursor );
264   Show( upd );
265 
266   PrintCursorPos();
267   Show( panel );
268 }
269 
270 ////////////////////////////////////////////////////////////////////////
271 // NAME       : EdWindow::LeftMouseButton
272 // DESCRIPTION: Left mouse button has been pressed. See what we can do.
273 // PARAMETERS : hex - hex the button was clicked over
274 // RETURNS    : -
275 ////////////////////////////////////////////////////////////////////////
276 
LeftMouseButton(const Point & hex)277 void EdWindow::LeftMouseButton( const Point &hex ) {
278   Map *map = mv.GetMap();
279 
280   if ( mode == ED_MODE_TERRAIN ) {
281     if ( map->IsBuilding( hex ) && map->GetBuilding(hex) &&
282        !(map->GetTerrainSet()->GetTerrainInfo(selected)->tt_type & TT_ENTRANCE) )
283       new NoteWindow( "Warning", "You have just removed a shop entrance. "
284                       "The corresponding shop has NOT been deleted! "
285                       "(to do so restore the entrance and delete the shop before "
286                       "erasing the entrance)", 0, view );
287     map->SetHexType( hex, selected );
288   } else if ( mode == ED_MODE_UNIT ) {
289     if ( map->GetMapObject(hex) ) new NoteWindow( "Error", "Hex occupied.", 0, view );
290     else {
291       UnitSet *us = map->GetUnitSet();
292       const UnitType *ut = us->GetUnitInfo( selected < us->NumTiles() ?
293                                             selected : selected - us->NumTiles() );
294       mission->CreateUnit( ut, selected < us->NumTiles() ? PLAYER_ONE : PLAYER_TWO, hex );
295     }
296   }
297 
298   SetCursor( hex );
299 }
300 
301 ////////////////////////////////////////////////////////////////////////
302 // NAME       : EdWindow::LoadMission
303 // DESCRIPTION: Load a mission from file and attach it to the display.
304 // PARAMETERS : file    - name of mission data file
305 //              filereq - pop up file requester
306 // RETURNS    : pointer to Mission object on success, NULL on error
307 ////////////////////////////////////////////////////////////////////////
308 
LoadMission(const char * file,bool filereq)309 Mission *EdWindow::LoadMission( const char *file, bool filereq ) {
310   string fn;
311   Mission *ms = NULL;
312 
313   if ( filereq )
314     fn = GetFileName( file, ".lev", mapdir, WIN_FILE_LOAD );
315   else fn = file;
316 
317   if ( fn.length() > 0 ) {
318     ms = new Mission();
319     if ( ms ) {
320       int err = ms->Load( fn.c_str() );
321       if ( err ) {
322         delete ms;
323         new NoteWindow( "Error", "Couldn't load map. This could be a file type mismatch.", 0, view );
324       } else {
325         SetNewMission( ms );
326       }
327     }
328   }
329 
330   return ms;
331 }
332 
333 ////////////////////////////////////////////////////////////////////////
334 // NAME       : EdWindow::SaveMission
335 // DESCRIPTION: Save a mission to file.
336 // PARAMETERS : file    - name of mission data file
337 //              filereq - pop up file requester
338 // RETURNS    : GUI status
339 ////////////////////////////////////////////////////////////////////////
340 
SaveMission(const char * file,bool filereq)341 GUI_Status EdWindow::SaveMission( const char *file, bool filereq ) {
342   string fn;
343   GUI_Status rc = GUI_OK;
344 
345   if ( filereq )
346     fn = GetFileName( file, ".lev", mapdir, WIN_FILE_SAVE );
347   else fn = file;
348 
349   if ( fn.length() > 0 ) {
350     if ( mission->Save( fn.c_str() ) != 0 ) {
351       new NoteWindow( "Error", "Could not save mission!", WIN_CENTER, view );
352     }
353   }
354   return rc;
355 }
356 
357 ////////////////////////////////////////////////////////////////////////
358 // NAME       : EdWindow::ExportMission
359 // DESCRIPTION: Save a mission to a plain text file.
360 // PARAMETERS : file    - default name of mission data file
361 //              filereq - pop up file requester
362 // RETURNS    : GUI status
363 ////////////////////////////////////////////////////////////////////////
364 
ExportMission(const char * file,bool filereq)365 GUI_Status EdWindow::ExportMission( const char *file, bool filereq ) {
366   string fn;
367   GUI_Status rc = GUI_OK;
368 
369   if ( filereq )
370     fn = GetFileName( file, ".src", mapdir, WIN_FILE_SAVE );
371   else fn = file;
372 
373   if ( fn.length() > 0 ) mission->Export( fn.c_str() );
374   return rc;
375 }
376 
377 ////////////////////////////////////////////////////////////////////////
378 // NAME       : EdWindow::GetFileName
379 // DESCRIPTION: Get the name of a file to load or to save to. Pop up a
380 //              file requester.
381 // PARAMETERS : filename - default filename
382 //              suffix   - filename suffix
383 //              dirname  - name of directory to look in
384 //              flag     - WIN_FILE_LOAD or WIN_FILE_SAVE
385 // RETURNS    : file name or empty string on error
386 ////////////////////////////////////////////////////////////////////////
387 
GetFileName(const char * filename,const char * suffix,const string & dirname,int flag) const388 string EdWindow::GetFileName( const char *filename, const char *suffix,
389                               const string &dirname, int flag ) const {
390   string file( filename );
391   if ( file.length() > 0 ) file.append( suffix );
392 
393   bool filesel;
394   do {
395     GUI_Status rc;
396     DialogWindow *dw;
397     bool done = false;
398     FileWindow *fw = new FileWindow( dirname.c_str(), file.c_str(),
399                                      suffix, flag, view );
400     fw->ok->SetID( 1 );
401     fw->cancel->SetID( 0 );
402 
403     do {
404       filesel = false;
405       rc = fw->EventLoop();
406 
407       if ( rc == 1 ) {
408         file = fw->GetFile();
409         if ( file.length() != 0 ) {
410           view->CloseWindow( fw );
411           done = true;
412         }
413       } else if ( rc == 0 ) {
414         view->CloseWindow( fw );
415         file.assign( "" );
416         done = true;
417       }
418 
419     } while ( !done );
420 
421     if ( file.length() > 0 && (flag == WIN_FILE_SAVE) && File::Exists( file ) ) {
422       // if file exists let user confirm the write
423       char *conmsg = new char[ file.length() + 20 ];
424       strcpy( conmsg, file.c_str() );
425       strcat( conmsg, " exists. Overwrite?" );
426       dw = new DialogWindow( NULL, conmsg, "_Yes|_No", 1, 0, view );
427       dw->SetButtonID( 0, 1 );
428       dw->SetButtonID( 1, 0 );
429       rc = dw->EventLoop();
430       view->CloseWindow( dw );
431       if ( rc == 0 ) {
432         filesel = true;
433         file = file_part( file );
434       }
435       delete [] conmsg;
436     }
437 
438   } while ( filesel );
439 
440   return file;
441 }
442 
443 ////////////////////////////////////////////////////////////////////////
444 // NAME       : EdWindow::WidgetActivated
445 // DESCRIPTION: This method is called when the user selects one of the
446 //              widgets associated with this window - the tiles or units
447 //              list, the map scrollers, or one of a number of buttons
448 //              including the main menu.
449 // PARAMETERS : widget - pointer to calling widget
450 //              win    - parent window of calling widget
451 // RETURNS    : GUI status
452 ////////////////////////////////////////////////////////////////////////
453 
WidgetActivated(Widget * widget,Window * win)454 GUI_Status EdWindow::WidgetActivated( Widget *widget, Window *win ) {
455   GUI_Status rc = GUI_OK;
456 
457   if ( win == this ) {
458 
459     switch ( widget->ID() ) {
460     case L_ID_UNITS:
461       if ( unit_wd->Selected() == -1 ) {
462         if ( tile_wd->Selected() == -1 ) {
463           mode = ED_MODE_VIEW;
464           selected = -1;
465         }
466       } else {
467         mode = ED_MODE_UNIT;
468         selected = unit_wd->Selected();
469         tile_wd->Select( -1 );
470       }
471       break;
472     case L_ID_TILES:
473       if ( tile_wd->Selected() == -1 ) {
474         if ( unit_wd->Selected() == -1 ) {
475           mode = ED_MODE_VIEW;
476           selected = -1;
477         }
478       } else {
479         mode = ED_MODE_TERRAIN;
480         selected = tile_wd->Selected();
481         unit_wd->Select( -1 );
482       }
483       break;
484 
485     case S_ID_VERT: {
486       Point offset = mv.GetOffsets();
487       mv.Scroll( 0, ud_wd->Level() - offset.y );
488       Show( mv );
489       break; }
490     case S_ID_HORIZ: {
491       Point offset = mv.GetOffsets();
492       mv.Scroll( lr_wd->Level() - offset.x, 0 );
493       Show( mv );
494       break; }
495     }
496 
497   } else {
498 
499     Map *map = mv.GetMap();
500 
501     // context menu?
502     if ( widget->ID() >= B_ID_TILE_GRAB )
503       static_cast<MenuWindow *>(win)->CloseParent();
504     view->CloseWindow(win);
505 
506     switch ( widget->ID() ) {
507     case B_ID_NEW: {
508       NewMissionWindow *nmw = new NewMissionWindow( view );
509       nmw->SetHook( this, B_ID_NEW_MISSION_OK );
510       break; }
511     case B_ID_LOAD:
512       LoadMission( "", true );
513       break;
514     case B_ID_SAVE:
515       rc = SaveMission( mission->GetTitle().c_str(), true );
516       break;
517     case B_ID_EXPORT:
518       rc = ExportMission( mission->GetTitle().c_str(), true );
519       break;
520     case B_ID_VALIDATE:
521       ValidateMission();
522       break;
523     case B_ID_SETTINGS:
524       new EdMissionSetupWindow( *mission, view );
525       break;
526     case B_ID_QUIT:
527       Quit();
528       break;
529 
530     case B_ID_TILE_GRAB:
531       tile_wd->Select( map->HexTypeID(selected_hex) );
532       break;
533     case B_ID_TILE_SWAP:
534       SwapTiles( map->HexTypeID(selected_hex), selected );
535       break;
536 
537     case B_ID_UNIT_INFO:
538       new UnitInfoWindow( map->GetUnit( selected_hex )->Type()->ID(), *map, view );
539       break;
540     case B_ID_UNIT_EDIT: {
541       EdUnitWindow *euw = new EdUnitWindow( *(map->GetUnit( selected_hex )), *mission, view );
542       euw->SetMapView( mv );
543       break; }
544     case B_ID_UNIT_DEL:
545       mission->DeleteUnit( map->GetUnit(selected_hex) );
546       Show( mv.UpdateHex( selected_hex ) );
547       PrintCursorPos();
548       Show( panel );
549       break;
550 
551     case B_ID_BLD_CREATE:
552       mission->CreateBuilding( PLAYER_ONE, selected_hex );  // fall through
553     case B_ID_BLD_EDIT:
554       new EdBuildingWindow( *(map->GetBuilding( selected_hex )),
555                           *mission, view );
556       break;
557     case B_ID_BLD_DEL:
558       mission->DeleteBuilding( map->GetBuilding( selected_hex ) );
559       PrintCursorPos();
560       Show( panel );
561       break;
562 
563     case B_ID_EVENTS:
564       new EdEventsWindow( *mission, view );
565       break;
566 
567     case B_ID_MESSAGES:
568       new EdMsgWindow( mission->GetMessages(), view );
569       break;
570 
571     case B_ID_NEW_MISSION_OK: {
572       NewMissionWindow *nmw = static_cast<NewMissionWindow *>(win);
573       if ( nmw->GetMission() )
574         SetNewMission( nmw->GetMission() );
575       break; }
576     }
577 
578   }
579 
580   return rc;
581 }
582 
583 ////////////////////////////////////////////////////////////////////////
584 // NAME       : EdWindow::Quit
585 // DESCRIPTION: Ask user for confirmation and leave the editor.
586 // PARAMETERS : -
587 // RETURNS    : -
588 ////////////////////////////////////////////////////////////////////////
589 
Quit(void) const590 void EdWindow::Quit( void ) const {
591   DialogWindow *req = new DialogWindow( NULL, "Do you really want to quit?",
592                                         "_Yes|_No", 1, 0, view );
593   req->SetButtonID( 0, GUI_QUIT );
594 }
595 
596 ////////////////////////////////////////////////////////////////////////
597 // NAME       : EdWindow::ShowContextMenu
598 // DESCRIPTION: Open a pop-up menu with with context-dependent options.
599 // PARAMETERS : clicked - hex coordinates of the mouse click or -1,-1 if
600 //                        no specific hex was selected
601 // RETURNS    : -
602 ////////////////////////////////////////////////////////////////////////
603 
ShowContextMenu(const Point & clicked)604 void EdWindow::ShowContextMenu( const Point &clicked ) {
605   MenuWindow *menu = new MenuWindow( PROGRAMNAME, this, view );
606 
607   if ( mission ) {
608     if ( clicked != Point(-1,-1) ) {
609       Map *map = mv.GetMap();
610       Unit *u = map->GetUnit( clicked );
611       selected_hex = clicked;
612 
613       // tile sub-menu
614       menu->AddMenu( 0, 0, "_Tile" );
615       menu->AddItem( 1, B_ID_TILE_GRAB, 0, "_Grab" );
616       menu->AddItem( 1, B_ID_TILE_SWAP,
617                      (mode == ED_MODE_TERRAIN ? 0 : WIDGET_DISABLED),
618                      "_Swap" );
619 
620       if ( u ) {  // unit sub-menu
621         menu->AddMenu( 0, 0, "_Unit" );
622         menu->AddItem( 1, B_ID_UNIT_INFO, 0, "_Info" );
623         menu->AddItem( 1, B_ID_UNIT_EDIT, 0, "_Edit..." );
624         menu->AddItem( 1, B_ID_UNIT_DEL, 0, "_Delete" );
625       }
626 
627       if ( map->IsBuilding( clicked ) ) {
628         Building *b = map->GetBuilding( clicked );
629 
630         menu->AddMenu( 0, 0, "_Building" );
631         if ( b ) {
632           menu->AddItem( 1, B_ID_BLD_EDIT,  0, "_Edit..." );
633           menu->AddItem( 1, B_ID_BLD_DEL,   0, "_Delete" );
634         } else menu->AddItem( 1, B_ID_BLD_CREATE, 0, "_Create..." );
635       }
636     }
637 
638     menu->AddItem( 0, B_ID_EVENTS, 0, "_Events..." );
639     menu->AddItem( 0, B_ID_MESSAGES, 0, "_Messages..." );
640     menu->AddBar( 0 );
641   }
642 
643   // file ops
644   menu->AddMenu( 0, 0, "_Project" );
645   menu->AddItem( 1, B_ID_NEW, 0, "_New..." );
646   menu->AddItem( 1, B_ID_LOAD, 0, "_Load..." );
647   menu->AddItem( 1, B_ID_SAVE, (mission ? 0 : WIDGET_DISABLED),
648                  "_Save..." );
649   menu->AddItem( 1, B_ID_EXPORT, (mission ? 0 : WIDGET_DISABLED),
650                  "_Export..." );
651   menu->AddItem( 1, B_ID_VALIDATE, (mission ? 0 : WIDGET_DISABLED),
652                  "_Validate" );
653   menu->AddItem( 1, B_ID_SETTINGS, (mission ? 0 : WIDGET_DISABLED),
654                  "Se_ttings" );
655   menu->AddItem( 1, B_ID_QUIT, 0, "_Quit..." );
656 
657   menu->Layout();
658 }
659 
660 ////////////////////////////////////////////////////////////////////////
661 // NAME       : EdWindow::SwapTiles
662 // DESCRIPTION: Replace each occurence of one tile on the map with
663 //              another tile.
664 // PARAMETERS : t1 - identifier of the tiles to be replaced
665 //              t2 - identifier of the tiles to replace t1
666 // RETURNS    : -
667 ////////////////////////////////////////////////////////////////////////
668 
SwapTiles(unsigned short t1,unsigned short t2)669 void EdWindow::SwapTiles( unsigned short t1, unsigned short t2 ) {
670   Map *map = mv.GetMap();
671   Point p;
672 
673   for ( p.y = 0; p.y < map->Height(); ++p.y ) {
674     for ( p.x = 0; p.x < map->Width(); ++p.x ) {
675       if ( map->HexTypeID(p) == t1 ) map->SetHexType( p, t2 );
676     }
677   }
678 
679   mv.Draw();
680   Show( mv );
681 }
682 
683 ////////////////////////////////////////////////////////////////////////
684 // NAME       : EdWindow::ValidateMission
685 // DESCRIPTION: Check mission integrity.
686 // PARAMETERS : -
687 // RETURNS    : -
688 ////////////////////////////////////////////////////////////////////////
689 
ValidateMission(void) const690 void EdWindow::ValidateMission( void ) const {
691   stringstream msg;
692 
693   if ( mission->Validate( msg ) > 0 ) {
694     new MessageWindow( "Validation Results", msg.str().c_str(), view );
695   } else {
696      new NoteWindow( "Validation successful",
697          "No errors or warnings have been detected.", 0, view );
698   }
699 }
700 
701 ////////////////////////////////////////////////////////////////////////
702 // NAME       : EdWindow::SetNewMission
703 // DESCRIPTION: Change the current mission.
704 // PARAMETERS : ms - new mission
705 // RETURNS    : -
706 ////////////////////////////////////////////////////////////////////////
707 
SetNewMission(Mission * ms)708 void EdWindow::SetNewMission( Mission *ms ) {
709   delete mission;
710 
711   mv.Disable();
712   mode = ED_MODE_VIEW;
713 
714   mission = ms;
715   Map &map = mission->GetMap();
716   mv.SetMap( &map );
717   mv.SetCursor( Point(0,0) );
718 
719   tile_wd->SetTerrainSet( map.GetTerrainSet() );
720   unit_wd->SetUnitSet( map.GetUnitSet() );
721 
722   short max = MAX( map.Height() * mv.TileHeight() +
723                    mv.TileShiftY() - mv.Height(), 0 );
724   ud_wd->Adjust( 0, max, mv.Height() );
725   ud_wd->ScrollTo( 0 );
726 
727   max = MAX( map.Width() * (mv.TileWidth() - mv.TileShiftX()) +
728              mv.TileShiftX() - mv.Width(), 0 );
729   lr_wd->Adjust( 0, max, mv.Width() );
730   lr_wd->ScrollTo( 0 );
731 
732   mv.Enable();
733   Draw();
734   Show();
735 }
736 
737