00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "messagedlg.h"
00019 #include "gamemap.h"
00020
00021
00022
00023 #include "paradialog.h"
00024 #include "gameeventsystem.h"
00025
00026
00027 #include "dialog.h"
00028 #include "dlg_box.h"
00029 #include "spfst.h"
00030
00031 #include "widgets/textrenderer.h"
00032
00033 #include "messages.h"
00034 #include "pgwidget.h"
00035 #include "widgets/playerselector.h"
00036 #include "pgrichedit.h"
00037 #include "pgmultilineedit.h"
00038 #include "pgtooltiphelp.h"
00039
00040 #include "dialogs/fieldmarker.h"
00041 #include "dialogs/selectionwindow.h"
00042
00043 #include "spfst-legacy.h"
00044
00045 class NewMessage : public ASC_PG_Dialog {
00046 GameMap* gamemap;
00047 Message* message;
00048 PG_MultiLineEdit* editor;
00049 PlayerSelector* to;
00050 PlayerSelector* cc;
00051 bool reminder;
00052
00053 bool ok()
00054 {
00055 if ( !message ) {
00056 message = new Message ( editor->GetText(), gamemap, 0, 1 << actmap->actplayer );
00057 gamemap->unsentmessage.push_back ( message );
00058 } else {
00059 message->text = editor->GetText();
00060 }
00061
00062 if ( reminder ) {
00063 message->cc = 0;
00064 message->to = 1 << actmap->actplayer;
00065 } else {
00066 message->cc = cc->getSelectedPlayers();
00067 message->to = to->getSelectedPlayers();
00068 }
00069 message->reminder = reminder;
00070
00071 QuitModal();
00072 return true;
00073 }
00074
00075 bool cancel()
00076 {
00077 QuitModal();
00078 return true;
00079 }
00080
00081 bool insertCursorCoordinates()
00082 {
00083 MapCoordinate pos = gamemap->getCursor();
00084 if ( pos.valid() ) {
00085 editor->InsertText( pos.toString() );
00086 return true;
00087 } else
00088 return false;
00089 }
00090
00091 bool insertCoordinates()
00092 {
00093 SelectFromMap::CoordinateList coordinates;
00094
00095 SelectFromMap sfm( coordinates, gamemap );
00096 sfm.Show();
00097 sfm.RunModal();
00098
00099 ASCString text = "#coord(";
00100 for ( SelectFromMap::CoordinateList::iterator i = coordinates.begin(); i != coordinates.end(); ++i ) {
00101 if ( i != coordinates.begin() )
00102 text += ";";
00103 text += ASCString::toString( i->x ) + "/" + ASCString::toString( i->y );
00104 }
00105 text += ")#";
00106 editor->InsertText( text );
00107 return true;
00108 }
00109
00110 bool eventKeyDown(const SDL_KeyboardEvent* key)
00111 {
00112 int mod = SDL_GetModState() & ~(KMOD_NUM | KMOD_CAPS | KMOD_MODE);
00113 if ( (mod & KMOD_CTRL) &&( key->keysym.sym == SDLK_r ))
00114 return insertCursorCoordinates();
00115
00116 return ASC_PG_Dialog::eventKeyDown(key);
00117 }
00118
00119 public:
00120 NewMessage ( GameMap* gamemap, Message* msg = NULL, bool reminder = false );
00121
00122 void Show(bool fade = false ) {
00123 editor->EditBegin();
00124 ASC_PG_Dialog::Show(fade);
00125 }
00126
00127 };
00128
00129
00130 NewMessage :: NewMessage ( GameMap* gamemap, Message* msg, bool reminder ) : ASC_PG_Dialog( NULL, PG_Rect( -1, -1, 600, 500 ), "new message" )
00131 {
00132 this->gamemap = gamemap;
00133 message = msg;
00134 if ( message )
00135 reminder = message->reminder;
00136 this->reminder = reminder;
00137
00138 int startY = 30;
00139
00140 if ( !reminder ) {
00141 new PG_Label ( this, PG_Rect( 20, 30, 30, 20 ), "TO:");
00142 to = new PlayerSelector ( this, PG_Rect( 50, 30, 150, 150 ), gamemap, true, 1 << gamemap->actplayer );
00143 if ( msg )
00144 to->setSelection( msg->to );
00145
00146 new PG_Label ( this, PG_Rect( 210, 30, 30, 20 ), "CC:" );
00147 cc = new PlayerSelector ( this, PG_Rect( 240, 30, 150, 150 ), gamemap, true, 1 << gamemap->actplayer );
00148 if ( msg )
00149 cc->setSelection( msg->cc );
00150
00151 startY += 170;
00152 }
00153
00154 editor = new PG_MultiLineEdit( this, PG_Rect(20, startY, Width() - 140, Height() - startY - 10 ));
00155 if ( message )
00156 editor->SetText( message->text );
00157 editor->SetInputFocus();
00158
00159 AddStandardButton("OK")->sigClick.connect( SigC::slot( *this, &NewMessage::ok ));
00160 AddStandardButton("Cancel")->sigClick.connect( SigC::slot( *this, &NewMessage::cancel ));
00161 AddStandardButton("");
00162 AddStandardButton("Coordinates")->sigClick.connect( SigC::slot( *this, &NewMessage::insertCoordinates ));
00163 PG_Button* coord = AddStandardButton("Cursor Coord");
00164 coord->sigClick.connect( SigC::slot( *this, &NewMessage::insertCursorCoordinates ));
00165 new PG_ToolTipHelp ( coord, "ctrl-r");
00166
00167 }
00168
00169
00170 class IngameMessageViewer : public ASC_PG_Dialog {
00171 TextRenderer* textViewer;
00172 const Message* message;
00173 PG_Label* from;
00174 PG_Label* to;
00175 PG_Label* cc;
00176 bool keepMessage;
00177
00178 bool ok()
00179 {
00180 keepMessage = false;
00181 Hide();
00182 QuitModal();
00183 return true;
00184 };
00185
00186 bool keep()
00187 {
00188 keepMessage = true;
00189 Hide();
00190 QuitModal();
00191 return true;
00192 };
00193
00194
00195 PG_Label* addHeaderLine( int y, const ASCString& name )
00196 {
00197 PG_Rect f( 10, y, Width() - 20, 25 );
00198
00199 new PG_Label( this, PG_Rect( f.x, f.y, 50, f.h ), name );
00200 Emboss* emb = new Emboss( this, PG_Rect( f.x + 50, f.y, f.w - 50, f.h ), true );
00201 return new PG_Label( emb, PG_Rect( 2, 2, emb->w - 4, emb->h - 4) );
00202 }
00203
00204 public:
00205 IngameMessageViewer ( const ASCString& title, const Message& msg, PG_Rect rect = PG_Rect( 50, 50, 500, 400 ), bool autoHeader = true ) : ASC_PG_Dialog ( NULL, rect, title ), message(NULL), from(NULL), to(NULL), cc(NULL)
00206 {
00207
00208 keepMessage = false;
00209 int footerHeight;
00210
00211 if ( msg.reminder ) {
00212 PG_Button* b = new PG_Button( this, PG_Rect( Width() - 70, Height() - 40, 60, 30), "Done" );
00213 b->sigClick.connect( SigC::slot( *this, &IngameMessageViewer::ok) );
00214
00215 PG_Button* b2 = new PG_Button( this, PG_Rect( Width() - 140, Height() - 40, 60, 30), "Keep" );
00216 b2->sigClick.connect( SigC::slot( *this, &IngameMessageViewer::keep) );
00217 } else {
00218 PG_Button* b = new PG_Button( this, PG_Rect( Width() - 110, Height() - 40, 100, 30), "OK" );
00219 b->sigClick.connect( SigC::slot( *this, &IngameMessageViewer::ok) );
00220 }
00221 footerHeight = 50;
00222
00223
00224
00225 int y = 40;
00226 if ( !msg.getFromText( actmap ).empty() ) {
00227
00228
00229 from = addHeaderLine( y, "From:");
00230 y += 30;
00231
00232 if ( !autoHeader || (msg.to && !(msg.from & 512)) ) {
00233 to = addHeaderLine( y, "To:");
00234 y += 30;
00235 }
00236
00237 if ( !autoHeader || msg.cc ) {
00238 cc = addHeaderLine( y, "CC:");
00239 y += 30;
00240 }
00241
00242
00243 }
00244
00245 PG_Rect r ( 10, y, Width() - 20, Height() - (y + footerHeight ));
00246 new Emboss( this, r, true );
00247
00248 #ifdef RICHEDIT
00249 PG_RichEdit* re = new PG_RichEdit( this, PG_Rect(r.x + 2, r.y+2, r.w-4, r.h-4));
00250
00251 ASCString text = msg.text;
00252 while ( text.find ( "#crt#" ) != ASCString::npos )
00253 text.replace ( text.find ("#crt#"), 5, " \n");
00254
00255 re->SetText( text );
00256 re->SetTransparency(255);
00257 textViewer = re;
00258 #else
00259 TextRenderer* tr = new TextRenderer( this, PG_Rect(r.x + 2, r.y+2, r.w-4, r.h-4));
00260 tr->SetText( msg.text );
00261 textViewer = tr;
00262 #endif
00263 SetMessage( msg );
00264 };
00265
00266 void SetMessage( const Message& msg )
00267 {
00268 if ( &msg != message) {
00269 PG_Application::BulkModeActivator bulk;
00270 textViewer->SetText( msg.text );
00271
00272 if ( from )
00273 from->SetText( msg.getFromText( actmap ));
00274
00275 if ( cc )
00276 cc->SetText( msg.getCcText( actmap ));
00277
00278 if ( to )
00279 to->SetText( msg.getToText( actmap ));
00280
00281
00282 bulk.disable();
00283 Show();
00284 message = &msg;
00285 } else
00286 Show();
00287 };
00288
00289
00290 void Hide (bool fade=false)
00291 {
00292 ASC_PG_Dialog::Hide(fade);
00293 getPGApplication().queueWidgetForDeletion( this );
00294 }
00295
00296 bool eventKeyDown (const SDL_KeyboardEvent *key)
00297 {
00298 if ( key->keysym.sym == SDLK_ESCAPE ) {
00299 quitModalLoop(10);
00300 return true;
00301 }
00302 if ( key->keysym.sym == SDLK_RETURN || key->keysym.sym == SDLK_KP_ENTER ) {
00303 quitModalLoop(11);
00304 return true;
00305 }
00306 if ( key->keysym.sym == SDLK_SPACE ) {
00307 quitModalLoop(12);
00308 return true;
00309 }
00310
00311 int mod = SDL_GetModState() & ~(KMOD_NUM | KMOD_CAPS | KMOD_MODE);
00312 if ( mod & KMOD_CTRL )
00313 if ( key->keysym.sym == 's' )
00314 textViewer->saveText( mod & KMOD_SHIFT );
00315
00316
00317 return ASC_PG_Dialog::eventKeyDown( key );
00318 };
00319
00320 bool getKeepMessage()
00321 {
00322 return keepMessage;
00323 }
00324
00325 ~IngameMessageViewer()
00326 {
00327 displayLogMessage ( 9, "~IngameMessageViewer\n" );
00328 }
00329
00330 };
00331
00332
00333
00334
00335 void newmessage()
00336 {
00337 if ( Player::getHumanPlayerNum( actmap ) < 2 ) {
00338 infoMessage( "nobody is listening to our transmissions");
00339 return;
00340 }
00341
00342
00343
00344 NewMessage nm ( actmap );
00345 nm.Show();
00346 nm.RunModal();
00347 }
00348
00349 void newreminder()
00350 {
00351 NewMessage nm ( actmap, NULL, true );
00352 nm.Show();
00353 nm.RunModal();
00354 }
00355
00356
00357
00358
00359 class MessageLineWidget: public SelectionWidget
00360 {
00361 Message* message;
00362 ASCString msgtime;
00363 GameMap* map;
00364 public:
00365 MessageLineWidget( PG_Widget* parent, const PG_Point& pos, int width, Message* msg, GameMap* gamemap );
00366
00367 ASCString getName() const
00368 {
00369 if ( message->from > 0 && message->from <= 255 )
00370 return map->getPlayer(getFirstBit(message->from)).getName();
00371 else
00372 if ( message->from & ( 1 << 9 ))
00373 return "system";
00374 return "";
00375 };
00376
00377 Message* getMessage() const { return message; };
00378
00379 protected:
00380
00381 void display( SDL_Surface * surface, const PG_Rect & src, const PG_Rect & dst )
00382 {
00383 }
00384 ;
00385 };
00386
00387
00388
00389 MessageLineWidget::MessageLineWidget( PG_Widget* parent, const PG_Point& pos, int width, Message* msg, GameMap* gamemap ) : SelectionWidget( parent, PG_Rect( pos.x, pos.y, width, 20 )), message( msg ), map(gamemap)
00390 {
00391 #ifndef ctime_r
00392 msgtime = ctime( &msg->time);
00393 #else
00394
00395 char c[100];
00396 ctime_r( &msg->time, c );
00397 msgtime = c;
00398 #endif
00399
00400 int col1 = width * 3 / 10;
00401 int col2 = col1 + width * 4 / 10;
00402
00403
00404 PG_Label* lbl1 = new PG_Label( this, PG_Rect( 0, 0, col1 - 10, Height() ), getName() );
00405 lbl1->SetFontSize( lbl1->GetFontSize() -2 );
00406
00407 PG_Label* lbl2 = new PG_Label( this, PG_Rect( col1, 0, col2-col1-10, Height() ), msgtime );
00408 lbl2->SetFontSize( lbl2->GetFontSize() -2 );
00409
00410 int x = 0;
00411 for ( int i = 0; i< gamemap->getPlayerCount(); ++i )
00412 if ( msg->to & (1 << i)) {
00413 new ColoredBar( gamemap->getPlayer(i).getColor(), this, PG_Rect( col2 + x, 0, 15 , 15 ));
00414 x += 18;
00415 }
00416
00417 SetTransparency( 255 );
00418 };
00419
00420
00421 class MessageListItemFactory: public SelectionItemFactory {
00422 protected:
00423 const MessagePntrContainer& messageContainer;
00424 MessagePntrContainer::const_iterator it;
00425 GameMap* gamemap;
00426
00427 public:
00428 MessageListItemFactory( const MessagePntrContainer& messages, GameMap* g );
00429
00430 void restart();
00431
00432 SelectionWidget* spawnNextItem( PG_Widget* parent, const PG_Point& pos );
00433
00434 void itemMarked( const SelectionWidget* widget );
00435 void itemSelected( const SelectionWidget* widget, bool mouse );
00436
00437 SigC::Signal1<void, Message* > messageSelected;
00438 };
00439
00440
00441 MessageListItemFactory::MessageListItemFactory( const MessagePntrContainer& messages, GameMap* map ) : messageContainer ( messages ), gamemap(map)
00442 {
00443 restart();
00444 };
00445
00446
00447 void MessageListItemFactory::restart()
00448 {
00449 it = messageContainer.begin();
00450 };
00451
00452 SelectionWidget* MessageListItemFactory::spawnNextItem( PG_Widget* parent, const PG_Point& pos )
00453 {
00454 if ( it != messageContainer.end() )
00455 return new MessageLineWidget( parent, pos, parent->Width() - 15, *(it++), gamemap );
00456 else
00457 return NULL;
00458 };
00459
00460
00461 void MessageListItemFactory::itemMarked( const SelectionWidget* widget )
00462 {
00463 if ( !widget )
00464 return;
00465
00466 const MessageLineWidget* mlw = dynamic_cast< const MessageLineWidget*>(widget);
00467 assert( mlw );
00468 messageSelected( mlw->getMessage() );
00469 }
00470
00471 void MessageListItemFactory::itemSelected( const SelectionWidget* widget, bool mouse )
00472 {
00473 if ( !widget )
00474 return;
00475
00476 const MessageLineWidget* fw = dynamic_cast<const MessageLineWidget*>(widget);
00477 assert( fw );
00478 messageSelected( fw->getMessage() );
00479 }
00480
00481
00482
00483
00484 class MessageSelectionWindow : public ASC_PG_Dialog {
00485 private:
00486 bool edit;
00487
00488 bool viewerDeleted( const PG_MessageObject* obj )
00489 {
00490 if ( viewer == obj )
00491 viewer = NULL;
00492 return true;
00493 };
00494
00495 bool ProcessEvent ( const SDL_Event * event, bool bModal = false )
00496 {
00497 if ( ASC_PG_Dialog::ProcessEvent( event, bModal ) )
00498 return true;
00499
00500 if ( viewer )
00501 if ( viewer->ProcessEvent( event, bModal ))
00502 return true;
00503
00504 return false;
00505 };
00506
00507 bool eventKeyDown(const SDL_KeyboardEvent* key)
00508 {
00509
00510 int mod = SDL_GetModState() & ~(KMOD_NUM | KMOD_CAPS | KMOD_MODE);
00511 if ( mod & KMOD_CTRL )
00512 if ( key->keysym.sym == 's' )
00513 if ( viewer )
00514 return viewer->eventKeyDown( key );
00515
00516 return ASC_PG_Dialog::eventKeyDown(key);
00517 }
00518
00519
00520
00521 protected:
00522 void messageSelected( Message* msg );
00523 IngameMessageViewer* viewer;
00524
00525 public:
00526 MessageSelectionWindow( PG_Widget *parent, const PG_Rect &r, const MessagePntrContainer& messages, GameMap* g, bool editable );
00527 };
00528
00529 void MessageSelectionWindow::messageSelected( Message* msg )
00530 {
00531 if ( edit ) {
00532 NewMessage nm ( actmap, msg );
00533 nm.Show();
00534 nm.RunModal();
00535 } else {
00536 if ( !viewer ) {
00537 PG_Rect r ( my_xpos + Width(), my_ypos, min( PG_Application::GetScreenWidth()/2, 500), Height() );
00538 viewer = new IngameMessageViewer( "Message", *msg, r );
00539 viewer->Show();
00540 viewer->sigDelete.connect( SigC::slot( *this, &MessageSelectionWindow::viewerDeleted ));
00541 } else
00542 viewer->SetMessage( *msg );
00543 }
00544 };
00545
00546
00547 MessageSelectionWindow::MessageSelectionWindow( PG_Widget *parent, const PG_Rect &r, const MessagePntrContainer& messages, GameMap* gamemap, bool editable )
00548 : ASC_PG_Dialog( parent, r, "Messages" ), edit( editable), viewer(NULL)
00549 {
00550
00551 MessageListItemFactory* factory = new MessageListItemFactory( messages, gamemap );
00552 factory->messageSelected.connect ( SigC::slot( *this, &MessageSelectionWindow::messageSelected ));
00553
00554 ItemSelectorWidget* isw = new ItemSelectorWidget( this, PG_Rect(10, GetTitlebarHeight(), r.Width() - 10, r.Height() - GetTitlebarHeight()), factory );
00555 isw->sigQuitModal.connect( SigC::slot( *this, &ItemSelectorWindow::QuitModal));
00556 };
00557
00558
00559
00560
00561 void viewmessages ( const char* title, const MessagePntrContainer& msg, bool editable )
00562 {
00563 int ww = min( PG_Application::GetScreenWidth()/2, 500 );
00564 PG_Rect r ( max(PG_Application::GetScreenWidth()/2,0) - ww, -1, ww, 500 );
00565 MessageSelectionWindow msw ( NULL, r, msg, actmap, editable );
00566 msw.Show();
00567 msw.RunModal();
00568 }
00569
00570
00571
00572 bool viewmessage ( const Message& message )
00573 {
00575 if ( PG_Application::GetScreen()==NULL )
00576 return false;
00577
00578 assert( !legacyEventSystemActive() );
00579 IngameMessageViewer igm( "incoming message...", message );
00580 igm.Show();
00581 igm.RunModal();
00582 return igm.getKeepMessage();
00583 }
00584
00585
00586
00587 void checkJournal( Player& player )
00588 {
00589 GameMap* actmap = player.getParentMap();
00590
00591 if ( actmap->lastjournalchange.abstime )
00592 if ( (actmap->lastjournalchange.turn() == actmap->time.turn() ) ||
00593 (actmap->lastjournalchange.turn() == actmap->time.turn()-1 && actmap->lastjournalchange.move() > actmap->actplayer ) )
00594 viewjournal( false );
00595 }
00596
00597 void viewjournal ( bool showEmptyDlg )
00598 {
00599 if ( !actmap->gameJournal.empty() ) {
00600 tviewanytext vat;
00601 vat.init ( "journal", actmap->gameJournal.c_str() );
00602 vat.run();
00603 vat.done();
00604 } else
00605 if ( showEmptyDlg )
00606 infoMessage("no entries to journal yet");
00607
00608 }
00609
00610 void editjournal ( void )
00611 {
00612 MultilineEdit ej ( actmap->newJournal, "Journal" );
00613 ej.init ();
00614 ej.run ();
00615 if ( ej.changed() )
00616 actmap->lastjournalchange.set ( actmap->time.turn(), actmap->actplayer );
00617 ej.done ();
00618 }
00619
00620
00621 void viewunreadmessages ( Player& player )
00622 {
00623
00629 static bool isRunning = false;
00630 if ( isRunning )
00631 return;
00632
00633 VariableLocker l( isRunning );
00634
00635 if( (player.stat == Player::human || player.stat == Player::supervisor) && player.getParentMap()->getPlayerView() == player.getPosition() ) {
00636 MessagePntrContainer::iterator mi = player.unreadmessage.begin();
00637 while ( mi != player.unreadmessage.end() ) {
00638 Message* msg = *mi;
00639 bool keep = viewmessage ( *msg );
00640
00641 if ( keep )
00642 ++mi;
00643 else {
00644 player.oldmessage.push_back ( *mi );
00645 mi = player.unreadmessage.erase ( mi );
00646 }
00647 }
00648 }
00649 }