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