home *** CD-ROM | disk | FTP | other *** search
/ Arawak OS/2 Shareware / PAKLED.ISO / docs / edm / edmi1-4.inf (.txt) < prev    next >
Encoding:
OS/2 Help File  |  1993-08-25  |  68.4 KB  |  1,383 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
  3.  
  4.            Welcome to EDM/2 - The Electronic OS/2 Developers Magazine!
  5.            Portions copyright (c) by Steve Luzynski, Larry Salomon Jr.
  6.                                 Volume 1, issue 4
  7.  
  8.  
  9. ΓòÉΓòÉΓòÉ 2. Copyright Notice (and other Legal Stuff) ΓòÉΓòÉΓòÉ
  10.  
  11. The editors of this electronic magazine are Steve Luzynski and Larry Salomon, 
  12. Jr. 
  13.  
  14. Portions of EDM/2 are copyrighted by the editors.  This publication may be 
  15. freely distributed in electronic form provided that all parts are present in 
  16. their original unmodified form.  A reasonable fee may be charged for the 
  17. physical act of distribution; no fee may be charged for the publication itself. 
  18.  
  19. All articles are copyrighted by their authors. No part of any article may be 
  20. reproduced without permission from the original author. 
  21.  
  22. Neither this publication nor the editors are affiliated with International 
  23. Business Machines Corporation. 
  24.  
  25. OS/2 is a registered trademark of International Business Machines Corporation. 
  26. Other trademarks are property of their respective owners.  Any mention of a 
  27. product in this publication does not constitute an endorsement or affiliation 
  28. unless specifically stated in the text. 
  29.  
  30.  
  31. ΓòÉΓòÉΓòÉ 3. From the Editors ΓòÉΓòÉΓòÉ
  32.  
  33. Well, after a serious hiccup in the release schedule, we are finally putting 
  34. out our next issue.  We is used because the founder and original 
  35. Editor-in-Chief, Steve Luzynski, found himself with too much work and not 
  36. enough time, so another editor was added (Larry Salomon), and he temporarily 
  37. assumed the duties as Editor-in-Chief until Steve can return in full capacity. 
  38.  
  39. Because the original July issue never made it out as far as I know, I've tried 
  40. to put together an issue containing material that I wrote and any articles that 
  41. I received via the Internet after the ballyhoo about Steve's untimely (and 
  42. hopefully temporary) demise.  My apologies if this issue appears to be a "Larry 
  43. Salomon-only" edition, but - hey - you have to work with what you have to work 
  44. with. 
  45.  
  46. Andre Asselin, whom you'll remember was writing the article series on IFS 
  47. Development, regretfully told us that he would not be able to meet our deadline 
  48. for this issue.  He added that he will try to continue it in the next issue, 
  49. fortunately. 
  50.  
  51. At the time of this writing, we were unable to reach Gavin Baker, the writer 
  52. for the Introduction to PM Programming column.  While we are continuing our 
  53. efforts in this area, we appreciate any contact that can be established by our 
  54. readers. 
  55.  
  56. Finally, our thanks and one box of Scooby Snacks go to the following people who 
  57. provided administrative support for this issue (usually in the form of 
  58. pre-release reviews): 
  59.  
  60. o Mir Assadullah 
  61.  
  62. Enjoy!
  63. The Editors
  64.  
  65.  
  66. ΓòÉΓòÉΓòÉ 4. This Month's Features ΓòÉΓòÉΓòÉ
  67.  
  68. The following articles constitute this month's features: 
  69.  
  70. o C++ Encapsulation of PM 
  71. o Development of a New Window Class - Part 1 
  72. o Programming the Container Control - Part 2 
  73.  
  74.  
  75. ΓòÉΓòÉΓòÉ 4.1. C++ Encapsulation of PM ΓòÉΓòÉΓòÉ
  76.  
  77.                          Written by Gordon W. Zeglinski
  78.  
  79.  
  80. ΓòÉΓòÉΓòÉ 4.1.1. Introduction ΓòÉΓòÉΓòÉ
  81.  
  82. The basic goal of encapsulating the PM interface is relating the non-C++ based 
  83. instance information into a C++ based form.  Specifically, in PM, each window 
  84. has its own unique handle.  In C++, each instance of a window would have its 
  85. own unique this pointer.  Thus, the goal in encapsulating PM, at the simplest 
  86. level, is to relate the handle passed to the window procedure to the C++ 
  87. instance. 
  88.  
  89. There are two methods by which the C++ "version" of the window can be 
  90. associated with the PM "version" which are discussed below, followed by a 
  91. detailed look at the implementation for one of these two methods.  This purpose 
  92. of this article is to promote the usage of the objects described here; it is 
  93. merely to illustrate some of the concepts of PM encapsulation and some of the 
  94. methods that can be used to implement this encapsulation. 
  95.  
  96. Having said this, the objects illustrated here are only part of my collection 
  97. of OS/2 objects, which are used in other programming projects.  The primary 
  98. design consideration behind this object hierarchy is to eliminate as much 
  99. tedium as possible with a minimization in complexity and performance 
  100. degradation.  Also, the development of this library is not my primary goal; the 
  101. library grows as I need more power or features in my other programs. 
  102.  
  103. Given sufficient interest, future articles could be written detailing the 
  104. encapsulation of other controls, thread manipulation in C++, and extending the 
  105. object hierarchy. 
  106.  
  107.  
  108. ΓòÉΓòÉΓòÉ 4.1.2. Encapsulation Strategies ΓòÉΓòÉΓòÉ
  109.  
  110. Method 1 
  111.  
  112. PM allows the application to allocate additional storage space that the 
  113. application can use to store window instance related data in (called window 
  114. words).  The first method of window encapsulation would use this additional 
  115. storage space to hold the this information. 
  116.  
  117. The problem with this approach is that in order to access this extra storage 
  118. space, the application must know the window's handle.  Generally, this is not a 
  119. problem but the C++ dispatch function has to rely on having a valid pointer. 
  120. The first opportunity the application has to validate the pointer is not until 
  121. the WM_CREATE message is received.  More importantly, while creating new 
  122. windows, the application cannot assume that any message received has a valid 
  123. pointer until the window being created has received the WM_CREATE message. 
  124. During this time, the application must rely on method 2 to dispatch the 
  125. messages to the appropriate window instance. 
  126.  
  127. Method 2 
  128.  
  129. This method does not use any additional storage space allocated by PM. Instead, 
  130. the application maintains a list of handles and their corresponding instances 
  131. and window procedures.  This is the approach taken here. 
  132.  
  133.  
  134. ΓòÉΓòÉΓòÉ 4.1.3. Support Objects ΓòÉΓòÉΓòÉ
  135.  
  136. The class hierarchy can be divided into two principle sections:  1) the objects 
  137. that are used to support the windowing objects and 2) the window objects 
  138. themselves.  The support objects will be described first so that their usage 
  139. can be seen when the window objects are detailed later.  Some of these objects 
  140. merely encapsulate individual data items used by PM into single objects while 
  141. others implement the primary message dispatching functions. 
  142.  
  143. PMainThread 
  144.  
  145. This class is used to register the thread with PM and to create its message 
  146. queue. 
  147.  
  148. class PMmainThread {
  149.    protected:
  150.       HAB hab;
  151.       HMQ hmq;
  152.  
  153.    public:
  154.       PMmainThread()
  155.       {
  156.          //----------------------------------------------------------------
  157.          // Either of the two indicate an error has occurred.
  158.          //----------------------------------------------------------------
  159.          if ((hab = WinInitialize(0)) == NULLHANDLE)
  160.             exit(1);
  161.          if ((hmq = WinCreateMsgQueue(hab,0)) == NULLHANDLE)
  162.             exit(1);
  163.       }
  164.  
  165.       virtual ~PMmainThread()
  166.       {
  167.          WinDestroyMsgQueue(hmq);
  168.          WinTerminate(hab);
  169.       }
  170.  
  171.       BOOL MessageLoop(void);
  172.       HAB GetHAB() { return hab; }
  173.       HMQ GetHMQ() { return hmq; }
  174. };
  175.  
  176. Also, this class provides a generic message loop that can be used by the 
  177. application. 
  178.  
  179. MessageStruct 
  180.  
  181. This class simply encapsulates the message parameters sent by PM into a single 
  182. structure. 
  183.  
  184. struct MessageStruct {
  185.    BaseWindow *TheWin;
  186.    ULONG iMessage;
  187.    MPARAM mParam1;
  188.    MPARAM mParam2;
  189.  
  190.    MessageStruct(BaseWindow *TW,ULONG iMess, MPARAM mP1, MPARAM mP2) {
  191.       TheWin=TW;
  192.       iMessage=iMess;
  193.       mParam1=mP1;
  194.       mParam2=mP2;
  195.    }
  196. };
  197.  
  198. Message Dispatch Objects 
  199.  
  200. WinEventHandler 
  201.  
  202. It is desirable to have a single generalized dispatch object that can be 
  203. referred to by a pointer which would then hide the multi-threading and message 
  204. dispatching from the rest of the code.  While it would be extremely difficult 
  205. for a single object to do all this, a hierarchy of objects can be used instead. 
  206. The parent of this hierarchy follows: 
  207.  
  208. class WinEventHandler {
  209.    public:
  210.       static const char Action_Chain;
  211.       static const char Action_CallDef;
  212.  
  213.       WinEventHandler() {};
  214.  
  215.       HAB GetAppHAB() { return (BaseWindow::App)->GetHAB(); }
  216.       virtual HAB GetHAB()=0;
  217.       virtual void SetHAB(HAB hb) {}
  218.  
  219.       virtual int MultiThread()=0;
  220.       virtual MRESULT Run(MessageStruct &Msg,char &Action)=0;
  221. };
  222.  
  223. The above class provides a generic interface to objects which actually 
  224. implement the code necessary to dispatch a message in a transparent manner. It 
  225. should be noted that it is an abstract class. 
  226.  
  227. WinNewEventHandler (template) 
  228.  
  229. Generally, since all derived classes need to implement the pure virtual 
  230. functions of their parent abstract class, object class templates are used to 
  231. allow the message handling function to be a non-virtual member function.  The 
  232. template below assumes that the message dispatch function belongs to the class 
  233. of the window object and it additionally specifies that a new thread should be 
  234. created to process the messages. 
  235.  
  236. template<class C1> class WinNewThreadHandler: public WinEventHandler {
  237.       HAB hab;
  238.       MRESULT (C1:: WinProc)(WinEventHandler &, MessageStruct &, char &);
  239.  
  240.    public:
  241.       WinNewThreadHandler(MRESULT (C1::* Proc)(WinEventHandler &,
  242.                                                MessageStruct &,
  243.                                                char &)): WinEventHandler() {
  244.          WinProc=Proc;
  245.       }
  246.  
  247.       virtual int MultiThread() { return 1; }
  248.       virtual MRESULT Run(MessageStruct &Msg,char &Action) {
  249.          return (((C1 *)(Msg.TheWin))->*WinProc)(*this,Msg,Action);
  250.       }
  251.  
  252.       virtual HAB GetHAB() { return hab; }
  253.       virtual void SetHAB(HAB hb) { hab=hb; }
  254. };
  255.  
  256. WinSameEventHandler (template) 
  257.  
  258. This object is similar to the object WinNewThreadHandler in every aspect except 
  259. that the message processing function is dispatched in the same thread as the 
  260. primary message dispatch function. 
  261.  
  262. template<class C1> class WinSameThreadHandler: public WinEventHandler {
  263.    MRESULT (C1::* WinProc)(WinEventHandler &,
  264.                            MessageStruct &,
  265.                            char &);
  266.    public:
  267.       WinSameThreadHandler(MRESULT (C1::* Proc)(WinEventHandler &,
  268.                                                 MessageStruct &,
  269.                                                 char &)): WinEventHandler() {
  270.          WinProc=Proc;
  271.       }
  272.  
  273.  
  274.       virtual int MultiThread() { return 0; }
  275.       virtual MRESULT Run(MessageStruct &Msg,char &Action) {
  276.          return (((C1*)(Msg.TheWin))->*WinProc)(*this,Msg,Action);
  277.       }
  278.  
  279.       virtual HAB GetHAB() { return GetAppHAB(); }
  280. };
  281.  
  282. WinNewEventHandlerDifInst (template) 
  283.  
  284. In some cases, it is desirable to have the function which processes the message 
  285. belonging to an object type different than that of the window.  The following 
  286. object allows this type of scenario to occur in a multi-threaded nature. 
  287.  
  288. template<class C1> class WinNewThreadHandlerDifInst: public WinEventHandler {
  289.    HAB hab;
  290.    C1 *TheClass;
  291.  
  292.    MRESULT (C1::* WinProc)(WinEventHandler &,
  293.                            MessageStruct &,
  294.                            char &);
  295.    public:
  296.       WinNewThreadHandlerDifInst(C1* Inst,
  297.                                  MRESULT (C1::* Proc)(WinEventHandler &,
  298.                                                       MessageStruct&,
  299.                                                       char &)): WinEventHandler() {
  300.          TheClass=Inst;
  301.          WinProc=Proc;
  302.       }
  303.  
  304.       virtual int MultiThread() { return 1; }
  305.       virtual MRESULT Run(MessageStruct &Msg,char &Action) {
  306.          return (TheClass->*WinProc)(*this,Msg,Action);
  307.       }
  308.  
  309.       virtual HAB GetHAB() { return hab; }
  310.       virtual void SetHAB(HAB hb) { hab=hb; }
  311. };
  312.  
  313. WinSameEventHandlerDifInst (template) 
  314.  
  315. Like the above, this object allows mixing of object types.  The difference here 
  316. is that the message is processed in the same thread that it is dispatched. 
  317.  
  318. template<class C1> class WinSameThreadHandlerDifInst: public WinEventHandler {
  319.    C1 *TheClass;
  320.    MRESULT (C1::* WinProc)(WinEventHandler &,
  321.                            MessageStruct &,
  322.                            char &);
  323.    public:
  324.       WinSameThreadHandlerDifInst(C1* Inst,
  325.                                   MRESULT (C1::* Proc)(WinEventHandler &,
  326.                                                        MessageStruct &,
  327.                                                        char &)): WinEventHandler() {
  328.          TheClass=Inst;
  329.          WinProc=Proc;
  330.       }
  331.  
  332.       virtual int MultiThread() { return 0; }
  333.       virtual MRESULT Run(MessageStruct &Msg,char &Action) {
  334.          return (TheClass->*WinProc)(*this,Msg,Action);
  335.       }
  336.  
  337.       virtual HAB GetHAB() { return GetAppHAB(); }
  338. };
  339.  
  340. Handler Thunks 
  341.  
  342. To create a thread under Borland C++ for OS/2 or IBM Cset++, one has to use the 
  343. function _beginthread().  Note that the function has different parameters in 
  344. the two compilers.  This function does not make any provision for starting 
  345. threads on C++ member functions, so a helper function is needed. The helper 
  346. function takes an object of the following class as an argument and dispatches 
  347. the multi-threaded message handler by issuing a call to 
  348. (TheHandler->run)(*TheMessage,temp). 
  349.  
  350. struct HandlerThunkData {
  351.    WinEventHandler *TheHandler;
  352.    MessageStruct *TheMessage;
  353.  
  354.    HandlerThunkData(WinEventHandler * TH,MessageStruct * TM) {
  355.       TheHandler=TH;
  356.       TheMessage=TM;
  357.    }
  358. };
  359.  
  360. WindowInfo 
  361.  
  362. This class is used to relate the window handle to the corresponding C++ window 
  363. instance. 
  364.  
  365. struct WindowInfo {
  366.    HWND hwnd;
  367.    BaseWindow *THIS;
  368.  
  369.    WindowInfo(HWND hw, BaseWindow * BW) {
  370.       hwnd=hw;
  371.       THIS=BW;
  372.    }
  373. };
  374.  
  375. InitializationData 
  376.  
  377. Objects of this class are passed to the window procedures so that when the 
  378. WM_INITDLG or WM_CREATE message is received, the window event handler can be 
  379. registered enabling it to process these messages. 
  380.  
  381. struct InitializationData {
  382.    BaseWindow *BW;
  383.    WinEventHandler *Event;
  384.  
  385.    InitializationData(BaseWindow *B, WinEventHandler *E) {
  386.       BW=B;
  387.       Event=E;
  388.    }
  389. };
  390.  
  391.  
  392. ΓòÉΓòÉΓòÉ 4.1.4. Window Objects ΓòÉΓòÉΓòÉ
  393.  
  394. Because the common denominator between all type of windows in PM is the window 
  395. handle, we will begin by creating a generic window object which encapsulates 
  396. the translation of the window handle to the instance pointer of the C++ object. 
  397. This generalized class also stores the the window's handle. 
  398.  
  399. In PM, nearly every window consists of a frame window and the client window. 
  400. This is so common, in fact, that a special function - WinCreateStdWindow - 
  401. exists in PM to create the frame and client windows simultaneously.  However, a 
  402. frame window is merely an instance of the window class WC_FRAME allowing us to 
  403. create it seperately using the WinCreateWindow function.  Because of this, our 
  404. C++ objects can be separated into frame windows and client windows. 
  405.  
  406. The window hierarchy is based upon the object BaseWindow.  Since this seems to 
  407. be the logical place to start, we will first describe this class. 
  408.  
  409. BaseWindow 
  410.  
  411. class BaseWindow {
  412.    protected:
  413.       static PApplication App;        // Pointer to Primary window thread object
  414.       static ULONG NumWindows;        // Each instance has a handler
  415.       static ULONG MaxNumWindows;     // list
  416.       static WindowInfo **Instances;
  417.  
  418.       USHORT NumHandlers;
  419.       USHORT MaxNumHandlers;
  420.       WinEventHandler **Handlers;
  421.  
  422.       HWND hWndFrame;                 // Window handle
  423.       HWND hWndParent;
  424.  
  425.       PSZ pszClassName;               // Window-class name
  426.       ULONG flClassStyle;             // Default-window style
  427.       USHORT usExtra;                 // Reserved storage
  428.  
  429.       static MRESULT EXPENTRY _export PrimWndProc(HWND hWnd,
  430.                                                   ULONG iMessage,
  431.                                                   MPARAM mParam1,
  432.                                                   MPARAM mParam2);
  433.  
  434.       static void AddWindow(BaseWindow *win, HWND hand);
  435.       static void RemoveWindow(BaseWindow *win, HWND hand);
  436.  
  437.    public:
  438.       BaseWindow();
  439.       BaseWindow(PSZ ClassName, ULONG ClassStyle, USHORT Extra, HWND Parent);
  440.       BaseWindow(PSZ ClassName, HWND Parent=HWND_DESKTOP);
  441.  
  442.       virtual ~BaseWindow();
  443.  
  444.       void Show();
  445.       void Hide();
  446.       void Close();
  447.  
  448.       static void SetApplication(PApplication applic) {
  449.          App=applic;
  450.       }
  451.  
  452.       static PApplication GetApplication() { return App; }
  453.       void AddStyle(ULONG style) { flClassStyle|=style; }
  454.  
  455.       BOOL Register() {
  456.          return WinRegisterClass(App->GetHAB(),
  457.                                  pszClassName,
  458.                                  &PrimWndProc,
  459.                                  flClassStyle,
  460.                                  usExtra);
  461.       }
  462.  
  463.       HWND GetHandle() { return hWndFrame; }
  464.       HWND GetParentHandle() { return hWndParent; }
  465.  
  466.       void ChangeIcon(ULONG Icon);
  467.  
  468.       void AddHandler(WinEventHandler *Event);
  469.  
  470.       BOOL PostMessage(ULONG iMess, MPARAM mP1=0, MPARAM mP2=0) {
  471.          return WinPostMsg(hWndFrame,iMess,mP1,mP2);
  472.       }
  473.  
  474.       MRESULT SendMessage(ULONG iMess, MPARAM mP1=0, MPARAM mP2=0) {
  475.          return WinSendMsg(hWndFrame,iMess,mP1,mP2);
  476.       }
  477.  
  478.       //-------------------------------------------------------------------
  479.       // In CSET, a static member function cannot be used as a window
  480.       // procedure thus they are friends here.  But above, the static
  481.       // functions are used in BC4OS2
  482.       //-------------------------------------------------------------------
  483.       friend MRESULT EXPENTRY PrimWndProc(HWND hWnd,
  484.                                           ULONG iMessage,
  485.                                           MPARAM mParam1,
  486.                                           MPARAM mParam2);
  487.  
  488.       friend MRESULT EXPENTRY PrimDlgProc(HWND hWnd,
  489.                                           ULONG iMessage,
  490.                                           MPARAM mParam1,
  491.                                           MPARAM mParam2);
  492.  
  493.    friend WinEventHandler;
  494.    friend class ModalDlgBox;
  495. };
  496.  
  497. Now let's look at exact role this class fills.  The data objects and member 
  498. functions 
  499.  
  500. o NumWindows 
  501. o MaxNumWindows 
  502. o Instances 
  503. o PrimWndProc 
  504. o AddWindow 
  505. o RemoveWindow 
  506.  
  507. store and manipulate the list of created windows.  The window information is 
  508. stored in an array which is unsorted and traversed by the function PrimWndProc 
  509. each time a message is received.  When the handle is matched, the instance of 
  510. the window is known.  At this time, the list of registered window handlers is 
  511. traversed. 
  512.  
  513. The following data objects and functions are used to manipulate the list of 
  514. registered handlers: 
  515.  
  516. o NumHandlers 
  517. o MaxNumHandlers 
  518. o Handlers 
  519. o AddHandler 
  520.  
  521. In summary, the window instance is registered using the function AddWindow. 
  522. Message handlers are registered using the function AddHandler.  Each time a 
  523. message is received, the list of registered windows is traversed.  If the 
  524. window handle  is found, the list of handlers corresponding to the instance 
  525. found is traversed and each message handler is allowed to process the message 
  526. until one of them block further processing. 
  527.  
  528. ModalDlgBox 
  529.  
  530. The dialog window creation process can also be encapsulated.  The following 
  531. class encapsulates the process of creating a modal dialog window contained in 
  532. the application's resources.  A similar class exists for modeless dialog 
  533. windows.  Also, classes exist for both type of dialog windows with menus. 
  534.  
  535. The function PrimDlgProc is identical to the function PrimWndProc except that 
  536. this function calls the default dialog procedure for unprocessed messages and 
  537. looks for the WM_INITDLG message whereas the latter calls the default window 
  538. procedure and looks for the WM_CREATE message. 
  539.  
  540. class ModalDlgBox: public BaseWindow {
  541.    protected:
  542.       ULONG RetVal;
  543.  
  544.       static MRESULT EXPENTRY _export PrimDlgProc(HWND hWnd,
  545.                                                   ULONG iMessage,
  546.                                                   MPARAM mParam1,
  547.                                                   MPARAM mParam2);
  548.  
  549.    public:
  550.        ModalDlgBox(BaseWindow *Owner,
  551.                    ULONG ResID,
  552.                    HMODULE ResMod=NULLHANDLE,
  553.                    HWND Parent=HWND_DESKTOP);
  554.  
  555.        ModalDlgBox(BaseWindow * Owner,
  556.                    ULONG ResID,
  557.                    WinEventHandler *Event,
  558.                    HMODULE ResMod=NULLHANDLE,
  559.                    HWND Parent=HWND_DESKTOP);
  560.  
  561.        ModalDlgBox(HWND Owner,
  562.                    ULONG ResID,
  563.                    HMODULE ResMod=NULLHANDLE,
  564.                    HWND Parent=HWND_DESKTOP);
  565.  
  566.        ModalDlgBox(HWND Owner,
  567.                    ULONG ResID,
  568.                    WinEventHandler *Event,
  569.                    HMODULE ResMod=NULLHANDLE,
  570.                    HWND Parent=HWND_DESKTOP);
  571.  
  572.        ULONG GetReturn() { return RetVal; }
  573.        BOOL Dismiss(ULONG x){ return WinDismissDlg(hWndFrame,x); }
  574.  
  575.        HWND GetControlID(ULONG ID) {
  576.            return WinWindowFromID(hWndFrame,ID);
  577.        }
  578.  
  579.    friend class ModlessDlgBox;
  580. };
  581.  
  582. FrameWindow 
  583.  
  584. Following is a generic frame window class.  This class is usable in this form 
  585. but one still has to provide the various control flags and such as paramters. 
  586. It would be a trivial task to derive subclasses that specify the various frame 
  587. creation options to this object without requiring them to be passed to their 
  588. constructors as paramters. 
  589.  
  590. The frame window (PM class WC_FRAME) is created by this object using the 
  591. WinCreateWindow function. 
  592.  
  593. class FrameWindow: public BaseWindow {
  594.    FRAMECDATA fcdata;
  595.    ULONG flFrameControlFlags;
  596.    PSZ Title;
  597.  
  598.    public:
  599.       FrameWindow();
  600.       FrameWindow(PSZ pTitle,
  601.                   ULONG ControlFlags,
  602.                   HMODULE ResMod,
  603.                   ULONG ResID,
  604.                   HWND ZOrder=HWND_TOP,
  605.                   ULONG IDNum=1,
  606.                   HWND Parent=HWND_DESKTOP);
  607.       FrameWindow(PSZ pTitle,
  608.                   ULONG ControlFlags,
  609.                   HMODULE ResMod,
  610.                   ULONG ResID,
  611.                   LONG XPos,
  612.                   LONG YPos,
  613.                   LONG Height,
  614.                   ULONG Width,
  615.                   HWND ZOrder=HWND_TOP,
  616.                   ULONG IDNum=1,
  617.                   HWND Parent=HWND_DESKTOP);
  618.  
  619.       ULONG GetFlags() { return flFrameControlFlags; }
  620. };
  621.  
  622. ClientWindow 
  623.  
  624. This object serves as the base from which client windows are derived.  This 
  625. object simply encapsulates the data and method of interacting with the object 
  626. FrameWindow.  Note a bug in BC4OS2's virtual function tables prevents this 
  627. object to work properly.  A work around is done in this code but it will be 
  628. changed once I get the product-level C/Set++. 
  629.  
  630. class ClientWindow :public BaseWindow {
  631.    FrameWindow *ParentWin;
  632.  
  633.    public:
  634.       static const char NoCreateWin;
  635.       static const char CreateWin;
  636.  
  637.       ClientWindow(FrameWindow* Parent,
  638.                    PSZ ClassName,
  639.                    ULONG ClassStyle,
  640.                    USHORT Extra,
  641.                    char Create=CreateWin);
  642.  
  643.       /* BUG in BC virtual table (corrupt)...trying the following...
  644.       virtual BOOL IsRegistered()=0;
  645.       virtual void SetRegistered()=0;
  646.       */
  647.  
  648.       virtual BOOL IsRegistered() { return 0; }
  649.       virtual void SetRegistered() {};
  650. };
  651.  
  652. Example Client Window Subclass 
  653.  
  654. Following is a simple client window that illustrates the prodecure of creating 
  655. new client window types.  The client object must keep track of whether it has 
  656. been previously registered to PM via WinRegisterClass so that it is not 
  657. re-registered. 
  658.  
  659. class SimpClient: public ClientWindow {
  660.    static BOOL Registered;
  661.    WinEventHandler *Event;
  662.  
  663.    public:
  664.       SimpClient(FrameWindow *Frm);
  665.       ~SimpClient();
  666.  
  667.       virtual BOOL IsRegistered() { return Registered; }
  668.       virtual void SetRegistered() { Registered=1; }
  669.  
  670.       MRESULT MessageHandler(WinEventHandler &hand,
  671.                              MessageStruct &Msg,
  672.                              char &Action);
  673. };
  674.  
  675. The static data member Registered is used to keep track of whether it has been 
  676. previously registered or not.  The data member Event is intialized in the 
  677. constructor with : 
  678.  
  679. Event=new WinSameThreadHandler<SimpClient>(&SimpClient::MessageHandler);
  680.  
  681. The function MessageHandler is the message handler for this object. 
  682.  
  683.  
  684. ΓòÉΓòÉΓòÉ 4.2. Development of a New Window Class - Part 1 ΓòÉΓòÉΓòÉ
  685.  
  686.                           Written by Larry Salomon, Jr.
  687.  
  688.  
  689. ΓòÉΓòÉΓòÉ 4.2.1. Introduction ΓòÉΓòÉΓòÉ
  690.  
  691. This article begins a multi-part series which will ultimately result in the 
  692. development of a new window class.  Everything from the functional requirements 
  693. to the design to the implementation will be described, giving you a good look 
  694. at how it is developed from the ground up; in the end, we'll have a completed 
  695. window class with a variety of uses. 
  696.  
  697. Inspiration 
  698.  
  699. Everyone can remember back to their days in middle school when three-ring 
  700. binders were all-the-rage and your parents purchased many packs of paper to 
  701. fill them.  The holes in the paper and the rings in the binders combined to 
  702. allow for easy transfer to other notebooks or for inscription on the paper 
  703. prior to storage in a binder. 
  704.  
  705. Now we're in the computer age, and paper is quickly becoming a thing of the 
  706. past.  However, direct translations of computer concepts to the "real world" is 
  707. now the thing to do; so, wouldn't it be nice to have a piece of loose-leaf on 
  708. your monitor to scratch notes on? 
  709.  
  710.  
  711. ΓòÉΓòÉΓòÉ 4.2.2. Functional Requirements ΓòÉΓòÉΓòÉ
  712.  
  713. Before we can do anything, we need to first define what our goals are going to 
  714. be.  What will our control do from the user's perspective?  What should it look 
  715. like?  What capabilities will it have from a programming standpoint?  These are 
  716. not easy questions to answer, nor do they have answers that are correct. 
  717. Because the nature of these questions is subjective, the best answers are going 
  718. to be the ones that address the issues of those people that are most affected. 
  719.  
  720. What will our control do from the user's perspective? 
  721.  
  722. Since the term "loose leaf" conjures up specific expectations about its 
  723. capabilities, we should try to adhere to those expectations: it should allow 
  724. the user to type text anywhere in the paper body and it should allow for a 
  725. title to be placed in the margin at the top. 
  726.  
  727. Additionally, good integration with the Workplace Shell is a necessity, so it 
  728. should respond appropriately if the user drops a new font or color from the 
  729. corresponding palette on the control. 
  730.  
  731. What should it look like? 
  732.  
  733. Similarly, it should look like a piece of loose leaf paper to minimize the 
  734. "state shock" that a user might encounter when using the control. 
  735.  
  736. What capabilities will it have from a programming standpoint? 
  737.  
  738. This is often the most difficult to answer, although placing yourself in the 
  739. role of a programmer using the control often helps.  What capabilities would I 
  740. want, were I to use this control?  Personally, I would like to see the 
  741. following "programmabilities": 
  742.  
  743. o The ability to query the contents of any line, as well as set the contents of 
  744.   any line. 
  745.  
  746. o The ability to query the text in the control, with each line ending with a 
  747.   "newline" character. 
  748.  
  749. o The ability to query what line the cursor is currently on as well as set the 
  750.   current line. 
  751.  
  752. o The ability to have the holes on the left or right side of the paper as well 
  753.   as have no holes at all.  The ability to query where the holes are on the 
  754.   paper would be nice also, should I choose to implement a binder to contain 
  755.   the pages within. 
  756.  
  757. Notifications are also quite important, so it is a must to be notified 
  758. whenever: 
  759.  
  760. o The cursor moves up or down 
  761. o Any mouse button is clicked or double-clicked 
  762. o The system-defined sequence for displaying the context menu is pressed 
  763. o Help is requested 
  764.  
  765. Now we have a basis on which to design the control.  Whether or not this list 
  766. of requirements is complete or even doable remains to be seen and the list 
  767. could be revised one or more times before it arrives at its final state. 
  768.  
  769.  
  770. ΓòÉΓòÉΓòÉ 4.2.3. Design ΓòÉΓòÉΓòÉ
  771.  
  772. It is often easiest to develop new code by repeatedly breaking the problem to 
  773. be solved into smaller pieces until you arrive at a level where each piece can 
  774. be coded and tested independently - or with little dependence - on the other 
  775. pieces of the code.  (C++ programmers will recognize this as being one of the 
  776. inherent advantages of the language, since in the purist form of the language 
  777. you are forced to do this as a preprocessing step.)  In a related manner, 
  778. writing a new window class is often easiest if you can break down the abstract 
  779. view of the control into distinct components that are independent of each 
  780. other. 
  781.  
  782. The paper control can be viewed as a collection of lines that can be written 
  783. on, as well as a side margin and a top margin.  Additionally, the holes (if 
  784. any) are placed in the side margin.  Since the user can type in only the top 
  785. margin and/or the lines, the remainder of the control is reduced to a little 
  786. more than proper painting of the components.  Even so, the handling of the 
  787. typing can be a major chore if we choose to process the keystrokes ourselves. 
  788.  
  789. Figure 1.  Different parts of the paper control 
  790.  
  791. Fortunately, we don't have to.  Borrowing the idea from the container control 
  792. (my apologies to everyone if this was done previously in another control), we 
  793. can create a single-line entryfield and by giving it in the proper position and 
  794. size, it will behave exactly as though we processed each and every keystroke. 
  795. But where will we store the handle to the entryfield? The answer lies in 
  796. something that has existed since version 1.1 called... 
  797.  
  798. Window Words 
  799.  
  800. In C, there are provisions for declaring variables with 3 scopes of visibility: 
  801. local, static, and global.  Given the high possibility of having more than one 
  802. window of a particular class existing simultaneously, the latter two are 
  803. unsatisfactory for storing data since the data is not unique to a specific 
  804. window; while local scope does provide for data unique to a window (since it is 
  805. provided for on the stack of the thread running), it is also insufficient, 
  806. since the data is not retained once the procedure exits. The PM developers saw 
  807. these limitations and provided an answer in the form of window words. 
  808.  
  809. Window words are 1 or more bytes of storage that is reserved for all windows 
  810. belonging to a specific class (called instances of that class).  The actual 
  811. number of bytes to be reserved is specified in the WinRegisterClass call and 
  812. the window words can be set/queried using the functions WinSetWindowPtr and 
  813. WinQueryWindowPtr. WinSetWindowULong and WinQueryWindowULong can also be used 
  814. with the QWL_USER parameter.  All of the standard PM controls reserve 8 bytes 
  815. of window words; bytes 4-7 are used to store a pointer to a private data 
  816. structure and bytes 0-3 are available for application use.  We, too, will adopt 
  817. this strategy; the memory will be allocated in the WM_CREATE message and will 
  818. be released to the system in the WM_DESTROY message.  The structure that this 
  819. memory will be used for can be gleaned with a little thought. 
  820.  
  821. typedef struct _PAPERINSTDATA {
  822.    USHORT usSzStruct;
  823.    HWND hwndOwner;
  824.    HAB habAnchor;
  825.    HWND hwndText;
  826.    PCHAR pchTitle;
  827.    SHORT sMaxLines;
  828.    SHORT sLine;
  829.    CHAR aachLines[256][256];
  830.    FONTMETRICS fmFont;
  831.    LONG lForeClr;
  832.    LONG lBackClr;
  833. } PAPERINSTDATA, *PPAPERINSTDATA;
  834.  
  835. The fields in the structure are explained below: 
  836.  
  837. usSzStruct          Contains the size of the structure.  This is needed because 
  838.                     PM performs a lot of thunking when calling its 16-bit code 
  839.                     from a 32-bit application and it needs to know how large of 
  840.                     an area to convert to a 16-bit segment. 
  841.  
  842. hwndOwner           Contains the handle of the owner window for notification 
  843.                     purposes. 
  844.  
  845. habAnchor           Contains the anchor block of the window. 
  846.  
  847. hwndText            Contains the handle of the entryfield window that will be 
  848.                     used for input. 
  849.  
  850. pchTitle            Points to the title text of the window, as set by the 
  851.                     WinSetWindowText function (and on the call to 
  852.                     WinCreateWindow). 
  853.  
  854. sMaxLines           Contains the maximum number of lines that are displayed. 
  855.                     The top line below the margin is the 0th line. 
  856.  
  857. sLine               Contains the current line number. 
  858.  
  859. aachLines           Contains the text of the control.  By defining it in this 
  860.                     fashion, we are defining a limitation of 256 lines and 255 
  861.                     characters per line (plus 1 for the terminating `\0'. 
  862.  
  863. fmFont              Contains information about the font currently being used. 
  864.  
  865. lForeClr            Specifies the "foreground" color, which is used to draw the 
  866.                     title text and that of the entryfield. 
  867.  
  868. lBackClr            Specifies the "background" color, which is used to draw the 
  869.                     text that has already been entered. 
  870.  
  871. Breaking Down the Walls 
  872.  
  873. As stated earlier, the implementation would be easier if we could divide the 
  874. control's functionality into distinct parts and tackle each individually. 
  875. Doing so yields the following: 
  876.  
  877. Painting            This part draws the control on the screen, and can be 
  878.                     considered the most important part from a user's 
  879.                     standpoint, since first impressions are everything. 
  880.  
  881. User input          This part handles the typing (including arrow keys, should 
  882.                     we choose to process them) and mouse actions. 
  883.  
  884. Owner notifications This part handles the notification of the control's owner 
  885.                     of significant events. 
  886.  
  887. Presentation parameters This part handles the presentation parameters, i.e. 
  888.                     dynamically alterable characters of the control; foreground 
  889.                     and background color and the desired font are the most 
  890.                     widely used of these and can be modified using the "Color 
  891.                     Palette" and "Font Palette" that are provided with the 
  892.                     system. 
  893.  
  894.  
  895. ΓòÉΓòÉΓòÉ 4.2.4. Additional Functions ΓòÉΓòÉΓòÉ
  896.  
  897. Two important additional functions are the initialization and termination of 
  898. the paper window class.  The initialization function allows us to register the 
  899. window class; the termination function will not be used in our implementation, 
  900. although it should still exist to create a "balanced" function set.  These two 
  901. functions are the only two that should be exposed to the programmer. 
  902.  
  903. BOOL PprInitialize(HAB habAnchor)
  904. {
  905.    WinRegisterClass(habAnchor,
  906.                     WC_PAPERPAGE,
  907.                     pprWndProc,
  908.                     CS_SIZEREDRAW|CS_CLIPSIBLINGS|CS_PARENTCLIP,
  909.                     sizeof(PVOID)*2);
  910.    return TRUE;
  911. }
  912.  
  913. BOOL PprTerminate(HAB habAnchor)
  914. {
  915.    return TRUE;
  916. }
  917.  
  918.  
  919. ΓòÉΓòÉΓòÉ 4.2.5. Summary ΓòÉΓòÉΓòÉ
  920.  
  921. We have seen how a little forethought can lead to a better design; by 
  922. attempting to anticipate the needs of those who will use our control, we can 
  923. shape the design of the control better and fill in more of the blanks that will 
  924. become apparent once the control is actually used.  Additionally, we have 
  925. learned about window words and how they can be used to store data that is 
  926. specific to a particular instance of a window class. 
  927.  
  928. Coming up in next month's article, we will wrap up the design of the control 
  929. and begin the implementation of the window procedure. 
  930.  
  931.  
  932. ΓòÉΓòÉΓòÉ 4.3. Programming the Container Control - Part 2 ΓòÉΓòÉΓòÉ
  933.  
  934.                           Written by Larry Salomon, Jr.
  935.  
  936.  
  937. ΓòÉΓòÉΓòÉ 4.3.1. Where were we? ΓòÉΓòÉΓòÉ
  938.  
  939. Last month I briefly introduced (in that oh-so-poor writing style that makes me 
  940. unique) the container control and some of the programming basics that everyone 
  941. should know.  This month, we will continue this trek into the unknown by 
  942. describing the tree view as well as some other nifty things.  Also, we will 
  943. write our first application using the container, on which we will build in next 
  944. month's installment. 
  945.  
  946. From Another Viewpoint 
  947.  
  948. As a quick note, the name and text views were omitted last month because they 
  949. are - from a programmer's perspective - identical to the icon view. 
  950.  
  951.  
  952. ΓòÉΓòÉΓòÉ 4.3.2. The Wood Nymph's Delight ΓòÉΓòÉΓòÉ
  953.  
  954. The tree view should be a highly familiar one; it parallels nicely with the 
  955. directory structure of a hard disk and has been used in the File Manager (from 
  956. OS/2 1.x) and the Help Manager as well as many other applications.  It also has 
  957. three subviews that can be specified in the CM_SETCNRINFO message:  tree icon 
  958. view, tree name view, and tree text view (these are specified by "or-ing" the 
  959. CV_ICON, CV_NAME, or CV_TEXT flags with the CV_TREE flag in the flWindowAttr 
  960. field, respectively).  The difference between these views is in the way the 
  961. data is represented:  icon view displays an icon with the text to the right and 
  962. to the left is a separate bitmap indicating whether the item is expanded or 
  963. collapsed (if applicable); name view is the same as icon view except that the 
  964. expanded/collapsed indicator is indicated in the icon; the text view provides 
  965. only the text string with the expanded/collapsed indicator as a separate bitmap 
  966. to the left. 
  967.  
  968. Note the difference between icon and name view.  Remember when, in the 
  969. beginning of the series, it was mentioned that there are a few differences 
  970. between using the MINIRECORDCORE and RECORDCORE structure?  This is one of 
  971. those differences; since only the RECORDCORE structure has different fields for 
  972. the expanded and collapsed icons, the name view cannot be used with the 
  973. CCS_MINIRECORDCORE style. 
  974.  
  975. When inserting records into the container, the parent record is specified in 
  976. the pRecordParent field of the RECORDINSERT structure.  In the icon view, we 
  977. specified this as NULL, since there is not parent/child relationships in that 
  978. view.  However, in the tree views, this is particularly important because it 
  979. specifies how the records are to be displayed.  All records that have children 
  980. will be displayed by adding an expanded/collapsed indicator to its left.  If, 
  981. by some action of the user, you switch to any other non-tree view, all but the 
  982. top-level records are hidden. 
  983.  
  984. Notifications 
  985.  
  986. In addition to the "normal" notifications, the container provides 2 
  987. notifications specific to the tree view: 
  988.  
  989. CN_COLLAPSETREE     This is sent to the owner after the container collapses a 
  990.                     subtree. mpParm2 points to the record that was collapsed. 
  991.  
  992. CN_EXPANDTREE       This is sent to the owner after the container expands a 
  993.                     subtree. mpParm2 points to the record that was expanded. 
  994.  
  995.  
  996. ΓòÉΓòÉΓòÉ 4.3.3. CNR2 - A Sample Application ΓòÉΓòÉΓòÉ
  997.  
  998. Now let us delve into the depths of our first sample application; it not only 
  999. showcases the container control, but it also contains some nifty tricks that I 
  1000. think you'll find useful.  The structure is that of a "typical" PM application 
  1001. and it should not be new to you.  In the code, I have placed several 
  1002. "landmarks"; these are marked by placing a comment of the form "@n" (where `n' 
  1003. is the landmark number) starting at column 55.  These landmarks are used to 
  1004. point out things of interest and are discussed in more detail later. 
  1005.  
  1006. The application does nothing more than add a record for each month of each year 
  1007. for the number of years specified in NUM_YEARS.  This is to show how the tree 
  1008. view is used.  A popup menu is displayed whenever the system key/mouse sequence 
  1009. is pressed, allowing to you switch between icon and tree views, etc. 
  1010.  
  1011. Landmark 1 
  1012.  
  1013. This is simply to point out the typedefs used. 
  1014.  
  1015. typedef struct _CLIENTDATA {                          // @1
  1016.    USHORT usSzStruct;         // Size of the structure
  1017.    HAB habAnchor;             // Anchor block of the window
  1018.    HWND hwndFrame;            // Frame of the client (== parent)
  1019.    HWND hwndCnr;              // Container window
  1020.    HPOINTER hptrYear;         // Icon for the year records
  1021.    HPOINTER hptrMonth;        // Icon for the month records
  1022.    HWND hwndWndMenu;          // Menu window
  1023. } CLIENTDATA, *PCLIENTDATA;
  1024.  
  1025. typedef struct _MYCNRREC {
  1026.    MINIRECORDCORE mrcCore;    // Base structure
  1027.    CHAR achText[64];          // Icon text
  1028. } MYCNRREC, *PMYCNRREC;
  1029.  
  1030. //-------------------------------------------------------------------------
  1031. // PFNSRCH is used by searchCnr().  The parameters are:  container window
  1032. // handle, record pointer, and user-data pointer, respectively.
  1033. //-------------------------------------------------------------------------
  1034. typedef BOOL (*PFNSRCH)(HWND,PVOID,PVOID);
  1035.  
  1036. CLIENTDATA is the instance data for the client window.  Granted, we could have 
  1037. used global variables, but that is poor programming practice so we avoid doing 
  1038. this (the only global we use is an constant array of pointers to the names of 
  1039. the months). 
  1040.  
  1041. MYCNRREC is the definition for the container records.  Note that we are using 
  1042. the MINIRECORDCORE structure. 
  1043.  
  1044. PFNSRCH is a pointer to a function accepting three parameters and returning a 
  1045. boolean.  It is used in a very interesting way that you'll see later. 
  1046.  
  1047. Landmark 2 
  1048.  
  1049. This is only to point out that while it is highly recommended that you allocate 
  1050. and insert as many records as possible each time sometimes it simply isn't 
  1051. possible.  We have to allocate/insert each year separately followed by the 
  1052. twelve months. 
  1053.  
  1054. Landmark 3 
  1055.  
  1056. Since we specified FALSE in the fInvalidateRecord field of the RECORDINSERT 
  1057. structure, we have to send a CM_INVALIDATERECORD message to update the 
  1058. container. 
  1059.  
  1060. Landmark 4 
  1061.  
  1062. This entire procedure is useful, since (as described in a previous issue of the 
  1063. magazine) you cannot specify MIS_CONDITIONALCASCADE in a resource file.  It 
  1064. should also be noted that since the cascade button takes up a bit more space, 
  1065. it is helpful to add a few blank spaces in the menu template to account for 
  1066. this.  As a general rule, I use three blanks. 
  1067.  
  1068. Landmark 5 
  1069.  
  1070. Here is another useful procedure; it performs a recursive, post-traversal 
  1071. search of the container, calling a user-specified procedure at each record to 
  1072. see if it matches the criteria also specified by the caller.  If you've ever 
  1073. wondered what a use for reserved parameter is, here is one.  We use the 
  1074. reserved parameter to specify the record we are currently checking; by having 
  1075. the user specify NULL, we can check to see if this is the initial call. 
  1076.  
  1077. Of course, we could have eliminated the reserved parameter and had this 
  1078. procedure call another procedure with a reserved parameter to remove the burden 
  1079. from the caller, but that's too easy.  (* grin *) 
  1080.  
  1081. Landmark 6 
  1082.  
  1083. This is to point out the allocation and initialization of the client instance 
  1084. data.  Note that the cleanup is done in the WM_DESTROY processing. 
  1085.  
  1086. Landmark 7 
  1087.  
  1088. This is to show a bug in the container - if the user uses the mouse to invoke 
  1089. the popup menu, the container sends us a WM_CONTROL message. Not so if the user 
  1090. uses the keyboard, thus we duplicate the code and check for the WM_CONTEXTMENU 
  1091. message. 
  1092.  
  1093. Landmark 8 
  1094.  
  1095. Finally, we invoke the searchCnr() function specifying a pointer to a boolean 
  1096. that contains the desired select state.  The searchSelect() function is an 
  1097. interesting one. 
  1098.  
  1099. BOOL searchSelect(HWND hwndCnr,PMYCNRREC pmcrRecord,PBOOL pbSelect)
  1100. //-------------------------------------------------------------------------
  1101. // This function is used to select/deselect all records.  Note that it
  1102. // always returns FALSE, so that searchCnr() will traverse the entire
  1103. // record list.
  1104. //
  1105. // Input:  hwndCnr - handle of the container window
  1106. //         pmcrRecord - pointer to the container record
  1107. //         pbSelect - pointer to a BOOL specifying whether to select the
  1108. //                    record or not
  1109. //-------------------------------------------------------------------------
  1110. {
  1111.    WinSendMsg(hwndCnr,
  1112.               CM_SETRECORDEMPHASIS,
  1113.               MPFROMP(pmcrRecord),
  1114.               MPFROM2SHORT(*pbSelect,CRA_SELECTED));
  1115.    return FALSE;
  1116. }
  1117.  
  1118. See how it simply sets the record's select state and always returns FALSE - 
  1119. indicating that this record does not match - forcing searchCnr() to traverse 
  1120. the entire container. 
  1121.  
  1122. That's It! 
  1123.  
  1124. That is all there is to it!  Note that while there is a lot of setup involved, 
  1125. the container is no more difficult to use than any of the other "standard" 
  1126. controls. 
  1127.  
  1128.  
  1129. ΓòÉΓòÉΓòÉ 4.3.4. Summary ΓòÉΓòÉΓòÉ
  1130.  
  1131. We have seen how tree views can be used for hierarchical structures and how 
  1132. they are not much different that the icon, name, and text views described last 
  1133. month.  We have used these concepts and introduced new ones in a sample 
  1134. application which will be used as a scaffold to add new features in future 
  1135. installments of this series. 
  1136.  
  1137. Next month we will describe the details view, selection states, direct editing, 
  1138. and possibly other ideas and will incorporate these concepts into our sample 
  1139. application. 
  1140.  
  1141.  
  1142. ΓòÉΓòÉΓòÉ 5. Columns ΓòÉΓòÉΓòÉ
  1143.  
  1144. The following columns can be found in this issue: 
  1145.  
  1146. o Questions and Answers 
  1147.  
  1148.  
  1149. ΓòÉΓòÉΓòÉ 5.1. Questions and Answers ΓòÉΓòÉΓòÉ
  1150.  
  1151. Welcome to this month's "Questions and Answers"!  Each month, I collect various 
  1152. questions sent to me via email and try to answer each directly; the ones that I 
  1153. feel contribute the most to developers, whether in terms of information or as a 
  1154. nifty trick to tuck into your cap, get published in this column (being able to 
  1155. answer the questions also influences my decision *grin*). 
  1156.  
  1157. To submit a question, send mail to my email address - os2man@panix.com - and be 
  1158. sure to grant permission to publish your question (those that forget will not 
  1159. be considered for publication). 
  1160.  
  1161. This month's deluge of questions covered the following topics: 
  1162.  
  1163. o Dynamically adding and removing the min/max/size buttons? 
  1164. o Disabling hard error popups? 
  1165. o Online help for dialogs 
  1166. o Spawing a batch file 
  1167. o Deleting profile data 
  1168. o A Q about Notebook controls 
  1169.  
  1170.  
  1171. ΓòÉΓòÉΓòÉ 5.1.1. Dynamically adding and removing the min/max/size buttons? ΓòÉΓòÉΓòÉ
  1172.  
  1173. David John Marotta (djm5g@virginia.edu) writes: 
  1174.  
  1175. I am trying to dynamically remove and add the min/max/size buttons and menu 
  1176. selections.  I am having trouble doing this especially with the buttons. I 
  1177. can't determine which Win calls to use.  Can someone help me?  Thanks. 
  1178.  
  1179. David, what you need to do is create or destroy the appropriate windows with 
  1180. the id's corresponding to the frame controls you wish to add or delete and send 
  1181. the frame a WM_UPDATEFRAME message, specifying the FCF_ flags that have changed 
  1182. in mpParm1. 
  1183.  
  1184.  
  1185. ΓòÉΓòÉΓòÉ 5.1.2. Disabling hard error popups? ΓòÉΓòÉΓòÉ
  1186.  
  1187. Christopher Fernandes (fernand@slinky.cs.nyu.edu) writes: 
  1188.  
  1189. I want to let users add files to a list using an add dialog.  Since I could 
  1190. only find canned "open"  and "save as"  dialogs in the toolkit, I modified code 
  1191. from a freeware program (earlier version of OS/2 I believe) that works very 
  1192. well except that when the user selects a floppy drive that does not have a 
  1193. floppy in it, and I DosSetDefaultDrive to it I get that huge screen.  Is there 
  1194. a call that is essentially a DosSetDefaultDrive that quietly returns an error 
  1195. code if there is no diskette? 
  1196.  
  1197. The solution is to use the DosError function, specifying FERR_DISABLEHARDERR | 
  1198. FERR_DISABLEEXCEPTION as its argument. 
  1199.  
  1200.  
  1201. ΓòÉΓòÉΓòÉ 5.1.3. Online help for dialogs ΓòÉΓòÉΓòÉ
  1202.  
  1203. Christopher Fernandes (fernand@slinky.cs.nyu.edu) has another question: 
  1204.  
  1205. I don't have a main screen, just directly go into a dialog.  I have a help push 
  1206. button that explains the program.  The help text can be viewed using the 
  1207. VIEW.EXE program, so I know that I can spawn a "view file.inf" when the help 
  1208. push button is pushed.  But I want to do it in a more standard way, using 
  1209. WinCreateHelpInstance and WinAssociateHelpInstance so that when the help push 
  1210. button is pushed in different child dialogs the user directly goes to the 
  1211. appropriate help topic.  However, all the examples that I've seen use 
  1212. HELPTABLES.  I don't want that since I don't even have a main window.  Would it 
  1213. be possible to get some code that did this? 
  1214.  
  1215. Nowhere does it state that you have to have a "main" window in order to use 
  1216. HELPTABLE's.  Since WinAssociateHelpInstance requires a frame window handle, 
  1217. with the following knowledge you can accomplish what you desire: 
  1218.  
  1219. o A dialog is a subclassed frame window 
  1220.  
  1221. o WinDlgBox is equivalent to the three calls WinLoadDlg, WinProcessDlg, 
  1222.   WinDestroyWindow 
  1223.  
  1224. Thus, if you call those three calls instead of WinDlgBox, you can insert a call 
  1225. to WinAssociateHelpInstance after the call to WinLoadDlg, allowing you to use 
  1226. HELPTABLE's as you would in any other program. 
  1227.  
  1228.  
  1229. ΓòÉΓòÉΓòÉ 5.1.4. Spawing a batch file ΓòÉΓòÉΓòÉ
  1230.  
  1231. Christopher Fernandes (fernand@slinky.cs.nyu.edu) has yet another question: 
  1232.  
  1233. My app generates a text file or optionally a Postscript file and spawns a 
  1234. lister/editor on these files if requested.  I also want to allow printing of 
  1235. these files from within the program.  I want to let the user specify a batch 
  1236. file, and then call that batch file with the file as the argument.  However i'm 
  1237. getting an error of bad EXE type, so my question is could I have the line of 
  1238. code that spawns a batch file. 
  1239.  
  1240. Try spawning CMD.EXE with the arguments "/K batch_file arguments". 
  1241.  
  1242.  
  1243. ΓòÉΓòÉΓòÉ 5.1.5. Deleting profile data ΓòÉΓòÉΓòÉ
  1244.  
  1245. Christopher Fernandes (fernand@slinky.cs.nyu.edu) can't stop himself: 
  1246.  
  1247. I save data across program invocations using the profile functions on a 
  1248. program-specific INI file.  That works fine, but I can't find calls to delete 
  1249. data or to compact the file.  Do the profile functions automatically compact if 
  1250. I write a zero-length object to that setting. 
  1251.  
  1252. If you call PrfWriteProfileData (or any of the other profile writing functions) 
  1253. specifying NULL for the pointer to the data, the data in the INI file is 
  1254. erased.  Unfortunately, there is no explicit way to compact the file from a API 
  1255. viewpoint. 
  1256.  
  1257. As a side note, it is possible to create a new copy of an INI file that is 
  1258. compacted.  You simply enumerate all of the applications and keys, query the 
  1259. data in the original INI file, and write the data to the new INI file. 
  1260. Unfortunately, you cannot copy the new version of the file over the original 
  1261. unless no application is currently using it - for OS2.INI and OS2SYS.INI, this 
  1262. is impossible unless you boot from your installation disks. 
  1263.  
  1264.  
  1265. ΓòÉΓòÉΓòÉ 5.1.6. A Q about Notebook controls ΓòÉΓòÉΓòÉ
  1266.  
  1267. Gordon W. Zeglinski (zeglins@ccu.umanitoba.ca) writes: 
  1268.  
  1269. I've read through all of IBM's docs on Notebook controls and I cannot find out 
  1270. how to determine if the page is being turned to via a "tab" or the directional 
  1271. arrows. 
  1272.  
  1273. It would seem that there is a way to do this because Borland seems to do this 
  1274. in their C++ compiler for OS/2.  The reason this is useful, is that it would 
  1275. make using "dummy" section seperator pages much easier to implement. 
  1276.  
  1277. I guess the question is how can one tell if the tab or arrow sections of a 
  1278. notebook control have brought the window to the top?  Alternatively, How does 
  1279. Borland do it? 
  1280.  
  1281. One last question... 
  1282.  
  1283. I've noticed that double clicking the top tab, would occasionally cause the top 
  1284. page to go blank.  I have set the control up to take care of all the redrawing 
  1285. automatically.  Is this a know bug?  or am I doing something wrong? 
  1286.  
  1287. There is no exposed way to determine if the directional arrows were used or 
  1288. not, but if you assume that the arrows always have the same id (a safe 
  1289. assumption in this case because there are always at most 1 set of these per 
  1290. notebook), then you can use WinWindowFromID to get the window handle of the 
  1291. arrows and then subclass them instead. 
  1292.  
  1293. In reference to your last question, I must say that I've never noticed this 
  1294. behavior in my applications that use notebooks, nor does the desktop suffer 
  1295. from this symptom, so it would appear that you have done something incorrectly. 
  1296.  
  1297.  
  1298. ΓòÉΓòÉΓòÉ 6. Future Attractions ΓòÉΓòÉΓòÉ
  1299.  
  1300. In the next issue, we hope to return to your regularly scheduled programming, 
  1301. meaning a diverse set of authors presenting a broad set of topics.  This is, 
  1302. after all, a programmer's magazine, and no one person knows everything about 
  1303. development for OS/2. 
  1304.  
  1305. Topics that we are interested in receiving (or even writing) articles on are 
  1306. "Online Documentation using IPF" and "Workplace Application Development".  The 
  1307. former is obviously of greater interest to me since that will mean authors can 
  1308. no longer submit articles in ASCII flat files using the excuse that they don't 
  1309. know IPF.  (*grin*) 
  1310.  
  1311. A plug is also needed for a new column, which we mentioned in the May/June 
  1312. issue.  The column's objective is to review any one OS/2-development related 
  1313. "product" per month.  The word "product" is used loosely, because freeware and 
  1314. shareware are also included in the scope of this column (and are preferred, 
  1315. since they are usually cheaper than their commercial counterparts).  While it 
  1316. would be ideal to have a single columnist for this, we realize that there 
  1317. probably isn't anyone who has nothing better to do but review applications, so 
  1318. we consider this to be an open-author column.  If you would like to review a 
  1319. product, keep reading. 
  1320.  
  1321. As always, we are always looking for (new) authors.  If you have a topic about 
  1322. which you would like to write, send a brief description of the topic 
  1323. electronically to any of the editors, whose addresses are listed below, by the 
  1324. 15th of the month in which your article will appear.  This alerts us that you 
  1325. will be sending an article so that we can plan the issue layout accordingly. 
  1326. Ideally, we will respond by sending you a copy of our Article Submission 
  1327. Guidelines (ideally, that is, because the guidelines are not completed yet.). 
  1328. The completed text of your article should be sent to us no later than the last 
  1329. day of the month; any articles received after that time may be pushed to the 
  1330. next issue. 
  1331.  
  1332. The editor's can be reached at the following email addresses: 
  1333.  
  1334. o Steve Luzynski - sal8@po.cwru.edu (Internet), 72677,2140 (Compuserve). 
  1335. o Larry Salomon - os2man@panix.com (Internet). 
  1336.  
  1337.  
  1338. ΓòÉΓòÉΓòÉ 7. Contributors to this issue ΓòÉΓòÉΓòÉ
  1339.  
  1340. The following people contributed to this issue in one form or another (in 
  1341. alphabetical order): 
  1342.  
  1343. o Steve Luzynski 
  1344. o Larry Salomon, Jr. 
  1345. o Gordon Zeglinski 
  1346.  
  1347.  
  1348. ΓòÉΓòÉΓòÉ 7.1. Steve Luzynski ΓòÉΓòÉΓòÉ
  1349.  
  1350. Steve Luzynski is the editor and creator of this magazine.  He is currently a 
  1351. Computer Engineering student at Case Western Reserve University in Cleveland, 
  1352. Ohio, where he spends a lot of time being cold.  Steve has released several 
  1353. small programs for IBM's TCP/IP using Pat Muellers rxSock library for REXX.  He 
  1354. is currently working on a new mail program that he hopes to have finished 
  1355. before IBM beats him to the punch with TCP/IP 32bit. 
  1356.  
  1357. Steve can by reached via e-mail at sal8@po.cwru.edu or on Compuserve at 
  1358. 72677,2140. 
  1359.  
  1360. The old fashioned kind of mail can currently find Steve home for the summer at: 
  1361.  
  1362. Steve Luzynski
  1363. 3604 S. 10th ST CT
  1364. Blue Springs, MO 64015-6238
  1365.  
  1366.  
  1367. ΓòÉΓòÉΓòÉ 7.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
  1368.  
  1369. Larry Salomon wrote his first Presentation Manager application for OS/2 version 
  1370. 1.1 in 1989.  Since that time, he has written numerous VIO and PM applications, 
  1371. including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen 
  1372. Capture trio included with the IBM Professional Developers Kit CD-ROM currently 
  1373. being distributed by IBM.  Currently, he works for International Masters 
  1374. Publishers in Stamford, Connecticut and resides in Bellerose, New York with his 
  1375. wife Lisa. 
  1376.  
  1377. Larry can be reached electronically via the Internet at os2man@panix.com. 
  1378.  
  1379.  
  1380. ΓòÉΓòÉΓòÉ 7.3. Gordon Zeglinski ΓòÉΓòÉΓòÉ
  1381.  
  1382. Gordon W. Zeglinski can be reached at zeglins@cc.umanitoba.ca (formerly 
  1383. zeglins@ccu.umanitoba.ca).