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