tgtk_client.c - vaccinewars - be a doctor and try to vaccinate the world
git clone git://src.adamsgaard.dk/vaccinewars
Log
Files
Refs
README
LICENSE
---
tgtk_client.c (115287B)
---
     1 /************************************************************************
     2  * gtk_client.c   dopewars client using the GTK+ toolkit                *
     3  * Copyright (C)  1998-2021  Ben Webb                                   *
     4  *                Email: benwebb@users.sf.net                           *
     5  *                WWW: https://dopewars.sourceforge.io/                 *
     6  *                                                                      *
     7  * This program is free software; you can redistribute it and/or        *
     8  * modify it under the terms of the GNU General Public License          *
     9  * as published by the Free Software Foundation; either version 2       *
    10  * of the License, or (at your option) any later version.               *
    11  *                                                                      *
    12  * This program is distributed in the hope that it will be useful,      *
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
    15  * GNU General Public License for more details.                         *
    16  *                                                                      *
    17  * You should have received a copy of the GNU General Public License    *
    18  * along with this program; if not, write to the Free Software          *
    19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,               *
    20  *                   MA  02111-1307, USA.                               *
    21  ************************************************************************/
    22 
    23 #ifdef HAVE_CONFIG_H
    24 #include 
    25 #endif
    26 
    27 #include 
    28 #include 
    29 #include 
    30 
    31 #include "configfile.h"
    32 #include "convert.h"
    33 #include "dopewars.h"
    34 #include "gtk_client.h"
    35 #include "message.h"
    36 #include "nls.h"
    37 #include "serverside.h"
    38 #include "sound.h"
    39 #include "tstring.h"
    40 #include "util.h"
    41 #include "gtkport/gtkport.h"
    42 #include "dopewars-pill.xpm"
    43 #include "optdialog.h"
    44 #include "newgamedia.h"
    45 
    46 #define BT_BUY  (GINT_TO_POINTER(1))
    47 #define BT_SELL (GINT_TO_POINTER(2))
    48 #define BT_DROP (GINT_TO_POINTER(3))
    49 
    50 #define ET_SPY    0
    51 #define ET_TIPOFF 1
    52 
    53 struct InventoryWidgets {
    54   GtkWidget *HereList, *CarriedList;
    55   GtkWidget *HereFrame, *CarriedFrame;
    56   GtkWidget *BuyButton, *SellButton, *DropButton;
    57   GtkWidget *vbbox;
    58 };
    59 
    60 struct StatusWidgets {
    61   GtkWidget *Location, *Date, *SpaceName, *SpaceValue, *CashName;
    62   GtkWidget *CashValue, *DebtName, *DebtValue, *BankName, *BankValue;
    63   GtkWidget *GunsName, *GunsValue, *BitchesName, *BitchesValue;
    64   GtkWidget *HealthName, *HealthValue;
    65 };
    66 
    67 struct ClientDataStruct {
    68   GtkWidget *window, *messages;
    69   Player *Play;
    70   DPGtkItemFactory *Menu;
    71   struct StatusWidgets Status;
    72   struct InventoryWidgets Drug, Gun, InvenDrug, InvenGun;
    73   GtkWidget *JetButton, *vbox, *PlayerList, *TalkList;
    74   guint JetAccel;
    75   struct CMDLINE *cmdline;
    76 };
    77 
    78 struct DealDiaStruct {
    79   GtkWidget *dialog, *cost, *carrying, *space, *afford, *amount;
    80   gint DrugInd;
    81   gpointer Type;
    82 };
    83 static struct DealDiaStruct DealDialog;
    84 
    85 GtkWidget *MainWindow = NULL;
    86 
    87 static struct ClientDataStruct ClientData;
    88 static gboolean InGame = FALSE;
    89 
    90 static GtkWidget *FightDialog = NULL, *SpyReportsDialog;
    91 static gboolean IsShowingPlayerList = FALSE, IsShowingTalkList = FALSE;
    92 static gboolean IsShowingInventory = FALSE, IsShowingGunShop = FALSE;
    93 static gboolean IsShowingDealDrugs = FALSE;
    94 
    95 static void display_intro(GtkWidget *widget, gpointer data);
    96 static void QuitGame(GtkWidget *widget, gpointer data);
    97 static void DestroyGtk(GtkWidget *widget, gpointer data);
    98 static void NewGame(GtkWidget *widget, gpointer data);
    99 static void AbandonGame(GtkWidget *widget, gpointer data);
   100 static void ToggleSound(GtkWidget *widget, gpointer data);
   101 static void ListScores(GtkWidget *widget, gpointer data);
   102 static void ListInventory(GtkWidget *widget, gpointer data);
   103 static void EndGame(void);
   104 static void Jet(GtkWidget *parent);
   105 static void UpdateMenus(void);
   106 
   107 #ifdef NETWORKING
   108 gboolean GetClientMessage(GIOChannel *source, GIOCondition condition,
   109                           gpointer data);
   110 void SocketStatus(NetworkBuffer *NetBuf, gboolean Read, gboolean Write,
   111                   gboolean Exception, gboolean CallNow);
   112 
   113 /* Data waiting to be sent to/read from the metaserver */
   114 CurlConnection MetaConn;
   115 #endif /* NETWORKING */
   116 
   117 static void HandleClientMessage(char *buf, Player *Play);
   118 static void PrepareHighScoreDialog(void);
   119 static void AddScoreToDialog(char *Data);
   120 static void CompleteHighScoreDialog(gboolean AtEnd);
   121 static void PrintMessage(char *Data, char *tagname);
   122 static void DisplayFightMessage(char *Data);
   123 static GtkWidget *CreateStatusWidgets(struct StatusWidgets *Status);
   124 static void DisplayStats(Player *Play, struct StatusWidgets *Status);
   125 static void UpdateStatus(Player *Play);
   126 static void SetJetButtonTitle(GtkAccelGroup *accel_group);
   127 static void UpdateInventory(struct InventoryWidgets *Inven,
   128                             Inventory *Objects, int NumObjects,
   129                             gboolean AreDrugs);
   130 static void JetButtonPressed(GtkWidget *widget, gpointer data);
   131 static void DealDrugs(GtkWidget *widget, gpointer data);
   132 static void DealGuns(GtkWidget *widget, gpointer data);
   133 static void QuestionDialog(char *Data, Player *From);
   134 static void TransferDialog(gboolean Debt);
   135 static void ListPlayers(GtkWidget *widget, gpointer data);
   136 static void TalkToAll(GtkWidget *widget, gpointer data);
   137 static void TalkToPlayers(GtkWidget *widget, gpointer data);
   138 static void TalkDialog(gboolean TalkToAll);
   139 static GtkWidget *CreatePlayerList(void);
   140 static void UpdatePlayerList(GtkWidget *clist, gboolean IncludeSelf);
   141 static void TipOff(GtkWidget *widget, gpointer data);
   142 static void SpyOnPlayer(GtkWidget *widget, gpointer data);
   143 static void ErrandDialog(gint ErrandType);
   144 static void SackBitch(GtkWidget *widget, gpointer data);
   145 static void DestroyShowing(GtkWidget *widget, gpointer data);
   146 static void SetShowing(GtkWidget *window, gboolean *showing);
   147 static gint DisallowDelete(GtkWidget *widget, GdkEvent * event,
   148                            gpointer data);
   149 static void GunShopDialog(void);
   150 static void NewNameDialog(void);
   151 static void UpdatePlayerLists(void);
   152 static void CreateInventory(GtkWidget *hbox, gchar *Objects,
   153                             GtkAccelGroup *accel_group,
   154                             gboolean CreateButtons, gboolean CreateHere,
   155                             struct InventoryWidgets *widgets,
   156                             GCallback CallBack);
   157 static void GetSpyReports(GtkWidget *widget, gpointer data);
   158 static void DisplaySpyReports(Player *Play);
   159 
   160 static DPGtkItemFactoryEntry menu_items[] = {
   161   /* The names of the menus and their items in the GTK+ client */
   162   {N_("/_Game"), NULL, NULL, 0, ""},
   163   {N_("/Game/_New..."), "N", NewGame, 0, NULL},
   164   {N_("/Game/_Abandon..."), "A", AbandonGame, 0, NULL},
   165   {N_("/Game/_Options..."), "O", OptDialog, 0, NULL},
   166   {N_("/Game/Enable _sound"), NULL, ToggleSound, 0, ""},
   167   {N_("/Game/_Quit..."), "Q", QuitGame, 0, NULL},
   168   {N_("/_Talk"), NULL, NULL, 0, ""},
   169   {N_("/Talk/To _All..."), NULL, TalkToAll, 0, NULL},
   170   {N_("/Talk/To _Player..."), NULL, TalkToPlayers, 0, NULL},
   171   {N_("/_List"), NULL, NULL, 0, ""},
   172   {N_("/List/_Players..."), NULL, ListPlayers, 0, NULL},
   173   {N_("/List/_Scores..."), NULL, ListScores, 0, NULL},
   174   {N_("/List/_Inventory..."), NULL, ListInventory, 0, NULL},
   175   {N_("/_Errands"), NULL, NULL, 0, ""},
   176   {N_("/Errands/_Spy..."), NULL, SpyOnPlayer, 0, NULL},
   177   {N_("/Errands/_Tipoff..."), NULL, TipOff, 0, NULL},
   178   /* N.B. "Sack Bitch" has to be recreated (and thus translated) at the
   179    * start of each game, below, so is not marked for gettext here */
   180   {"/Errands/S_ack Bitch...", NULL, SackBitch, 0, NULL},
   181   {N_("/Errands/_Get spy reports..."), NULL, GetSpyReports, 0, NULL},
   182   {N_("/_Help"), NULL, NULL, 0, ""},
   183   {N_("/Help/_About..."), "F1", display_intro, 0, NULL}
   184 };
   185 
   186 static gchar *MenuTranslate(const gchar *path, gpointer func_data)
   187 {
   188   /* Translate menu items, using gettext */
   189   return _(path);
   190 }
   191 
   192 static void LogMessage(const gchar *log_domain, GLogLevelFlags log_level,
   193                        const gchar *message, gpointer user_data)
   194 {
   195   GtkMessageBox(MainWindow, message,
   196                 /* Titles of the message boxes for warnings and errors */
   197                 log_level & G_LOG_LEVEL_WARNING ? _("Warning") :
   198                 log_level & G_LOG_LEVEL_CRITICAL ? _("Error") :
   199                 _("Message"), GTK_MESSAGE_INFO,
   200                 MB_OK | (gtk_main_level() > 0 ? MB_IMMRETURN : 0));
   201 }
   202 
   203 /*
   204  * Creates an hbutton_box widget, and sets a sensible spacing and layout.
   205  */
   206 GtkWidget *my_hbbox_new(void)
   207 {
   208   GtkWidget *hbbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
   209   gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
   210   gtk_box_set_spacing(GTK_BOX(hbbox), 8);
   211   return hbbox;
   212 }
   213 
   214 /*
   215  * Do the equivalent of gtk_box_pack_start_defaults().
   216  * This has been removed from GTK+3.
   217  */
   218 void my_gtk_box_pack_start_defaults(GtkBox *box, GtkWidget *child)
   219 {
   220 #ifdef CYGWIN
   221   /* For compatibility with older dopewars */
   222   gtk_box_pack_start(box, child, FALSE, FALSE, 0);
   223 #else
   224   gtk_box_pack_start(box, child, TRUE, TRUE, 0);
   225 #endif
   226 }
   227 
   228 /*
   229  * Sets the initial size and window manager hints of a dialog.
   230  */
   231 void my_set_dialog_position(GtkWindow *dialog)
   232 {
   233   gtk_window_set_type_hint(dialog, GDK_WINDOW_TYPE_HINT_DIALOG);
   234   gtk_window_set_position(dialog, GTK_WIN_POS_CENTER_ON_PARENT);
   235 }
   236 
   237 void QuitGame(GtkWidget *widget, gpointer data)
   238 {
   239   if (!InGame || GtkMessageBox(ClientData.window,
   240                                /* Prompt in 'quit game' dialog */
   241                                _("Abandon current game?"),
   242                                /* Title of 'quit game' dialog */
   243                                _("Quit Game"), GTK_MESSAGE_QUESTION,
   244                                MB_YESNO) == IDYES) {
   245     gtk_main_quit();
   246   }
   247 }
   248 
   249 void DestroyGtk(GtkWidget *widget, gpointer data)
   250 {
   251   gtk_main_quit();
   252 }
   253 
   254 gint MainDelete(GtkWidget *widget, GdkEvent * event, gpointer data)
   255 {
   256   return (InGame
   257           && GtkMessageBox(ClientData.window, _("Abandon current game?"),
   258                            _("Quit Game"), GTK_MESSAGE_QUESTION,
   259                            MB_YESNO) == IDNO);
   260 }
   261 
   262 
   263 void NewGame(GtkWidget *widget, gpointer data)
   264 {
   265   if (InGame) {
   266     if (GtkMessageBox(ClientData.window, _("Abandon current game?"),
   267                       /* Title of 'stop game to start a new game' dialog */
   268                       _("Start new game"), GTK_MESSAGE_QUESTION,
   269                       MB_YESNO) == IDYES)
   270       EndGame();
   271     else
   272       return;
   273   }
   274 
   275   /* Save the configuration, so we can restore those elements that get
   276    * overwritten when we connect to a dopewars server */
   277   BackupConfig();
   278 
   279 #ifdef NETWORKING
   280   NewGameDialog(ClientData.Play, SocketStatus, &MetaConn);
   281 #else
   282   NewGameDialog(ClientData.Play);
   283 #endif
   284 }
   285 
   286 void AbandonGame(GtkWidget *widget, gpointer data)
   287 {
   288   if (InGame && GtkMessageBox(ClientData.window, _("Abandon current game?"),
   289                               /* Title of 'abandon game' dialog */
   290                               _("Abandon game"), GTK_MESSAGE_QUESTION,
   291                               MB_YESNO) == IDYES) {
   292     EndGame();
   293   }
   294 }
   295 
   296 void ToggleSound(GtkWidget *widget, gpointer data)
   297 {
   298   gboolean enable;
   299 
   300   widget = dp_gtk_item_factory_get_widget(ClientData.Menu,
   301                                           "
/Game/Enable sound"); 302 if (widget) { 303 enable = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); 304 SoundEnable(enable); 305 } 306 } 307 308 void ListScores(GtkWidget *widget, gpointer data) 309 { 310 if (InGame) { 311 SendClientMessage(ClientData.Play, C_NONE, C_REQUESTSCORE, NULL, NULL); 312 } else { 313 SendNullClientMessage(ClientData.Play, C_NONE, C_REQUESTSCORE, NULL, NULL); 314 } 315 } 316 317 void ListInventory(GtkWidget *widget, gpointer data) 318 { 319 GtkWidget *window, *button, *hsep, *vbox, *hbox, *hbbox; 320 GtkAccelGroup *accel_group; 321 322 if (IsShowingInventory) 323 return; 324 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 325 gtk_window_set_default_size(GTK_WINDOW(window), 550, 120); 326 accel_group = gtk_accel_group_new(); 327 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); 328 329 /* Title of inventory window */ 330 gtk_window_set_title(GTK_WINDOW(window), _("Inventory")); 331 my_set_dialog_position(GTK_WINDOW(window)); 332 333 SetShowing(window, &IsShowingInventory); 334 335 gtk_window_set_transient_for(GTK_WINDOW(window), 336 GTK_WINDOW(ClientData.window)); 337 gtk_container_set_border_width(GTK_CONTAINER(window), 7); 338 339 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 340 341 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 7); 342 CreateInventory(hbox, Names.Drugs, accel_group, FALSE, FALSE, 343 &ClientData.InvenDrug, NULL); 344 CreateInventory(hbox, Names.Guns, accel_group, FALSE, FALSE, 345 &ClientData.InvenGun, NULL); 346 347 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); 348 349 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 350 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 351 352 hbbox = my_hbbox_new(); 353 button = gtk_button_new_with_mnemonic(_("_Close")); 354 g_signal_connect_swapped(G_OBJECT(button), "clicked", 355 G_CALLBACK(gtk_widget_destroy), 356 G_OBJECT(window)); 357 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 358 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 359 360 gtk_container_add(GTK_CONTAINER(window), vbox); 361 362 UpdateInventory(&ClientData.InvenDrug, ClientData.Play->Drugs, NumDrug, 363 TRUE); 364 UpdateInventory(&ClientData.InvenGun, ClientData.Play->Guns, NumGun, 365 FALSE); 366 367 gtk_widget_show_all(window); 368 } 369 370 #ifdef NETWORKING 371 gboolean GetClientMessage(GIOChannel *source, GIOCondition condition, 372 gpointer data) 373 { 374 gchar *pt; 375 NetworkBuffer *NetBuf; 376 gboolean DoneOK, datawaiting; 377 NBStatus status, oldstatus; 378 NBSocksStatus oldsocks; 379 380 NetBuf = &ClientData.Play->NetBuf; 381 382 oldstatus = NetBuf->status; 383 oldsocks = NetBuf->sockstat; 384 385 datawaiting = 386 PlayerHandleNetwork(ClientData.Play, condition & G_IO_IN, 387 condition & G_IO_OUT, 388 condition & G_IO_ERR, &DoneOK); 389 status = NetBuf->status; 390 391 /* Handle pre-game stuff */ 392 if (status != NBS_CONNECTED) { 393 /* The start game dialog isn't visible once we're connected... */ 394 DisplayConnectStatus(oldstatus, oldsocks); 395 } 396 if (oldstatus != NBS_CONNECTED && (status == NBS_CONNECTED || !DoneOK)) { 397 FinishServerConnect(DoneOK); 398 } 399 400 if (status == NBS_CONNECTED && datawaiting) { 401 while ((pt = GetWaitingPlayerMessage(ClientData.Play)) != NULL) { 402 HandleClientMessage(pt, ClientData.Play); 403 g_free(pt); 404 } 405 } 406 if (!DoneOK) { 407 if (status == NBS_CONNECTED) { 408 /* The network connection to the server was dropped unexpectedly */ 409 g_warning(_("Connection to server lost - switching to " 410 "single player mode")); 411 SwitchToSinglePlayer(ClientData.Play); 412 UpdatePlayerLists(); 413 UpdateMenus(); 414 } else { 415 ShutdownNetworkBuffer(&ClientData.Play->NetBuf); 416 } 417 } 418 return TRUE; 419 } 420 421 void SocketStatus(NetworkBuffer *NetBuf, gboolean Read, gboolean Write, 422 gboolean Exception, gboolean CallNow) 423 { 424 if (NetBuf->InputTag) 425 dp_g_source_remove(NetBuf->InputTag); 426 NetBuf->InputTag = 0; 427 if (Read || Write || Exception) { 428 NetBuf->InputTag = dp_g_io_add_watch(NetBuf->ioch, 429 (Read ? G_IO_IN : 0) | 430 (Write ? G_IO_OUT : 0) | 431 (Exception ? G_IO_ERR : 0), 432 GetClientMessage, 433 NetBuf->CallBackData); 434 } 435 if (CallNow) 436 GetClientMessage(NetBuf->ioch, 0, NetBuf->CallBackData); 437 } 438 #endif /* NETWORKING */ 439 440 void HandleClientMessage(char *pt, Player *Play) 441 { 442 char *Data; 443 DispMode DisplayMode; 444 AICode AI; 445 MsgCode Code; 446 Player *From, *tmp; 447 gchar *text; 448 gboolean Handled; 449 GtkWidget *MenuItem; 450 GSList *list; 451 452 if (ProcessMessage(pt, Play, &From, &AI, &Code, 453 &Data, FirstClient) == -1) { 454 return; 455 } 456 457 Handled = 458 HandleGenericClientMessage(From, AI, Code, Play, Data, &DisplayMode); 459 switch (Code) { 460 case C_STARTHISCORE: 461 PrepareHighScoreDialog(); 462 break; 463 case C_HISCORE: 464 AddScoreToDialog(Data); 465 break; 466 case C_ENDHISCORE: 467 CompleteHighScoreDialog((strcmp(Data, "end") == 0)); 468 break; 469 case C_PRINTMESSAGE: 470 PrintMessage(Data, NULL); 471 break; 472 case C_FIGHTPRINT: 473 DisplayFightMessage(Data); 474 break; 475 case C_PUSH: 476 /* The server admin has asked us to leave - so warn the user, and do 477 so */ 478 g_warning(_("You have been pushed from the server.\n" 479 "Switching to single player mode.")); 480 SwitchToSinglePlayer(Play); 481 UpdatePlayerLists(); 482 UpdateMenus(); 483 break; 484 case C_QUIT: 485 /* The server has sent us notice that it is shutting down */ 486 g_warning(_("The server has terminated.\n" 487 "Switching to single player mode.")); 488 SwitchToSinglePlayer(Play); 489 UpdatePlayerLists(); 490 UpdateMenus(); 491 break; 492 case C_NEWNAME: 493 NewNameDialog(); 494 break; 495 case C_BANK: 496 TransferDialog(FALSE); 497 break; 498 case C_LOANSHARK: 499 TransferDialog(TRUE); 500 break; 501 case C_GUNSHOP: 502 GunShopDialog(); 503 break; 504 case C_MSG: 505 text = g_strdup_printf("%s: %s", GetPlayerName(From), Data); 506 PrintMessage(text, "talk"); 507 g_free(text); 508 SoundPlay(Sounds.TalkToAll); 509 break; 510 case C_MSGTO: 511 text = g_strdup_printf("%s->%s: %s", GetPlayerName(From), 512 GetPlayerName(Play), Data); 513 PrintMessage(text, "page"); 514 g_free(text); 515 SoundPlay(Sounds.TalkPrivate); 516 break; 517 case C_JOIN: 518 text = g_strdup_printf(_("%s joins the game!"), Data); 519 PrintMessage(text, "join"); 520 g_free(text); 521 SoundPlay(Sounds.JoinGame); 522 UpdatePlayerLists(); 523 UpdateMenus(); 524 break; 525 case C_LEAVE: 526 if (From != &Noone) { 527 text = g_strdup_printf(_("%s has left the game."), Data); 528 PrintMessage(text, "leave"); 529 g_free(text); 530 SoundPlay(Sounds.LeaveGame); 531 UpdatePlayerLists(); 532 UpdateMenus(); 533 } 534 break; 535 case C_QUESTION: 536 QuestionDialog(Data, From == &Noone ? NULL : From); 537 break; 538 case C_SUBWAYFLASH: 539 DisplayFightMessage(NULL); 540 for (list = FirstClient; list; list = g_slist_next(list)) { 541 tmp = (Player *)list->data; 542 tmp->Flags &= ~FIGHTING; 543 } 544 /* Message displayed when the player "jets" to a new location */ 545 text = dpg_strdup_printf(_("Jetting to %tde"), 546 Location[(int)Play->IsAt].Name); 547 PrintMessage(text, "jet"); 548 g_free(text); 549 SoundPlay(Sounds.Jet); 550 break; 551 case C_ENDLIST: 552 MenuItem = dp_gtk_item_factory_get_widget(ClientData.Menu, 553 "
/Errands/Sack Bitch..."); 554 555 /* Text for the Errands/Sack Bitch menu item */ 556 text = dpg_strdup_printf(_("%/Sack Bitch menu item/S_ack %Tde..."), 557 Names.Bitch); 558 SetAccelerator(MenuItem, text, NULL, NULL, NULL, FALSE); 559 g_free(text); 560 561 MenuItem = dp_gtk_item_factory_get_widget(ClientData.Menu, 562 "
/Errands/Spy..."); 563 564 /* Text to update the Errands/Spy menu item with the price for spying */ 565 text = dpg_strdup_printf(_("_Spy (%P)"), Prices.Spy); 566 SetAccelerator(MenuItem, text, NULL, NULL, NULL, FALSE); 567 g_free(text); 568 569 /* Text to update the Errands/Tipoff menu item with the price for a 570 tipoff */ 571 text = dpg_strdup_printf(_("_Tipoff (%P)"), Prices.Tipoff); 572 MenuItem = dp_gtk_item_factory_get_widget(ClientData.Menu, 573 "
/Errands/Tipoff..."); 574 SetAccelerator(MenuItem, text, NULL, NULL, NULL, FALSE); 575 g_free(text); 576 if (FirstClient->next) 577 ListPlayers(NULL, NULL); 578 UpdateMenus(); 579 break; 580 case C_UPDATE: 581 if (From == &Noone) { 582 ReceivePlayerData(Play, Data, Play); 583 UpdateStatus(Play); 584 } else { 585 ReceivePlayerData(Play, Data, From); 586 DisplaySpyReports(From); 587 } 588 break; 589 case C_DRUGHERE: 590 UpdateInventory(&ClientData.Drug, Play->Drugs, NumDrug, TRUE); 591 if (IsShowingInventory) { 592 UpdateInventory(&ClientData.InvenDrug, Play->Drugs, NumDrug, TRUE); 593 } 594 if (IsShowingDealDrugs) { 595 gtk_widget_destroy(DealDialog.dialog); 596 } 597 break; 598 default: 599 if (!Handled) { 600 g_print("Unknown network message received: %s^%c^%s^%s", 601 GetPlayerName(From), Code, GetPlayerName(Play), Data); 602 } 603 break; 604 } 605 } 606 607 struct HiScoreDiaStruct { 608 GtkWidget *dialog, *grid, *vbox; 609 GtkAccelGroup *accel_group; 610 }; 611 static struct HiScoreDiaStruct HiScoreDialog = { NULL, NULL, NULL, NULL }; 612 613 /* 614 * Creates an empty dialog to display high scores. 615 */ 616 void PrepareHighScoreDialog(void) 617 { 618 GtkWidget *dialog, *vbox, *hsep, *grid; 619 620 /* Make sure the server doesn't fool us into creating multiple dialogs */ 621 if (HiScoreDialog.dialog) 622 return; 623 624 HiScoreDialog.dialog = dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 625 HiScoreDialog.accel_group = gtk_accel_group_new(); 626 gtk_window_add_accel_group(GTK_WINDOW(dialog), HiScoreDialog.accel_group); 627 628 /* Title of the GTK+ high score dialog */ 629 gtk_window_set_title(GTK_WINDOW(dialog), _("High Scores")); 630 my_set_dialog_position(GTK_WINDOW(dialog)); 631 632 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 633 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 634 gtk_window_set_transient_for(GTK_WINDOW(dialog), 635 GTK_WINDOW(ClientData.window)); 636 637 HiScoreDialog.vbox = vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 638 HiScoreDialog.grid = grid = dp_gtk_grid_new(NUMHISCORE, 4, FALSE); 639 gtk_grid_set_row_spacing(GTK_GRID(grid), 5); 640 gtk_grid_set_column_spacing(GTK_GRID(grid), 30); 641 642 gtk_box_pack_start(GTK_BOX(vbox), grid, TRUE, TRUE, 0); 643 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 644 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 645 gtk_container_add(GTK_CONTAINER(dialog), vbox); 646 gtk_widget_show_all(dialog); 647 } 648 649 /* 650 * Adds a single high score (coded in "Data", which is the information 651 * received in the relevant network message) to the dialog created by 652 * PrepareHighScoreDialog(), above. 653 */ 654 void AddScoreToDialog(char *Data) 655 { 656 GtkWidget *label; 657 char *cp; 658 gchar **spl1, **spl2; 659 int index, slen; 660 gboolean bold; 661 662 if (!HiScoreDialog.dialog) 663 return; 664 665 cp = Data; 666 index = GetNextInt(&cp, 0); 667 if (!cp || strlen(cp) < 3) 668 return; 669 670 bold = (*cp == 'B'); /* Is this score "our" score? (Currently 671 * ignored) */ 672 673 /* Step past the 'bold' character, and the initial '>' (if present) */ 674 cp += 2; 675 g_strchug(cp); 676 677 /* Get the first word - the score */ 678 spl1 = g_strsplit(cp, " ", 2); 679 if (!spl1 || !spl1[0] || !spl1[1]) { 680 /* Error - the high score from the server is invalid */ 681 g_warning(_("Corrupt high score!")); 682 g_strfreev(spl1); 683 return; 684 } 685 label = make_bold_label(spl1[0], bold); 686 set_label_alignment(label, 1.0, 0.5); 687 dp_gtk_grid_attach(GTK_GRID(HiScoreDialog.grid), label, 0, index, 1, 1, TRUE); 688 gtk_widget_show(label); 689 690 /* Remove any leading whitespace from the remainder, since g_strsplit 691 * will split at every space character, not at a run of them */ 692 g_strchug(spl1[1]); 693 694 /* Get the second word - the date */ 695 spl2 = g_strsplit(spl1[1], " ", 2); 696 if (!spl2 || !spl2[0] || !spl2[1]) { 697 g_warning(_("Corrupt high score!")); 698 g_strfreev(spl2); 699 return; 700 } 701 label = make_bold_label(spl2[0], bold); 702 set_label_alignment(label, 0.5, 0.5); 703 dp_gtk_grid_attach(GTK_GRID(HiScoreDialog.grid), label, 1, index, 1, 1, TRUE); 704 gtk_widget_show(label); 705 706 /* The remainder is the name, terminated with (R.I.P.) if the player 707 * died, and '<' for the 'current' score */ 708 g_strchug(spl2[1]); 709 710 /* Remove '<' suffix if present */ 711 slen = strlen(spl2[1]); 712 if (slen >= 1 && spl2[1][slen - 1] == '<') { 713 spl2[1][slen - 1] = '\0'; 714 } 715 slen--; 716 717 /* Check for (R.I.P.) suffix, and add it to the 4th column if found */ 718 if (slen > 8 && spl2[1][slen - 1] == ')' && spl2[1][slen - 8] == '(') { 719 label = make_bold_label(&spl2[1][slen - 8], bold); 720 set_label_alignment(label, 0.5, 0.5); 721 dp_gtk_grid_attach(GTK_GRID(HiScoreDialog.grid), label, 3, index, 1, 1, 722 TRUE); 723 gtk_widget_show(label); 724 spl2[1][slen - 8] = '\0'; /* Remove suffix from the player name */ 725 } 726 727 /* Finally, add in what's left of the player name */ 728 g_strchomp(spl2[1]); 729 label = make_bold_label(spl2[1], bold); 730 set_label_alignment(label, 0, 0.5); 731 dp_gtk_grid_attach(GTK_GRID(HiScoreDialog.grid), label, 2, index, 1, 1, TRUE); 732 gtk_widget_show(label); 733 734 g_strfreev(spl1); 735 g_strfreev(spl2); 736 } 737 738 /* 739 * If the high scores are being displayed at the end of the game, 740 * this function is used to end the game when the high score dialog's 741 * "OK" button is pressed. 742 */ 743 static void EndHighScore(GtkWidget *widget) 744 { 745 EndGame(); 746 } 747 748 /* 749 * Called when all high scores have been received. Finishes off the 750 * high score dialog by adding an "OK" button. If the game has ended, 751 * then "AtEnd" is TRUE, and clicking this button will end the game. 752 */ 753 void CompleteHighScoreDialog(gboolean AtEnd) 754 { 755 GtkWidget *button, *dialog, *hbbox; 756 757 dialog = HiScoreDialog.dialog; 758 759 if (!HiScoreDialog.dialog) { 760 return; 761 } 762 763 hbbox = my_hbbox_new(); 764 button = gtk_button_new_with_mnemonic(_("_Close")); 765 g_signal_connect_swapped(G_OBJECT(button), "clicked", 766 G_CALLBACK(gtk_widget_destroy), 767 G_OBJECT(dialog)); 768 if (AtEnd) { 769 InGame = FALSE; 770 g_signal_connect(G_OBJECT(dialog), "destroy", 771 G_CALLBACK(EndHighScore), NULL); 772 } 773 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 774 gtk_box_pack_start(GTK_BOX(HiScoreDialog.vbox), hbbox, FALSE, FALSE, 0); 775 776 gtk_widget_set_can_default(button, TRUE); 777 gtk_widget_grab_default(button); 778 gtk_widget_show_all(hbbox); 779 780 /* OK, we're done - allow the creation of new high score dialogs */ 781 HiScoreDialog.dialog = NULL; 782 } 783 784 /* 785 * Prints an information message in the display area of the GTK+ client. 786 * This area is used for displaying drug busts, messages from other 787 * players, etc. The message is passed in as the string "text". 788 */ 789 void PrintMessage(char *text, char *tagname) 790 { 791 GtkTextView *messages = GTK_TEXT_VIEW(ClientData.messages); 792 793 g_strdelimit(text, "^", '\n'); 794 TextViewAppend(messages, text, tagname, FALSE); 795 TextViewAppend(messages, "\n", NULL, TRUE); 796 } 797 798 static void FreeCombatants(void); 799 800 /* 801 * Called when one of the action buttons in the Fight dialog is clicked. 802 * "data" specifies which button (Deal Drugs/Run/Fight/Stand) was pressed. 803 */ 804 static void FightCallback(GtkWidget *widget, gpointer data) 805 { 806 gint Answer; 807 Player *Play; 808 gchar text[4]; 809 GtkWidget *window; 810 gpointer CanRunHere = NULL; 811 812 window = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); 813 if (window) { 814 CanRunHere = g_object_get_data(G_OBJECT(window), "CanRunHere"); 815 } 816 817 Answer = GPOINTER_TO_INT(data); 818 Play = ClientData.Play; 819 switch (Answer) { 820 case 'D': 821 gtk_widget_hide(FightDialog); 822 if (!(Play->Flags & FIGHTING)) { 823 FreeCombatants(); 824 gtk_widget_destroy(FightDialog); 825 FightDialog = NULL; 826 if (HaveAbility(Play, A_DONEFIGHT)) { 827 SendClientMessage(Play, C_NONE, C_DONE, NULL, NULL); 828 } 829 } 830 break; 831 case 'R': 832 if (CanRunHere) { 833 SendClientMessage(Play, C_NONE, C_FIGHTACT, NULL, "R"); 834 } else { 835 Jet(FightDialog); 836 } 837 break; 838 case 'F': 839 case 'S': 840 text[0] = Answer; 841 text[1] = '\0'; 842 SendClientMessage(Play, C_NONE, C_FIGHTACT, NULL, text); 843 break; 844 } 845 } 846 847 /* 848 * Adds an action button to the hbox at the base of the Fight dialog. 849 * The button's caption is given by "Text", and the keyboard shortcut 850 * (if any) is added to "accel_group". "Answer" gives the identifier 851 * passed to FightCallback, above. 852 */ 853 static GtkWidget *AddFightButton(gchar *Text, GtkAccelGroup *accel_group, 854 GtkBox *box, gint Answer) 855 { 856 GtkWidget *button; 857 858 button = gtk_button_new_with_label(""); 859 SetAccelerator(button, Text, button, "clicked", accel_group, FALSE); 860 g_signal_connect(G_OBJECT(button), "clicked", 861 G_CALLBACK(FightCallback), 862 GINT_TO_POINTER(Answer)); 863 gtk_box_pack_start(box, button, TRUE, TRUE, 0); 864 return button; 865 } 866 867 /* Data used to keep track of the widgets giving the information about a 868 * player/cop involved in a fight */ 869 struct combatant { 870 GtkWidget *name, *bitches, *healthprog, *healthlabel; 871 }; 872 873 /* 874 * Creates an empty Fight dialog. Usually this only needs to be done once, 875 * as when the user "closes" it, it is only hidden, ready to be reshown 876 * later. Buttons for all actions are added here, and are hidden/shown 877 * as necessary. 878 */ 879 static void CreateFightDialog(void) 880 { 881 GtkWidget *dialog, *vbox, *button, *hbox, *hbbox, *hsep, *text, *grid; 882 GtkAccelGroup *accel_group; 883 GArray *combatants; 884 gchar *buf; 885 886 FightDialog = dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 887 gtk_window_set_default_size(GTK_WINDOW(dialog), 350, 250); 888 g_signal_connect(G_OBJECT(dialog), "delete_event", 889 G_CALLBACK(DisallowDelete), NULL); 890 accel_group = gtk_accel_group_new(); 891 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 892 gtk_window_set_title(GTK_WINDOW(dialog), _("Fight")); 893 my_set_dialog_position(GTK_WINDOW(dialog)); 894 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 895 896 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 897 gtk_window_set_transient_for(GTK_WINDOW(dialog), 898 GTK_WINDOW(ClientData.window)); 899 900 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 901 902 grid = dp_gtk_grid_new(2, 4, FALSE); 903 gtk_grid_set_row_spacing(GTK_GRID(grid), 7); 904 gtk_grid_set_column_spacing(GTK_GRID(grid), 10); 905 906 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 907 dp_gtk_grid_attach(GTK_GRID(grid), hsep, 0, 1, 3, 1, TRUE); 908 gtk_widget_show_all(grid); 909 gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 0); 910 g_object_set_data(G_OBJECT(dialog), "grid", grid); 911 912 combatants = g_array_new(FALSE, TRUE, sizeof(struct combatant)); 913 g_array_set_size(combatants, 1); 914 g_object_set_data(G_OBJECT(dialog), "combatants", combatants); 915 916 text = gtk_scrolled_text_view_new(&hbox); 917 gtk_widget_set_size_request(text, 150, 120); 918 919 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); 920 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); 921 g_object_set_data(G_OBJECT(dialog), "text", text); 922 gtk_widget_show_all(hbox); 923 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); 924 925 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 926 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 927 gtk_widget_show(hsep); 928 929 hbbox = my_hbbox_new(); 930 931 /* Button for closing the "Fight" dialog and going back to dealing drugs 932 (%Tde = "Drugs" by default) */ 933 buf = dpg_strdup_printf(_("_Deal %Tde"), Names.Drugs); 934 button = AddFightButton(buf, accel_group, GTK_BOX(hbbox), 'D'); 935 g_object_set_data(G_OBJECT(dialog), "deal", button); 936 g_free(buf); 937 938 /* Button for shooting at other players in the "Fight" dialog, or for 939 popping up the "Fight" dialog from the main window */ 940 button = AddFightButton(_("_Fight"), accel_group, GTK_BOX(hbbox), 'F'); 941 g_object_set_data(G_OBJECT(dialog), "fight", button); 942 943 /* Button to stand and take it in the "Fight" dialog */ 944 button = AddFightButton(_("_Stand"), accel_group, GTK_BOX(hbbox), 'S'); 945 g_object_set_data(G_OBJECT(dialog), "stand", button); 946 947 /* Button to run from combat in the "Fight" dialog */ 948 button = AddFightButton(_("_Run"), accel_group, GTK_BOX(hbbox), 'R'); 949 g_object_set_data(G_OBJECT(dialog), "run", button); 950 951 gtk_widget_show(hsep); 952 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 953 gtk_widget_show(hbbox); 954 gtk_widget_show(vbox); 955 gtk_container_add(GTK_CONTAINER(dialog), vbox); 956 gtk_widget_show(dialog); 957 } 958 959 /* 960 * Updates the display of information for a player/cop in the Fight dialog. 961 * If the player's name (DefendName) already exists, updates the display of 962 * total health and number of bitches - otherwise, adds a new entry. If 963 * DefendBitches is -1, then the player has left. 964 */ 965 static void UpdateCombatant(gchar *DefendName, int DefendBitches, 966 gchar *BitchName, int DefendHealth) 967 { 968 guint i, RowIndex; 969 const gchar *name; 970 struct combatant *compt; 971 GArray *combatants; 972 GtkWidget *grid; 973 gchar *BitchText, *HealthText; 974 gfloat ProgPercent; 975 976 combatants = (GArray *)g_object_get_data(G_OBJECT(FightDialog), 977 "combatants"); 978 grid = GTK_WIDGET(g_object_get_data(G_OBJECT(FightDialog), "grid")); 979 if (!combatants) { 980 return; 981 } 982 983 if (DefendName[0]) { 984 compt = NULL; 985 for (i = 1, RowIndex = 2; i < combatants->len; i++, RowIndex++) { 986 compt = &g_array_index(combatants, struct combatant, i); 987 988 if (!compt || !compt->name) { 989 compt = NULL; 990 continue; 991 } 992 name = gtk_label_get_text(GTK_LABEL(compt->name)); 993 if (name && strcmp(name, DefendName) == 0) { 994 break; 995 } 996 compt = NULL; 997 } 998 if (!compt) { 999 i = combatants->len; 1000 g_array_set_size(combatants, i + 1); 1001 compt = &g_array_index(combatants, struct combatant, i); 1002 1003 dp_gtk_grid_resize(GTK_GRID(grid), i + 2, 4); 1004 RowIndex = i + 1; 1005 } 1006 } else { 1007 compt = &g_array_index(combatants, struct combatant, 0); 1008 1009 RowIndex = 0; 1010 } 1011 1012 /* Display of number of bitches or deputies during combat 1013 (%tde="bitches" or "deputies" (etc.) by default) */ 1014 BitchText = dpg_strdup_printf(_("%/Combat: Bitches/%d %tde"), 1015 DefendBitches, BitchName); 1016 1017 /* Display of health during combat */ 1018 if (DefendBitches == -1) { 1019 HealthText = g_strdup(_("(Left)")); 1020 } else if (DefendHealth == 0 && DefendBitches == 0) { 1021 HealthText = g_strdup(_("(Dead)")); 1022 } else { 1023 HealthText = g_strdup_printf(_("Health: %d"), DefendHealth); 1024 } 1025 1026 ProgPercent = (gfloat)DefendHealth / 100.0; 1027 1028 if (compt->name) { 1029 if (DefendName[0]) { 1030 gtk_label_set_text(GTK_LABEL(compt->name), DefendName); 1031 } 1032 if (DefendBitches >= 0) { 1033 gtk_label_set_text(GTK_LABEL(compt->bitches), BitchText); 1034 } 1035 gtk_label_set_text(GTK_LABEL(compt->healthlabel), HealthText); 1036 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(compt->healthprog), 1037 ProgPercent); 1038 } else { 1039 /* Display of the current player's name during combat */ 1040 compt->name = gtk_label_new(DefendName[0] ? DefendName : _("You")); 1041 1042 dp_gtk_grid_attach(GTK_GRID(grid), compt->name, 0, RowIndex, 1, 1, FALSE); 1043 compt->bitches = gtk_label_new(DefendBitches >= 0 ? BitchText : ""); 1044 dp_gtk_grid_attach(GTK_GRID(grid), compt->bitches, 1, RowIndex, 1, 1, 1045 FALSE); 1046 compt->healthprog = gtk_progress_bar_new(); 1047 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(compt->healthprog), 1048 ProgPercent); 1049 dp_gtk_grid_attach(GTK_GRID(grid), compt->healthprog, 2, RowIndex, 1, 1, 1050 TRUE); 1051 compt->healthlabel = gtk_label_new(HealthText); 1052 dp_gtk_grid_attach(GTK_GRID(grid), compt->healthlabel, 3, RowIndex, 1, 1, 1053 FALSE); 1054 gtk_widget_show(compt->name); 1055 gtk_widget_show(compt->bitches); 1056 gtk_widget_show(compt->healthprog); 1057 gtk_widget_show(compt->healthlabel); 1058 } 1059 1060 g_free(BitchText); 1061 g_free(HealthText); 1062 } 1063 1064 /* 1065 * Cleans up the list of all players/cops involved in a fight. 1066 */ 1067 static void FreeCombatants(void) 1068 { 1069 GArray *combatants; 1070 1071 combatants = (GArray *)g_object_get_data(G_OBJECT(FightDialog), 1072 "combatants"); 1073 if (combatants) { 1074 g_array_free(combatants, TRUE); 1075 } 1076 } 1077 1078 static void EnableFightButton(GtkWidget *button, gboolean enable) 1079 { 1080 if (enable) { 1081 gtk_widget_set_sensitive(button, TRUE); 1082 gtk_widget_show(button); 1083 } else { 1084 gtk_widget_hide(button); 1085 gtk_widget_set_sensitive(button, FALSE); 1086 } 1087 } 1088 1089 /* 1090 * Given the network message "Data" concerning some happening during 1091 * combat, extracts the relevant data and updates the Fight dialog, 1092 * creating and/or showing it if necessary. 1093 * If "Data" is NULL, then closes the dialog. If "Data" is a blank 1094 * string, then just shows the dialog, displaying no new messages. 1095 */ 1096 void DisplayFightMessage(char *Data) 1097 { 1098 Player *Play; 1099 GtkAccelGroup *accel_group; 1100 GtkWidget *Deal, *Fight, *Stand, *Run; 1101 GtkTextView *textview; 1102 gchar *AttackName, *DefendName, *BitchName, *Message; 1103 FightPoint fp; 1104 int DefendHealth, DefendBitches, BitchesKilled, ArmPercent; 1105 gboolean CanRunHere, Loot, CanFire; 1106 1107 if (!Data) { 1108 if (FightDialog) { 1109 FreeCombatants(); 1110 gtk_widget_destroy(FightDialog); 1111 FightDialog = NULL; 1112 } 1113 return; 1114 } 1115 if (FightDialog) { 1116 if (IsShowingDealDrugs) { 1117 gtk_widget_destroy(DealDialog.dialog); 1118 } 1119 if (!gtk_widget_get_visible(FightDialog)) { 1120 gtk_widget_show(FightDialog); 1121 } 1122 } else { 1123 CreateFightDialog(); 1124 } 1125 if (!FightDialog || !Data[0]) { 1126 return; 1127 } 1128 1129 Deal = GTK_WIDGET(g_object_get_data(G_OBJECT(FightDialog), "deal")); 1130 Fight = GTK_WIDGET(g_object_get_data(G_OBJECT(FightDialog), "fight")); 1131 Stand = GTK_WIDGET(g_object_get_data(G_OBJECT(FightDialog), "stand")); 1132 Run = GTK_WIDGET(g_object_get_data(G_OBJECT(FightDialog), "run")); 1133 textview = GTK_TEXT_VIEW(g_object_get_data(G_OBJECT(FightDialog), "text")); 1134 1135 Play = ClientData.Play; 1136 1137 if (HaveAbility(Play, A_NEWFIGHT)) { 1138 ReceiveFightMessage(Data, &AttackName, &DefendName, &DefendHealth, 1139 &DefendBitches, &BitchName, &BitchesKilled, 1140 &ArmPercent, &fp, &CanRunHere, &Loot, &CanFire, 1141 &Message); 1142 Play->Flags |= FIGHTING; 1143 switch (fp) { 1144 case F_HIT: 1145 case F_ARRIVED: 1146 case F_MISS: 1147 UpdateCombatant(DefendName, DefendBitches, BitchName, DefendHealth); 1148 break; 1149 case F_LEAVE: 1150 if (AttackName[0]) { 1151 UpdateCombatant(AttackName, -1, BitchName, 0); 1152 } 1153 break; 1154 case F_LASTLEAVE: 1155 Play->Flags &= ~FIGHTING; 1156 break; 1157 default: 1158 break; 1159 } 1160 accel_group = (GtkAccelGroup *) 1161 g_object_get_data(G_OBJECT(ClientData.window), "accel_group"); 1162 SetJetButtonTitle(accel_group); 1163 } else { 1164 Message = Data; 1165 if (Play->Flags & FIGHTING) { 1166 fp = F_MSG; 1167 } else { 1168 fp = F_LASTLEAVE; 1169 } 1170 CanFire = (Play->Flags & CANSHOOT); 1171 CanRunHere = FALSE; 1172 } 1173 g_object_set_data(G_OBJECT(FightDialog), "CanRunHere", 1174 GINT_TO_POINTER(CanRunHere)); 1175 1176 g_strdelimit(Message, "^", '\n'); 1177 if (strlen(Message) > 0) { 1178 TextViewAppend(textview, Message, NULL, FALSE); 1179 TextViewAppend(textview, "\n", NULL, TRUE); 1180 } 1181 1182 EnableFightButton(Deal, !CanRunHere || fp == F_LASTLEAVE); 1183 EnableFightButton(Fight, CanFire && TotalGunsCarried(Play) > 0); 1184 EnableFightButton(Stand, CanFire && TotalGunsCarried(Play) == 0); 1185 EnableFightButton(Run, fp != F_LASTLEAVE); 1186 } 1187 1188 /* 1189 * Updates the display of pertinent data about player "Play" (location, 1190 * health, etc. in the status widgets given by "Status". This can point 1191 * to the widgets at the top of the main window, or those in a Spy 1192 * Reports dialog. 1193 */ 1194 void DisplayStats(Player *Play, struct StatusWidgets *Status) 1195 { 1196 gchar *prstr; 1197 GString *text; 1198 1199 text = g_string_new(NULL); 1200 1201 dpg_string_printf(text, _("%/Current location/%tde"), 1202 Location[Play->IsAt].Name); 1203 gtk_label_set_text(GTK_LABEL(Status->Location), text->str); 1204 1205 GetDateString(text, Play); 1206 gtk_label_set_text(GTK_LABEL(Status->Date), text->str); 1207 1208 g_string_printf(text, "%d", Play->CoatSize); 1209 gtk_label_set_text(GTK_LABEL(Status->SpaceValue), text->str); 1210 1211 prstr = FormatPrice(Play->Cash); 1212 gtk_label_set_text(GTK_LABEL(Status->CashValue), prstr); 1213 g_free(prstr); 1214 1215 prstr = FormatPrice(Play->Bank); 1216 gtk_label_set_text(GTK_LABEL(Status->BankValue), prstr); 1217 g_free(prstr); 1218 1219 prstr = FormatPrice(Play->Debt); 1220 gtk_label_set_text(GTK_LABEL(Status->DebtValue), prstr); 1221 g_free(prstr); 1222 1223 /* Display of the total number of guns carried (%Tde="Guns" by default) */ 1224 dpg_string_printf(text, _("%/Stats: Guns/%Tde"), Names.Guns); 1225 gtk_label_set_text(GTK_LABEL(Status->GunsName), text->str); 1226 g_string_printf(text, "%d", TotalGunsCarried(Play)); 1227 gtk_label_set_text(GTK_LABEL(Status->GunsValue), text->str); 1228 1229 if (!WantAntique) { 1230 /* Display of number of bitches in GTK+ client status window 1231 (%Tde="Bitches" by default) */ 1232 dpg_string_printf(text, _("%/GTK Stats: Bitches/%Tde"), 1233 Names.Bitches); 1234 gtk_label_set_text(GTK_LABEL(Status->BitchesName), text->str); 1235 g_string_printf(text, "%d", Play->Bitches.Carried); 1236 gtk_label_set_text(GTK_LABEL(Status->BitchesValue), text->str); 1237 } else { 1238 gtk_label_set_text(GTK_LABEL(Status->BitchesName), NULL); 1239 gtk_label_set_text(GTK_LABEL(Status->BitchesValue), NULL); 1240 } 1241 1242 g_string_printf(text, "%d", Play->Health); 1243 gtk_label_set_text(GTK_LABEL(Status->HealthValue), text->str); 1244 1245 g_string_free(text, TRUE); 1246 } 1247 1248 /* 1249 * Updates all of the player status in response to a message from the 1250 * server. This includes the main window display, the gun shop (if 1251 * displayed) and the inventory (if displayed). 1252 */ 1253 void UpdateStatus(Player *Play) 1254 { 1255 GtkAccelGroup *accel_group; 1256 1257 DisplayStats(Play, &ClientData.Status); 1258 UpdateInventory(&ClientData.Drug, ClientData.Play->Drugs, NumDrug, TRUE); 1259 accel_group = (GtkAccelGroup *) 1260 g_object_get_data(G_OBJECT(ClientData.window), "accel_group"); 1261 SetJetButtonTitle(accel_group); 1262 if (IsShowingGunShop) { 1263 UpdateInventory(&ClientData.Gun, ClientData.Play->Guns, NumGun, FALSE); 1264 } 1265 if (IsShowingInventory) { 1266 UpdateInventory(&ClientData.InvenDrug, ClientData.Play->Drugs, 1267 NumDrug, TRUE); 1268 UpdateInventory(&ClientData.InvenGun, ClientData.Play->Guns, 1269 NumGun, FALSE); 1270 } 1271 } 1272 1273 /* Columns in inventory list */ 1274 enum { 1275 INVEN_COL_NAME = 0, 1276 INVEN_COL_NUM, 1277 INVEN_COL_INDEX, 1278 INVEN_NUM_COLS 1279 }; 1280 1281 /* Get the currently selected inventory item (drug/gun) as an index into 1282 the drug/gun array, or -1 if none is selected */ 1283 static int get_selected_inventory(GtkTreeSelection *treesel) 1284 { 1285 GtkTreeModel *model; 1286 GtkTreeIter iter; 1287 if (gtk_tree_selection_get_selected(treesel, &model, &iter)) { 1288 int ind; 1289 gtk_tree_model_get(model, &iter, INVEN_COL_INDEX, &ind, -1); 1290 return ind; 1291 } else { 1292 return -1; 1293 } 1294 } 1295 1296 static void scroll_to_selection(GtkTreeModel *model, GtkTreePath *path, 1297 GtkTreeIter *iter, gpointer data) 1298 { 1299 GtkTreeView *tv = data; 1300 gtk_tree_view_scroll_to_cell(tv, path, NULL, FALSE, 0., 0.); 1301 } 1302 1303 void UpdateInventory(struct InventoryWidgets *Inven, 1304 Inventory *Objects, int NumObjects, gboolean AreDrugs) 1305 { 1306 GtkWidget *herelist, *carrylist; 1307 gint i; 1308 price_t price; 1309 gchar *titles[2]; 1310 gboolean CanBuy = FALSE, CanSell = FALSE, CanDrop = FALSE; 1311 GtkTreeIter iter; 1312 GtkTreeView *tv[2]; 1313 GtkListStore *carrystore, *herestore; 1314 int numlist, selectrow[2]; 1315 1316 herelist = Inven->HereList; 1317 carrylist = Inven->CarriedList; 1318 1319 numlist = (herelist ? 2 : 1); 1320 1321 /* Get current selections */ 1322 tv[0] = GTK_TREE_VIEW(carrylist); 1323 carrystore = GTK_LIST_STORE(gtk_tree_view_get_model(tv[0])); 1324 if (herelist) { 1325 tv[1] = GTK_TREE_VIEW(herelist); 1326 herestore = GTK_LIST_STORE(gtk_tree_view_get_model(tv[1])); 1327 } else { 1328 tv[1] = NULL; 1329 herestore = NULL; 1330 } 1331 1332 for (i = 0; i < numlist; i++) { 1333 selectrow[i] = get_selected_inventory(gtk_tree_view_get_selection(tv[i])); 1334 } 1335 1336 gtk_list_store_clear(carrystore); 1337 1338 if (herelist) { 1339 gtk_list_store_clear(herestore); 1340 } 1341 1342 for (i = 0; i < NumObjects; i++) { 1343 if (AreDrugs) { 1344 titles[0] = dpg_strdup_printf(_("%/Inventory drug name/%tde"), 1345 Drug[i].Name); 1346 price = Objects[i].Price; 1347 } else { 1348 titles[0] = dpg_strdup_printf(_("%/Inventory gun name/%tde"), 1349 Gun[i].Name); 1350 price = Gun[i].Price; 1351 } 1352 1353 if (herelist && price > 0) { 1354 CanBuy = TRUE; 1355 titles[1] = FormatPrice(price); 1356 gtk_list_store_append(herestore, &iter); 1357 gtk_list_store_set(herestore, &iter, INVEN_COL_NAME, titles[0], 1358 INVEN_COL_NUM, titles[1], INVEN_COL_INDEX, i, -1); 1359 g_free(titles[1]); 1360 if (i == selectrow[1]) { 1361 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(tv[1]), 1362 &iter); 1363 } 1364 } 1365 1366 if (Objects[i].Carried > 0) { 1367 if (price > 0) { 1368 CanSell = TRUE; 1369 } else { 1370 CanDrop = TRUE; 1371 } 1372 if (HaveAbility(ClientData.Play, A_DRUGVALUE) && AreDrugs) { 1373 titles[1] = dpg_strdup_printf("%d @ %P", Objects[i].Carried, 1374 Objects[i].TotalValue / 1375 Objects[i].Carried); 1376 } else { 1377 titles[1] = g_strdup_printf("%d", Objects[i].Carried); 1378 } 1379 gtk_list_store_append(carrystore, &iter); 1380 gtk_list_store_set(carrystore, &iter, INVEN_COL_NAME, titles[0], 1381 INVEN_COL_NUM, titles[1], INVEN_COL_INDEX, i, -1); 1382 g_free(titles[1]); 1383 if (i == selectrow[0]) { 1384 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(tv[0]), 1385 &iter); 1386 } 1387 } 1388 g_free(titles[0]); 1389 } 1390 1391 #ifdef CYGWIN 1392 /* Our Win32 GtkTreeView implementation doesn't auto-sort, so force it */ 1393 if (herelist) { 1394 gtk_tree_view_sort(GTK_TREE_VIEW(herelist)); 1395 } 1396 #endif 1397 1398 /* Scroll so that selection is visible */ 1399 for (i = 0; i < numlist; i++) { 1400 gtk_tree_selection_selected_foreach(gtk_tree_view_get_selection(tv[i]), 1401 scroll_to_selection, tv[i]); 1402 } 1403 1404 if (Inven->vbbox) { 1405 gtk_widget_set_sensitive(Inven->BuyButton, CanBuy); 1406 gtk_widget_set_sensitive(Inven->SellButton, CanSell); 1407 gtk_widget_set_sensitive(Inven->DropButton, CanDrop); 1408 } 1409 } 1410 1411 static void JetCallback(GtkWidget *widget, gpointer data) 1412 { 1413 int NewLocation; 1414 gchar *text; 1415 GtkWidget *JetDialog; 1416 1417 JetDialog = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), "dialog")); 1418 NewLocation = GPOINTER_TO_INT(data); 1419 gtk_widget_destroy(JetDialog); 1420 text = g_strdup_printf("%d", NewLocation); 1421 SendClientMessage(ClientData.Play, C_NONE, C_REQUESTJET, NULL, text); 1422 g_free(text); 1423 } 1424 1425 void JetButtonPressed(GtkWidget *widget, gpointer data) 1426 { 1427 if (InGame) { 1428 if (ClientData.Play->Flags & FIGHTING) { 1429 DisplayFightMessage(""); 1430 } else { 1431 Jet(NULL); 1432 } 1433 } 1434 } 1435 1436 void Jet(GtkWidget *parent) 1437 { 1438 GtkWidget *dialog, *grid, *button, *label, *vbox; 1439 GtkAccelGroup *accel_group; 1440 gint boxsize, i, row, col; 1441 gchar *name, AccelChar; 1442 1443 accel_group = gtk_accel_group_new(); 1444 1445 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1446 /* Title of 'Jet' dialog */ 1447 gtk_window_set_title(GTK_WINDOW(dialog), _("Jet to location")); 1448 my_set_dialog_position(GTK_WINDOW(dialog)); 1449 1450 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 1451 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 1452 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 1453 gtk_window_set_transient_for(GTK_WINDOW(dialog), 1454 parent ? GTK_WINDOW(parent) 1455 : GTK_WINDOW(ClientData.window)); 1456 1457 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 1458 1459 /* Prompt in 'Jet' dialog */ 1460 label = gtk_label_new(_("Where to, dude ? ")); 1461 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 1462 1463 /* Generate a square box of buttons for all locations */ 1464 boxsize = 1; 1465 while (boxsize * boxsize < NumLocation) { 1466 boxsize++; 1467 } 1468 col = boxsize; 1469 row = 1; 1470 1471 /* Avoid creating a box with an entire row empty at the bottom */ 1472 while (row * col < NumLocation) { 1473 row++; 1474 } 1475 1476 grid = dp_gtk_grid_new(row, col, TRUE); 1477 1478 for (i = 0; i < NumLocation; i++) { 1479 if (i < 9) { 1480 AccelChar = '1' + i; 1481 } else if (i < 35) { 1482 AccelChar = 'A' + i - 9; 1483 } else { 1484 AccelChar = '\0'; 1485 } 1486 1487 row = i / boxsize; 1488 col = i % boxsize; 1489 if (AccelChar == '\0') { 1490 name = dpg_strdup_printf(_("%/Location to jet to/%tde"), 1491 Location[i].Name); 1492 button = gtk_button_new_with_label(name); 1493 g_free(name); 1494 } else { 1495 button = gtk_button_new_with_label(""); 1496 1497 /* Display of locations in 'Jet' window (%tde="The Bronx" etc. by 1498 default) */ 1499 name = dpg_strdup_printf(_("_%c. %tde"), AccelChar, Location[i].Name); 1500 SetAccelerator(button, name, button, "clicked", accel_group, FALSE); 1501 /* Add keypad shortcuts as well */ 1502 if (i < 9) { 1503 gtk_widget_add_accelerator(button, "clicked", accel_group, 1504 GDK_KEY_KP_1 + i, 0, 1505 GTK_ACCEL_VISIBLE); 1506 } 1507 g_free(name); 1508 } 1509 gtk_widget_set_sensitive(button, i != ClientData.Play->IsAt); 1510 g_object_set_data(G_OBJECT(button), "dialog", dialog); 1511 g_signal_connect(G_OBJECT(button), "clicked", 1512 G_CALLBACK(JetCallback), GINT_TO_POINTER(i)); 1513 dp_gtk_grid_attach(GTK_GRID(grid), button, col, row, 1, 1, TRUE); 1514 } 1515 gtk_box_pack_start(GTK_BOX(vbox), grid, TRUE, TRUE, 0); 1516 1517 gtk_container_add(GTK_CONTAINER(dialog), vbox); 1518 gtk_widget_show_all(dialog); 1519 } 1520 1521 static void UpdateDealDialog(void) 1522 { 1523 GString *text; 1524 GtkAdjustment *spin_adj; 1525 gint DrugInd, CanDrop, CanCarry, CanAfford, MaxDrug; 1526 Player *Play; 1527 1528 text = g_string_new(NULL); 1529 DrugInd = DealDialog.DrugInd; 1530 Play = ClientData.Play; 1531 1532 /* Display of the current price of the selected drug in 'Deal Drugs' 1533 dialog */ 1534 dpg_string_printf(text, _("at %P"), Play->Drugs[DrugInd].Price); 1535 gtk_label_set_text(GTK_LABEL(DealDialog.cost), text->str); 1536 1537 CanDrop = Play->Drugs[DrugInd].Carried; 1538 1539 /* Display of current inventory of the selected drug in 'Deal Drugs' 1540 dialog (%tde="Opium" etc. by default) */ 1541 dpg_string_printf(text, _("You are currently carrying %d %tde"), 1542 CanDrop, Drug[DrugInd].Name); 1543 gtk_label_set_text(GTK_LABEL(DealDialog.carrying), text->str); 1544 1545 CanCarry = Play->CoatSize; 1546 1547 /* Available space for drugs in 'Deal Drugs' dialog */ 1548 g_string_printf(text, _("Available space: %d"), CanCarry); 1549 gtk_label_set_text(GTK_LABEL(DealDialog.space), text->str); 1550 1551 if (DealDialog.Type == BT_BUY) { 1552 /* Just in case a price update from the server slips through */ 1553 if (Play->Drugs[DrugInd].Price == 0) { 1554 CanAfford = 0; 1555 } else { 1556 CanAfford = Play->Cash / Play->Drugs[DrugInd].Price; 1557 } 1558 1559 /* Number of the selected drug that you can afford in 'Deal Drugs' 1560 dialog */ 1561 g_string_printf(text, _("You can afford %d"), CanAfford); 1562 gtk_label_set_text(GTK_LABEL(DealDialog.afford), text->str); 1563 MaxDrug = MIN(CanCarry, CanAfford); 1564 } else { 1565 MaxDrug = CanDrop; 1566 } 1567 1568 spin_adj = (GtkAdjustment *)gtk_adjustment_new(MaxDrug, 0.0, MaxDrug, 1569 1.0, 10.0, 0.0); 1570 gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(DealDialog.amount), 1571 spin_adj); 1572 gtk_spin_button_set_value(GTK_SPIN_BUTTON(DealDialog.amount), MaxDrug); 1573 1574 g_string_free(text, TRUE); 1575 } 1576 1577 /* Columns in deal list */ 1578 enum { 1579 DEAL_COL_NAME = 0, 1580 DEAL_COL_INDEX = 1, 1581 DEAL_NUM_COLS 1582 }; 1583 1584 static void DealSelectCallback(GtkWidget *widget, gpointer data) 1585 { 1586 GtkTreeIter iter; 1587 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter)) { 1588 GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); 1589 gtk_tree_model_get(model, &iter, DEAL_COL_INDEX, &DealDialog.DrugInd, -1); 1590 UpdateDealDialog(); 1591 } 1592 } 1593 1594 static void DealOKCallback(GtkWidget *widget, gpointer data) 1595 { 1596 GtkWidget *spinner; 1597 gint amount; 1598 gchar *text; 1599 1600 spinner = DealDialog.amount; 1601 1602 gtk_spin_button_update(GTK_SPIN_BUTTON(spinner)); 1603 amount = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinner)); 1604 1605 text = g_strdup_printf("drug^%d^%d", DealDialog.DrugInd, 1606 data == BT_BUY ? amount : -amount); 1607 1608 gtk_widget_destroy(DealDialog.dialog); 1609 1610 SendClientMessage(ClientData.Play, C_NONE, C_BUYOBJECT, NULL, text); 1611 g_free(text); 1612 } 1613 1614 void DealDrugs(GtkWidget *widget, gpointer data) 1615 { 1616 GtkWidget *dialog, *label, *hbox, *hbbox, *button, *spinner, *combo_box, 1617 *vbox, *hsep, *defbutton; 1618 GtkListStore *store; 1619 GtkTreeIter iter; 1620 GtkCellRenderer *renderer; 1621 GtkAdjustment *spin_adj; 1622 GtkAccelGroup *accel_group; 1623 GtkWidget *tv; 1624 gchar *Action; 1625 GString *text; 1626 Player *Play; 1627 gint DrugInd, i, SelIndex, FirstInd; 1628 gboolean DrugIndOK; 1629 1630 g_assert(!IsShowingDealDrugs); 1631 1632 /* Action in 'Deal Drugs' dialog - "Buy/Sell/Drop Drugs" */ 1633 if (data == BT_BUY) { 1634 Action = _("Buy"); 1635 } else if (data == BT_SELL) { 1636 Action = _("Sell"); 1637 } else if (data == BT_DROP) { 1638 Action = _("Drop"); 1639 } else { 1640 g_warning("Bad DealDrug type"); 1641 return; 1642 } 1643 1644 DealDialog.Type = data; 1645 Play = ClientData.Play; 1646 1647 if (data == BT_BUY) { 1648 tv = ClientData.Drug.HereList; 1649 } else { 1650 tv = ClientData.Drug.CarriedList; 1651 } 1652 DrugInd = get_selected_inventory( 1653 gtk_tree_view_get_selection(GTK_TREE_VIEW(tv))); 1654 1655 DrugIndOK = FALSE; 1656 FirstInd = -1; 1657 for (i = 0; i < NumDrug; i++) { 1658 if ((data == BT_DROP && Play->Drugs[i].Carried > 0 1659 && Play->Drugs[i].Price == 0) 1660 || (data == BT_SELL && Play->Drugs[i].Carried > 0 1661 && Play->Drugs[i].Price != 0) 1662 || (data == BT_BUY && Play->Drugs[i].Price != 0)) { 1663 if (FirstInd == -1) { 1664 FirstInd = i; 1665 } 1666 if (DrugInd == i) { 1667 DrugIndOK = TRUE; 1668 } 1669 } 1670 } 1671 if (!DrugIndOK) { 1672 if (FirstInd == -1) { 1673 return; 1674 } else { 1675 DrugInd = FirstInd; 1676 } 1677 } 1678 1679 text = g_string_new(NULL); 1680 accel_group = gtk_accel_group_new(); 1681 1682 dialog = DealDialog.dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1683 gtk_window_set_title(GTK_WINDOW(dialog), Action); 1684 my_set_dialog_position(GTK_WINDOW(dialog)); 1685 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 1686 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 1687 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 1688 gtk_window_set_transient_for(GTK_WINDOW(dialog), 1689 GTK_WINDOW(ClientData.window)); 1690 SetShowing(dialog, &IsShowingDealDrugs); 1691 1692 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 1693 1694 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 7); 1695 1696 label = gtk_label_new(Action); 1697 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); 1698 1699 store = gtk_list_store_new(DEAL_NUM_COLS, G_TYPE_STRING, G_TYPE_INT); 1700 SelIndex = -1; 1701 for (i = 0; i < NumDrug; i++) { 1702 if ((data == BT_DROP && Play->Drugs[i].Carried > 0 1703 && Play->Drugs[i].Price == 0) 1704 || (data == BT_SELL && Play->Drugs[i].Carried > 0 1705 && Play->Drugs[i].Price != 0) 1706 || (data == BT_BUY && Play->Drugs[i].Price != 0)) { 1707 dpg_string_printf(text, _("%/DealDrugs drug name/%tde"), Drug[i].Name); 1708 gtk_list_store_append(store, &iter); 1709 gtk_list_store_set(store, &iter, DEAL_COL_NAME, text->str, 1710 DEAL_COL_INDEX, i, -1); 1711 if (DrugInd >= i) { 1712 SelIndex++; 1713 } 1714 } 1715 } 1716 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); 1717 g_object_unref(store); 1718 renderer = gtk_cell_renderer_text_new(); 1719 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE); 1720 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, 1721 "text", DEAL_COL_NAME, NULL); 1722 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), SelIndex); 1723 gtk_box_pack_start(GTK_BOX(hbox), combo_box, TRUE, TRUE, 0); 1724 1725 DealDialog.DrugInd = DrugInd; 1726 1727 label = DealDialog.cost = gtk_label_new(NULL); 1728 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); 1729 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); 1730 1731 label = DealDialog.carrying = gtk_label_new(NULL); 1732 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 1733 1734 label = DealDialog.space = gtk_label_new(NULL); 1735 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 1736 1737 if (data == BT_BUY) { 1738 label = DealDialog.afford = gtk_label_new(NULL); 1739 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 1740 } 1741 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 7); 1742 if (data == BT_BUY) { 1743 /* Prompts for action in the "deal drugs" dialog */ 1744 g_string_printf(text, _("Buy how many?")); 1745 } else if (data == BT_SELL) { 1746 g_string_printf(text, _("Sell how many?")); 1747 } else { 1748 g_string_printf(text, _("Drop how many?")); 1749 } 1750 label = gtk_label_new(text->str); 1751 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); 1752 spin_adj = (GtkAdjustment *)gtk_adjustment_new(1.0, 0.0, 2.0, 1753 1.0, 10.0, 0.0); 1754 spinner = DealDialog.amount = gtk_spin_button_new(spin_adj, 1.0, 0); 1755 g_signal_connect(G_OBJECT(spinner), "activate", 1756 G_CALLBACK(DealOKCallback), data); 1757 gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0); 1758 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); 1759 1760 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 1761 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 1762 1763 hbbox = my_hbbox_new(); 1764 button = gtk_button_new_with_mnemonic(_("_OK")); 1765 g_signal_connect(G_OBJECT(button), "clicked", 1766 G_CALLBACK(DealOKCallback), data); 1767 gtk_widget_set_can_default(button, TRUE); 1768 defbutton = button; 1769 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 1770 1771 button = gtk_button_new_with_mnemonic(_("_Cancel")); 1772 g_signal_connect_swapped(G_OBJECT(button), "clicked", 1773 G_CALLBACK(gtk_widget_destroy), 1774 G_OBJECT(dialog)); 1775 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 1776 1777 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 1778 gtk_container_add(GTK_CONTAINER(dialog), vbox); 1779 1780 g_signal_connect(G_OBJECT(combo_box), "changed", 1781 G_CALLBACK(DealSelectCallback), NULL); 1782 1783 g_string_free(text, TRUE); 1784 UpdateDealDialog(); 1785 1786 gtk_widget_show_all(dialog); 1787 gtk_widget_grab_default(defbutton); 1788 } 1789 1790 void DealGuns(GtkWidget *widget, gpointer data) 1791 { 1792 GtkWidget *tv, *dialog; 1793 gint GunInd; 1794 gchar *Title; 1795 GString *text; 1796 1797 dialog = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); 1798 1799 if (data == BT_BUY) { 1800 tv = ClientData.Gun.HereList; 1801 } else { 1802 tv = ClientData.Gun.CarriedList; 1803 } 1804 GunInd = get_selected_inventory( 1805 gtk_tree_view_get_selection(GTK_TREE_VIEW(tv))); 1806 if (GunInd < 0) { 1807 return; 1808 } 1809 1810 1811 /* Title of 'gun shop' dialog (%tde="guns" by default) "Buy/Sell/Drop 1812 * Guns" */ 1813 if (data == BT_BUY) { 1814 Title = dpg_strdup_printf(_("Buy %tde"), Names.Guns); 1815 } else if (data == BT_SELL) { 1816 Title = dpg_strdup_printf(_("Sell %tde"), Names.Guns); 1817 } else { 1818 Title = dpg_strdup_printf(_("Drop %tde"), Names.Guns); 1819 } 1820 1821 text = g_string_new(""); 1822 1823 if (data != BT_BUY && TotalGunsCarried(ClientData.Play) == 0) { 1824 dpg_string_printf(text, _("You don't have any %tde to sell!"), 1825 Names.Guns); 1826 GtkMessageBox(dialog, text->str, Title, GTK_MESSAGE_WARNING, MB_OK); 1827 } else if (data == BT_BUY && TotalGunsCarried(ClientData.Play) >= 1828 ClientData.Play->Bitches.Carried + 2) { 1829 dpg_string_printf(text, 1830 _("You'll need more %tde to carry any more %tde!"), 1831 Names.Bitches, Names.Guns); 1832 GtkMessageBox(dialog, text->str, Title, GTK_MESSAGE_WARNING, MB_OK); 1833 } else if (data == BT_BUY 1834 && Gun[GunInd].Space > ClientData.Play->CoatSize) { 1835 dpg_string_printf(text, 1836 _("You don't have enough space to carry that %tde!"), 1837 Names.Gun); 1838 GtkMessageBox(dialog, text->str, Title, GTK_MESSAGE_WARNING, MB_OK); 1839 } else if (data == BT_BUY && Gun[GunInd].Price > ClientData.Play->Cash) { 1840 dpg_string_printf(text, 1841 _("You don't have enough cash to buy that %tde!"), 1842 Names.Gun); 1843 GtkMessageBox(dialog, text->str, Title, GTK_MESSAGE_WARNING, MB_OK); 1844 } else if (data == BT_SELL && ClientData.Play->Guns[GunInd].Carried == 0) { 1845 GtkMessageBox(dialog, _("You don't have any to sell!"), Title, 1846 GTK_MESSAGE_WARNING, MB_OK); 1847 } else { 1848 g_string_printf(text, "gun^%d^%d", GunInd, data == BT_BUY ? 1 : -1); 1849 SendClientMessage(ClientData.Play, C_NONE, C_BUYOBJECT, NULL, 1850 text->str); 1851 } 1852 g_free(Title); 1853 g_string_free(text, TRUE); 1854 } 1855 1856 static void QuestionCallback(GtkWidget *widget, gpointer data) 1857 { 1858 gint Answer; 1859 gchar text[5]; 1860 GtkWidget *dialog; 1861 Player *To; 1862 1863 dialog = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), "dialog")); 1864 To = (Player *)g_object_get_data(G_OBJECT(dialog), "From"); 1865 Answer = GPOINTER_TO_INT(data); 1866 1867 text[0] = (gchar)Answer; 1868 text[1] = '\0'; 1869 SendClientMessage(ClientData.Play, C_NONE, C_ANSWER, To, text); 1870 1871 gtk_widget_destroy(dialog); 1872 } 1873 1874 void QuestionDialog(char *Data, Player *From) 1875 { 1876 GtkWidget *dialog, *label, *vbox, *hsep, *hbbox, *button; 1877 GtkAccelGroup *accel_group; 1878 gchar *Responses, **split, *LabelText, *trword, *underline; 1879 1880 /* Button titles that correspond to the single-keypress options provided 1881 by the curses client (e.g. _Yes corresponds to 'Y' etc.) */ 1882 gchar *Words[] = { N_("_Yes"), N_("_No"), N_("_Run"), 1883 N_("_Fight"), N_("_Attack"), N_("_Evade") 1884 }; 1885 guint numWords = sizeof(Words) / sizeof(Words[0]); 1886 guint i, j; 1887 1888 split = g_strsplit(Data, "^", 2); 1889 if (!split[0] || !split[1]) { 1890 g_warning("Bad QUESTION message %s", Data); 1891 return; 1892 } 1893 1894 g_strdelimit(split[1], "^", '\n'); 1895 1896 Responses = split[0]; 1897 LabelText = split[1]; 1898 1899 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1900 accel_group = gtk_accel_group_new(); 1901 g_signal_connect(G_OBJECT(dialog), "delete_event", 1902 G_CALLBACK(DisallowDelete), NULL); 1903 g_object_set_data(G_OBJECT(dialog), "From", (gpointer)From); 1904 1905 /* Title of the 'ask player a question' dialog */ 1906 gtk_window_set_title(GTK_WINDOW(dialog), _("Question")); 1907 my_set_dialog_position(GTK_WINDOW(dialog)); 1908 1909 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 1910 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 1911 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 1912 gtk_window_set_transient_for(GTK_WINDOW(dialog), 1913 GTK_WINDOW(ClientData.window)); 1914 1915 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 1916 while (*LabelText == '\n') 1917 LabelText++; 1918 label = gtk_label_new(LabelText); 1919 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 1920 1921 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 1922 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 1923 1924 hbbox = my_hbbox_new(); 1925 1926 for (i = 0; i < strlen(Responses); i++) { 1927 switch (Responses[i]) { 1928 case 'Y': 1929 button = gtk_button_new_with_mnemonic(_("_Yes")); 1930 break; 1931 case 'N': 1932 button = gtk_button_new_with_mnemonic(_("_No")); 1933 break; 1934 default: 1935 for (j = 0, trword = NULL; j < numWords && !trword; j++) { 1936 underline = strchr(Words[j], '_'); 1937 if (underline && toupper(underline[1]) == Responses[i]) { 1938 trword = _(Words[j]); 1939 } 1940 } 1941 button = gtk_button_new_with_label(""); 1942 if (trword) { 1943 SetAccelerator(button, trword, button, "clicked", accel_group, FALSE); 1944 } else { 1945 trword = g_strdup_printf("_%c", Responses[i]); 1946 SetAccelerator(button, trword, button, "clicked", accel_group, FALSE); 1947 g_free(trword); 1948 } 1949 break; 1950 } 1951 g_object_set_data(G_OBJECT(button), "dialog", (gpointer)dialog); 1952 g_signal_connect(G_OBJECT(button), "clicked", 1953 G_CALLBACK(QuestionCallback), 1954 GINT_TO_POINTER((gint)Responses[i])); 1955 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 1956 } 1957 gtk_box_pack_start(GTK_BOX(vbox), hbbox, TRUE, TRUE, 0); 1958 gtk_container_add(GTK_CONTAINER(dialog), vbox); 1959 gtk_widget_show_all(dialog); 1960 1961 g_strfreev(split); 1962 } 1963 1964 void GuiStartGame(void) 1965 { 1966 Player *Play = ClientData.Play; 1967 1968 if (!Network) { 1969 ClientData.cmdline->antique = WantAntique; 1970 InitConfiguration(ClientData.cmdline); 1971 } 1972 StripTerminators(GetPlayerName(Play)); 1973 InitAbilities(Play); 1974 SendAbilities(Play); 1975 SendNullClientMessage(Play, C_NONE, C_NAME, NULL, GetPlayerName(Play)); 1976 InGame = TRUE; 1977 UpdateMenus(); 1978 gtk_widget_show_all(ClientData.vbox); 1979 UpdatePlayerLists(); 1980 SoundPlay(Sounds.StartGame); 1981 } 1982 1983 void EndGame(void) 1984 { 1985 DisplayFightMessage(NULL); 1986 gtk_widget_hide(ClientData.vbox); 1987 TextViewClear(GTK_TEXT_VIEW(ClientData.messages)); 1988 ShutdownNetwork(ClientData.Play); 1989 UpdatePlayerLists(); 1990 CleanUpServer(); 1991 RestoreConfig(); 1992 InGame = FALSE; 1993 UpdateMenus(); 1994 SoundPlay(Sounds.EndGame); 1995 } 1996 1997 static gint DrugSortByName(GtkTreeModel *model, GtkTreeIter *a, 1998 GtkTreeIter *b, gpointer data) 1999 { 2000 int indexa, indexb; 2001 gtk_tree_model_get(model, a, INVEN_COL_INDEX, &indexa, -1); 2002 gtk_tree_model_get(model, b, INVEN_COL_INDEX, &indexb, -1); 2003 if (indexa < 0 || indexa >= NumDrug || indexb < 0 || indexb >= NumDrug) { 2004 return 0; 2005 } 2006 return g_ascii_strcasecmp(Drug[indexa].Name, Drug[indexb].Name); 2007 } 2008 2009 static gint DrugSortByPrice(GtkTreeModel *model, GtkTreeIter *a, 2010 GtkTreeIter *b, gpointer data) 2011 { 2012 int indexa, indexb; 2013 price_t pricediff; 2014 gtk_tree_model_get(model, a, INVEN_COL_INDEX, &indexa, -1); 2015 gtk_tree_model_get(model, b, INVEN_COL_INDEX, &indexb, -1); 2016 if (indexa < 0 || indexa >= NumDrug || indexb < 0 || indexb >= NumDrug) { 2017 return 0; 2018 } 2019 pricediff = ClientData.Play->Drugs[indexa].Price - 2020 ClientData.Play->Drugs[indexb].Price; 2021 return pricediff == 0 ? 0 : pricediff < 0 ? -1 : 1; 2022 } 2023 2024 void UpdateMenus(void) 2025 { 2026 gboolean MultiPlayer; 2027 gint Bitches; 2028 2029 MultiPlayer = (FirstClient && FirstClient->next != NULL); 2030 Bitches = InGame && ClientData.Play ? ClientData.Play->Bitches.Carried : 0; 2031 2032 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget(ClientData.Menu, 2033 "
/Talk"), 2034 InGame && Network); 2035 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2036 (ClientData.Menu, "
/Game/Options..."), 2037 !InGame); 2038 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2039 (ClientData.Menu, "
/Game/Abandon..."), 2040 InGame); 2041 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2042 (ClientData.Menu, "
/List/Inventory..."), 2043 InGame); 2044 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2045 (ClientData.Menu, "
/List/Players..."), 2046 InGame && Network); 2047 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2048 (ClientData.Menu, "
/Errands"), InGame); 2049 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2050 (ClientData.Menu, "
/Errands/Spy..."), 2051 InGame && MultiPlayer); 2052 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2053 (ClientData.Menu, "
/Errands/Tipoff..."), 2054 InGame && MultiPlayer); 2055 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2056 (ClientData.Menu, 2057 "
/Errands/Sack Bitch..."), Bitches > 0); 2058 gtk_widget_set_sensitive(dp_gtk_item_factory_get_widget 2059 (ClientData.Menu, 2060 "
/Errands/Get spy reports..."), InGame 2061 && MultiPlayer); 2062 } 2063 2064 GtkWidget *CreateStatusWidgets(struct StatusWidgets *Status) 2065 { 2066 GtkWidget *grid, *label; 2067 2068 grid = dp_gtk_grid_new(3, 6, FALSE); 2069 gtk_grid_set_row_spacing(GTK_GRID(grid), 3); 2070 gtk_grid_set_column_spacing(GTK_GRID(grid), 3); 2071 gtk_container_set_border_width(GTK_CONTAINER(grid), 3); 2072 2073 label = Status->Location = gtk_label_new(NULL); 2074 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 2, 1, TRUE); 2075 2076 label = Status->Date = gtk_label_new(NULL); 2077 dp_gtk_grid_attach(GTK_GRID(grid), label, 2, 0, 2, 1, TRUE); 2078 2079 /* Available space label in GTK+ client status display */ 2080 label = Status->SpaceName = gtk_label_new(_("Space")); 2081 2082 dp_gtk_grid_attach(GTK_GRID(grid), label, 4, 0, 1, 1, TRUE); 2083 label = Status->SpaceValue = gtk_label_new(NULL); 2084 dp_gtk_grid_attach(GTK_GRID(grid), label, 5, 0, 1, 1, TRUE); 2085 2086 /* Player's cash label in GTK+ client status display */ 2087 label = Status->CashName = gtk_label_new(_("Cash")); 2088 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1, TRUE); 2089 2090 label = Status->CashValue = gtk_label_new(NULL); 2091 dp_gtk_grid_attach(GTK_GRID(grid), label, 1, 1, 1, 1, TRUE); 2092 2093 /* Player's debt label in GTK+ client status display */ 2094 label = Status->DebtName = gtk_label_new(_("Debt")); 2095 dp_gtk_grid_attach(GTK_GRID(grid), label, 2, 1, 1, 1, TRUE); 2096 2097 label = Status->DebtValue = gtk_label_new(NULL); 2098 dp_gtk_grid_attach(GTK_GRID(grid), label, 3, 1, 1, 1, TRUE); 2099 2100 /* Player's bank balance label in GTK+ client status display */ 2101 label = Status->BankName = gtk_label_new(_("Bank")); 2102 dp_gtk_grid_attach(GTK_GRID(grid), label, 4, 1, 1, 1, TRUE); 2103 2104 label = Status->BankValue = gtk_label_new(NULL); 2105 dp_gtk_grid_attach(GTK_GRID(grid), label, 5, 1, 1, 1, TRUE); 2106 2107 label = Status->GunsName = gtk_label_new(NULL); 2108 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1, TRUE); 2109 label = Status->GunsValue = gtk_label_new(NULL); 2110 dp_gtk_grid_attach(GTK_GRID(grid), label, 1, 2, 1, 1, TRUE); 2111 2112 label = Status->BitchesName = gtk_label_new(NULL); 2113 dp_gtk_grid_attach(GTK_GRID(grid), label, 2, 2, 1, 1, TRUE); 2114 label = Status->BitchesValue = gtk_label_new(NULL); 2115 dp_gtk_grid_attach(GTK_GRID(grid), label, 3, 2, 1, 1, TRUE); 2116 2117 /* Player's health label in GTK+ client status display */ 2118 label = Status->HealthName = gtk_label_new(_("Health")); 2119 dp_gtk_grid_attach(GTK_GRID(grid), label, 4, 2, 1, 1, TRUE); 2120 2121 label = Status->HealthValue = gtk_label_new(NULL); 2122 dp_gtk_grid_attach(GTK_GRID(grid), label, 5, 2, 1, 1, TRUE); 2123 return grid; 2124 } 2125 2126 void SetJetButtonTitle(GtkAccelGroup *accel_group) 2127 { 2128 GtkWidget *button; 2129 guint accel_key; 2130 gchar *caption; 2131 2132 button = ClientData.JetButton; 2133 accel_key = ClientData.JetAccel; 2134 2135 if (accel_key) { 2136 gtk_widget_remove_accelerator(button, accel_group, accel_key, 0); 2137 } 2138 2139 if (ClientData.Play && ClientData.Play->Flags & FIGHTING) { 2140 caption = _("_Fight"); 2141 } else { 2142 /* Caption of 'Jet' button in main window */ 2143 caption = _("_Jet!"); 2144 } 2145 ClientData.JetAccel = SetAccelerator(button, caption, button, 2146 "clicked", accel_group, FALSE); 2147 } 2148 2149 static void SetIcon(GtkWidget *window, char **xpmdata) 2150 { 2151 #ifndef CYGWIN 2152 GdkPixbuf *icon; 2153 icon = gdk_pixbuf_new_from_xpm_data((const char**)xpmdata); 2154 gtk_window_set_icon(GTK_WINDOW(window), icon); 2155 #endif 2156 } 2157 2158 static void make_tags(GtkTextView *textview) 2159 { 2160 GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview); 2161 2162 gtk_text_buffer_create_tag(buffer, "jet", "foreground", 2163 "#00000000FFFF", NULL); 2164 gtk_text_buffer_create_tag(buffer, "talk", "foreground", 2165 "#FFFF00000000", NULL); 2166 gtk_text_buffer_create_tag(buffer, "page", "foreground", 2167 "#FFFF0000FFFF", NULL); 2168 gtk_text_buffer_create_tag(buffer, "join", "foreground", 2169 "#000000008B8B", NULL); 2170 gtk_text_buffer_create_tag(buffer, "leave", "foreground", 2171 "#8B8B00000000", NULL); 2172 } 2173 2174 #ifdef CYGWIN 2175 gboolean GtkLoop(HINSTANCE hInstance, HINSTANCE hPrevInstance, 2176 struct CMDLINE *cmdline, gboolean ReturnOnFail) 2177 #else 2178 gboolean GtkLoop(int *argc, char **argv[], 2179 struct CMDLINE *cmdline, gboolean ReturnOnFail) 2180 #endif 2181 { 2182 GtkWidget *window, *vbox, *vbox2, *hbox, *frame, *grid, *menubar, *text, 2183 *vpaned, *button, *tv, *widget; 2184 GtkAccelGroup *accel_group; 2185 GtkTreeSortable *sortable; 2186 int i; 2187 DPGtkItemFactory *item_factory; 2188 gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); 2189 2190 #ifdef CYGWIN 2191 win32_init(hInstance, hPrevInstance, "mainicon"); 2192 #else 2193 if (ReturnOnFail && !gtk_init_check(argc, argv)) 2194 return FALSE; 2195 else if (!ReturnOnFail) 2196 gtk_init(argc, argv); 2197 #endif 2198 2199 /* GTK+2 (and the GTK emulation code on WinNT systems) expects all 2200 * strings to be UTF-8, so we force gettext to return all translations 2201 * in this encoding here. */ 2202 bind_textdomain_codeset(PACKAGE, "UTF-8"); 2203 2204 Conv_SetInternalCodeset("UTF-8"); 2205 WantUTF8Errors(TRUE); 2206 2207 InitConfiguration(cmdline); 2208 ClientData.cmdline = cmdline; 2209 2210 /* Set up message handlers */ 2211 ClientMessageHandlerPt = HandleClientMessage; 2212 2213 if (!CheckHighScoreFileConfig()) { 2214 return TRUE; 2215 } 2216 2217 /* Have the GLib log messages pop up in a nice dialog box */ 2218 g_log_set_handler(NULL, 2219 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING | 2220 G_LOG_LEVEL_CRITICAL, LogMessage, NULL); 2221 2222 SoundOpen(cmdline->plugin); 2223 2224 /* Create the main player */ 2225 ClientData.Play = g_new(Player, 1); 2226 FirstClient = AddPlayer(0, ClientData.Play, FirstClient); 2227 if (PlayerName && PlayerName[0]) { 2228 SetPlayerName(ClientData.Play, PlayerName); 2229 } 2230 2231 window = MainWindow = ClientData.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2232 2233 /* Title of main window in GTK+ client */ 2234 gtk_window_set_title(GTK_WINDOW(window), _("dopewars")); 2235 gtk_window_set_default_size(GTK_WINDOW(window), 450, 390); 2236 g_signal_connect(G_OBJECT(window), "delete_event", 2237 G_CALLBACK(MainDelete), NULL); 2238 g_signal_connect(G_OBJECT(window), "destroy", 2239 G_CALLBACK(DestroyGtk), NULL); 2240 2241 accel_group = gtk_accel_group_new(); 2242 g_object_set_data(G_OBJECT(window), "accel_group", accel_group); 2243 item_factory = ClientData.Menu = dp_gtk_item_factory_new("
", 2244 accel_group); 2245 dp_gtk_item_factory_set_translate_func(item_factory, MenuTranslate, NULL, 2246 NULL); 2247 2248 dp_gtk_item_factory_create_items(item_factory, nmenu_items, menu_items, 2249 NULL); 2250 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); 2251 menubar = dp_gtk_item_factory_get_widget(item_factory, "
"); 2252 2253 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 2254 gtk_box_pack_start(GTK_BOX(vbox2), menubar, FALSE, FALSE, 0); 2255 gtk_widget_show_all(menubar); 2256 UpdateMenus(); 2257 SoundEnable(UseSounds); 2258 widget = dp_gtk_item_factory_get_widget(ClientData.Menu, 2259 "
/Game/Enable sound"); 2260 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), UseSounds); 2261 2262 vbox = ClientData.vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); 2263 frame = gtk_frame_new(_("Stats")); 2264 gtk_container_set_border_width(GTK_CONTAINER(frame), 3); 2265 2266 grid = CreateStatusWidgets(&ClientData.Status); 2267 2268 gtk_container_add(GTK_CONTAINER(frame), grid); 2269 2270 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); 2271 2272 vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); 2273 2274 text = ClientData.messages = gtk_scrolled_text_view_new(&hbox); 2275 make_tags(GTK_TEXT_VIEW(text)); 2276 gtk_widget_set_size_request(text, 100, 80); 2277 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); 2278 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); 2279 gtk_paned_pack1(GTK_PANED(vpaned), hbox, TRUE, TRUE); 2280 2281 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 7); 2282 CreateInventory(hbox, Names.Drugs, accel_group, TRUE, TRUE, 2283 &ClientData.Drug, G_CALLBACK(DealDrugs)); 2284 tv = ClientData.Drug.HereList; 2285 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(tv), TRUE); 2286 sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model(GTK_TREE_VIEW(tv))); 2287 gtk_tree_sortable_set_sort_func(sortable, 0, DrugSortByName, NULL, NULL); 2288 gtk_tree_sortable_set_sort_func(sortable, 1, DrugSortByPrice, NULL, NULL); 2289 for (i = 0; i < 2; ++i) { 2290 GtkTreeViewColumn *col = gtk_tree_view_get_column(GTK_TREE_VIEW(tv), i); 2291 gtk_tree_view_column_set_sort_column_id(col, i); 2292 } 2293 2294 button = ClientData.JetButton = gtk_button_new_with_label(""); 2295 ClientData.JetAccel = 0; 2296 g_signal_connect(G_OBJECT(button), "clicked", 2297 G_CALLBACK(JetButtonPressed), NULL); 2298 gtk_box_pack_start(GTK_BOX(ClientData.Drug.vbbox), button, TRUE, TRUE, 0); 2299 SetJetButtonTitle(accel_group); 2300 2301 #ifdef CYGWIN 2302 /* GtkFrames don't look quite right in Win32 yet */ 2303 gtk_paned_pack2(GTK_PANED(vpaned), hbox, TRUE, TRUE); 2304 #else 2305 frame = gtk_frame_new(NULL); 2306 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); 2307 gtk_container_add(GTK_CONTAINER(frame), hbox); 2308 gtk_paned_pack2(GTK_PANED(vpaned), frame, TRUE, TRUE); 2309 #endif 2310 2311 gtk_box_pack_start(GTK_BOX(vbox), vpaned, TRUE, TRUE, 0); 2312 2313 gtk_box_pack_start(GTK_BOX(vbox2), vbox, TRUE, TRUE, 0); 2314 gtk_container_add(GTK_CONTAINER(window), vbox2); 2315 2316 /* Just show the window, not the vbox - we'll do that when the game 2317 * starts */ 2318 gtk_widget_show(vbox2); 2319 gtk_widget_show(window); 2320 2321 gtk_widget_realize(window); 2322 2323 SetIcon(window, dopewars_pill_xpm); 2324 2325 #ifdef NETWORKING 2326 CurlInit(&MetaConn); 2327 #endif 2328 2329 gtk_main(); 2330 2331 #ifdef NETWORKING 2332 CurlCleanup(&MetaConn); 2333 #endif 2334 2335 /* Free the main player */ 2336 FirstClient = RemovePlayer(ClientData.Play, FirstClient); 2337 2338 return TRUE; 2339 } 2340 2341 static void PackCentredURL(GtkWidget *vbox, gchar *title, gchar *target, 2342 gchar *browser) 2343 { 2344 GtkWidget *hbox, *label, *url; 2345 2346 /* There must surely be a nicer way of making the URL centred - but I 2347 * can't think of one... */ 2348 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 2349 label = gtk_label_new(""); 2350 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); 2351 2352 url = gtk_url_new(title, target, browser); 2353 gtk_box_pack_start(GTK_BOX(hbox), url, FALSE, FALSE, 0); 2354 2355 label = gtk_label_new(""); 2356 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, FALSE, 0); 2357 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); 2358 } 2359 2360 void display_intro(GtkWidget *widget, gpointer data) 2361 { 2362 GtkWidget *dialog, *label, *grid, *OKButton, *vbox, *hsep, *hbbox; 2363 gchar *VersionStr, *docindex; 2364 const int rows = 8, cols = 3; 2365 int i, j; 2366 GtkAccelGroup *accel_group; 2367 gchar *table_data[8][3] = { 2368 /* Credits labels in GTK+ 'about' dialog */ 2369 {N_("English Translation"), N_("Ben Webb"), NULL}, 2370 {N_("Icons and graphics"), "Ocelot Mantis", NULL}, 2371 {N_("Sounds"), "Robin Kohli, 19.5degs.com", NULL}, 2372 {N_("Drug Dealing and Research"), "Dan Wolf", NULL}, 2373 {N_("Play Testing"), "Phil Davis", "Owen Walsh"}, 2374 {N_("Extensive Play Testing"), "Katherine Holt", 2375 "Caroline Moore"}, 2376 {N_("Constructive Criticism"), "Andrea Elliot-Smith", 2377 "Pete Winn"}, 2378 {N_("Unconstructive Criticism"), "James Matthews", NULL} 2379 }; 2380 2381 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2382 accel_group = gtk_accel_group_new(); 2383 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 2384 2385 /* Title of GTK+ 'about' dialog */ 2386 gtk_window_set_title(GTK_WINDOW(dialog), _("About dopewars")); 2387 my_set_dialog_position(GTK_WINDOW(dialog)); 2388 2389 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 2390 gtk_window_set_transient_for(GTK_WINDOW(dialog), 2391 GTK_WINDOW(ClientData.window)); 2392 gtk_container_set_border_width(GTK_CONTAINER(dialog), 10); 2393 2394 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); 2395 2396 /* Main content of GTK+ 'about' dialog */ 2397 label = gtk_label_new(_("Based on John E. Dell's old Drug Wars game, " 2398 "dopewars is a simulation of an\nimaginary drug " 2399 "market. dopewars is an All-American game which " 2400 "features\nbuying, selling, and trying to get " 2401 "past the cops!\n\nThe first thing you need to " 2402 "do is pay off your debt to the Loan Shark. " 2403 "After\nthat, your goal is to make as much " 2404 "money as possible (and stay alive)! You\n" 2405 "have one month of game time to make " 2406 "your fortune.\n")); 2407 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 2408 2409 /* Version and copyright notice in GTK+ 'about' dialog */ 2410 VersionStr = g_strdup_printf(_("Version %s " 2411 "Copyright (C) 1998-2021 " 2412 "Ben Webb benwebb@users.sf.net\n" 2413 "dopewars is released under the " 2414 "GNU General Public License\n"), VERSION); 2415 label = gtk_label_new(VersionStr); 2416 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 2417 g_free(VersionStr); 2418 2419 grid = dp_gtk_grid_new(rows, cols, FALSE); 2420 gtk_grid_set_row_spacing(GTK_GRID(grid), 3); 2421 gtk_grid_set_column_spacing(GTK_GRID(grid), 3); 2422 for (i = 0; i < rows; i++) { 2423 if (i > 0 || strcmp(_(table_data[i][1]), "Ben Webb") != 0) { 2424 for (j = 0; j < cols; j++) { 2425 if (table_data[i][j]) { 2426 if (j == 0 || i == 0) { 2427 label = gtk_label_new(_(table_data[i][j])); 2428 } else { 2429 label = gtk_label_new(table_data[i][j]); 2430 } 2431 dp_gtk_grid_attach(GTK_GRID(grid), label, j, i, 1, 1, TRUE); 2432 } 2433 } 2434 } 2435 } 2436 gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 0); 2437 2438 /* Label at the bottom of GTK+ 'about' dialog */ 2439 label = gtk_label_new(_("\nFor information on the command line " 2440 "options, type dopewars -h at your\n" 2441 "Unix prompt. This will display a help " 2442 "screen, listing the available options.\n")); 2443 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 2444 2445 docindex = GetDocIndex(); 2446 PackCentredURL(vbox, _("Local HTML documentation"), docindex, OurWebBrowser); 2447 g_free(docindex); 2448 2449 PackCentredURL(vbox, "https://dopewars.sourceforge.io/", 2450 "https://dopewars.sourceforge.io/", OurWebBrowser); 2451 2452 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 2453 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 2454 2455 hbbox = my_hbbox_new(); 2456 OKButton = gtk_button_new_with_mnemonic(_("_OK")); 2457 g_signal_connect_swapped(G_OBJECT(OKButton), "clicked", 2458 G_CALLBACK(gtk_widget_destroy), 2459 G_OBJECT(dialog)); 2460 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), OKButton); 2461 2462 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 2463 gtk_container_add(GTK_CONTAINER(dialog), vbox); 2464 2465 gtk_widget_set_can_default(OKButton, TRUE); 2466 gtk_widget_grab_default(OKButton); 2467 2468 gtk_widget_show_all(dialog); 2469 } 2470 2471 static void SendDoneMessage(GtkWidget *widget, gpointer data) 2472 { 2473 SendClientMessage(ClientData.Play, C_NONE, C_DONE, NULL, NULL); 2474 } 2475 2476 static void TransferPayAll(GtkWidget *widget, GtkWidget *dialog) 2477 { 2478 gchar *text; 2479 2480 text = pricetostr(ClientData.Play->Debt); 2481 SendClientMessage(ClientData.Play, C_NONE, C_PAYLOAN, NULL, text); 2482 g_free(text); 2483 gtk_widget_destroy(dialog); 2484 } 2485 2486 static void TransferOK(GtkWidget *widget, GtkWidget *dialog) 2487 { 2488 gpointer Debt; 2489 GtkWidget *deposit, *entry; 2490 gchar *text, *title; 2491 price_t money; 2492 gboolean withdraw = FALSE; 2493 2494 Debt = g_object_get_data(G_OBJECT(dialog), "debt"); 2495 entry = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), "entry")); 2496 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); 2497 money = strtoprice(text); 2498 g_free(text); 2499 2500 if (Debt) { 2501 /* Title of loan shark dialog - (%Tde="The Loan Shark" by default) */ 2502 title = dpg_strdup_printf(_("%/LoanShark window title/%Tde"), 2503 Names.LoanSharkName); 2504 if (money > ClientData.Play->Debt) { 2505 money = ClientData.Play->Debt; 2506 } 2507 } else { 2508 /* Title of bank dialog - (%Tde="The Bank" by default) */ 2509 title = dpg_strdup_printf(_("%/BankName window title/%Tde"), 2510 Names.BankName); 2511 deposit = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), "deposit")); 2512 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(deposit))) { 2513 withdraw = TRUE; 2514 } 2515 } 2516 2517 if (money < 0) { 2518 GtkMessageBox(dialog, _("You must enter a positive amount of money!"), 2519 title, GTK_MESSAGE_WARNING, MB_OK); 2520 } else if (!Debt && withdraw && money > ClientData.Play->Bank) { 2521 GtkMessageBox(dialog, _("There isn't that much money available..."), 2522 title, GTK_MESSAGE_WARNING, MB_OK); 2523 } else if (!withdraw && money > ClientData.Play->Cash) { 2524 GtkMessageBox(dialog, _("You don't have that much money!"), 2525 title, GTK_MESSAGE_WARNING, MB_OK); 2526 } else { 2527 text = pricetostr(withdraw ? -money : money); 2528 SendClientMessage(ClientData.Play, C_NONE, 2529 Debt ? C_PAYLOAN : C_DEPOSIT, NULL, text); 2530 g_free(text); 2531 gtk_widget_destroy(dialog); 2532 } 2533 g_free(title); 2534 } 2535 2536 void TransferDialog(gboolean Debt) 2537 { 2538 GtkWidget *dialog, *button, *label, *radio, *grid, *vbox; 2539 GtkWidget *hbbox, *hsep, *entry; 2540 GtkAccelGroup *accel_group; 2541 GSList *group; 2542 GString *text; 2543 2544 text = g_string_new(""); 2545 2546 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2547 accel_group = gtk_accel_group_new(); 2548 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 2549 2550 g_signal_connect(G_OBJECT(dialog), "destroy", 2551 G_CALLBACK(SendDoneMessage), NULL); 2552 if (Debt) { 2553 /* Title of loan shark dialog - (%Tde="The Loan Shark" by default) */ 2554 dpg_string_printf(text, _("%/LoanShark window title/%Tde"), 2555 Names.LoanSharkName); 2556 } else { 2557 /* Title of bank dialog - (%Tde="The Bank" by default) */ 2558 dpg_string_printf(text, _("%/BankName window title/%Tde"), 2559 Names.BankName); 2560 } 2561 gtk_window_set_title(GTK_WINDOW(dialog), text->str); 2562 my_set_dialog_position(GTK_WINDOW(dialog)); 2563 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 2564 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 2565 gtk_window_set_transient_for(GTK_WINDOW(dialog), 2566 GTK_WINDOW(ClientData.window)); 2567 2568 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 2569 grid = dp_gtk_grid_new(4, 3, FALSE); 2570 gtk_grid_set_row_spacing(GTK_GRID(grid), 4); 2571 gtk_grid_set_column_spacing(GTK_GRID(grid), 4); 2572 2573 /* Display of player's cash in bank or loan shark dialog */ 2574 dpg_string_printf(text, _("Cash: %P"), ClientData.Play->Cash); 2575 label = gtk_label_new(text->str); 2576 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 3, 1, TRUE); 2577 2578 if (Debt) { 2579 /* Display of player's debt in loan shark dialog */ 2580 dpg_string_printf(text, _("Debt: %P"), ClientData.Play->Debt); 2581 } else { 2582 /* Display of player's bank balance in bank dialog */ 2583 dpg_string_printf(text, _("Bank: %P"), ClientData.Play->Bank); 2584 } 2585 label = gtk_label_new(text->str); 2586 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 3, 1, TRUE); 2587 2588 g_object_set_data(G_OBJECT(dialog), "debt", GINT_TO_POINTER(Debt)); 2589 if (Debt) { 2590 /* Prompt for paying back a loan */ 2591 label = gtk_label_new(_("Pay back:")); 2592 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 2, FALSE); 2593 } else { 2594 /* Radio button selected if you want to pay money into the bank */ 2595 radio = gtk_radio_button_new_with_label(NULL, _("Deposit")); 2596 g_object_set_data(G_OBJECT(dialog), "deposit", radio); 2597 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)); 2598 dp_gtk_grid_attach(GTK_GRID(grid), radio, 0, 2, 1, 1, FALSE); 2599 2600 /* Radio button selected if you want to withdraw money from the bank */ 2601 radio = gtk_radio_button_new_with_label(group, _("Withdraw")); 2602 dp_gtk_grid_attach(GTK_GRID(grid), radio, 0, 3, 1, 1, FALSE); 2603 } 2604 label = gtk_label_new(Currency.Symbol); 2605 entry = gtk_entry_new(); 2606 gtk_entry_set_text(GTK_ENTRY(entry), "0"); 2607 g_object_set_data(G_OBJECT(dialog), "entry", entry); 2608 g_signal_connect(G_OBJECT(entry), "activate", 2609 G_CALLBACK(TransferOK), dialog); 2610 2611 if (Currency.Prefix) { 2612 dp_gtk_grid_attach(GTK_GRID(grid), label, 1, 2, 1, 2, FALSE); 2613 dp_gtk_grid_attach(GTK_GRID(grid), entry, 2, 2, 1, 2, TRUE); 2614 } else { 2615 dp_gtk_grid_attach(GTK_GRID(grid), label, 2, 2, 1, 2, FALSE); 2616 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 2, 1, 2, TRUE); 2617 } 2618 2619 gtk_box_pack_start(GTK_BOX(vbox), grid, TRUE, TRUE, 0); 2620 2621 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 2622 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 2623 2624 hbbox = my_hbbox_new(); 2625 button = gtk_button_new_with_mnemonic(_("_OK")); 2626 g_signal_connect(G_OBJECT(button), "clicked", 2627 G_CALLBACK(TransferOK), dialog); 2628 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 2629 2630 if (Debt && ClientData.Play->Cash >= ClientData.Play->Debt) { 2631 /* Button to pay back the entire loan/debt */ 2632 button = gtk_button_new_with_label(_("Pay all")); 2633 g_signal_connect(G_OBJECT(button), "clicked", 2634 G_CALLBACK(TransferPayAll), dialog); 2635 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 2636 } 2637 button = gtk_button_new_with_mnemonic(_("_Cancel")); 2638 g_signal_connect_swapped(G_OBJECT(button), "clicked", 2639 G_CALLBACK(gtk_widget_destroy), 2640 G_OBJECT(dialog)); 2641 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 2642 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 2643 2644 gtk_container_add(GTK_CONTAINER(dialog), vbox); 2645 2646 gtk_widget_show_all(dialog); 2647 2648 g_string_free(text, TRUE); 2649 } 2650 2651 void ListPlayers(GtkWidget *widget, gpointer data) 2652 { 2653 GtkWidget *dialog, *clist, *button, *vbox, *hsep, *hbbox; 2654 GtkAccelGroup *accel_group; 2655 2656 if (IsShowingPlayerList) 2657 return; 2658 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2659 accel_group = gtk_accel_group_new(); 2660 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 2661 2662 /* Title of player list dialog */ 2663 gtk_window_set_title(GTK_WINDOW(dialog), _("Player List")); 2664 my_set_dialog_position(GTK_WINDOW(dialog)); 2665 2666 gtk_window_set_default_size(GTK_WINDOW(dialog), 200, 180); 2667 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 2668 2669 gtk_window_set_modal(GTK_WINDOW(dialog), FALSE); 2670 gtk_window_set_transient_for(GTK_WINDOW(dialog), 2671 GTK_WINDOW(ClientData.window)); 2672 SetShowing(dialog, &IsShowingPlayerList); 2673 2674 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 2675 2676 clist = ClientData.PlayerList = CreatePlayerList(); 2677 UpdatePlayerList(clist, FALSE); 2678 gtk_box_pack_start(GTK_BOX(vbox), clist, TRUE, TRUE, 0); 2679 2680 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 2681 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 2682 2683 hbbox = my_hbbox_new(); 2684 button = gtk_button_new_with_mnemonic(_("_Close")); 2685 g_signal_connect_swapped(G_OBJECT(button), "clicked", 2686 G_CALLBACK(gtk_widget_destroy), 2687 G_OBJECT(dialog)); 2688 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 2689 2690 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 2691 gtk_container_add(GTK_CONTAINER(dialog), vbox); 2692 gtk_widget_show_all(dialog); 2693 } 2694 2695 struct TalkStruct { 2696 GtkWidget *dialog, *clist, *entry, *checkbutton; 2697 }; 2698 2699 /* Columns in player list */ 2700 enum { 2701 PLAYER_COL_NAME = 0, 2702 PLAYER_COL_PT, 2703 PLAYER_NUM_COLS 2704 }; 2705 2706 static void TalkSendSelected(GtkTreeModel *model, GtkTreePath *path, 2707 GtkTreeIter *iter, gpointer data) 2708 { 2709 Player *Play; 2710 gchar *text = data; 2711 gtk_tree_model_get(model, iter, PLAYER_COL_PT, &Play, -1); 2712 if (Play) { 2713 gchar *msg = g_strdup_printf( 2714 "%s->%s: %s", GetPlayerName(ClientData.Play), 2715 GetPlayerName(Play), text); 2716 SendClientMessage(ClientData.Play, C_NONE, C_MSGTO, Play, text); 2717 PrintMessage(msg, "page"); 2718 g_free(msg); 2719 } 2720 } 2721 2722 static void TalkSend(GtkWidget *widget, struct TalkStruct *TalkData) 2723 { 2724 gboolean AllPlayers; 2725 gchar *text; 2726 GString *msg; 2727 2728 AllPlayers = 2729 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON 2730 (TalkData->checkbutton)); 2731 text = gtk_editable_get_chars(GTK_EDITABLE(TalkData->entry), 0, -1); 2732 gtk_editable_delete_text(GTK_EDITABLE(TalkData->entry), 0, -1); 2733 if (!text) 2734 return; 2735 2736 msg = g_string_new(""); 2737 2738 if (AllPlayers) { 2739 SendClientMessage(ClientData.Play, C_NONE, C_MSG, NULL, text); 2740 g_string_printf(msg, "%s: %s", GetPlayerName(ClientData.Play), text); 2741 PrintMessage(msg->str, "talk"); 2742 } else { 2743 GtkTreeSelection *tsel = gtk_tree_view_get_selection( 2744 GTK_TREE_VIEW(TalkData->clist)); 2745 gtk_tree_selection_selected_foreach(tsel, TalkSendSelected, text); 2746 } 2747 g_free(text); 2748 g_string_free(msg, TRUE); 2749 } 2750 2751 void TalkToAll(GtkWidget *widget, gpointer data) 2752 { 2753 TalkDialog(TRUE); 2754 } 2755 2756 void TalkToPlayers(GtkWidget *widget, gpointer data) 2757 { 2758 TalkDialog(FALSE); 2759 } 2760 2761 void TalkDialog(gboolean TalkToAll) 2762 { 2763 GtkWidget *dialog, *clist, *button, *entry, *label, *vbox, *hsep, 2764 *checkbutton, *hbbox; 2765 GtkAccelGroup *accel_group; 2766 static struct TalkStruct TalkData; 2767 2768 if (IsShowingTalkList) 2769 return; 2770 dialog = TalkData.dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2771 accel_group = gtk_accel_group_new(); 2772 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 2773 2774 /* Title of talk dialog */ 2775 gtk_window_set_title(GTK_WINDOW(dialog), _("Talk to player(s)")); 2776 my_set_dialog_position(GTK_WINDOW(dialog)); 2777 2778 gtk_window_set_default_size(GTK_WINDOW(dialog), 200, 190); 2779 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 2780 2781 gtk_window_set_modal(GTK_WINDOW(dialog), FALSE); 2782 gtk_window_set_transient_for(GTK_WINDOW(dialog), 2783 GTK_WINDOW(ClientData.window)); 2784 SetShowing(dialog, &IsShowingTalkList); 2785 2786 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 2787 2788 clist = TalkData.clist = ClientData.TalkList = CreatePlayerList(); 2789 UpdatePlayerList(clist, FALSE); 2790 gtk_tree_selection_set_mode( 2791 gtk_tree_view_get_selection(GTK_TREE_VIEW(clist)), 2792 GTK_SELECTION_MULTIPLE); 2793 gtk_box_pack_start(GTK_BOX(vbox), clist, TRUE, TRUE, 0); 2794 2795 checkbutton = TalkData.checkbutton = 2796 /* Checkbutton set if you want to talk to all players */ 2797 gtk_check_button_new_with_label(_("Talk to all players")); 2798 2799 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TalkToAll); 2800 gtk_box_pack_start(GTK_BOX(vbox), checkbutton, FALSE, FALSE, 0); 2801 2802 /* Prompt for you to enter the message to be sent to other players */ 2803 label = gtk_label_new(_("Message:-")); 2804 2805 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 2806 2807 entry = TalkData.entry = gtk_entry_new(); 2808 g_signal_connect(G_OBJECT(entry), "activate", 2809 G_CALLBACK(TalkSend), (gpointer)&TalkData); 2810 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0); 2811 2812 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 2813 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 2814 2815 hbbox = my_hbbox_new(); 2816 2817 /* Button to send a message to other players */ 2818 button = gtk_button_new_with_label(_("Send")); 2819 2820 g_signal_connect(G_OBJECT(button), "clicked", 2821 G_CALLBACK(TalkSend), (gpointer)&TalkData); 2822 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 2823 2824 button = gtk_button_new_with_mnemonic(_("_Close")); 2825 g_signal_connect_swapped(G_OBJECT(button), "clicked", 2826 G_CALLBACK(gtk_widget_destroy), 2827 G_OBJECT(dialog)); 2828 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 2829 2830 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 2831 2832 gtk_container_add(GTK_CONTAINER(dialog), vbox); 2833 gtk_widget_show_all(dialog); 2834 } 2835 2836 GtkWidget *CreatePlayerList(void) 2837 { 2838 GtkWidget *view; 2839 GtkListStore *store; 2840 GtkCellRenderer *renderer; 2841 2842 store = gtk_list_store_new(PLAYER_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER); 2843 2844 view = gtk_tree_view_new(); 2845 renderer = gtk_cell_renderer_text_new(); 2846 gtk_tree_view_insert_column_with_attributes( 2847 GTK_TREE_VIEW(view), -1, "Name", renderer, "text", 2848 PLAYER_COL_NAME, NULL); 2849 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); 2850 g_object_unref(store); /* so it is freed when the view is */ 2851 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(view), FALSE); 2852 return view; 2853 } 2854 2855 void UpdatePlayerList(GtkWidget *clist, gboolean IncludeSelf) 2856 { 2857 GtkListStore *store; 2858 GSList *list; 2859 GtkTreeIter iter; 2860 Player *Play; 2861 2862 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(clist))); 2863 2864 /* Don't update the widget until we're done */ 2865 g_object_ref(store); 2866 gtk_tree_view_set_model(GTK_TREE_VIEW(clist), NULL); 2867 2868 gtk_list_store_clear(store); 2869 for (list = FirstClient; list; list = g_slist_next(list)) { 2870 Play = (Player *)list->data; 2871 if (IncludeSelf || Play != ClientData.Play) { 2872 gtk_list_store_append(store, &iter); 2873 gtk_list_store_set(store, &iter, PLAYER_COL_NAME, GetPlayerName(Play), 2874 PLAYER_COL_PT, Play, -1); 2875 } 2876 } 2877 2878 gtk_tree_view_set_model(GTK_TREE_VIEW(clist), GTK_TREE_MODEL(store)); 2879 g_object_unref(store); 2880 } 2881 2882 static void ErrandOK(GtkWidget *widget, GtkWidget *clist) 2883 { 2884 GtkTreeSelection *treesel; 2885 GtkTreeModel *model; 2886 GtkTreeIter iter; 2887 GtkWidget *dialog; 2888 gint ErrandType; 2889 2890 2891 dialog = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), "dialog")); 2892 ErrandType = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), 2893 "errandtype")); 2894 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(clist)); 2895 if (gtk_tree_selection_get_selected(treesel, &model, &iter)) { 2896 Player *Play; 2897 gtk_tree_model_get(model, &iter, PLAYER_COL_PT, &Play, -1); 2898 if (ErrandType == ET_SPY) { 2899 SendClientMessage(ClientData.Play, C_NONE, C_SPYON, Play, NULL); 2900 } else { 2901 SendClientMessage(ClientData.Play, C_NONE, C_TIPOFF, Play, NULL); 2902 } 2903 gtk_widget_destroy(dialog); 2904 } 2905 } 2906 2907 void SpyOnPlayer(GtkWidget *widget, gpointer data) 2908 { 2909 ErrandDialog(ET_SPY); 2910 } 2911 2912 void TipOff(GtkWidget *widget, gpointer data) 2913 { 2914 ErrandDialog(ET_TIPOFF); 2915 } 2916 2917 void ErrandDialog(gint ErrandType) 2918 { 2919 GtkWidget *dialog, *clist, *button, *vbox, *hbbox, *hsep, *label; 2920 GtkAccelGroup *accel_group; 2921 gchar *text; 2922 2923 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2924 accel_group = gtk_accel_group_new(); 2925 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group); 2926 2927 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7); 2928 2929 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); 2930 gtk_window_set_transient_for(GTK_WINDOW(dialog), 2931 GTK_WINDOW(ClientData.window)); 2932 2933 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 2934 2935 if (ErrandType == ET_SPY) { 2936 /* Title of dialog to select a player to spy on */ 2937 gtk_window_set_title(GTK_WINDOW(dialog), _("Spy On Player")); 2938 2939 /* Informative text for "spy on player" dialog. (%tde = "bitch", 2940 "bitch", "guns", "drugs", respectively, by default) */ 2941 text = dpg_strdup_printf(_("Please choose the player to spy on. " 2942 "Your %tde will\nthen offer his " 2943 "services to the player, and if " 2944 "successful,\nyou will be able to " 2945 "view the player's stats with the\n" 2946 "\"Get spy reports\" menu. Remember " 2947 "that the %tde will leave\nyou, so " 2948 "any %tde or %tde that he's " 2949 "carrying may be lost!"), Names.Bitch, 2950 Names.Bitch, Names.Guns, Names.Drugs); 2951 label = gtk_label_new(text); 2952 g_free(text); 2953 } else { 2954 2955 /* Title of dialog to select a player to tip the cops off to */ 2956 gtk_window_set_title(GTK_WINDOW(dialog), _("Tip Off The Cops")); 2957 2958 /* Informative text for "tip off cops" dialog. (%tde = "bitch", 2959 "bitch", "guns", "drugs", respectively, by default) */ 2960 text = dpg_strdup_printf(_("Please choose the player to tip off " 2961 "the cops to. Your %tde will\nhelp " 2962 "the cops to attack that player, " 2963 "and then report back to you\non " 2964 "the encounter. Remember that the " 2965 "%tde will leave you temporarily,\n" 2966 "so any %tde or %tde that he's " 2967 "carrying may be lost!"), Names.Bitch, 2968 Names.Bitch, Names.Guns, Names.Drugs); 2969 label = gtk_label_new(text); 2970 g_free(text); 2971 } 2972 my_set_dialog_position(GTK_WINDOW(dialog)); 2973 2974 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 2975 2976 clist = ClientData.PlayerList = CreatePlayerList(); 2977 UpdatePlayerList(clist, FALSE); 2978 gtk_box_pack_start(GTK_BOX(vbox), clist, TRUE, TRUE, 0); 2979 2980 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 2981 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 2982 2983 hbbox = my_hbbox_new(); 2984 button = gtk_button_new_with_mnemonic(_("_OK")); 2985 g_object_set_data(G_OBJECT(button), "dialog", dialog); 2986 g_object_set_data(G_OBJECT(button), "errandtype", 2987 GINT_TO_POINTER(ErrandType)); 2988 g_signal_connect(G_OBJECT(button), "clicked", 2989 G_CALLBACK(ErrandOK), (gpointer)clist); 2990 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 2991 button = gtk_button_new_with_mnemonic(_("_Cancel")); 2992 g_signal_connect_swapped(G_OBJECT(button), "clicked", 2993 G_CALLBACK(gtk_widget_destroy), 2994 G_OBJECT(dialog)); 2995 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 2996 2997 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 2998 gtk_container_add(GTK_CONTAINER(dialog), vbox); 2999 gtk_widget_show_all(dialog); 3000 } 3001 3002 void SackBitch(GtkWidget *widget, gpointer data) 3003 { 3004 char *title, *text; 3005 3006 /* Cannot sack bitches if you don't have any! */ 3007 if (ClientData.Play->Bitches.Carried <= 0) 3008 return; 3009 3010 /* Title of dialog to sack a bitch (%Tde = "Bitch" by default) */ 3011 title = dpg_strdup_printf(_("%/Sack Bitch dialog title/Sack %Tde"), 3012 Names.Bitch); 3013 3014 /* Confirmation message for sacking a bitch. (%tde = "guns", "drugs", 3015 "bitch", respectively, by default) */ 3016 text = dpg_strdup_printf(_("Are you sure? (Any %tde or %tde carried\n" 3017 "by this %tde may be lost!)"), Names.Guns, 3018 Names.Drugs, Names.Bitch); 3019 3020 if (GtkMessageBox(ClientData.window, text, title, GTK_MESSAGE_QUESTION, 3021 MB_YESNO) == IDYES) { 3022 ClientData.Play->Bitches.Carried--; 3023 UpdateMenus(); 3024 SendClientMessage(ClientData.Play, C_NONE, C_SACKBITCH, NULL, NULL); 3025 } 3026 g_free(text); 3027 g_free(title); 3028 } 3029 3030 void CreateInventory(GtkWidget *hbox, gchar *Objects, 3031 GtkAccelGroup *accel_group, gboolean CreateButtons, 3032 gboolean CreateHere, struct InventoryWidgets *widgets, 3033 GCallback CallBack) 3034 { 3035 GtkWidget *scrollwin, *tv, *vbbox, *frame[2], *button[3]; 3036 GtkCellRenderer *renderer; 3037 GtkListStore *store; 3038 GtkTreeSelection *treesel; 3039 gint i, mini, icol; 3040 GString *text; 3041 gchar *titles[2][2]; 3042 gchar *button_text[3]; 3043 gpointer button_type[3] = { BT_BUY, BT_SELL, BT_DROP }; 3044 3045 /* Column titles for display of drugs/guns carried or available for 3046 purchase */ 3047 titles[0][0] = titles[1][0] = _("Name"); 3048 titles[0][1] = _("Price"); 3049 titles[1][1] = _("Number"); 3050 3051 /* Button titles for buying/selling/dropping guns or drugs */ 3052 button_text[0] = _("_Buy ->"); 3053 button_text[1] = _("<- _Sell"); 3054 button_text[2] = _("_Drop <-"); 3055 3056 text = g_string_new(""); 3057 3058 if (CreateHere) { 3059 /* Title of the display of available drugs/guns (%Tde = "Guns" or 3060 "Drugs" by default) */ 3061 dpg_string_printf(text, _("%Tde here"), Objects); 3062 widgets->HereFrame = frame[0] = gtk_frame_new(text->str); 3063 } 3064 3065 /* Title of the display of carried drugs/guns (%Tde = "Guns" or "Drugs" 3066 by default) */ 3067 dpg_string_printf(text, _("%Tde carried"), Objects); 3068 3069 widgets->CarriedFrame = frame[1] = gtk_frame_new(text->str); 3070 3071 widgets->HereList = widgets->CarriedList = NULL; 3072 mini = (CreateHere ? 0 : 1); 3073 for (i = mini; i < 2; i++) { 3074 GtkWidget *hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 3075 gtk_box_set_homogeneous(GTK_BOX(hbox2), TRUE); 3076 gtk_container_set_border_width(GTK_CONTAINER(frame[i]), 3); 3077 3078 tv = gtk_scrolled_tree_view_new(&scrollwin); 3079 renderer = gtk_cell_renderer_text_new(); 3080 store = gtk_list_store_new(INVEN_NUM_COLS, G_TYPE_STRING, 3081 G_TYPE_STRING, G_TYPE_INT); 3082 gtk_tree_view_set_model(GTK_TREE_VIEW(tv), GTK_TREE_MODEL(store)); 3083 g_object_unref(store); 3084 for (icol = 0; icol < 2; ++icol) { 3085 GtkTreeViewColumn *col; 3086 if (i == 0 && icol == 1) { 3087 /* Right align prices */ 3088 GtkCellRenderer *rren = gtk_cell_renderer_text_new(); 3089 g_object_set(G_OBJECT(rren), "xalign", 1.0, NULL); 3090 col = gtk_tree_view_column_new_with_attributes( 3091 titles[i][icol], rren, "text", icol, NULL); 3092 gtk_tree_view_column_set_alignment(col, 1.0); 3093 } else { 3094 col = gtk_tree_view_column_new_with_attributes( 3095 titles[i][icol], renderer, "text", icol, NULL); 3096 } 3097 gtk_tree_view_insert_column(GTK_TREE_VIEW(tv), col, -1); 3098 } 3099 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(tv), FALSE); 3100 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); 3101 gtk_tree_selection_set_mode(treesel, GTK_SELECTION_SINGLE); 3102 gtk_box_pack_start(GTK_BOX(hbox2), scrollwin, TRUE, TRUE, 0); 3103 gtk_container_set_border_width(GTK_CONTAINER(hbox2), 3); 3104 gtk_container_add(GTK_CONTAINER(frame[i]), hbox2); 3105 if (i == 0) { 3106 widgets->HereList = tv; 3107 } else { 3108 widgets->CarriedList = tv; 3109 } 3110 } 3111 if (CreateHere) { 3112 gtk_box_pack_start(GTK_BOX(hbox), frame[0], TRUE, TRUE, 0); 3113 } 3114 3115 if (CreateButtons) { 3116 widgets->vbbox = vbbox = gtk_button_box_new(GTK_ORIENTATION_VERTICAL); 3117 gtk_button_box_set_layout(GTK_BUTTON_BOX(vbbox), GTK_BUTTONBOX_SPREAD); 3118 3119 3120 for (i = 0; i < 3; i++) { 3121 button[i] = gtk_button_new_with_label(""); 3122 SetAccelerator(button[i], _(button_text[i]), button[i], 3123 "clicked", accel_group, FALSE); 3124 if (CallBack) { 3125 g_signal_connect(G_OBJECT(button[i]), "clicked", 3126 G_CALLBACK(CallBack), button_type[i]); 3127 } 3128 gtk_box_pack_start(GTK_BOX(vbbox), button[i], TRUE, TRUE, 0); 3129 } 3130 widgets->BuyButton = button[0]; 3131 widgets->SellButton = button[1]; 3132 widgets->DropButton = button[2]; 3133 gtk_box_pack_start(GTK_BOX(hbox), vbbox, FALSE, FALSE, 0); 3134 } else { 3135 widgets->vbbox = NULL; 3136 } 3137 3138 gtk_box_pack_start(GTK_BOX(hbox), frame[1], TRUE, TRUE, 0); 3139 g_string_free(text, TRUE); 3140 } 3141 3142 void SetShowing(GtkWidget *window, gboolean *showing) 3143 { 3144 g_assert(showing); 3145 3146 *showing = TRUE; 3147 g_signal_connect(G_OBJECT(window), "destroy", 3148 G_CALLBACK(DestroyShowing), (gpointer)showing); 3149 } 3150 3151 void DestroyShowing(GtkWidget *widget, gpointer data) 3152 { 3153 gboolean *IsShowing = (gboolean *)data; 3154 3155 if (IsShowing) { 3156 *IsShowing = FALSE; 3157 } 3158 } 3159 3160 static void NewNameOK(GtkWidget *widget, GtkWidget *window) 3161 { 3162 GtkWidget *entry; 3163 gchar *text; 3164 3165 entry = GTK_WIDGET(g_object_get_data(G_OBJECT(window), "entry")); 3166 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); 3167 if (text[0]) { 3168 StripTerminators(text); 3169 SetPlayerName(ClientData.Play, text); 3170 SendNullClientMessage(ClientData.Play, C_NONE, C_NAME, NULL, text); 3171 gtk_widget_destroy(window); 3172 } 3173 g_free(text); 3174 } 3175 3176 void NewNameDialog(void) 3177 { 3178 GtkWidget *window, *button, *hsep, *vbox, *label, *entry; 3179 GtkAccelGroup *accel_group; 3180 3181 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 3182 accel_group = gtk_accel_group_new(); 3183 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); 3184 3185 /* Title of dialog for changing a player's name */ 3186 gtk_window_set_title(GTK_WINDOW(window), _("Change Name")); 3187 my_set_dialog_position(GTK_WINDOW(window)); 3188 3189 gtk_window_set_modal(GTK_WINDOW(window), TRUE); 3190 gtk_window_set_transient_for(GTK_WINDOW(window), 3191 GTK_WINDOW(ClientData.window)); 3192 gtk_container_set_border_width(GTK_CONTAINER(window), 7); 3193 g_signal_connect(G_OBJECT(window), "delete_event", 3194 G_CALLBACK(DisallowDelete), NULL); 3195 3196 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 3197 3198 /* Informational text to prompt the player to change his/her name */ 3199 label = gtk_label_new(_("Unfortunately, somebody else is already " 3200 "using \"your\" name. Please change it:-")); 3201 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 3202 3203 entry = gtk_entry_new(); 3204 g_object_set_data(G_OBJECT(window), "entry", entry); 3205 g_signal_connect(G_OBJECT(entry), "activate", 3206 G_CALLBACK(NewNameOK), window); 3207 gtk_entry_set_text(GTK_ENTRY(entry), GetPlayerName(ClientData.Play)); 3208 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0); 3209 3210 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 3211 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 3212 3213 button = gtk_button_new_with_mnemonic(_("_OK")); 3214 g_signal_connect(G_OBJECT(button), "clicked", 3215 G_CALLBACK(NewNameOK), window); 3216 gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0); 3217 gtk_widget_set_can_default(button, TRUE); 3218 gtk_widget_grab_default(button); 3219 3220 gtk_container_add(GTK_CONTAINER(window), vbox); 3221 gtk_widget_show_all(window); 3222 } 3223 3224 gint DisallowDelete(GtkWidget *widget, GdkEvent *event, gpointer data) 3225 { 3226 return TRUE; 3227 } 3228 3229 void GunShopDialog(void) 3230 { 3231 GtkWidget *window, *button, *hsep, *vbox, *hbox, *hbbox; 3232 GtkAccelGroup *accel_group; 3233 gchar *text; 3234 3235 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 3236 gtk_window_set_default_size(GTK_WINDOW(window), 600, 190); 3237 g_signal_connect(G_OBJECT(window), "destroy", 3238 G_CALLBACK(SendDoneMessage), NULL); 3239 accel_group = gtk_accel_group_new(); 3240 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); 3241 3242 /* Title of 'gun shop' dialog in GTK+ client (%Tde="Dan's House of Guns" 3243 by default) */ 3244 text = dpg_strdup_printf(_("%/GTK GunShop window title/%Tde"), 3245 Names.GunShopName); 3246 gtk_window_set_title(GTK_WINDOW(window), text); 3247 my_set_dialog_position(GTK_WINDOW(window)); 3248 g_free(text); 3249 gtk_window_set_modal(GTK_WINDOW(window), TRUE); 3250 gtk_window_set_transient_for(GTK_WINDOW(window), 3251 GTK_WINDOW(ClientData.window)); 3252 gtk_container_set_border_width(GTK_CONTAINER(window), 7); 3253 SetShowing(window, &IsShowingGunShop); 3254 3255 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7); 3256 3257 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 7); 3258 CreateInventory(hbox, Names.Guns, accel_group, TRUE, TRUE, 3259 &ClientData.Gun, G_CALLBACK(DealGuns)); 3260 3261 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); 3262 3263 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 3264 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0); 3265 3266 hbbox = my_hbbox_new(); 3267 button = gtk_button_new_with_mnemonic(_("_Close")); 3268 g_signal_connect_swapped(G_OBJECT(button), "clicked", 3269 G_CALLBACK(gtk_widget_destroy), 3270 G_OBJECT(window)); 3271 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button); 3272 3273 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0); 3274 gtk_container_add(GTK_CONTAINER(window), vbox); 3275 3276 UpdateInventory(&ClientData.Gun, ClientData.Play->Guns, NumGun, FALSE); 3277 gtk_widget_show_all(window); 3278 } 3279 3280 void UpdatePlayerLists(void) 3281 { 3282 if (IsShowingPlayerList) { 3283 UpdatePlayerList(ClientData.PlayerList, FALSE); 3284 } 3285 if (IsShowingTalkList) { 3286 UpdatePlayerList(ClientData.TalkList, FALSE); 3287 } 3288 } 3289 3290 void GetSpyReports(GtkWidget *Widget, gpointer data) 3291 { 3292 SendClientMessage(ClientData.Play, C_NONE, C_CONTACTSPY, NULL, NULL); 3293 } 3294 3295 static void DestroySpyReports(GtkWidget *widget, gpointer data) 3296 { 3297 SpyReportsDialog = NULL; 3298 } 3299 3300 static void CreateSpyReports(void) 3301 { 3302 GtkWidget *window, *button, *vbox, *notebook; 3303 GtkAccelGroup *accel_group; 3304 3305 SpyReportsDialog = window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 3306 accel_group = gtk_accel_group_new(); 3307 g_object_set_data(G_OBJECT(window), "accel_group", accel_group); 3308 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); 3309 3310 /* Title of window to display reports from spies with other players */ 3311 gtk_window_set_title(GTK_WINDOW(window), _("Spy reports")); 3312 my_set_dialog_position(GTK_WINDOW(window)); 3313 3314 gtk_window_set_modal(GTK_WINDOW(window), TRUE); 3315 gtk_window_set_transient_for(GTK_WINDOW(window), 3316 GTK_WINDOW(ClientData.window)); 3317 gtk_container_set_border_width(GTK_CONTAINER(window), 7); 3318 g_signal_connect(G_OBJECT(window), "destroy", 3319 G_CALLBACK(DestroySpyReports), NULL); 3320 3321 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); 3322 notebook = gtk_notebook_new(); 3323 g_object_set_data(G_OBJECT(window), "notebook", notebook); 3324 3325 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); 3326 3327 button = gtk_button_new_with_mnemonic(_("_Close")); 3328 g_signal_connect_swapped(G_OBJECT(button), "clicked", 3329 G_CALLBACK(gtk_widget_destroy), 3330 G_OBJECT(window)); 3331 gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0); 3332 3333 gtk_container_add(GTK_CONTAINER(window), vbox); 3334 3335 gtk_widget_show_all(window); 3336 } 3337 3338 void DisplaySpyReports(Player *Play) 3339 { 3340 GtkWidget *dialog, *notebook, *vbox, *hbox, *frame, *label, *grid; 3341 GtkAccelGroup *accel_group; 3342 struct StatusWidgets Status; 3343 struct InventoryWidgets SpyDrugs, SpyGuns; 3344 3345 if (!SpyReportsDialog) 3346 CreateSpyReports(); 3347 dialog = SpyReportsDialog; 3348 notebook = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), "notebook")); 3349 accel_group = 3350 (GtkAccelGroup *)(g_object_get_data(G_OBJECT(dialog), "accel_group")); 3351 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); 3352 frame = gtk_frame_new("Stats"); 3353 gtk_container_set_border_width(GTK_CONTAINER(frame), 3); 3354 grid = CreateStatusWidgets(&Status); 3355 gtk_container_add(GTK_CONTAINER(frame), grid); 3356 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); 3357 3358 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); 3359 CreateInventory(hbox, Names.Drugs, accel_group, FALSE, FALSE, &SpyDrugs, 3360 NULL); 3361 CreateInventory(hbox, Names.Guns, accel_group, FALSE, FALSE, &SpyGuns, 3362 NULL); 3363 3364 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); 3365 label = gtk_label_new(GetPlayerName(Play)); 3366 3367 DisplayStats(Play, &Status); 3368 UpdateInventory(&SpyDrugs, Play->Drugs, NumDrug, TRUE); 3369 UpdateInventory(&SpyGuns, Play->Guns, NumGun, FALSE); 3370 3371 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label); 3372 3373 gtk_widget_show_all(notebook); 3374 }