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 // extwindow2.cpp
21 ///////////////////////////////////////////////////////////////////////
22 
23 #include <stdlib.h>
24 
25 #include "extwindow2.h"
26 #include "extwindow.h"
27 #include "eventwindow.h"
28 #include "filewindow.h"
29 #include "fileio.h"
30 #include "uiaux.h"
31 #include "mapgen.h"
32 
33 // static vars
34 const char *player2_labels[] = { "One", "Two", 0 };
35 const char *player3_labels[] = { "One", "Two", "Neutral", 0 };
36 
37 const char *dir_labels[] = { "North", "Northeast", "Southeast",
38                              "South", "Southwest", "Northwest", 0 };
39 const char *xp_labels[] = { "0", "1", "2", "3", "4", "5", "6", 0 };
40 const char *size_labels[] = { "1", "2", "3", "4", "5", "6", 0 };
41 
42 extern const char *event_labels[];
43 extern const char *etrigger_labels[];
44 
45 // non-private widget ID for the SelectUnitWindow
46 #define B_ID_SUW_OK      102
47 
48 
49 ////////////////////////////////////////////////////////////////////////
50 // NAME       : ListExchangeWindow::ListExchangeWindow
51 // DESCRIPTION: This window displays two TLWList objects. The user can
52 //              move nodes from one list to the other and vice versa.
53 // PARAMETERS : l1     - first TLWList
54 //              l2     - second TLWList
55 //              label1 - label to be shown for l1
56 //              label2 - label to be shown for l2
57 //              view   - pointer to the window's view
58 // RETURNS    : -
59 ////////////////////////////////////////////////////////////////////////
60 
ListExchangeWindow(TLWList & l1,TLWList & l2,const char * label1,const char * label2,View * view)61 ListExchangeWindow::ListExchangeWindow( TLWList &l1, TLWList &l2,
62             const char *label1, const char *label2, View *view )
63           : Window(WIN_CENTER, view), list1(l1), list2(l2) {
64   // calculate window size
65   unsigned short maxlen = sfont->TextWidth(label1), nodes1 = 0, nodes2 = 0;
66   TLWNode *n;
67 
68   for ( n = static_cast<TLWNode *>(l1.Head()); n;
69         n = static_cast<TLWNode *>(n->Next()) ) {
70     maxlen = MAX( maxlen, sfont->TextWidth(n->Name()) );
71     ++nodes1;
72   }
73   for ( n = static_cast<TLWNode *>(l2.Head()); n;
74         n = static_cast<TLWNode *>(n->Next()) ) {
75     maxlen = MAX( maxlen, sfont->TextWidth(n->Name()) );
76     ++nodes2;
77   }
78 
79   Rect win( 0, 0, (maxlen * 2 + 10) + sfont->Width() + 30,
80             (MAX(nodes1, nodes2) + 4) * (sfont->Height() + 4) + 30 );
81   win.Clip( *view );
82   SetSize( win );
83 
84   wd_l1 = new TextListWidget( 0, 5, sfont->Height() + 10,
85           w/2 - sfont->Width() - 10, h - sfont->Height() * 2 - 20,
86           &l1, -1, WIDGET_ALIGN_TOP, label1, this );
87 
88   wd_l2 = new TextListWidget( 0, w - 5 - wd_l1->Width(),
89           wd_l1->TopEdge(), wd_l1->Width(), wd_l1->Height(),
90           &l2, -1, WIDGET_ALIGN_TOP, label2, this );
91 
92   Widget *wd;
93   wd = new ButtonWidget( B_ID_RIGHT,
94        wd_l1->LeftEdge() + wd_l1->Width() + 5,
95        wd_l1->TopEdge() + wd_l1->Height()/2 - sfont->Height() - 15,
96        wd_l2->LeftEdge() - wd_l1->LeftEdge() - wd_l1->Width() - 10,
97        sfont->Height() + 8, 0, "_>", this );
98   wd->SetHook( this );
99 
100   wd = new ButtonWidget( B_ID_LEFT, wd->LeftEdge(),
101        wd->TopEdge() + wd->Height() + 7, wd->Width(), wd->Height(),
102        0, "_<", this );
103   wd->SetHook( this );
104 
105   ok = new ButtonWidget( B_ID_OK, 1, h - sfont->Height() - 9,
106                          w - 2, sfont->Height() + 8,
107                          WIDGET_DEFAULT, "_OK", this );
108   ok->SetHook( this );
109 
110   Draw();
111   Show();
112 }
113 
114 ////////////////////////////////////////////////////////////////////////
115 // NAME       : ListExchangeWindow::WidgetActivated
116 // DESCRIPTION: React to user actions.
117 // PARAMETERS : widget - activated widget
118 //              win    - window containing the widget
119 // RETURNS    : GUI status
120 ////////////////////////////////////////////////////////////////////////
121 
WidgetActivated(Widget * widget,Window * win)122 GUI_Status ListExchangeWindow::WidgetActivated( Widget *widget, Window *win ) {
123   TLWNode *n;
124 
125   switch ( widget->ID() ) {
126   case B_ID_RIGHT:
127     n = static_cast<TLWNode *>(wd_l1->Selected());
128     if ( n ) {
129       n->Remove();
130       list2.InsertNodeSorted( n );
131       wd_l1->Update(); wd_l1->Draw(); wd_l1->Show();
132       wd_l2->Update(); wd_l2->Draw(); wd_l2->Show();
133     }
134     break;
135   case B_ID_LEFT:
136     n = static_cast<TLWNode *>(wd_l2->Selected());
137     if ( n ) {
138       n->Remove();
139       list1.InsertNodeSorted( n );
140       wd_l1->Update(); wd_l1->Draw(); wd_l1->Show();
141       wd_l2->Update(); wd_l2->Draw(); wd_l2->Show();
142     }
143     break;
144   case B_ID_OK:
145     view->CloseWindow( this );
146     break;
147   }
148 
149   return GUI_OK;
150 }
151 
152 ////////////////////////////////////////////////////////////////////////
153 // NAME       : ListSelectWindow::ListSelectWindow
154 // DESCRIPTION: This window displays the contents of a TLWList object.
155 //              The user can select a single node (or none) and click
156 //              OK.
157 // PARAMETERS : title  - window title
158 //              list   - TLWList object to display
159 //              defid  - identifier of the node to be highlighted by
160 //                       default (-1 means no node selected)
161 //              nullok - if true, accept user hitting OK without a node
162 //                       selected and deselect a node if clicked twice;
163 //                       if false, refuse to close the window until the
164 //                       user has made a choice. If the list is empty,
165 //                       it will close even if nullok is false.
166 //              hook   - WidgetHook to notify when the user hits OK
167 //              button - button identifier passed to the hook
168 //              view   - pointer to the window's view
169 // RETURNS    : -
170 ////////////////////////////////////////////////////////////////////////
171 
ListSelectWindow(const char * title,TLWList & list,short defid,bool nullok,WidgetHook * hook,short button,View * view)172 ListSelectWindow::ListSelectWindow( const char *title, TLWList &list,
173                   short defid, bool nullok, WidgetHook *hook,
174                   short button, View *view ) :
175           Window(WIN_CENTER, view), button(button), nullok(nullok),
176           list(list), client(hook), current(0) {
177   // calculate window size
178   unsigned short maxlen = sfont->TextWidth(title) + 10, nodes = 0;
179   short defnode = -1;
180   TLWNode *n;
181 
182   for ( n = static_cast<TLWNode *>(list.Head()); n;
183         n = static_cast<TLWNode *>(n->Next()) ) {
184     maxlen = MAX( maxlen, sfont->TextWidth(n->Name()) );
185 
186     if ( n->ID() == defid ) {
187       defnode = nodes;
188       current = n;
189     }
190     ++nodes;
191   }
192 
193   Rect win( 0, 0, maxlen + 20, (nodes + 2) * (sfont->Height() + 4) + 25 );
194   win.Clip( *view );
195   SetSize( win );
196 
197   lw = new TextListWidget( button + 1, 5, sfont->Height() + 10,
198            w - 10, h - sfont->Height() * 2 - 20, &list, defnode,
199            WIDGET_ALIGN_TOP|WIDGET_HSCROLLKEY|WIDGET_VSCROLLKEY,
200            title, this );
201   lw->SetHook( this );
202 
203   Widget *wd = new ButtonWidget( button, 1, h - sfont->Height() - 9,
204                    w - 2, sfont->Height() + 8, WIDGET_DEFAULT,
205                    "_OK", this );
206   wd->SetHook( this );
207 
208   Draw();
209   Show();
210 }
211 
212 ////////////////////////////////////////////////////////////////////////
213 // NAME       : ListSelectWindow::WidgetActivated
214 // DESCRIPTION: React to user actions.
215 // PARAMETERS : widget - activated widget
216 //              win    - window containing the widget
217 // RETURNS    : GUI status
218 ////////////////////////////////////////////////////////////////////////
219 
WidgetActivated(Widget * widget,Window * win)220 GUI_Status ListSelectWindow::WidgetActivated( Widget *widget, Window *win ) {
221 
222   if ( widget->ID() == button ) {
223 
224     if ( nullok || current || (list.CountNodes() == 0) ) {
225       view->CloseWindow( this );
226       client->WidgetActivated( widget, this );
227     } else new NoteWindow( "Error", "You must make a selection", 0, view );
228 
229   } else {
230 
231     TLWNode *n = static_cast<TLWNode *>(lw->Selected());
232     if ( (n == current) && nullok ) {
233       lw->Select( -1 );
234       current = NULL;
235     } else current = n;
236   }
237 
238   return GUI_OK;
239 }
240 
241 ////////////////////////////////////////////////////////////////////////
242 // NAME       : NewMissionWindow::NewMissionWindow
243 // DESCRIPTION: This window pops up when the user wants to create a new
244 //              mission. He can adjust a few basic settings like map
245 //              size or tileset and create the mission.
246 // PARAMETERS : view - pointer to the window's view
247 // RETURNS    : -
248 ////////////////////////////////////////////////////////////////////////
249 
NewMissionWindow(View * view)250 NewMissionWindow::NewMissionWindow( View *view ) :
251    Window(WIN_CENTER, view), mission(0), hook(0), ok_id(B_ID_OK) {
252   // calculate window dimensions
253   Font *f = SmallFont();
254   unsigned short width = MIN( f->Width() * 38 + 20, view->Width() );
255   unsigned short height = f->Height() * 6 + 60;
256   unsigned short xoff, yoff = 5;
257   xoff = MAX(f->TextWidth("Map Width:"), f->TextWidth("Map Height:")) + 10;
258 
259   SetSize( width, height );
260 
261   m_width = new NumberWidget( 1, xoff, yoff, f->Width() * 4 + 10,
262             f->Height() + 6, 40, 20, 180, WIDGET_ALIGN_LEFT,
263             "Map _Width:", this );
264 
265   width = MIN( f->Width() * 11 + 10, w - xoff - m_width->w - 24 - f->Width() * 12 );
266   tileset = new StringWidget( 3, xoff + m_width->w + 10 + f->Width() * 9,
267             yoff, width, m_width->h, "default", 10,
268             WIDGET_ALIGN_LEFT|WIDGET_STR_CONST, "_Tile Set:", this );
269 
270   ButtonWidget *btn = new ButtonWidget( B_ID_TILESET,
271              tileset->x + tileset->w, tileset->y,
272              f->Width() * 3 + 4, tileset->h, 0, "...", this );
273   btn->SetKey( 't' );
274   btn->SetHook( this );
275 
276   yoff += m_width->Height() + 5;
277 
278   m_height = new NumberWidget( 2, xoff, yoff, m_width->w, m_width->h,
279             40, 20, 180, WIDGET_ALIGN_LEFT, "Map _Height:", this );
280 
281   unitset = new StringWidget( 4, tileset->x, yoff, tileset->w, tileset->h,
282             "default", 10, WIDGET_ALIGN_LEFT|WIDGET_STR_CONST,
283             "_Unit Set:", this );
284 
285   btn = new ButtonWidget( B_ID_UNITSET,
286              btn->x, unitset->y, btn->w, unitset->h, 0, "...", this );
287   btn->SetKey( 'u' );
288   btn->SetHook( this );
289 
290   yoff += btn->Height() + 5;
291   gen_random = new CheckboxWidget( B_ID_RANDOM, 5, yoff,
292                DEFAULT_CBW_SIZE, DEFAULT_CBW_SIZE, true,
293                WIDGET_STYLE_GFX|WIDGET_STYLE_NOBORDER|WIDGET_ALIGN_RIGHT,
294                "_Generate random terrain", this );
295   gen_random->SetHook( this );
296 
297   yoff += gen_random->Height() + 5;
298   xoff = gen_random->LeftEdge() + 10 + f->TextWidth("Water Level");
299   gen_water = new SliderWidget( 0, xoff, yoff,
300               w - xoff - 5, DEFAULT_SLIDER_SIZE, 0, 10, 4, 1,
301               WIDGET_HSCROLL|WIDGET_ALIGN_LEFT, "W_ater Level", this );
302 
303   yoff += gen_water->Height() + 5;
304   gen_roughness = new SliderWidget( 0, xoff, yoff,
305                   w - xoff - 5, DEFAULT_SLIDER_SIZE, 1, 20, 6, 2,
306                   WIDGET_HSCROLL|WIDGET_ALIGN_LEFT, "_Roughness", this );
307 
308   yoff = h - f->Height() - 15;
309   btn = new ButtonWidget( B_ID_OK, 10, yoff,
310         f->Width() * 10, f->Height() + 10, 0, "_OK", this );
311   btn->SetHook( this );
312 
313   new ButtonWidget( GUI_CLOSE,
314       w - 10 - btn->w, yoff, btn->w, btn->h, 0, "_Cancel", this );
315 
316   Draw();
317   Show();
318 }
319 
320 ////////////////////////////////////////////////////////////////////////
321 // NAME       : NewMissionWindow::GetMapSize
322 // DESCRIPTION: Get the current slider settings.
323 // PARAMETERS : -
324 // RETURNS    : selected size as a Point
325 ////////////////////////////////////////////////////////////////////////
326 
GetMapSize(void) const327 Point NewMissionWindow::GetMapSize( void ) const {
328   return Point( m_width->Number(), m_height->Number() );
329 }
330 
331 ////////////////////////////////////////////////////////////////////////
332 // NAME       : NewMissionWindow::WidgetActivated
333 // DESCRIPTION: React to user actions.
334 // PARAMETERS : button - activated button widget
335 //              win    - window containing the button
336 // RETURNS    : GUI status
337 ////////////////////////////////////////////////////////////////////////
338 
WidgetActivated(Widget * button,Window * win)339 GUI_Status NewMissionWindow::WidgetActivated( Widget *button, Window *win ) {
340   FileWindow *fw;
341 
342   switch ( button->ID() ) {
343   case B_ID_TILESET:
344     fw = new FileWindow( get_data_dir().c_str(),
345                          GetTileSet(), ".tiles", 0, view );
346     fw->ok->SetID( B_ID_TILESET_OK );
347     fw->ok->SetHook( this );
348     break;
349   case B_ID_TILESET_OK: {
350     string file = static_cast<FileWindow *>(win)->GetFile();
351     if ( file.length() > 0 ) {
352       file = file_part( file );
353       file.erase( file.length() - 6 );   // remove file name suffix
354       tileset->SetString( file.c_str() );
355       view->CloseWindow( win );
356 
357       if ( file == "default" ) gen_random->Enable();
358       else gen_random->Disable();
359       gen_random->Draw();
360       WidgetActivated( gen_random, this );
361     }
362     break; }
363   case B_ID_UNITSET:
364     fw = new FileWindow( get_data_dir().c_str(),
365                          GetUnitSet(), ".units", 0, view );
366     fw->ok->SetID( B_ID_UNITSET_OK );
367     fw->ok->SetHook( this );
368     break;
369   case B_ID_UNITSET_OK: {
370     string file = static_cast<FileWindow *>(win)->GetFile();
371     if ( file.length() > 0 ) {
372       file = file_part( file );
373       file.erase( file.length() - 6 );   // remove file name suffix
374       unitset->SetString( file.c_str() );
375       view->CloseWindow( win );
376     }
377     break; }
378   case B_ID_RANDOM:
379     if ( button->Disabled() || !button->Clicked() ) {
380       gen_water->Disable();
381       gen_roughness->Disable();
382     } else {
383       gen_water->Enable();
384       gen_roughness->Enable();
385     }
386     gen_water->Draw();
387     gen_roughness->Draw();
388     Show();
389     break;
390   case B_ID_OK:
391     if ( hook ) {
392       mission = NewMission( GetMapSize(),
393                 GetTileSet(), GetUnitSet() );
394       if ( mission ) {
395         if ( !gen_random->Disabled() && gen_random->Clicked() ) {
396           MapGenerator gen;
397           gen.Generate( mission->GetMap(),
398               gen_water->Level(), gen_roughness->Level() );
399         }
400 
401         // add the most basic messages
402         Language en;
403         en.SetID( CF_LANG_DEFAULT );
404         en.AddMsg( "Unnamed" );   // map name
405         en.AddMsg( "Player 1" );  // name player 1
406         en.AddMsg( "Player 2" );  // name player 2
407         en.AddMsg( "HQ 1" );      // HQ player 1
408         en.AddMsg( "HQ 2" );      // HQ player 2
409         mission->GetMessages().AddLanguage( en );
410         mission->SetName( 0 );
411         mission->GetPlayer( PLAYER_ONE ).SetNameID( 1 );
412         mission->GetPlayer( PLAYER_TWO ).SetNameID( 2 );
413 
414         // use the button ID supplied by the caller
415         button->SetID( ok_id );
416         hook->WidgetActivated( button, this );
417         button->SetID( B_ID_OK );
418       }
419     }
420   }
421 
422   return GUI_OK;
423 }
424 
425 ////////////////////////////////////////////////////////////////////////
426 // NAME       : NewMissionWindow::NewMission
427 // DESCRIPTION: Create a new mission.
428 // PARAMETERS : size - map size
429 //              tset - tile set name
430 //              uset - unit set name
431 // RETURNS    : pointer to new mission or NULL on error
432 ////////////////////////////////////////////////////////////////////////
433 
NewMission(const Point & size,const string & tset,const string & uset)434 Mission *NewMissionWindow::NewMission( const Point &size,
435          const string &tset, const string &uset ) {
436   Mission *ms = NULL;
437   TerrainSet *ts = new TerrainSet;
438   UnitSet *us = new UnitSet;
439 
440   string tname( get_data_dir() + tset + ".tiles" );
441   File tfile( tname );
442 
443   string uname( get_data_dir() + uset + ".units" );
444   File ufile( uname );
445 
446   if ( !tfile.Open("rb") || ts->Load( tfile, tset.c_str() ) ) {
447     new NoteWindow( "Error", "Tile set not available", 0, view );
448   } else if ( !ufile.Open("rb") || us->Load( ufile, uset.c_str() ) ) {
449     new NoteWindow( "Error", "Unit set not available", 0, view );
450   } else {
451     ms = new Mission( size, ts, us );
452   }
453 
454   if ( !ms ) {
455     delete ts;
456     delete us;
457   }
458 
459   tfile.Close();
460   ufile.Close();
461   return ms;
462 }
463 
464 ////////////////////////////////////////////////////////////////////////
465 // NAME       : EdMissionSetupWindow::EdMissionSetupWindow
466 // DESCRIPTION: This window allows modification of the basic mission
467 //              settings like player names.
468 // PARAMETERS : mission - current mission
469 //              view    - pointer to the window's view
470 // RETURNS    : -
471 ////////////////////////////////////////////////////////////////////////
472 
EdMissionSetupWindow(Mission & mission,View * view)473 EdMissionSetupWindow::EdMissionSetupWindow( Mission &mission, View *view ) :
474        Window(WIN_CENTER, view), mission(mission) {
475   // calculate dimensions
476   SetSize( MIN(sfont->Width() * 40 + 20, view->Width()),
477            MIN(sfont->Height() * 11 + 110, view->Height()) );
478 
479   static const char *msg_labels[] = { "Level Name", "Level Info",
480                                       "Name 1", "Name 2",
481                                       "Briefing 1", "Briefing 2",
482                                       "Campaign Name", "Campaign Info", 0 };
483   static const char *type_labels[] = { "Solo", "Duel", 0 };
484 
485   unsigned short wdh = sfont->Height() + 8, wdx, wdy = 5;
486   Widget *wd;
487 
488   wdx = sfont->Width() * 4 + 10;
489   wd = new CycleWidget( B_ID_PLAYERS, wdx, wdy,
490             w/2 - wdx - 5, wdh, WIDGET_ALIGN_LEFT,
491             "_Type", mission.GetNumPlayers()-1, type_labels, this );
492   wd->SetHook( this );
493 
494   wdx = w/2 + sfont->Width() * 8 + 10;
495   wd = new StringWidget( S_ID_SEQUEL, wdx, wdy, w - wdx - 5, wdh,
496        mission.GetSequel(), 20, WIDGET_ALIGN_LEFT, "_Next Map", this );
497   wd->SetHook( this );
498 
499   wdy += wdh + 5;
500   wd = new StringWidget( S_ID_MUSIC, wdx, wdy, w - wdx - 5, wdh,
501        mission.GetMusic(), 20, WIDGET_ALIGN_LEFT, "M_usic", this );
502   wd->SetHook( this );
503 
504   wd = new CheckboxWidget( B_ID_CAMPAIGN, 5, wdy + (wdh - DEFAULT_CBW_SIZE)/2,
505        DEFAULT_CBW_SIZE, DEFAULT_CBW_SIZE, mission.IsCampaign(),
506        WIDGET_STYLE_NOBORDER|WIDGET_STYLE_GFX|WIDGET_ALIGN_RIGHT,
507        "_Campaign Map", this );
508   wd->SetHook( this );
509 
510   wdx = wd->LeftEdge() + wd->Width() + sfont->TextWidth("Campaign Map") + 15;
511   wd = new CheckboxWidget( B_ID_SKIRMISH, wdx, wd->TopEdge(),
512        DEFAULT_CBW_SIZE, DEFAULT_CBW_SIZE, mission.IsSkirmish(),
513        WIDGET_STYLE_NOBORDER|WIDGET_STYLE_GFX|WIDGET_ALIGN_RIGHT,
514        "S_kirmish Map", this );
515   wd->SetHook( this );
516 
517   wdy += wdh + 10;
518   wd = new ButtonWidget( B_ID_SET, w - 15 - sfont->Width() * 3, wdy,
519        sfont->Width() * 3 + 10, wdh, 0, "_Set", this );
520   wd->SetHook( this );
521 
522   // the user can use the following widget to choose which message to
523   // display in the textbox below
524   wdx = sfont->Width() * 7 + 10;
525   showmsg = new CycleWidget( B_ID_SHOWMSG, wdx, wdy,
526             wd->LeftEdge() - wdx - 5, wdh, WIDGET_ALIGN_LEFT,
527             "_Message", 0, msg_labels, this );
528   showmsg->SetHook( this );
529 
530   wdy += wdh + 5;
531   msg = new TextScrollWidget( 0, 5, wdy,
532         w - 10, h - wdh - 5 - wdy, mission.GetMessage(mission.GetName()),
533         0, NULL, this );
534 
535   new ButtonWidget( GUI_CLOSE, 1, h - wdh - 1, w - 2, wdh,
536                     WIDGET_DEFAULT, "_OK", this );
537 
538   Draw();
539   Show();
540   TLWListBuilder::BuildMsgList( mission.GetMessages(), tlist );
541 }
542 
543 ////////////////////////////////////////////////////////////////////////
544 // NAME       : EdMissionSetupWindow::WidgetActivated
545 // DESCRIPTION: React to user actions.
546 // PARAMETERS : widget - activated widget
547 //              win    - window containing the button
548 // RETURNS    : GUI status
549 ////////////////////////////////////////////////////////////////////////
550 
WidgetActivated(Widget * widget,Window * win)551 GUI_Status EdMissionSetupWindow::WidgetActivated( Widget *widget, Window *win ) {
552   TLWNode *n;
553   unsigned char pid;
554 
555   switch ( widget->ID() ) {
556   case B_ID_BRIEF1:
557   case B_ID_BRIEF2:
558     pid = widget->ID() - B_ID_BRIEF1;
559     n = static_cast<ListSelectWindow *>(win)->Selected();
560 
561     mission.GetPlayer(pid).SetBriefing( n ? n->ID() : -1 );
562     msg->SetText( n ? mission.GetMessage(n->ID()) : NULL );
563     break;
564 
565   case B_ID_NAME:
566     n = static_cast<ListSelectWindow *>(win)->Selected();
567     mission.SetName( n ? n->ID() : -1 );
568     msg->SetText( n ? mission.GetMessage(n->ID()) : NULL );
569     break;
570 
571   case B_ID_INFO:
572     n = static_cast<ListSelectWindow *>(win)->Selected();
573     mission.SetLevelInfoMsg( n ? n->ID() : -1 );
574     msg->SetText( n ? mission.GetMessage(n->ID()) : NULL );
575     break;
576 
577   case B_ID_CAMPAIGN:
578     mission.SetCampaign( widget->Clicked() );
579     break;
580 
581   case B_ID_CAMPAIGN_INFO:
582     n = static_cast<ListSelectWindow *>(win)->Selected();
583     mission.SetCampaignInfo( n ? n->ID() : -1 );
584     msg->SetText( n ? mission.GetMessage(n->ID()) : NULL );
585     break;
586 
587   case B_ID_CAMPAIGN_NAME:
588     n = static_cast<ListSelectWindow *>(win)->Selected();
589     mission.SetCampaignName( n ? n->ID() : -1 );
590     msg->SetText( n ? mission.GetMessage(n->ID()) : NULL );
591     break;
592 
593   case B_ID_SKIRMISH:
594     mission.SetSkirmish( widget->Clicked() );
595     break;
596 
597   case S_ID_SEQUEL:
598     mission.SetSequel( static_cast<StringWidget *>(widget)->String() );
599     break;
600 
601   case S_ID_MUSIC:
602     mission.SetMusic( static_cast<StringWidget *>(widget)->String() );
603     break;
604 
605   case B_ID_PLAYER1:
606   case B_ID_PLAYER2:
607     pid = widget->ID() - B_ID_PLAYER1;
608     n = static_cast<ListSelectWindow *>(win)->Selected();
609 
610     if ( n ) {
611       const char *name = mission.GetMessage(n->ID());
612       if ( name && strlen(name) > 30 ) {
613         new NoteWindow( "Error",
614                         "Player names must not be longer than 30 characters.",
615                         0, view );
616       } else {
617         mission.GetPlayer(pid).SetNameID( n->ID() );
618         msg->SetText( name );
619       }
620     } else {
621       mission.GetPlayer(pid).SetNameID( -1 );
622       msg->SetText( NULL );
623     }
624     break;
625 
626   case B_ID_SET: {
627     short msgid;
628     bool optional = true;
629     const char *label;
630 
631     switch ( showmsg->GetValue() ) {
632     case B_ID_NAME: msgid = mission.GetName(); optional = false;
633                     label = "Select name"; break;
634     case B_ID_INFO: msgid = mission.GetLevelInfoMsg();
635                     label = "Select info"; break;
636     case B_ID_PLAYER1: msgid = mission.GetPlayer(PLAYER_ONE).NameID(); optional = false;
637                     label = "Select name"; break;
638     case B_ID_PLAYER2: msgid = mission.GetPlayer(PLAYER_TWO).NameID(); optional = false;
639                     label = "Select name"; break;
640     case B_ID_BRIEF1: msgid = mission.GetPlayer(PLAYER_ONE).Briefing();
641                     label = "Select briefing"; break;
642     case B_ID_BRIEF2: msgid = mission.GetPlayer(PLAYER_TWO).Briefing();
643                     label = "Select briefing"; break;
644     case B_ID_CAMPAIGN_NAME: msgid = mission.GetCampaignName();
645                     label = "Select campaign name"; break;
646     case B_ID_CAMPAIGN_INFO: msgid = mission.GetCampaignInfo();
647                     label = "Select campaign info"; break;
648     default: return GUI_OK;
649     }
650 
651     new ListSelectWindow( label, tlist, msgid, optional,
652         this, showmsg->GetValue(), view );
653     break; }
654 
655   case B_ID_SHOWMSG: {
656     short msgid;
657 
658     switch ( showmsg->GetValue() ) {
659     case B_ID_NAME: msgid = mission.GetName(); break;
660     case B_ID_INFO: msgid = mission.GetLevelInfoMsg(); break;
661     case B_ID_PLAYER1: msgid = mission.GetPlayer(PLAYER_ONE).NameID(); break;
662     case B_ID_PLAYER2: msgid = mission.GetPlayer(PLAYER_TWO).NameID(); break;
663     case B_ID_BRIEF1: msgid = mission.GetPlayer(PLAYER_ONE).Briefing(); break;
664     case B_ID_BRIEF2: msgid = mission.GetPlayer(PLAYER_TWO).Briefing(); break;
665     case B_ID_CAMPAIGN_INFO: msgid = mission.GetCampaignInfo(); break;
666     case B_ID_CAMPAIGN_NAME: msgid = mission.GetCampaignName(); break;
667     default: msgid = -1;
668     }
669 
670     msg->SetText( (msgid == -1) ? NULL : mission.GetMessage(msgid) );
671     break; }
672 
673   case B_ID_PLAYERS:
674     mission.SetNumPlayers( static_cast<CycleWidget *>(widget)->GetValue()+1 );
675     break;
676   }
677 
678   msg->Draw();
679   msg->Show();
680   return GUI_OK;
681 }
682 
683 ////////////////////////////////////////////////////////////////////////
684 // NAME       : EdBuildingWindow::EdBuildingWindow
685 // DESCRIPTION: This window contains the settings for a building.
686 // PARAMETERS : bld     - building to be modified
687 //              mission - currently active mission
688 //              view    - pointer to the window's view
689 // RETURNS    : -
690 ////////////////////////////////////////////////////////////////////////
691 
EdBuildingWindow(Building & bld,Mission & mission,View * view)692 EdBuildingWindow::EdBuildingWindow( Building &bld, Mission &mission, View *view ) :
693                   Window(WIN_CENTER, view), b(bld), mission(mission) {
694   // calculate dimensions
695   SetSize( MIN(sfont->Width() * 50, view->Width()),
696            MIN(sfont->Height() * 12, view->Height()) );
697 
698   unsigned short xoff, yoff = 5, wdh = sfont->Height() + 10;
699 
700   ButtonWidget *btn;
701   btn = new ButtonWidget( B_ID_NAME, 5, yoff, sfont->Width() * 4 + 10, wdh,
702                           0, "_Name", this ),
703   btn->SetHook( this );
704 
705   xoff = 10 + btn->Width();
706   const char *name = b.Name();
707   b_name = new StringWidget( 0, xoff, yoff, w/2 - xoff - 5, wdh,
708                              name ? name : "Error", 30, WIDGET_STR_CONST,
709                              NULL, this );
710 
711   xoff = w/2 + 10 + sfont->TextWidth("Player");
712   b_player = new CycleWidget( B_ID_PLAYER, xoff, yoff, w - 5 - xoff, wdh,
713                               0, "_Player", b.Owner(), player3_labels, this );
714   yoff += 5 + wdh;
715 
716   btn = new CheckboxWidget( B_ID_WORKSHOP, 5, yoff, DEFAULT_CBW_SIZE,
717                             DEFAULT_CBW_SIZE, b.IsWorkshop(),
718                             WIDGET_STYLE_NOBORDER|WIDGET_STYLE_GFX|WIDGET_ALIGN_RIGHT,
719                             "_Workshop", this );
720   btn->SetHook( this );
721 
722   btn = new CheckboxWidget( B_ID_FACTORY, 5, yoff + DEFAULT_CBW_SIZE + 5,
723                             DEFAULT_CBW_SIZE, DEFAULT_CBW_SIZE, b.IsFactory(),
724                             WIDGET_STYLE_NOBORDER|WIDGET_STYLE_GFX|WIDGET_ALIGN_RIGHT,
725                             "_Factory", this );
726   btn->SetHook( this );
727 
728   btn = new ButtonWidget( B_ID_UNITS, w/2 + 5, yoff, (w - 20)/2, wdh,
729                           0, "_Units...", this ),
730   btn->SetHook( this );
731   yoff += 5 + wdh;
732 
733   b_factory_units = new ButtonWidget( B_ID_FACTORY_UNITS, btn->LeftEdge(), yoff,
734                     btn->Width(), wdh, (b.IsFactory() ? 0 : WIDGET_DISABLED),
735                     "F_actory Settings...", this );
736   b_factory_units->SetHook( this );
737   yoff += 5 + wdh;
738 
739   xoff = 10 + sfont->TextWidth("Crystals");
740   b_crystals = new NumberWidget( 0, xoff, yoff, sfont->Width() * 4 + 10, wdh,
741                                  b.Crystals(), 0, b.MaxCrystals(),
742                                  WIDGET_ALIGN_LEFT, "_Crystals", this );
743 
744   xoff += b_crystals->Width() + 10 + sfont->TextWidth("of");
745   b_maxcrystals = new NumberWidget( N_ID_MAXCRYSTALS, xoff, yoff,
746                                   b_crystals->Width(), wdh,
747                                   b.MaxCrystals(), 0, 10000,
748                                   WIDGET_ALIGN_LEFT, "of", this );
749   b_maxcrystals->SetHook( this );
750 
751   xoff = w/2 + 5 + sfont->Width() * 7;
752   b_mining = new NumberWidget( 0, xoff, yoff,
753                                b_crystals->Width(), wdh,
754                                b.CrystalProduction(), 0, 1000,
755                                WIDGET_ALIGN_LEFT, "O_utput", this );
756 
757   btn = new ButtonWidget( B_ID_OK, 1, h - 1 - wdh, w - 2, wdh,
758                           WIDGET_DEFAULT, "_OK", this ),
759   btn->SetHook( this );
760 
761   Draw();
762   Show();
763 
764   TLWListBuilder::BuildUnitTypesList( mission.GetUnitSet(), fact_list_na );
765   if ( b.IsFactory() ) {
766     TLWNode *n = static_cast<TLWNode *>(fact_list_na.Head()), *n2;
767 
768     while ( n ) {
769       n2 = static_cast<TLWNode *>(n->Next());
770       if ( b.CanProduce( n->ID() ) ) {
771         n->Remove();
772         fact_list_ok.InsertNodeSorted( n );
773       }
774       n = n2;
775     }
776   }
777   TLWListBuilder::BuildMsgList( mission.GetMessages(), msg_list );
778 }
779 
780 ////////////////////////////////////////////////////////////////////////
781 // NAME       : EdBuildingWindow::WidgetActivated
782 // DESCRIPTION: React to user actions.
783 // PARAMETERS : widget - activated widget
784 //              win    - window containing the widget
785 // RETURNS    : GUI status
786 ////////////////////////////////////////////////////////////////////////
787 
WidgetActivated(Widget * widget,Window * win)788 GUI_Status EdBuildingWindow::WidgetActivated( Widget *widget, Window *win ) {
789 
790   switch ( widget->ID() ) {
791   case B_ID_WORKSHOP:
792     if ( widget->Clicked() ) b.SetFlags( BLD_WORKSHOP );
793     else b.UnsetFlags( BLD_WORKSHOP );
794     break;
795   case B_ID_FACTORY:
796     if ( widget->Clicked() ) {
797       b.SetFlags( BLD_FACTORY );
798       b_factory_units->Enable();
799     } else {
800       b.UnsetFlags( BLD_FACTORY );
801       b_factory_units->Disable();
802     }
803     b_factory_units->Draw();
804     b_factory_units->Show();
805     break;
806   case B_ID_UNITS:
807     new EdUnitsWindow( mission, b, view );
808     break;
809   case B_ID_FACTORY_UNITS:
810     new ListExchangeWindow( fact_list_na, fact_list_ok,
811                             "Not Available", "Available", view );
812     break;
813   case B_ID_OK:
814     view->CloseWindow( win );
815     b.SetOwner( b_player->GetValue() );
816     b.SetCrystals( b_crystals->Number() );
817     b.SetMaxCrystals( b_maxcrystals->Number() );
818     b.SetCrystalProduction( b_mining->Number() );
819 
820     for ( Unit *u = static_cast<Unit *>(mission.GetUnits().Head()); u;
821           u = static_cast<Unit *>(u->Next()) ) {
822       if ( u->Position() == b.Position() ) u->SetOwner( b.Owner() );
823     }
824 
825     // fix factory settings
826     if ( b.IsFactory() ) {
827       unsigned long unitmask = 0;
828 
829       for ( TLWNode *n = static_cast<TLWNode *>(fact_list_ok.Head());
830             n; n = static_cast<TLWNode *>(n->Next()) ) {
831         unitmask |= (1 << n->ID());
832       }
833       b.SetUnitProduction( unitmask );
834     } else b.SetUnitProduction( 0 );
835     break;
836   case N_ID_MAXCRYSTALS:
837     b_crystals->SetMax( b_maxcrystals->Number() );
838     break;
839   case B_ID_NAME:
840     new ListSelectWindow( "Select name", msg_list,
841         b.NameID(), false, this, B_ID_NAME_OK, view );
842     break;
843   case B_ID_NAME_OK: {
844     TLWNode *mn = static_cast<ListSelectWindow *>(win)->Selected();
845 
846     const char *name = (mn ? mission.GetMessage(mn->ID()) : NULL);
847     if ( name && strlen(name) > 30 ) {
848       new NoteWindow( "Error",
849                       "Shop names must not be longer than 30 characters.",
850                       0, view );
851     } else {
852       b.SetNameID( mn ? mn->ID() : -1 );
853       b.SetName( name );
854       b_name->SetString( name ? name : "Error" );
855     }
856     break; }
857   }
858 
859   return GUI_OK;
860 }
861 
862 ////////////////////////////////////////////////////////////////////////
863 // NAME       : EdUnitWindow::EdUnitWindow
864 // DESCRIPTION: This window contains the settings for a unit.
865 // PARAMETERS : unit    - unit to be modified
866 //              mission - currently active mission
867 //              view    - pointer to the window's view
868 // RETURNS    : -
869 ////////////////////////////////////////////////////////////////////////
870 
EdUnitWindow(Unit & unit,Mission & mission,View * view)871 EdUnitWindow::EdUnitWindow( Unit &unit, Mission &mission, View *view ) :
872               Window(WIN_CENTER, view), u(unit), mission(mission), mv(0) {
873   // calculate dimensions
874   SetSize( MIN(sfont->Width() * 50, view->Width()),
875            MIN(sfont->Height() * 12, view->Height()) );
876 
877   unsigned short xoff, yoff = 5, wdh = sfont->Height() + 10;
878 
879   Widget *wd = new StringWidget( 0, 5, yoff, w/2 - 10, wdh,
880                    u.Name(), 19,
881                    WIDGET_STYLE_NOBORDER|WIDGET_STR_CONST,
882                    NULL, this );
883   xoff = w/2 + 10 + sfont->TextWidth("Player");
884   u_player = new CycleWidget( B_ID_PLAYER, xoff, yoff, w - 5 - xoff, wdh,
885                               (u.IsSheltered() ? WIDGET_DISABLED : 0),
886                               "_Player", u.Owner(),
887                               (u.IsSheltered() ? player3_labels : player2_labels),
888                               this );
889   u_player->SetHook( this );
890   yoff += 5 + wdh;
891 
892   new NumberWidget( 0, wd->LeftEdge() + 10 + sfont->Width() * 2,
893                     yoff, sfont->Width() * 6, wdh, u.ID(), 0, 32000,
894                     WIDGET_ALIGN_LEFT|WIDGET_STYLE_NOBORDER|WIDGET_STR_CONST,
895                     "ID", this );
896 
897   xoff = w/2 + 10 + sfont->TextWidth("Direction");
898   wd = new CycleWidget( B_ID_DIRECTION, xoff, yoff, w - 5 - xoff, wdh,
899                         0, "_Direction", u.GetDirection(), dir_labels, this );
900   wd->SetHook( this );
901   yoff += 5 + wdh;
902 
903   xoff = 10 + sfont->TextWidth("Squad Size");
904   wd = new CycleWidget( B_ID_SIZE, xoff, yoff, w/2 - 5 - xoff, wdh,
905                         0, "Squad _Size", u.GroupSize() - 1, size_labels, this );
906   wd->SetHook( this );
907 
908   xoff = w/2 + 10 + sfont->TextWidth("XP Level");
909   wd = new CycleWidget( B_ID_XP, xoff, yoff, w - 5 - xoff, wdh,
910                         0, "XP _Level", u.XPLevel(), xp_labels, this );
911   wd->SetHook( this );
912 
913   yoff += 5 + wdh;
914   if ( u.IsTransport() && !u.IsSheltered() ) {
915     xoff = 10 + sfont->TextWidth("Crystals");
916     // maximum number of crystals should be
917     // (mission.StorageLeft(u) + (u.Crystals() + 9)/10) * 10,
918     // but we can't update that maxium when the list of contained units
919     // changes, so we allow the maximum here and update manually when the
920     // widget is activated if necessary
921     wd = new NumberWidget( N_ID_CRYSTALS, xoff, yoff, sfont->Width() * 6, wdh,
922                            u.Crystals(), 0, u.Type()->Slots() * 10,
923                            WIDGET_ALIGN_LEFT, "_Crystals", this );
924     wd->SetHook( this );
925 
926     wd = new ButtonWidget( B_ID_UNITS, w/2 + 5, yoff, (w - 20)/2, wdh,
927                            0, "_Units...", this ),
928     wd->SetHook( this );
929   }
930 
931   wd = new ButtonWidget( B_ID_OK, 1, h - 1 - wdh, w - 2, wdh,
932                          WIDGET_DEFAULT, "_OK", this ),
933   wd->SetHook( this );
934 
935   Draw();
936   Show();
937 }
938 
939 ////////////////////////////////////////////////////////////////////////
940 // NAME       : EdUnitWindow::WidgetActivated
941 // DESCRIPTION: React to user actions.
942 // PARAMETERS : widget - activated widget
943 //              win    - window containing the widget
944 // RETURNS    : GUI status
945 ////////////////////////////////////////////////////////////////////////
946 
WidgetActivated(Widget * widget,Window * win)947 GUI_Status EdUnitWindow::WidgetActivated( Widget *widget, Window *win ) {
948   GUI_Status rc = GUI_OK;
949 
950   switch ( widget->ID() ) {
951   case B_ID_DIRECTION:
952     u.SetDirection( static_cast<CycleWidget *>(widget)->GetValue() );
953     break;
954   case B_ID_SIZE:
955     u.SetGroupSize( static_cast<CycleWidget *>(widget)->GetValue() + 1 );
956     break;
957   case B_ID_XP:
958     u.SetXP( static_cast<CycleWidget *>(widget)->GetValue() * XP_PER_LEVEL );
959     break;
960   case B_ID_UNITS:
961     new EdUnitsWindow( mission, u, view );
962     break;
963   case N_ID_CRYSTALS: {
964     NumberWidget *cwidget = static_cast<NumberWidget *>(widget);
965     unsigned short crystals = MIN( cwidget->Number(),
966                   (mission.StorageLeft(u) + (u.Crystals() + 9)/10) * 10 );
967     if ( cwidget->Number() != crystals ) cwidget->SetNumber( crystals );
968     u.SetCrystals( crystals );
969     break; }
970   case B_ID_OK:
971     view->CloseWindow( win );
972     u.SetOwner( u_player->GetValue() );
973 
974     if ( u.IsTransport() && !u.IsSheltered() ) {
975       for ( Unit *walk = static_cast<Unit *>(mission.GetUnits().Head());
976             walk; walk = static_cast<Unit *>(walk->Next()) ) {
977         if ( walk->Position() == u.Position() ) walk->SetOwner( u.Owner() );
978       }
979     }
980 
981     if ( mv ) view->Refresh( mv->UpdateHex(u.Position()) );
982     break;
983   }
984 
985   return rc;
986 }
987 
988 ////////////////////////////////////////////////////////////////////////
989 // NAME       : EdEventsWindow::EdEventsWindow
990 // DESCRIPTION: This window presents the list of events for selection.
991 // PARAMETERS : mission - current mission
992 //              view    - pointer to the window's view
993 // RETURNS    : -
994 ////////////////////////////////////////////////////////////////////////
995 
EdEventsWindow(Mission & mission,View * view)996 EdEventsWindow::EdEventsWindow( Mission &mission, View *view ) :
997               Window(WIN_CENTER, view), mission(mission) {
998   // calculate dimensions
999   SetSize( MIN(sfont->Width() * 20 + 20, view->Width()),
1000            MIN( MAX(mission.GetEvents().CountNodes() + 2, 6) *
1001                    (sfont->Height() + 2) + 40,
1002                 view->Height()) );
1003 
1004   TLWListBuilder::BuildEventList( mission.GetEvents(), nodes );
1005 
1006   e_list = new TextListWidget( 0, 5, 5, w - 10, h - sfont->Height() * 2 - 30,
1007                &nodes, -1, WIDGET_HSCROLLKEY|WIDGET_VSCROLLKEY, NULL, this );
1008 
1009   ButtonWidget *btn = new ButtonWidget( B_ID_NEW,
1010                1, h - sfont->Height() * 2 - 17, (w - 2)/2, sfont->Height() + 8,
1011                0, "_New", this );
1012   btn->SetHook( this );
1013 
1014   btn = new ButtonWidget( B_ID_EDIT,
1015               btn->LeftEdge() + btn->Width(), btn->TopEdge(),
1016               btn->Width(), btn->Height(), 0, "_Edit", this );
1017   btn->SetHook( this );
1018 
1019   btn = new ButtonWidget( B_ID_DELETE,
1020               btn->LeftEdge() - btn->Width(), btn->TopEdge() + btn->Height(),
1021               btn->Width(), btn->Height(), 0, "_Delete", this );
1022   btn->SetHook( this );
1023 
1024   btn = new ButtonWidget( B_ID_OK,
1025               btn->LeftEdge() + btn->Width(), btn->TopEdge(),
1026               btn->Width(), btn->Height(), 0, "_OK", this );
1027   btn->SetHook( this );
1028 
1029   Draw();
1030   Show();
1031 }
1032 
1033 ////////////////////////////////////////////////////////////////////////
1034 // NAME       : EdEventsWindow::WidgetActivated
1035 // DESCRIPTION: React to user actions.
1036 // PARAMETERS : button - activated button widget
1037 //              win    - window containing the button
1038 // RETURNS    : GUI status
1039 ////////////////////////////////////////////////////////////////////////
1040 
WidgetActivated(Widget * button,Window * win)1041 GUI_Status EdEventsWindow::WidgetActivated( Widget *button, Window *win ) {
1042 
1043   switch ( button->ID() ) {
1044   case B_ID_EDIT: {
1045     TLWNode *n = static_cast<TLWNode *>( e_list->Selected() );
1046     if ( n ) {
1047       Event *e = (Event *)n->UserData();
1048       new EdEventGenericWindow( *e, mission, view );
1049     }
1050     break; }
1051   case B_ID_NEW:
1052     view->CloseWindow( win );
1053     new SelectEventWindow( mission, view );
1054     break;
1055   case B_ID_DELETE: {
1056     TLWNode *n = static_cast<TLWNode *>( e_list->Selected() );
1057     if ( n ) {
1058       Event *e = (Event *)n->UserData();
1059 
1060       n->Remove();
1061       delete n;
1062       e_list->Update();
1063       e_list->Draw();
1064       e_list->Show();
1065 
1066       mission.DeleteEvent( e );
1067     }
1068     break; }
1069   case B_ID_OK:
1070     view->CloseWindow( win );
1071     break;
1072   }
1073 
1074   return GUI_OK;
1075 }
1076 
1077 
1078 ////////////////////////////////////////////////////////////////////////
1079 // NAME       : SelectEventWindow::SelectEventWindow
1080 // DESCRIPTION: This window presents the list of event types for the
1081 //              purpose of creating a new event.
1082 // PARAMETERS : mission - current mission
1083 //              view    - pointer to the window's view
1084 // RETURNS    : -
1085 ////////////////////////////////////////////////////////////////////////
1086 
SelectEventWindow(Mission & mission,View * view)1087 SelectEventWindow::SelectEventWindow( Mission &mission, View *view ) :
1088                    Window(WIN_CENTER, view), mission(mission) {
1089   // calculate dimensions
1090   SetSize( MIN(sfont->Width() * 18 + 20, view->Width()),
1091            MIN( 10 * (sfont->Height() + 2) + 42, view->Height()) );
1092 
1093   BuildTLWList( nodes );
1094 
1095   e_list = new TextListWidget( 0, 5, 5, w - 10, h - sfont->Height() * 3 - 36,
1096                &nodes, -1, WIDGET_HSCROLLKEY|WIDGET_VSCROLLKEY, NULL, this );
1097 
1098   e_trigger = new CycleWidget( B_ID_TRIGGER, e_list->LeftEdge(),
1099                   e_list->TopEdge() + e_list->Height() + sfont->Height() + 10,
1100                   e_list->Width(), sfont->Height() + 8, WIDGET_ALIGN_TOP,
1101                   "_Triggered by", 0, etrigger_labels, this );
1102 
1103   ButtonWidget *btn = new ButtonWidget( B_ID_OK,
1104                1, h - sfont->Height() - 9, (w - 2)/2, sfont->Height() + 8,
1105                WIDGET_DEFAULT, "_OK", this );
1106   btn->SetHook( this );
1107 
1108   btn = new ButtonWidget( B_ID_CANCEL,
1109               btn->LeftEdge() + btn->Width(), btn->TopEdge(),
1110               btn->Width(), btn->Height(), 0, "_Cancel", this );
1111   btn->SetHook( this );
1112 
1113   Draw();
1114   Show();
1115 }
1116 
1117 ////////////////////////////////////////////////////////////////////////
1118 // NAME       : SelectEventWindow::WidgetActivated
1119 // DESCRIPTION: React to user actions.
1120 // PARAMETERS : button - activated button widget
1121 //              win    - window containing the button
1122 // RETURNS    : GUI status
1123 ////////////////////////////////////////////////////////////////////////
1124 
WidgetActivated(Widget * button,Window * win)1125 GUI_Status SelectEventWindow::WidgetActivated( Widget *button, Window *win ) {
1126 
1127   switch ( button->ID() ) {
1128   case B_ID_OK:
1129     if ( e_list->Selected() ) {
1130       view->CloseWindow( win );
1131 
1132       Event *e = mission.CreateEvent(
1133                      static_cast<TLWNode *>(e_list->Selected())->ID(),
1134                      e_trigger->GetValue() );
1135       if ( e ) new EdEventGenericWindow( *e, mission, view );
1136     }
1137     break;
1138   case B_ID_CANCEL:
1139     view->CloseWindow( win );
1140     break;
1141   }
1142 
1143   return GUI_OK;
1144 }
1145 
1146 ////////////////////////////////////////////////////////////////////////
1147 // NAME       : SelectEventWindow::BuildTLWList
1148 // DESCRIPTION: Create a list for a TextListWidget containing all
1149 //              available event types.
1150 // PARAMETERS : list - list of TLWNodes to be filled
1151 // RETURNS    : -
1152 ////////////////////////////////////////////////////////////////////////
1153 
BuildTLWList(TLWList & events) const1154 void SelectEventWindow::BuildTLWList( TLWList &events ) const {
1155   for ( int i = 0; event_labels[i]; ++i )
1156     events.InsertNodeSorted( new TLWNode(event_labels[i], 0, i+1) );
1157 }
1158 
1159 
1160 ////////////////////////////////////////////////////////////////////////
1161 // NAME       : EdUnitsWindow::EdUnitsWindow
1162 // DESCRIPTION: This window presents the list of units in a unit
1163 //              container (building or transporter) for selection.
1164 // PARAMETERS : mission   - current mission
1165 //              container - selected unit container
1166 //              view      - pointer to the window's view
1167 // RETURNS    : -
1168 ////////////////////////////////////////////////////////////////////////
1169 
EdUnitsWindow(Mission & mission,MapObject & container,View * view)1170 EdUnitsWindow::EdUnitsWindow( Mission &mission, MapObject &container, View *view ) :
1171               Window(WIN_CENTER, view), mission(mission), container(container) {
1172   TLWListBuilder::BuildUnitList( mission.GetUnits(), nodes, container.Position() );
1173 
1174   // calculate dimensions
1175   SetSize( MIN(sfont->Width() * 30 + 20, view->Width()),
1176            MIN( MAX(nodes.CountNodes() + 2, 6) *
1177                    (sfont->Height() + 2) + 40,
1178                 view->Height()) );
1179 
1180   e_list = new TextListWidget( 0, 5, 5, w - 10, h - sfont->Height() * 2 - 30,
1181                &nodes, -1, WIDGET_HSCROLLKEY|WIDGET_VSCROLLKEY, NULL, this );
1182 
1183   ButtonWidget *btn = new ButtonWidget( B_ID_NEW,
1184                1, h - sfont->Height() * 2 - 17, (w - 2)/2, sfont->Height() + 8,
1185                0, "_New", this );
1186   btn->SetHook( this );
1187 
1188   btn = new ButtonWidget( B_ID_EDIT,
1189               btn->LeftEdge() + btn->Width(), btn->TopEdge(),
1190               btn->Width(), btn->Height(), 0, "_Edit", this );
1191   btn->SetHook( this );
1192 
1193   btn = new ButtonWidget( B_ID_DELETE,
1194               btn->LeftEdge() - btn->Width(), btn->TopEdge() + btn->Height(),
1195               btn->Width(), btn->Height(), 0, "_Delete", this );
1196   btn->SetHook( this );
1197 
1198   btn = new ButtonWidget( B_ID_OK,
1199               btn->LeftEdge() + btn->Width(), btn->TopEdge(),
1200               btn->Width(), btn->Height(), 0, "_OK", this );
1201   btn->SetHook( this );
1202 
1203   Draw();
1204   Show();
1205 }
1206 
1207 ////////////////////////////////////////////////////////////////////////
1208 // NAME       : EdUnitsWindow::WidgetActivated
1209 // DESCRIPTION: React to user actions.
1210 // PARAMETERS : button - activated button widget
1211 //              win    - window containing the button
1212 // RETURNS    : GUI status
1213 ////////////////////////////////////////////////////////////////////////
1214 
WidgetActivated(Widget * button,Window * win)1215 GUI_Status EdUnitsWindow::WidgetActivated( Widget *button, Window *win ) {
1216   TLWNode *n;
1217 
1218   switch ( button->ID() ) {
1219   case B_ID_EDIT:
1220     n = static_cast<TLWNode *>( e_list->Selected() );
1221     if ( n ) new EdUnitWindow( *((Unit *)n->UserData()), mission, view );
1222     break;
1223   case B_ID_NEW:
1224     utypes.Clear();
1225     BuildTLWList( utypes );
1226     new SelectUnitWindow( mission, utypes, this, view );
1227     break;
1228   case B_ID_DELETE:
1229     n = static_cast<TLWNode *>( e_list->Selected() );
1230     if ( n ) {
1231       Unit *u = (Unit *)n->UserData();
1232 
1233       n->Remove();
1234       delete n;
1235       e_list->Update();
1236       e_list->Draw();
1237       e_list->Show();
1238 
1239       mission.DeleteUnit( u );
1240     }
1241     break;
1242   case B_ID_OK:
1243     view->CloseWindow( win );
1244     break;
1245 
1246   case B_ID_SUW_OK: {
1247     const UnitType *ut = static_cast<SelectUnitWindow *>(win)->Selected();
1248     view->CloseWindow( win );
1249 
1250     if ( ut ) {
1251       Unit *u = mission.CreateUnit( ut, container.Owner(), container.Position() );
1252       if ( u ) {
1253         char buf[28];
1254         sprintf( buf, "%s (%d)", u->Name(), u->ID() );
1255         nodes.InsertNodeSorted( new TLWNode( buf, u ) );
1256         e_list->Update();
1257         e_list->Draw();
1258         e_list->Show();
1259       }
1260     }
1261     break; }
1262   }
1263 
1264   return GUI_OK;
1265 }
1266 
1267 ////////////////////////////////////////////////////////////////////////
1268 // NAME       : EdUnitsWindow::BuildTLWList
1269 // DESCRIPTION: Construct a list with all legal unit types for the
1270 //              current container.
1271 // PARAMETERS : list - list to add nodes to
1272 // RETURNS    : -
1273 ////////////////////////////////////////////////////////////////////////
1274 
BuildTLWList(TLWList & list) const1275 void EdUnitsWindow::BuildTLWList( TLWList &list ) const {
1276   const UnitSet &us = mission.GetUnitSet();
1277   TLWListBuilder::BuildUnitTypesList( us, list );
1278 
1279   // now remove all unit types which are not allowed by the container
1280   unsigned short maxw = container.MaxWeight();
1281   if ( container.IsUnit() )
1282     maxw = MIN( maxw, mission.StorageLeft(static_cast<Unit &>(container)) );
1283 
1284   TLWNode *n = static_cast<TLWNode *>(list.Head()), *n2;
1285   while ( n ) {
1286 
1287     const UnitType *ut = us.GetUnitInfo( n->ID() );
1288     if ( (ut->Weight() < container.MinWeight()) ||
1289          (ut->Weight() > maxw) ) {
1290       n2 = static_cast<TLWNode *>(n->Next());
1291       n->Remove();
1292       delete n;
1293       n = n2;
1294     } else n = static_cast<TLWNode *>(n->Next());
1295   }
1296 }
1297 
1298 ////////////////////////////////////////////////////////////////////////
1299 // NAME       : SelectUnitWindow::SelectUnitWindow
1300 // DESCRIPTION: This window presents the list of unit types.
1301 // PARAMETERS : mission - current mission
1302 //              hook    - widget hook to activate when OK is pressed
1303 //              view    - pointer to the window's view
1304 // RETURNS    : -
1305 ////////////////////////////////////////////////////////////////////////
1306 
SelectUnitWindow(Mission & mission,WidgetHook * hook,View * view)1307 SelectUnitWindow::SelectUnitWindow( Mission &mission,
1308                   WidgetHook *hook, View *view ) :
1309                    Window(WIN_CENTER, view), mission(mission) {
1310   TLWListBuilder::BuildUnitTypesList( mission.GetUnitSet(), nodes );
1311 
1312   Init( nodes, hook );
1313 }
1314 
1315 
1316 ////////////////////////////////////////////////////////////////////////
1317 // NAME       : SelectUnitWindow::SelectUnitWindow
1318 // DESCRIPTION: This window presents the list of unit types.
1319 // PARAMETERS : mission - current mission
1320 //              list    - use this list instead of constructing our own
1321 //              hook    - widget hook to activate when OK is pressed
1322 //              view    - pointer to the window's view
1323 // RETURNS    : -
1324 ////////////////////////////////////////////////////////////////////////
1325 
SelectUnitWindow(Mission & mission,TLWList & list,WidgetHook * hook,View * view)1326 SelectUnitWindow::SelectUnitWindow( Mission &mission, TLWList &list,
1327                   WidgetHook *hook, View *view ) :
1328                   Window(WIN_CENTER, view), mission(mission) {
1329   Init( list, hook );
1330 }
1331 
1332 ////////////////////////////////////////////////////////////////////////
1333 // NAME       : SelectUnitWindow::Init
1334 // DESCRIPTION: Set up the window properties and create the widgets.
1335 // PARAMETERS : list - list to present
1336 //              hook - widget hook to activate when OK is pressed
1337 // RETURNS    : -
1338 ////////////////////////////////////////////////////////////////////////
1339 
Init(TLWList & list,WidgetHook * hook)1340 void SelectUnitWindow::Init( TLWList &list, WidgetHook *hook ) {
1341   // calculate dimensions
1342   SetSize( MIN(sfont->Width() * 24 + 20, view->Width()),
1343            MIN( MAX(list.CountNodes() + 1, 6) *
1344                    (sfont->Height() + 2) + 25,
1345                 view->Height()) );
1346 
1347   e_list = new TextListWidget( 0, 5, 5, w - 10, h - sfont->Height() - 15,
1348                &list, -1, WIDGET_HSCROLLKEY|WIDGET_VSCROLLKEY, NULL, this );
1349 
1350   ButtonWidget *btn = new ButtonWidget( B_ID_SUW_OK,
1351                1, h - sfont->Height() - 9, (w - 2)/2, sfont->Height() + 8,
1352                WIDGET_DEFAULT, "_OK", this );
1353   btn->SetHook( hook );
1354 
1355   btn = new ButtonWidget( B_ID_CANCEL,
1356               btn->LeftEdge() + btn->Width(), btn->TopEdge(),
1357               btn->Width(), btn->Height(), 0, "_Cancel", this );
1358   btn->SetHook( this );
1359 
1360   Draw();
1361   Show();
1362 }
1363 
1364 ////////////////////////////////////////////////////////////////////////
1365 // NAME       : SelectUnitWindow::WidgetActivated
1366 // DESCRIPTION: React to user actions.
1367 // PARAMETERS : button - activated button widget
1368 //              win    - window containing the button
1369 // RETURNS    : GUI status
1370 ////////////////////////////////////////////////////////////////////////
1371 
WidgetActivated(Widget * button,Window * win)1372 GUI_Status SelectUnitWindow::WidgetActivated( Widget *button, Window *win ) {
1373 
1374   if ( button->ID() == B_ID_CANCEL ) view->CloseWindow( win );
1375 
1376   return GUI_OK;
1377 }
1378 
1379 ////////////////////////////////////////////////////////////////////////
1380 // NAME       : SelectUnitWindow::Selected
1381 // DESCRIPTION: Get the selected unit type.
1382 // PARAMETERS : -
1383 // RETURNS    : selected unit type or NULL if none selected
1384 ////////////////////////////////////////////////////////////////////////
1385 
Selected(void) const1386 const UnitType *SelectUnitWindow::Selected( void ) const {
1387   TLWNode *n = static_cast<TLWNode *>(e_list->Selected());
1388 
1389   if ( n ) return mission.GetUnitSet().GetUnitInfo( n->ID() );
1390   return NULL;
1391 }
1392 
1393 
1394 ////////////////////////////////////////////////////////////////////////
1395 // NAME       : EdMsgWindow::EdMsgWindow
1396 // DESCRIPTION: This window offers a way to create new language catalogs
1397 //              and messages or modify existing ones.
1398 // PARAMETERS : locale - locale of the current mission
1399 //              view   - pointer to the window's view
1400 // RETURNS    : -
1401 ////////////////////////////////////////////////////////////////////////
1402 
EdMsgWindow(Locale & locale,View * view)1403 EdMsgWindow::EdMsgWindow( Locale &locale, View *view ) :
1404      Window(WIN_CENTER, view), locale(locale), cur_lang(0), cur_msg_id(-1) {
1405   // calculate dimensions
1406   SetSize( MIN(sfont->Width() * 35 + 20, view->Width()),
1407            MIN((sfont->Height() + 2) * 10 + 50, view->Height()) );
1408 
1409   BuildLangsList();
1410   if ( !l_langs.IsEmpty() ) {
1411     cur_lang = static_cast<Language *>(static_cast<TLWNode *>(l_langs.Head())->UserData());
1412     BuildMsgsList();
1413     if ( !l_msgs.IsEmpty() ) cur_msg_id = 0;
1414   }
1415 
1416   ButtonWidget *btn;
1417   unsigned short wdh = sfont->Height() + 8, wdw, wdy;
1418 
1419   wdw = sfont->TextWidth("Languages") + 10;
1420 
1421   w_langs = new TextListWidget( L_ID_LANGS,
1422             wdw * 2 + 20, 10, w - 30 - wdw * 2, sfont->Height() + 10 + 2 * wdh,
1423             &l_langs, l_langs.IsEmpty() ? -1 : 0, WIDGET_HSCROLLKEY, NULL, this );
1424   w_langs->SetHook( this );
1425 
1426   btn = new ButtonWidget( B_ID_LANG_NEW,
1427             10, w_langs->TopEdge() + sfont->Height() + 5, wdw, wdh,
1428             0, "_New", this );
1429   btn->SetHook( this );
1430 
1431   wdw = sfont->TextWidth("ID") + 5;
1432   w_lid = new StringWidget( 0,
1433           btn->LeftEdge() + wdw, btn->TopEdge() + wdh + 5,
1434           btn->Width() - wdw, wdh,
1435           NULL, 2, WIDGET_ALIGN_LEFT, "_ID", this );
1436 
1437   btn = new ButtonWidget( B_ID_LANG_DELETE,
1438             btn->LeftEdge() + btn->Width() + 5, btn->TopEdge(),
1439             btn->Width(), wdh, 0, "_Delete", this );
1440   btn->SetHook( this );
1441 
1442   wdy = w_lid->TopEdge() + wdh + 10;
1443   w_msgs = new TextListWidget( L_ID_MSGS,
1444            5, wdy, w - 10, h - wdy - 2 * wdh - 15,
1445            &l_msgs, cur_msg_id, WIDGET_VSCROLLKEY, NULL, this );
1446   w_msgs->SetHook( this );
1447 
1448   wdw = sfont->TextWidth("Text") + 10;
1449   w_msg = new StringWidget( 0,
1450           wdw, w_msgs->TopEdge() + w_msgs->Height() + 5, w - wdw - 5, wdh,
1451           cur_msg_id == -1 ? NULL : cur_lang->GetMsg(cur_msg_id), 4096,
1452           WIDGET_ALIGN_LEFT, "_Text", this );
1453 
1454   btn = new ButtonWidget( B_ID_MSG_ADD,
1455             w_msgs->LeftEdge(), w_msg->TopEdge() + wdh + 5, (w - 20) / 4, wdh,
1456             0, "_Add", this );
1457   btn->SetHook( this );
1458   btn = new ButtonWidget( B_ID_MSG_MODIFY,
1459             btn->LeftEdge() + btn->Width() + 5, btn->TopEdge(), btn->Width(), wdh,
1460             0, "_Modify", this );
1461   btn->SetHook( this );
1462 
1463   btn = new ButtonWidget( GUI_CLOSE,
1464             w - 5 - btn->Width(), btn->TopEdge(), btn->Width(), wdh,
1465             0, "_OK", this );
1466 
1467   Draw();
1468   Show();
1469 }
1470 
1471 ////////////////////////////////////////////////////////////////////////
1472 // NAME       : EdMsgWindow::WidgetActivated
1473 // DESCRIPTION: React to user actions.
1474 // PARAMETERS : widget - activated widget
1475 //              win    - window containing the widget
1476 // RETURNS    : GUI status
1477 ////////////////////////////////////////////////////////////////////////
1478 
WidgetActivated(Widget * widget,Window * win)1479 GUI_Status EdMsgWindow::WidgetActivated( Widget *widget, Window *win ) {
1480   TLWNode *n;
1481 
1482   switch ( widget->ID() ) {
1483   case L_ID_LANGS:
1484     n = static_cast<TLWNode *>( w_langs->Selected() );
1485     if ( n ) {
1486       cur_lang = static_cast<Language *>( n->UserData() );
1487       BuildMsgsList();
1488       w_msgs->Update();
1489       if ( cur_msg_id != -1 ) {
1490         w_msgs->Select( cur_msg_id );
1491         w_msg->SetString( cur_lang->GetMsg( cur_msg_id ) );
1492       }
1493       w_msgs->Draw();
1494       w_msgs->Show();
1495     }
1496     break;
1497   case L_ID_MSGS:
1498     n = static_cast<TLWNode *>( w_msgs->Selected() );
1499     if ( n ) {
1500       w_msg->SetString( (char *)n->UserData() );
1501       cur_msg_id = n->ID();
1502     }
1503     break;
1504 
1505   case B_ID_LANG_NEW: {
1506     Language l;
1507 
1508     if ( l_langs.CountNodes() == 0 ) {
1509       // add empty English catalog
1510       l.SetID( CF_LANG_DEFAULT );
1511     } else {
1512       // copy English catalog to new language
1513       const char *id = w_lid->String();
1514       if ( id ) {
1515         if ( locale.GetLanguage( id ) ) {
1516           new NoteWindow( "Error", "This ID is already taken.", 0, view );
1517           break;
1518         } else {
1519           const Language *def = locale.GetLanguage( CF_LANG_DEFAULT );
1520           l.SetID( id );
1521           for ( int i = 0; i < def->Size(); ++i )
1522             l.AddMsg( def->GetMsg( i ) );
1523         }
1524       } else {
1525         new NoteWindow( "Error", "Please enter an ID first.", 0, view );
1526         break;
1527       }
1528     }
1529     locale.AddLanguage( l );
1530     BuildLangsList();
1531     w_langs->Update();
1532     w_langs->Draw();
1533     w_langs->Show();
1534     break; }
1535   case B_ID_LANG_DELETE:
1536     if ( cur_lang ) {
1537       if ( (string(cur_lang->ID()) == CF_LANG_DEFAULT) && (l_langs.CountNodes() > 1) ) {
1538         new NoteWindow( "Error", "You cannot delete the default catalog", 0, view );
1539       } else {
1540         locale.RemoveLanguage( *cur_lang );
1541         BuildLangsList();
1542         w_langs->Update();
1543         w_langs->Draw();
1544         w_langs->Show();
1545 
1546         if ( w_langs->Selected() ) {
1547           cur_lang = static_cast<Language *>(
1548                      static_cast<TLWNode *>(w_langs->Selected())->UserData()
1549                      );
1550         } else cur_lang = NULL;
1551         BuildMsgsList();
1552         cur_msg_id = l_msgs.IsEmpty() ? -1 : 0;
1553         w_msgs->Update();
1554         w_msgs->Select( cur_msg_id );
1555         w_msgs->Draw();
1556         w_msgs->Show();
1557         w_msg->SetString( cur_lang ? cur_lang->GetMsg( cur_msg_id ) : NULL );
1558       }
1559     }
1560     break;
1561   case B_ID_MSG_ADD:
1562     if ( w_msg->String() ) {
1563       // append the message to _all_ languages
1564       for ( TLWNode *n = static_cast<TLWNode *>( l_langs.Head() );
1565             n; n = static_cast<TLWNode *>( n->Next() ) ) {
1566         Language *l = static_cast<Language *>( n->UserData() );
1567         l->AddMsg( w_msg->String() );
1568       }
1569       BuildMsgsList();
1570       w_msgs->Update();
1571       if ( cur_msg_id == -1 ) w_msg->SetString( NULL );
1572       else w_msgs->Select( cur_lang->Size() - 1 );
1573       w_msgs->Draw();
1574       w_msgs->Show();
1575     }
1576     break;
1577   case B_ID_MSG_MODIFY:
1578     if ( (cur_msg_id != -1) && w_msg->String() ) {
1579       cur_lang->SetMsg( w_msg->String(), cur_msg_id );
1580       BuildMsgsList();
1581       w_msgs->Draw();
1582       w_msgs->Show();
1583     }
1584     break;
1585   }
1586 
1587   return GUI_OK;
1588 }
1589 
1590 ////////////////////////////////////////////////////////////////////////
1591 // NAME       : EdMsgWindow::Draw
1592 // DESCRIPTION: Draw the window.
1593 // PARAMETERS : -
1594 // RETURNS    : -
1595 ////////////////////////////////////////////////////////////////////////
1596 
Draw(void)1597 void EdMsgWindow::Draw( void ) {
1598   Window::Draw();
1599   DrawBox( Rect(5, 5, w - 10, w_langs->Height() + 10), BOX_CARVED );
1600   sfont->Write( "Languages", this, 10, 10 );
1601 }
1602 
1603 ////////////////////////////////////////////////////////////////////////
1604 // NAME       : EdMsgWindow::BuildLangsList
1605 // DESCRIPTION: Compile the list of available languages.
1606 // PARAMETERS : -
1607 // RETURNS    : -
1608 ////////////////////////////////////////////////////////////////////////
1609 
BuildLangsList(void)1610 void EdMsgWindow::BuildLangsList( void ) {
1611   const std::map<const string, Language> &lib = locale.GetLibrary();
1612   l_langs.Clear();
1613 
1614   for ( map<const string, Language>::const_iterator iter = lib.begin();
1615         iter != lib.end(); ++iter ) {
1616     l_langs.InsertNodeSorted( new TLWNode( iter->second.ID(),
1617             const_cast<Language *>(&iter->second) ) );
1618   }
1619 }
1620 
1621 ////////////////////////////////////////////////////////////////////////
1622 // NAME       : EdMsgWindow::BuildMsgsList
1623 // DESCRIPTION: Compile the list of messages in the current language.
1624 // PARAMETERS : -
1625 // RETURNS    : -
1626 ////////////////////////////////////////////////////////////////////////
1627 
BuildMsgsList(void)1628 void EdMsgWindow::BuildMsgsList( void ) {
1629   l_msgs.Clear();
1630 
1631   if ( cur_lang ) {
1632     const char *m;
1633 
1634     for ( int i = 0; (m = cur_lang->GetMsg(i)) != 0; ++i ) {
1635       l_msgs.AddTail( new TLWNode(string(m).substr(0, 30).c_str(), const_cast<char *>(m), i) );
1636     }
1637   }
1638 }
1639 
1640