home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / TTTOCF.PAK / TTT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  29.9 KB  |  1,242 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows - (C) Copyright 1992,1994 by Borland International
  3. //
  4. //   TicTacToe Demo Program
  5. //
  6. //   Plays a game of TicTacToe with the user.
  7. //
  8. //   TGameApp - Main TicTacToe application, derived from TApplication
  9. //   TGame -   Game document
  10. //   TGameOptionsBox - A TDialog box for setting TicTacToe options
  11. //   TGameView -   View window for the game, derived from TToolBox
  12. //   Square - Game squares (gadgets), derived from TButtonGadget
  13. //   TAboutBox - A TDialog box for info about TicTacToe
  14. //
  15. //----------------------------------------------------------------------------
  16. #include <owl/pch.h>
  17. #include <owl/applicat.h>
  18. #include <owl/framewin.h>
  19. #include <owl/decframe.h>
  20. #include <owl/dialog.h>
  21. #include <owl/dc.h>
  22. #include <owl/button.h>
  23. #include <owl/static.h>
  24. #include <owl/radiobut.h>
  25. #include <owl/groupbox.h>
  26. #include <owl/buttonga.h>
  27. #include <owl/toolbox.h>
  28. #include <owl/celarray.h>
  29. #include <owl/buttonga.h>
  30. #include <owl/ocfevent.h>
  31. #include <ocf/ocreg.h>
  32. #include <ocf/ocapp.h>
  33. #include <ocf/ocdoc.h>
  34. #include <ocf/ocremvie.h>
  35. #include <ocf/ocstorag.h>
  36. #include <owl/controlb.h>
  37. #include <owl/olefacto.h>
  38.  
  39. #include "ttt.h"
  40.  
  41. #define WM_GAMEVIEWSYNC WM_USER + 100
  42.  
  43. const char  GameStreamName[] = "TicTacToe";
  44. const int DllIdleTime = 200;    // time in MS for idle action polling
  45. const int TTT_DLLIDLE = 32000;  // Idle timer ID for DLL servers
  46.  
  47. // App dictionary & Ole registrar objects
  48. //
  49. DEFINE_APP_DICTIONARY(AppDictionary);
  50. static TPointer<TOcRegistrar> Registrar;
  51.  
  52. REGISTRATION_FORMAT_BUFFER(100)
  53.  
  54. BEGIN_REGISTRATION(AppReg)
  55. #if defined(BI_APP_DLL)
  56.  REGDATA(clsid,      "{5E4BD420-8ABC-101B-A23B-CE4E85D07ED2}")
  57. // REGDATA(progid,     "TicTacToe.DllServer")
  58. #else
  59.  REGDATA(clsid,      "{5E4BD425-8ABC-101B-A23B-CE4E85D07ED2}")
  60. // REGDATA(progid,     "TicTacToe.Application")
  61. #endif
  62.  REGDATA(description, "TicTacToe Application")
  63.  REGDATA(appname,     "TicTacToe")
  64. END_REGISTRATION
  65.  
  66. BEGIN_REGISTRATION(DocReg)
  67. #if defined(BI_APP_DLL)
  68.  REGDATA(progid,     "TicTacToeDll.Game.1")
  69.  REGDATA(description,"TicTacToeDll Game")
  70. // REGDATA(serverctx, "Inproc")
  71. #else
  72.  REGDATA(progid,     "TicTacToeExe.Game.1")
  73.  REGDATA(description,"TicTacToeExe Game")
  74. // REGDATA(debugger,   "tdw")
  75. #endif
  76.  REGDATA(menuname,   "Game")
  77.  REGDATA(insertable, "")
  78.  REGDATA(extension,  "ttt")
  79.  REGDATA(docfilter,  "*.ttt")
  80.  REGDATA(verb0,      "&Play")
  81. // REGFORMAT(0, ocrEmbedSource,  ocrContent, ocrIStorage, ocrGet)
  82.  REGFORMAT(0, ocrEmbedSource,  ocrContent, ocrIStorage, ocrGet)
  83.  REGFORMAT(1, ocrMetafilePict, ocrContent, ocrMfPict,   ocrGet)
  84. END_REGISTRATION
  85.  
  86. TRegLink* RegLinkHead;
  87. TRegLink  tttGameLink(::DocReg, ::RegLinkHead);
  88.  
  89. //----------------------------------------------------------------------------
  90.  
  91. class TGameApp : public TApplication, public TOcModule {
  92.   public:
  93.     TGameApp();
  94.    ~TGameApp();
  95.     TUnknown* CreateOleObject(uint32 options, TRegLink* link);
  96.  
  97.   private:
  98.     void   CmAbout();
  99.     void   InitMainWindow();
  100.  
  101.   DECLARE_RESPONSE_TABLE(TGameApp);
  102. };
  103. DEFINE_RESPONSE_TABLE1(TGameApp, TApplication)
  104.   EV_COMMAND(CM_ABOUT, CmAbout),
  105. END_RESPONSE_TABLE;
  106.  
  107. //----------------------------------------------------------------------------
  108.  
  109. static const int freeMasks[] = {
  110.   0x006, 0x005, 0x003, 0x030, 0x028, 0x018, 0x180, 0x140, 0x0C0, // row
  111.   0x048, 0x041, 0x009, 0x090, 0x082, 0x012, 0x120, 0x104, 0x024, // col
  112.   0x110, 0x101, 0x011, 0x050, 0x044, 0x014                       // diagonal
  113. };
  114.  
  115. static const int winningMasks[] = {
  116.   0x1C0, 0x038, 0x007, 0x124, 0x092, 0x049, 0x111, 0x054
  117. };
  118.  
  119. //
  120. //
  121. //
  122. static void
  123. freeSquare(int mask, int i, int& square)
  124. {
  125.   int mode = i/9; // row, col, or diag
  126.   if (mode == 0)
  127.     mask ^= 0x007 << (i/3)*3;
  128.   else if (mode == 1)
  129.     mask ^= 0x049 << (i%9)/3;
  130.   else if (((i%9)/3) == 0)
  131.     mask ^= 0x111;
  132.   else
  133.     mask ^= 0x054;
  134.   int j;
  135.   int test;
  136.   for (j = 0, test = 1; test; test <<= 1, j++)
  137.     if (test & mask)
  138.       break;
  139.   square = j;
  140. }
  141.  
  142. //----------------------------------------------------------------------------
  143.  
  144. enum TSquareState {
  145.   SqsEmpty,
  146.   SqsX,
  147.   SqsO,
  148.   SqsCount
  149. };
  150.  
  151. class TGame {
  152.   public:
  153.     TGame(TGame* p = 0, int dim=3);
  154.  
  155.     void New();
  156.     void SetView(TWindow* view) {View = view;}
  157.  
  158.     int  GetDimension() const {return Dim;}  // Dim x Dim game
  159.     bool IsOver();// const;
  160.     bool IsWon();// const;
  161.     bool IsPlaying() const {return Playing;}
  162.     TSquareState GetState(int sq) const;
  163.  
  164.     void UserTurn(int sq);
  165.     void ComputerTurn();
  166.     void SetApplication(TGameApp* app) {App = app;}
  167.     TGameApp* GetApplication() {return App;}
  168.  
  169.   private:
  170.     int          Dim;
  171.     int          UserBoardMap;
  172.     int          ComputerBoardMap;
  173.     TSquareState UserSide;
  174.     TSquareState ComputerSide;
  175.     bool         ComputerGoesFirst;
  176.     bool         Playing;
  177.     TWindow*     View;
  178.     TGameApp*    App;
  179.  
  180.     friend class TGameOptionsBox; // sleazy
  181.  
  182.   private:
  183.     void SetState(int sq, TSquareState state);
  184.     void MakeAMark(int sq, TSquareState mark);
  185.     bool IsAWinner(int sq) const;
  186.  
  187.   friend class TGameView;
  188. };
  189.  
  190. TGame::TGame(TGame* /*not used*/, int dim)
  191. {
  192.   UserSide = SqsX;
  193.   ComputerSide = SqsO;
  194.   ComputerGoesFirst = false;
  195.   Playing = true;
  196.   UserBoardMap = ComputerBoardMap = 0;
  197.   Dim = dim;
  198. }
  199.  
  200. void
  201. TGame::New()
  202. {
  203.   Playing = true;
  204.   UserBoardMap = 0;
  205.   ComputerBoardMap = 0;
  206.   ComputerSide = UserSide==SqsX ? SqsO : SqsX;
  207.  
  208.   if (ComputerGoesFirst)
  209.     ComputerTurn();
  210.  
  211.   if (View)
  212.    View->PostMessage(WM_GAMEVIEWSYNC, 0xFFFF);
  213. }
  214.  
  215. //
  216. // Assign a square to user or computer and repaint the game board
  217. //
  218. void
  219. TGame::MakeAMark(int sq, TSquareState mark)
  220. {
  221.   SetState(sq, mark);
  222.   if (View)
  223.     View->PostMessage(WM_GAMEVIEWSYNC, sq);
  224. }
  225.  
  226. //
  227. // Check if the game is over, ie, no more blank squares
  228. //
  229. bool
  230. TGame::IsOver()
  231. {
  232.   if ((UserBoardMap|ComputerBoardMap) == 0x1FF)
  233.     Playing = false;
  234.   return !Playing;
  235. }
  236.  
  237. //
  238. // Check if the game is won by either user or computer
  239. //
  240. bool
  241. TGame::IsWon()
  242. {
  243.   for (int i = 0; i < 8; i++)
  244.     if ((UserBoardMap & winningMasks[i]) == winningMasks[i] ||
  245.         (ComputerBoardMap & winningMasks[i]) == winningMasks[i]) {
  246.       Playing = false;
  247.       return true;
  248.     }
  249.   return false;
  250. }
  251.  
  252. //
  253. // Check if a square belongs to user or computer
  254. //
  255. TSquareState
  256. TGame::GetState(int sq) const
  257. {
  258.   int mask = 1 << sq;
  259.  
  260.   if (ComputerBoardMap & mask)
  261.     return ComputerSide;
  262.   if (UserBoardMap & mask)
  263.     return UserSide;
  264.   return SqsEmpty;
  265. }
  266.  
  267. //
  268. // Assign a square to a user or computer or neither
  269. //
  270. void
  271. TGame::SetState(int sq, TSquareState state)
  272. {
  273.   int mask = 1 << sq;
  274.  
  275.   if (state == SqsEmpty) {
  276.     ComputerBoardMap &= ~mask;
  277.     UserBoardMap &= ~mask;
  278.  
  279.   } else if (state == UserSide) {
  280.     ComputerBoardMap &= ~mask;
  281.     UserBoardMap |= mask;
  282.  
  283.   } else {
  284.     ComputerBoardMap |= mask;
  285.     UserBoardMap &= ~mask;
  286.   }
  287. }
  288.  
  289. //
  290. // The computer tries to determine if there is a winning move
  291. //
  292. bool
  293. TGame::IsAWinner(int sq) const
  294. {
  295.   int map = ComputerBoardMap | (1 << sq);
  296.   for (int i = 0; i < 8; i++)
  297.     if ((map & winningMasks[i]) == winningMasks[i])
  298.       return true;
  299.   return false;
  300. }
  301.  
  302. //
  303. //
  304. //
  305. void
  306. TGame::UserTurn(int sq)
  307. {
  308.   if (GetState(sq) == SqsEmpty)
  309.     MakeAMark(sq, UserSide);
  310. }
  311.  
  312. //
  313. //
  314. //
  315. void
  316. TGame::ComputerTurn()
  317. {
  318.   bool madeMove = false;
  319.  
  320.   // Look for a winning move first.
  321.   //
  322.   {
  323.     for (int i = 0; i < Dim*Dim; i++) {
  324.       if (GetState(i) == SqsEmpty && IsAWinner(i)) {
  325.         MakeAMark(i, ComputerSide);
  326.         madeMove = true;
  327.         break;
  328.       }
  329.     }
  330.   }
  331.  
  332.   // Look for a blocking move. This is the key to not losing the game!
  333.   //
  334.   if (!madeMove) {
  335.     for (int i = 0; i < 24; i++)
  336.       if ((UserBoardMap & freeMasks[i]) == freeMasks[i]) {
  337.         int sq;
  338.       freeSquare(freeMasks[i], i, sq);
  339.         if (GetState(sq) == ComputerSide)
  340.           continue;
  341.         MakeAMark(sq, ComputerSide);
  342.         madeMove = true;
  343.         break;
  344.       }
  345.   }
  346.  
  347.   // Nothing to block, go to the middle if empty, else first open corner, else
  348.   // first open edge
  349.   // Game can be beaten if user knows how computer plays: try 5,7,8, & 2 to win
  350.   //
  351.   if (!madeMove) {
  352.     int mask = UserBoardMap|ComputerBoardMap;
  353.     if (!(mask & 0x010)) {
  354.       MakeAMark(4, ComputerSide);    // #4 is the middle of 3x3
  355.  
  356.     } else if ((mask & 0x145) != 0x145) {  // corner mask
  357.       int sq;
  358.       if (!(mask & 0x001))
  359.         sq = 0;
  360.       else if (!(mask & 0x004))
  361.         sq = 2;
  362.       else if (!(mask & 0x040))
  363.         sq = 6;
  364.     else  // 0x100
  365.         sq = 8;
  366.       MakeAMark(sq, ComputerSide);    // #4 is the middle of 3x3
  367.  
  368.     } else {
  369.       int i;
  370.       for (i = 0; mask & 1; mask >>= 1)
  371.         i++;
  372.       MakeAMark(i, ComputerSide);
  373.     }
  374.   }
  375.  
  376.   // See if the game is over now.
  377.   //
  378.   if (IsWon() || IsOver())
  379.     Playing = false;
  380. }
  381.  
  382. //----------------------------------------------------------------------------
  383.  
  384. class TGameOptionsBox : public TDialog {
  385.   public:
  386.     TGameOptionsBox(TWindow* parent, TGame* game);
  387.  
  388.   private:
  389.     void SetupWindow();
  390.  
  391.    TGroupBox     *YouMeGroup, *XOGroup;
  392.     TRadioButton  *You, *Me, *X, *O;
  393.     TGame*         Game;
  394.  
  395.     void HandleYouMeGroupMsg(UINT);
  396.     void HandleXOGroupMsg(UINT);
  397.  
  398.   DECLARE_RESPONSE_TABLE(TGameOptionsBox);
  399. };
  400. DEFINE_RESPONSE_TABLE1(TGameOptionsBox, TDialog)
  401.   EV_CHILD_NOTIFY_ALL_CODES(IDYOUMEGROUP, HandleYouMeGroupMsg),
  402.   EV_CHILD_NOTIFY_ALL_CODES(IDXOGROUP, HandleXOGroupMsg),
  403. END_RESPONSE_TABLE;
  404.  
  405. TGameOptionsBox::TGameOptionsBox(TWindow* parent, TGame* game)
  406. :
  407.   TDialog(parent, IDD_OPTIONS),
  408.   Game(game)
  409. {
  410.   new TButton(this, IDOK, "Ok", 30, 240, 40, 40, true);
  411.   new TButton(this, IDCANCEL, "Cancel", 150, 240, 40, 40, true);
  412.   YouMeGroup = new TGroupBox(this, IDYOUMEGROUP, "Who goes first?", 15,
  413.                      30, 200, 20);
  414.   You = new TRadioButton(this, IDYOU, "You (Human)", 15, 55, 150, 20, YouMeGroup);
  415.   Me = new TRadioButton(this, IDME, "Me (Computer)", 15, 80, 150, 20, YouMeGroup);
  416.   XOGroup = new TGroupBox(this, IDXOGROUP, "I am playing", 15, 120, 200, 20);
  417.   X = new TRadioButton(this, IDX, "X\'s", 15, 145, 50, 20, XOGroup);
  418.   O = new TRadioButton(this, IDO, "O\'s", 15, 170, 50, 20, XOGroup);
  419. }
  420.  
  421. void
  422. TGameOptionsBox::SetupWindow()
  423. {
  424.   TDialog::SetupWindow();
  425.   if (Game->ComputerGoesFirst)
  426.     Me->Check();
  427.   else
  428.     You->Check();
  429.  
  430.   if (Game->UserSide == SqsX)
  431.     O->Check();
  432.   else
  433.     X->Check();
  434. }
  435.  
  436. void
  437. TGameOptionsBox::HandleYouMeGroupMsg(UINT)
  438. {
  439.   Game->ComputerGoesFirst = (You->GetCheck() == BF_CHECKED) ? false : true;
  440. }
  441.  
  442. void
  443. TGameOptionsBox::HandleXOGroupMsg(UINT)
  444. {
  445.   Game->UserSide = (X->GetCheck() == BF_CHECKED) ? SqsO : SqsX;
  446.   Game->Playing = false;
  447.   Parent->PostMessage(WM_COMMAND, CM_GAMENEW);
  448. }
  449.  
  450. //----------------------------------------------------------------------------
  451.  
  452. class TSquare : public TButtonGadget {
  453.   public:
  454.     TSquare(int id);
  455.    ~TSquare() {CelArray = 0;}
  456.  
  457.     void      SetGlyph(int g);
  458.     void      Paint(TDC& dc);
  459.  
  460.     public:
  461.     static TGame* Game;
  462.  
  463.   protected:
  464.     void      Activate(TPoint& p);
  465.     int       Glyph;
  466.  
  467.     void      LButtonDown(uint modKeys, TPoint& p);
  468.  
  469.   private:
  470.     static TCelArray*  StateCels[3];
  471.   friend class TGameView;
  472. };
  473. typedef TSquare* PTSquare;
  474.  
  475. TCelArray* TSquare::StateCels[SqsCount]; // entry for each state
  476. TGame* TSquare::Game = NULL;
  477. static TResId StateResId[SqsCount] = { IDB_EMPTY, IDB_X, IDB_O };
  478.  
  479. TSquare::TSquare(int id)
  480. :
  481.   TButtonGadget(IDB_EMPTY, id, NonExclusive, true, Up, false),
  482.   Glyph(0)
  483. {
  484. }
  485.  
  486. void
  487. TSquare::SetGlyph(int g)
  488. {
  489.   Glyph = g;
  490.   SetButtonState(Glyph ? Down : Up);
  491.   Invalidate();
  492. }
  493.  
  494. void
  495. TSquare::Paint(TDC& dc)
  496. {
  497.   // Fool button gadget into making our special state cel arrays
  498.   //
  499.   delete CelArray;   // delete any that might have been created
  500.   for (int i = 0; i < SqsCount; i++)
  501.     if (!StateCels[i]) {
  502.       ResId = StateResId[i];
  503.       CelArray = 0;     // leak?
  504.       BuildCelArray();
  505.       StateCels[i] = CelArray;
  506.     }
  507.  
  508.   // slip in the correct cel array just before painting
  509.   //
  510.   CelArray = StateCels[Glyph];
  511.   
  512.   TButtonGadget::Paint(dc);
  513.   
  514.   CelArray = 0;
  515. }
  516.  
  517. //
  518. // Modify activate behavior to make the buttons 'sticky'
  519. //
  520. void
  521. TSquare::Activate(TPoint& pt)
  522. {
  523.   if (!Glyph) {
  524.     TButtonGadget::Activate(pt);
  525.     Window->PostMessage(WM_COMMAND, GetId()); // post to owner view too
  526.   } else
  527.     CancelPressed(pt);
  528. }
  529.  
  530. //
  531. // if game is over don't change button state
  532. //
  533. void      
  534. TSquare::LButtonDown(uint modKeys, TPoint& p)
  535. {
  536.     if (Game->IsPlaying ())
  537.       TButtonGadget::LButtonDown(modKeys, p);
  538. }    
  539.  
  540. //----------------------------------------------------------------------------
  541. class TGameView : public TToolBox {
  542.   public:
  543.     TGameView(TGame& game);
  544.    ~TGameView();
  545.     TUnknown* SetupView(bool isEmbeded);
  546.     TGame&    Game;
  547.  
  548.     static const char far* StaticName() {return "TGameView";}
  549.   private:
  550.     void      SetupWindow();
  551.     void      CleanupWindow();
  552.     LRESULT   EvCommand(UINT id, HWND hWndCtl, UINT notifyCode);
  553.     void      EvCommandEnable(TCommandEnabler& ce);
  554.     LRESULT   EvGameViewSync(WPARAM wParam, LPARAM lParam);
  555.  
  556.     LRESULT   EvOcEvent(WPARAM wParam, LPARAM lParam);
  557.  
  558.     const char far* EvOcViewTitle();
  559.     bool   EvOcPartInvalid(TOcPart far&){return false;}
  560.     bool   EvOcViewSavePart(TOcSaveLoad far& ocSave);
  561.     bool   EvOcViewLoadPart(TOcSaveLoad far& ocLoad);
  562.     bool   EvOcViewPaint(TOcViewPaint far&);
  563.     bool   EvOcViewInsMenus(TOcMenuDescr far&);
  564.     bool   EvOcViewShowTools(TOcToolBarInfo far&);
  565.     bool   EvOcViewGetPalette(LOGPALETTE far* far*);
  566.     bool   EvOcViewClipData(TOcFormatData far&);
  567.     bool   EvOcViewClose();
  568.     bool   EvOcViewAttachWindow(bool attach);
  569.         bool      EvOcViewPartSize(TOcPartSize far& ps);
  570.     void   CmGameNew();
  571.     void   CmGameOptions();
  572.  
  573.     bool   IdleAction(long idleCount);
  574.  
  575.     enum TEndReason { Scratch, UserWon, ComputerWon };
  576.     void      GameOver(TEndReason why);
  577.  
  578.     TSquare** Board;
  579.     int       Dim;
  580.     TControlBar* ToolBar;
  581.     char*     ContainerName;
  582.     bool      IsEmbedded;
  583.  
  584.   public:
  585.     TOcRemView*  RemView;
  586.     TOcDocument* Document;
  587.  
  588.   DECLARE_RESPONSE_TABLE(TGameView);
  589. };
  590.  
  591. DEFINE_RESPONSE_TABLE1(TGameView, TToolBox)
  592.   EV_MESSAGE(WM_GAMEVIEWSYNC, EvGameViewSync),
  593.   EV_MESSAGE(WM_OCEVENT, EvOcEvent),
  594.  
  595.   EV_OC_VIEWPAINT,
  596.   EV_OC_VIEWSAVEPART,
  597.   EV_OC_VIEWLOADPART,
  598.   EV_OC_VIEWINSMENUS,
  599.   EV_OC_VIEWSHOWTOOLS,
  600.   EV_OC_VIEWGETPALETTE,
  601.   EV_OC_VIEWCLIPDATA,
  602.   EV_OC_VIEWCLOSE,
  603.   EV_OC_VIEWATTACHWINDOW, 
  604.   EV_OC_VIEWPARTSIZE,
  605.  
  606.   EV_COMMAND(CM_GAMENEW, CmGameNew),
  607.   EV_COMMAND(CM_GAMEOPTIONS, CmGameOptions),
  608. END_RESPONSE_TABLE;
  609.  
  610. TGameView::TGameView(TGame& game)
  611. :
  612.   Game(game),
  613.   TToolBox(0, game.GetDimension(), AS_MANY_AS_NEEDED, Horizontal, game.GetApplication()),
  614.   RemView(0)
  615. {
  616.   Dim = Game.GetDimension();
  617.   Board = new PTSquare[Dim*Dim];
  618.   ToolBar = 0;
  619.   ContainerName = new char[255];
  620.   IsEmbedded = false;
  621.  
  622.   TOcApp* ocApp = Game.GetApplication()->OcApp;
  623.   Document = new TOcDocument(*ocApp, 0, 0);  // temp file (deletes on close)
  624.  
  625.     TSquare::Game = &game; // assign static member
  626.   for (int i = 0; i < Dim; i++)
  627.     for (int j = 0; j < Dim; j++) {
  628.       TSquare* sq = new TSquare(CM_SQUARE + i*Dim + j);
  629.       Board[i*Dim + j] = sq;
  630.       Insert(*sq);
  631.     }
  632.   Game.SetView(this);
  633.  
  634.   ocApp->AddRef();
  635. }
  636.  
  637. //
  638. // Called after the window is created to create & attach to the OcRemView
  639. //
  640. TUnknown*
  641. TGameView::SetupView(bool isEmbedded)
  642. {
  643.   IsEmbedded = isEmbedded;
  644.   RemView = new TOcRemView(*Document, &::DocReg);
  645.   RemView->SetupWindow(*this, isEmbedded);
  646.   return RemView;
  647. }
  648.  
  649. static TControlBar*
  650. BuildControlBar(TWindow* parent, TControlBar::TTileDirection direction)
  651. {
  652.   TControlBar* cb = new TControlBar(parent, direction, new TGadgetWindowFont);
  653.   cb->Insert(*new TButtonGadget(CM_GAMENEW, CM_GAMENEW));
  654.   cb->Insert(*new TButtonGadget(CM_GAMEOPTIONS, CM_GAMEOPTIONS));
  655.   cb->Insert(*new TButtonGadget(CM_ABOUT, CM_ABOUT));
  656.   cb->Attr.Style |= WS_CLIPSIBLINGS;    // since toolbar may move around
  657.   cb->Attr.X = cb->Attr.Y = 0;
  658.   cb->Attr.W = 9999;
  659.   cb->Attr.Id = IDW_TOOLBAR;
  660.   return cb;
  661. }
  662.  
  663. TGameView::~TGameView()
  664. {
  665.   if (Document)
  666.     Document->Close();           // close all the embedded parts first
  667.   if (RemView && !IsEmbedded)
  668.     RemView->ReleaseObject();
  669.   delete Document;
  670.   Game.GetApplication()->OcApp->Release();
  671.  
  672.   delete [] Board;
  673.   delete [] ContainerName;
  674. }
  675.  
  676. void
  677. TGameView::SetupWindow()
  678. {
  679.   TWindow::SetupWindow();
  680.   SetFocus();
  681.   ToolBar = BuildControlBar(this, TControlBar::Horizontal);
  682.   ToolBar->Attr.Style &= ~WS_VISIBLE;
  683.   ToolBar->Create();
  684. }
  685.  
  686. void
  687. TGameView::CleanupWindow()
  688. {
  689.   if (RemView && !IsEmbedded){
  690.     RemView->EvClose();
  691.   }
  692.   TToolBox::CleanupWindow();
  693. }
  694.  
  695. void
  696. TGameView::GameOver(TEndReason why)
  697. {
  698.   static char* msgs[] = { "Scratch", "You won!", "I won!" };
  699.   MessageBox(msgs[why], "Game Over", MB_OK);
  700. }
  701.  
  702. LRESULT
  703. TGameView::EvCommand(UINT id, HWND hWndCtl, UINT notifyCode)
  704. {
  705.   if (id >= CM_SQUARE
  706.       && id < CM_SQUARE+Game.GetDimension()*Game.GetDimension()
  707.       && !hWndCtl) {
  708.  
  709.     int square = id - CM_SQUARE;
  710.  
  711.     if (Game.GetState(square) != SqsEmpty || !Game.IsPlaying())
  712.       return 0;
  713.  
  714.     Game.UserTurn(square);
  715.  
  716.     if (Game.IsWon())
  717.       GameOver(UserWon);
  718.  
  719.     else if (Game.IsOver())
  720.       GameOver(Scratch);
  721.  
  722.     else {
  723.       Game.ComputerTurn();
  724.  
  725.       // See if the game is over now.
  726.       //
  727.       if (Game.IsWon())
  728.         GameOver(ComputerWon);
  729.  
  730.       else if (Game.IsOver())
  731.         GameOver(Scratch);
  732.     }
  733.     return 0;
  734.   }
  735.   return TToolBox::EvCommand(id, hWndCtl, notifyCode);
  736. }
  737.  
  738. //
  739. // Handle the command Ids for the TTT squares by hand, otherwise, if embeded
  740. // route them normally, else forward to base
  741. //
  742. void
  743. TGameView::EvCommandEnable(TCommandEnabler& ce)
  744. {
  745.   if (ce.Id >= CM_SQUARE
  746.       && ce.Id < CM_SQUARE+Game.GetDimension()*Game.GetDimension()) {
  747.     ce.Enable(Game.IsPlaying());
  748.   }
  749.   else if (IsEmbedded) {
  750.     // Get the focus, in case it is a child that should receive the cmds
  751.     // fall back to this window in case the Ole menu bar stole focus!
  752.     //
  753.     HWND  hCmdTarget = ::GetFocus();
  754.     if (hCmdTarget != HWindow && !IsChild(hCmdTarget))
  755.       hCmdTarget = HWindow;
  756.  
  757.     RouteCommandEnable(hCmdTarget, ce);
  758.   }
  759.   else
  760.     TToolBox::EvCommandEnable(ce);
  761. }
  762.  
  763. LRESULT
  764. TGameView::EvGameViewSync(WPARAM wParam, LPARAM)
  765. {
  766.   if (wParam == 0xFFFF)
  767.     for (int i = 0; i < Dim*Dim; i++)
  768.       Board[i]->SetGlyph(Game.GetState(i));
  769.  
  770.   else
  771.     Board[wParam]->SetGlyph(Game.GetState(wParam));
  772.  
  773.   if (RemView)
  774.     RemView->Invalidate(invView);
  775.  
  776.   return 0;
  777. }
  778.  
  779. //
  780. // Handle & sub-dispatch the OC event message.
  781. //
  782. LRESULT
  783. TGameView::EvOcEvent(WPARAM wParam, LPARAM lParam)
  784. {
  785.   TEventHandler::TEventInfo eventInfo(WM_OCEVENT, wParam);  //WM_OWLNOTIFY
  786.   if (Find(eventInfo))
  787.     return Dispatch(eventInfo, wParam, lParam);
  788.   return 0;
  789. }
  790.  
  791. //
  792. // Return our frame's title
  793. //
  794. const char far*
  795. TGameView::EvOcViewTitle()
  796. {
  797.   Parent->GetWindowText(ContainerName, 255 - 1);
  798.   return ContainerName;
  799. }
  800.  
  801. //
  802. // Ask server to close the document
  803. //
  804. bool
  805. TGameView::EvOcViewClose()
  806. {
  807.   if (Document)
  808.     Document->Close();
  809.   return true;
  810. }
  811.  
  812.  
  813. //
  814. // Attach this view back to its owl parent for either open editing, or
  815. // mearly inplace de-activating
  816. //
  817. bool
  818. TGameView::EvOcViewAttachWindow(bool attach)
  819. {
  820.  
  821.   TFrameWindow* mainWindow = GetApplication()->GetMainWindow();
  822.  
  823.   if (attach) {
  824.     if (RemView->GetState()==TOcRemView::OpenEditing) {
  825.       mainWindow->Show(SW_SHOW);    // in case it was hidden
  826.       mainWindow->BringWindowToTop();
  827.  
  828.       // Derived class needs to managed setting up frame differently, like
  829.       // for MDI etc.
  830.       //
  831.       mainWindow->SetClientWindow(this);
  832.     }
  833.   }
  834.  
  835.   return true;
  836. }
  837.  
  838. //
  839. // Save the game to the IStorage provided by the container
  840. //
  841. bool
  842. TGameView::EvOcViewSavePart(TOcSaveLoad far& ocSave)
  843. {
  844.   PRECONDITION(ocSave.StorageI);
  845.  
  846.   Document->SetStorage(ocSave.StorageI);
  847.  
  848.   // Save remote view info such as origin and extent
  849.   //
  850.   RemView->Save(ocSave.StorageI);
  851.  
  852.   // Write out the embedded objects, if any
  853.   //
  854.  
  855.   // Create/open a stream in our storage to save part information
  856.   //
  857.   TOcStorage Storage(ocSave.StorageI);
  858.   STATSTG  statstg;
  859.   if (!HRSucceeded(Storage.Stat(&statstg, STATFLAG_NONAME)))
  860.     return false;
  861.  
  862.   TOcStream  stream(Storage, ::GameStreamName, true, statstg.grfMode);
  863.  
  864.   // Write Game data into stream
  865.   //
  866.   ulong count;
  867.   if (!(SUCCEEDED(stream.Write(&Game.Dim, sizeof(int), &count)) &&
  868.       SUCCEEDED(stream.Write(&Game.UserBoardMap, sizeof(int), &count)) &&
  869.       SUCCEEDED(stream.Write(&Game.ComputerBoardMap, sizeof(int), &count)) &&
  870.       SUCCEEDED(stream.Write(&Game.UserSide, sizeof(TSquareState), &count)) &&
  871.       SUCCEEDED(stream.Write(&Game.ComputerSide, sizeof(TSquareState), &count)) &&
  872.       SUCCEEDED(stream.Write(&Game.ComputerGoesFirst, sizeof(bool), &count)) &&
  873.       SUCCEEDED(stream.Write(&Game.Playing, sizeof(bool), &count))))
  874.     return false;
  875.  
  876.   for (int i = 0; i < Dim; i++)
  877.     for (int j = 0; j < Dim; j++) {
  878.       TSquare* sq = Board[i*Dim + j];
  879.       if (!SUCCEEDED(stream.Write(&sq->Glyph, sizeof(int), &count)))
  880.         return false;
  881.     }
  882.  
  883.   return true;
  884. }
  885.  
  886. //
  887. // Load the game from the IStorage provided by the container
  888. //
  889. bool
  890. TGameView::EvOcViewLoadPart(TOcSaveLoad far& ocLoad)
  891. {
  892.   PRECONDITION(ocLoad.StorageI);
  893.  
  894.   // Assign new storage to OcDocument
  895.   //
  896.   Document->SetStorage(ocLoad.StorageI);
  897.  
  898.   // Load remote view info such as origin and extent
  899.   //
  900.   RemView->Load(ocLoad.StorageI);
  901.  
  902.   // Read in the embedded objects, if any
  903.   //
  904.   // Create/open a stream in our storage to save part information
  905.   //
  906.   TOcStorage Storage(ocLoad.StorageI);
  907.   STATSTG  statstg;
  908.   if (!SUCCEEDED(Storage.Stat(&statstg, STATFLAG_NONAME)))
  909.     return false;
  910.  
  911.   TOcStream  stream(Storage, ::GameStreamName, false, statstg.grfMode);
  912.  
  913.  
  914.   // Read in the game data
  915.   //
  916.   ulong count;
  917.   if (!(SUCCEEDED(stream.Read(&Game.Dim, sizeof(int), &count)) &&
  918.       SUCCEEDED(stream.Read(&Game.UserBoardMap, sizeof(int), &count)) &&
  919.       SUCCEEDED(stream.Read(&Game.ComputerBoardMap, sizeof(int), &count)) &&
  920.       SUCCEEDED(stream.Read(&Game.UserSide, sizeof(TSquareState), &count)) &&
  921.       SUCCEEDED(stream.Read(&Game.ComputerSide, sizeof(TSquareState), &count)) &&
  922.       SUCCEEDED(stream.Read(&Game.ComputerGoesFirst, sizeof(bool), &count)) &&
  923.       SUCCEEDED(stream.Read(&Game.Playing, sizeof(bool), &count))))
  924.     return false;
  925.  
  926.   for (int i = 0; i < Dim; i++)
  927.     for (int j = 0; j < Dim; j++) {
  928.       TSquare* sq = Board[i*Dim + j];
  929.       if (!SUCCEEDED(stream.Read(&sq->Glyph, sizeof(int), &count)))
  930.         return false;
  931.     }
  932.  
  933.   if (! ocLoad.Remember)
  934.     Document->SetStorage((IStorage*)0);
  935.  
  936.   PostMessage(WM_GAMEVIEWSYNC, 0xFFFF);
  937.  
  938.   return true;
  939. }
  940.  
  941. bool
  942. TGameView::EvOcViewPaint(TOcViewPaint far& vp)
  943. {
  944.   // paint according to the view paint structure
  945.   //
  946.   TDC dc(vp.DC);
  947.  
  948.   Dim = Game.GetDimension();
  949.   for (int i = 0; i < Dim; i++)
  950.     for (int j = 0; j < Dim; j++) {
  951.       TSquare* sq = Board[i*Dim + j];
  952.       TPoint pt(-sq->GetBounds().left, -sq->GetBounds().top);
  953.       dc.SetWindowOrg(pt);
  954.       sq->Paint(dc);
  955.     }
  956.   return true;
  957. }
  958.  
  959. bool
  960. TGameView::EvOcViewInsMenus(TOcMenuDescr far& sharedMenu)
  961. {
  962.   // Recreate a temporary composite menu for frame and child
  963.   //
  964.   TMenuDescr compMenuDesc; // empty menudescr
  965.   compMenuDesc.Merge(TMenuDescr("IDM_TTTSVRGAME",0,0,0,1,0,1));
  966.   compMenuDesc.Merge(TMenuDescr(0,  -1, 0, -1, 0, -1, 0));
  967.  
  968.   TMenuDescr shMenuDescr(sharedMenu.HMenu,
  969.                          sharedMenu.Width[0],
  970.                          sharedMenu.Width[1],
  971.                          sharedMenu.Width[2],
  972.                          sharedMenu.Width[3],
  973.                          sharedMenu.Width[4],
  974.                          sharedMenu.Width[5]);
  975.   shMenuDescr.Merge(compMenuDesc);
  976.  
  977.   for (int i = 0; i < 6; i++)
  978.     sharedMenu.Width[i] = shMenuDescr.GetGroupCount(i);
  979.  
  980.   return true;
  981. }
  982.  
  983. //
  984. // Let OC have our toolbar to insert
  985. //
  986. bool
  987. TGameView::EvOcViewShowTools(TOcToolBarInfo far& tbi)
  988. {
  989.   tbi.HTopTB = *ToolBar;
  990. //  tbi.HBottomTB = *ToolBar;  // Can put it on the bottom too
  991.   return true;
  992. }
  993.  
  994. //
  995. // Get server's color palette
  996. //
  997. bool
  998. TGameView::EvOcViewGetPalette(LOGPALETTE far* far*)
  999. {
  1000.   return false;
  1001. }
  1002.  
  1003. //
  1004. // Ask server to provide data according to the format in a handle
  1005. //
  1006. bool
  1007. TGameView::EvOcViewClipData(TOcFormatData far&)
  1008. {
  1009.   return 0;
  1010. }
  1011.  
  1012. bool      
  1013. TGameView::EvOcViewPartSize(TOcPartSize far& ps)
  1014. {
  1015.     GetWindowRect (ps.PartRect);
  1016.     return true;  
  1017. }    
  1018.  
  1019. void
  1020. TGameView::CmGameNew()
  1021. {
  1022.   Game.New();
  1023. }
  1024.  
  1025. void
  1026. TGameView::CmGameOptions()
  1027. {
  1028.   TGameOptionsBox(this, &Game).Execute();
  1029. }
  1030.  
  1031. static void
  1032. DoIdleAction(TWindow* win, void* idleCount)
  1033. {
  1034.   win->IdleAction(*(long*)idleCount);
  1035. }
  1036.  
  1037. //
  1038. // TFrameWindow processes idle action occurs once per block of messages
  1039. //
  1040. bool
  1041. TGameView::IdleAction(long idleCount)
  1042. {
  1043.   if (idleCount == 0) {
  1044.     // give child windows an opportunity to do any idle processing
  1045.     //
  1046.     ForEach(DoIdleAction, &idleCount);
  1047.   }
  1048.   return false;  // we don't need any more time for now
  1049. }
  1050.  
  1051. //----------------------------------------------------------------------------
  1052.  
  1053. class TAboutBox : public TDialog {
  1054.   public:
  1055.     TAboutBox::TAboutBox(TWindow* parent, TResId dlgId, const char far* msg);
  1056. };
  1057.  
  1058. TAboutBox::TAboutBox(TWindow* parent, TResId dlgId, const char far* msg)
  1059. :
  1060.   TDialog(parent, dlgId)
  1061. {
  1062.   TStatic* stat = new TStatic(this, IDSTATIC, msg, 23, 20, 190, 45, 0);
  1063.   stat->Attr.Style |= SS_CENTER;
  1064.   new TButton(this, IDOK, "OK", 80, 90, 40, 40, true);
  1065. }
  1066.  
  1067. //----------------------------------------------------------------------------
  1068.  
  1069. class TAppFrame : public TFrameWindow {
  1070.   public:
  1071.     TAppFrame(const char far* title,
  1072.               TResId          menuResId,
  1073.               TGameApp*       module);
  1074.    ~TAppFrame();
  1075.  
  1076.   private:
  1077.     void       SetupWindow();
  1078.     void       CleanupWindow();
  1079.  
  1080.     LRESULT    EvOcEvent(WPARAM wParam, LPARAM lParam);
  1081.     bool       EvOcAppShutdown();
  1082.     void       EvTimer(uint timerId);
  1083.  
  1084.     TGameApp*  GameApp;
  1085.  
  1086.   DECLARE_RESPONSE_TABLE(TAppFrame);
  1087. };
  1088. DEFINE_RESPONSE_TABLE1(TAppFrame, TFrameWindow)
  1089.   EV_WM_TIMER,
  1090.   EV_MESSAGE(WM_OCEVENT, EvOcEvent),
  1091.   EV_OC_APPSHUTDOWN,
  1092. END_RESPONSE_TABLE;
  1093.  
  1094. TAppFrame::TAppFrame(const char far* title,
  1095.                      TResId          menuResId,
  1096.                      TGameApp*       module)
  1097. :
  1098.   TFrameWindow(0, title, 0, true, module),
  1099.   GameApp(module)
  1100. {
  1101.   AssignMenu(menuResId);
  1102. }
  1103.  
  1104. TAppFrame::~TAppFrame()
  1105. {
  1106. }
  1107.  
  1108. void
  1109. TAppFrame::SetupWindow()
  1110. {
  1111.   TFrameWindow::SetupWindow();
  1112.  
  1113.   // Wire up ObjectConnections to our hWnd
  1114.   //
  1115.   GameApp->OcApp->SetupWindow(*this);
  1116.  
  1117.   // Create a timer to allow us to poll for idle time when we are a dll server
  1118.   //
  1119.   if (!GameApp->OcApp->IsOptionSet(amExeMode))
  1120.     SetTimer(TTT_DLLIDLE, DllIdleTime);
  1121. }
  1122.  
  1123. void
  1124. TAppFrame::CleanupWindow()
  1125. {
  1126.   if (!GameApp->OcApp->IsOptionSet(amExeMode))
  1127.     KillTimer(TTT_DLLIDLE);
  1128. }
  1129.  
  1130. bool
  1131. TAppFrame::EvOcAppShutdown()
  1132. {
  1133.   // called by TOcApp::ShutdownMaybe when last embedding is closed
  1134.   // if that's the only reason the app is up we need to shut ourselves down
  1135.   //
  1136.   SendMessage(WM_CLOSE);
  1137.   return true;
  1138. }
  1139.  
  1140. //
  1141. // Handle & sub-dispatch the OC event message to ourselves & to the app
  1142. //
  1143. LRESULT
  1144. TAppFrame::EvOcEvent(WPARAM wParam, LPARAM lParam)
  1145. {
  1146.   TEventHandler::TEventInfo eventInfo(WM_OCEVENT, wParam);
  1147.   if (Find(eventInfo))
  1148.     return Dispatch(eventInfo, wParam, lParam);
  1149.   if (GetApplication()->Find(eventInfo))
  1150.     return GetApplication()->Dispatch(eventInfo, wParam, lParam);
  1151.   return 0;
  1152. }
  1153.  
  1154. //
  1155. //
  1156. //
  1157. void
  1158. TAppFrame::EvTimer(uint timerId)
  1159. {
  1160.   if (timerId == TTT_DLLIDLE)
  1161.     GetApplication()->PostDispatchAction();
  1162.   TWindow::EvTimer(timerId);
  1163. }
  1164.  
  1165. //----------------------------------------------------------------------------
  1166.  
  1167. TGameApp::TGameApp()
  1168. :
  1169.   TApplication(::AppReg["description"], ::Module, &::AppDictionary)
  1170. {
  1171.   EnableBWCC();
  1172. }
  1173.  
  1174. TGameApp::~TGameApp()
  1175. {
  1176. }
  1177.  
  1178. void
  1179. TGameApp::InitMainWindow()
  1180. {
  1181.   nCmdShow = IsOptionSet(amEmbedding) ? SW_HIDE : SW_SHOWNORMAL;
  1182.   MainWindow = new TAppFrame(GetName(), "IDM_TTTGAME", this);
  1183.   MainWindow->Attr.Style &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
  1184.   MainWindow->SetIcon(this, IDI_TTT);
  1185.   MainWindow->SetFlag (wfPropagateIdle);
  1186. }
  1187.  
  1188. TUnknown*
  1189. TGameApp::CreateOleObject(uint32 options, TRegLink* regLink)
  1190. {
  1191.   // create the only object we know about. If we served more objects, we would
  1192.   // need to examine regLink to determine which to create
  1193.   //
  1194.   TGame* game = new TGame;
  1195.   game->SetApplication(this);
  1196.   TGameView* view = new TGameView(*game);
  1197.  
  1198.   if (options & amRun)
  1199.     GetMainWindow()->SetClientWindow(view);
  1200.   else
  1201.     view->SetParent(GetMainWindow());
  1202.  
  1203.   if (!GetMainWindow()->HWindow)
  1204.     return 0;
  1205.  
  1206.   view->Create();
  1207.   return regLink ? view->SetupView(true) : 0;
  1208. }
  1209.  
  1210. void
  1211. TGameApp::CmAbout()
  1212. {
  1213.   TAboutBox(&TWindow(::GetFocus(), this),
  1214.             IDD_ABOUT,
  1215.             "Tic Tac Toe\n(C) Borland International\n 1992,1994"
  1216.             ).Execute();
  1217. }
  1218.  
  1219. //----------------------------------------------------------------------------
  1220.  
  1221. int
  1222. OwlMain(int /*argc*/, char* /*argv*/ [])
  1223. {
  1224.   try {
  1225.     // App registration object
  1226.     //
  1227.     Registrar = new TOcRegistrar(AppReg, TOleFactory<TGameApp>(),
  1228.                                  TApplication::GetCmdLine(), ::RegLinkHead);
  1229.  
  1230.     // We are all done if one of the action options was specified on the
  1231.     // cmdLine
  1232.     //
  1233.     if (Registrar->IsOptionSet(amAnyRegOption))
  1234.       return 0;  // Could display a msg box here if desired
  1235.  
  1236.     return Registrar->Run();
  1237.   }
  1238.   catch (xmsg& x) {
  1239.     return ::HandleGlobalException(x, "Exception");
  1240.   }
  1241. }
  1242.