home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-04-10 | 42.5 KB | 1,401 lines |
- ================================================================
-
- Subject: Stream Manager
-
- Location: TV
-
- Item: #00001
-
- Submitted by: Ken Vogel [74007,564]
-
- Problem: The stream manager does not follow documentation in the Turbo
- Vision User's Guide. The following does not work as documented on page 176.
-
- 1) Create an object.
- 2) Have two pointers point to the object.
- 3) Write out both pointers to the stream.
- 4) Read the object back into memory via the two pointers.
-
- The book states that the pointers should be pointing to the same object, but
- they do not. The following code demonstrates the problem.
-
- Attach information:
- ------------------------ Cut Here ------------------------
- #include <stdio.h>
-
- #define Uses_Tobject
- #define Uses_TStreamable
- #define Uses_TStreamableClass
- #define Uses_ofpstream
- #define Uses_ifpstream
- #include <tv.h>
-
- // Two classes of different sizes
- class FirstClass : public TStreamable
- {
- public:
- int Dummy;
- FirstClass() { Dummy = 1; }
-
- // Stream Stuff
- public:
- static const char * const near ClassNameP;
- static TStreamable *build();
- private:
- virtual const char *streamableName() const
- { return ClassNameP; }
- protected:
- FirstClass (StreamableInit);
- virtual void *read( ipstream& );
- virtual void write( opstream& );
- };
-
- inline opstream& operator << ( opstream& os, FirstClass* cl )
- { return os << (TStreamable *)cl; }
- inline ipstream& operator >> ( ipstream& is, FirstClass*& cl )
- { return is >> (void *&)cl; }
-
- const char * const near FirstClass::ClassNameP = "FirstClass";
-
- TStreamableClass RFirstClass( FirstClass::ClassNameP,
- FirstClass::build,
- __DELTA(FirstClass)
- );
-
- TStreamable *FirstClass::build()
- {
- return new FirstClass( streamableInit );
- }
-
-
- FirstClass::FirstClass( StreamableInit )
- {
- }
-
- void FirstClass::write( opstream& os )
- {
- os << Dummy;
- }
-
- void *FirstClass::read( ipstream& is )
- {
- is >> Dummy;
- return this;
- }
-
- class SecondClass : public TStreamable
- {
- public:
- int Dummy1, Dummy2;
- SecondClass() { Dummy1 = Dummy2 = 1; }
-
- // Stream Stuff
- public:
- static const char * const near ClassNameP;
- static TStreamable *build();
- private:
- virtual const char *streamableName() const
- { return ClassNameP; }
- protected:
- SecondClass (StreamableInit);
- virtual void *read( ipstream& );
- virtual void write( opstream& );
- };
-
- inline opstream& operator << ( opstream& os, SecondClass* cl )
- { return os << (TStreamable *)cl; }
- inline ipstream& operator >> ( ipstream& is, SecondClass*& cl )
- { return is >> (void *&)cl; }
-
- const char * const near SecondClass::ClassNameP = "SecondClass";
-
- TStreamableClass RSecondClass( SecondClass::ClassNameP,
- SecondClass::build,
- __DELTA(SecondClass)
- );
-
- TStreamable *SecondClass::build()
- {
- return new SecondClass( streamableInit );
- }
-
-
- SecondClass::SecondClass( StreamableInit )
- {
- }
-
- void SecondClass::write( opstream& os )
- {
- os << Dummy1 << Dummy2;
- }
-
- void *SecondClass::read( ipstream& is )
- {
- is >> Dummy1 >> Dummy2;
- return this;
- }
-
- void main (void)
- {
- FirstClass *Common1AP = new FirstClass();
- FirstClass *Common1BP = Common1AP;
- SecondClass *Common2AP = new SecondClass();
- SecondClass *Common2BP = Common2AP;
-
- printf ("1. Should be the same: A = %p B = %p\n", Common1AP,
- Common1BP);
- printf ("2. Should be the same: A = %p B = %p\n", Common2AP,
- Common2BP);
-
- ofpstream *OutP = new ofpstream ("TEMP.OUT", ios::out | ios::trunc);
- if (OutP)
- {
- *OutP << Common1AP << Common2AP << Common1BP << Common2BP;
- delete OutP;
- }
-
- Common1AP = Common1BP = 0;
- Common2AP = Common2BP = 0;
-
- // Read back
- ifpstream *InP = new ifpstream ("TEMP.OUT");
- if (InP)
- {
- *InP >> Common1AP >> Common2AP >> Common1BP >> Common2BP;
- delete InP;
- }
-
- printf ("1. Should be the same: A = %p B = %p\n", Common1AP,
- Common1BP);
- printf ("2. Should be the same: A = %p B = %p\n", Common2AP,
- Common2BP);
- }
- ------------------------ Cut Here ------------------------
-
-
- Borland Response: This is a problem with the documentation or the stream
- manager.
-
- Workarounds: None.
-
- Additional Information: None.
-
- ================================================================
-
- Subject: Help Compiler
-
- Location: TV
-
- Item: #00002
-
- Submitted by: Doug Amaral [75300,2010]
-
- Problem: The help compiler gets a divide by 0 message when compiling a file
- with many references. The following file demonstrates the problem.
-
- Attach Information:
- ------------------------ Cut Here ------------------------
- Builder Command Reference
-
- {Adaptor:Adaptor}
- {And:And}
- {BCFAdd:BCFAdd}
- {BCFCopy:BCFCopy}
- {BCFDelete:BCFDelete}
- {Beep:Beep}
- {BLDAttrib:BLDAttrib}
- {BLDChDir:BLDChDir}
- {BLDCopy:BLDCopy}
- {BLDDel:BLDDel}
- {BLDMkDir:BLDMkDir}
- {Box:Box}
- {CALL:CALL}
- {Canceled:Canceled}
- {CapsLockOff:CapsLockOff}
- {CapsLockOn:CapsLockOn}
- {CapsLockStatus:CapsLockStatus}
- {Case:Case}
- {CharToNum:CharToNum}
- {ClearLineTo:ClearLineTo}
- {Clock:Clock}
- {ClockOff:ClockOff}
- {Close:Close}
- {CloseAllFiles:CloseAllFiles}
- {CLS:CLS}
- {CLS On:CLS_On}
- {Contains:Contains}
- {CoprocessorInstalled:CoprocessorInstalled}
- {CPU:CPU}
- {CurrentDir:CurrentDir}
- {CurrentDrive:CurrentDrive}
- {DayoftheWeek:DayoftheWeek}
- {DirExists:DirExists}
- {DiskFree:DiskFree}
- {DiskLabel:DiskLabel}
- {DiskReady:DiskReady}
- {DiskSize:DiskSize}
- {DOSErrorLevel:DOSErrorLevel}
- {DOSInstalled:DOSInstalled}
- {DOSRAM:DOSRAM}
- {DriveSize:DriveSize}
- {DropDown:DropDown}
- {ECHO:ECHO}
- {EMSAvail:EMSAvail}
- {EMSInstalled:EMSInstalled}
- {EMSMajor:EMSMajor}
- {EMSMinor:EMSMinor}
- {EMSTotal:EMSTotal}
- {EMSVersion:EMSVersion}
- {ENVAvail:ENVAvail}
- {ENVTotal:ENVTotal}
- {EOF:EOF}
- {Exit:Exit}
- {External:External}
- {File:File}
- {FileChangedDateToLong:FileChangedDateToLong}
- {FileChangedTimeToLong:FileChangedTimeToLong}
- {FileSize:FileSize}
- {FlushKeyboard:FlushKeyboard}
- {FOR:FOR}
- {FillArea:FillArea}
- {FreeArray:FreeArray}
- {GetFilePos:GetFilePos}
- {GetKey:GetKey}
- {GetYN:GetYN}
- {GOTO:GOTO}
- {GoToXY:GoToXY}
- {HasExtension:HasExtension}
- {Help:Help}
- {IF:IF}
- {Input:Input}
- {Input @:Input_@}
- {Integer:Integer}
- {IntegerArray:IntegerArray}
- {IntToStr:IntToStr}
- {Item:Item}
- {Keypressed:Keypressed}
- {LastKey:LastKey}
- {Length:Length}
- {LightBar:LightBar}
- {Location:Location}
- {LongInt:LongInt}
- {LongIntArray:LongIntArray}
- {LongToDateStr:LongToDateStr}
- {LongToStr:LongToStr}
- {LongToTimeStr:LongToTimeStr}
- {LowerCase:LowerCase}
- {LTrim:LTrim}
- {MaxCols:MaxCols}
- {MaxRows:MaxRows}
- {Menu Style:Menu_Style}
- {MidStr:MidStr}
- {MouseHideCursor:MouseHideCursor}
- {MouseInstalled:MouseInstalled}
- {MouseShowCursor:MouseShowCursor}
- {Move:Move}
- {NestedItem:NestedItem}
- {NOT:NOT}
- {NumLockOff:NumLockOff}
- {NumLockOn:NumLockOn}
- {NumLockStatus:NumLockStatus}
- {NumofDrives:NumofDrives}
- {NumToChar:NumToChar}
- {OnPath:OnPath}
- {Open:Open}
- {Or:Or}
- {OSMajor:OSMajor}
- {OSMinor:OSMinor}
- {OSVersion:OSVersion}
- {ParamCount:ParamCount}
- {Password:Password}
- {Pause:Pause}
- {PickFile:PickFile}
- {PickList:PickList}
- {PopUp:PopUp}
- {PrinterReady:PrinterReady}
- {Put:Put}
- {Read:Read}
- {ReadLine:ReadLine}
- {ReadScrLine:ReadScrLine}
- {Reboot:Reboot}
- {REM:REM}
- {RenSub:RenSub}
- {Repeat:Repeat}
- {RestoreScreen:RestoreScreen}
- {Rewind:Rewind}
- {RowCol:RowCol}
- {RTrim:RTrim}
- {Run:Run}
- {RunString:RunString}
- {RunSwap:RunSwap}
- {SaveScreen:SaveScreen}
- {Say:Say}
- {ScreenBlank:ScreenBlank}
- {ScrollLockOff:ScrollLockOff}
- {ScrollLockOn:ScrollLockOn}
- {ScrollLockStatus:ScrollLockStatus}
- {Set:Set}
- {SetFilePos:SetFilePos}
- {SHIFT:SHIFT}
- {Sleep:Sleep}
- {SleepUntil:SleepUntil}
- {StrDateToLong:StrDateToLong}
- {String:String}
- {StringArray:StringArray}
- {StrTimeToLong:StrTimeToLong}
- {StrToNum:StrToNum}
- {Stuff:Stuff}
- {Sub:Sub}
- {SysDateToLong:SysDateToLong}
- {System:System}
- {SysTimeToLong:SysTimeToLong}
- {Text On:Text_On}
- {Trim:Trim}
- {Unshift:Unshift}
- {UpperCase:UpperCase}
- {BIOS:BIOS}
- {Dont Use BrightColors:Dont_Use_BrightColors}
- {Dont Use Commas:Dont_Use_Commas}
- {Dont Use CtrlBreak:Dont_Use_CtrlBreak}
- {Dont Use Cursor:Dont_Use_Cursor}
- {Direct:Direct}
- {DOS:DOS}
- {Dont Use Encryption:Dont_Use_Encryption}
- {Use ExtendedKeyboardBuffer:Use_ExtendedKeyboardBuffer}
- {VideoMode:VideoMode}
- {Wait:Wait}
- {WaitUntil:WaitUntil}
- {WhereX:WhereX}
- {WhereY:WhereY}
- {While:While}
- {Write:Write}
- {WriteLine:WriteLine}
- {XMSAvail:XMSAvail}
- {XMSInstalled:XMSInstalled}
- {XMSMajor:XMSMajor}
- {XMSMinor:XMSMinor}
- {XMSTotal:XMSTotal}
- {XMSVersion:XMSVersion}
- ------------------------ Cut Here ------------------------
-
- Borland Response: This a problem with the help compiler.
-
- Workarounds: None.
-
- Additional information: None.
-
- ================================================================
-
- Subject: TVFORMS and memory leaks
-
- Location: TV
-
- Item: # 00003
-
- Submitted by: Jens Kjaerby [70742,433]
-
- Problem: There is some kind memory leak at an unconfirmed location within
- TVFORMS. When there more information I will add it.
-
- The following are comments on a TurboVision memory leak (heap). All the
- files contained in the zip-file should be copied to:
-
- \borlandc\tvision\demos
-
- or or copy of this, because the replace some of the original files.
-
-
- The following is written by a client of mine, Allan Henson.
-
- ****************************************************************************
-
- Comments on modifications to the code:
-
- - first the minimum to add the
- indication of heap - this is in the TVForms.cpp/.h and clearly marked with
- /*********** new additions *****/.
- new code.......
- /*********** end new additions *****/.
- These additions are as follows:-
- 1. Taken from TVDEMO3.cpp - the idle override section - patched into
- TVForms.cpp - This required adjustments for declarations and so on.
- 2. The addition of gadgets.cpp in the TVFORMS.PRJ.
- 3. A polish to the about box to indicate what for !!.
-
- I refer to this later as the unmodified code because functionally the code
- is the same but for the addition of the heap and clock indication. If the
- compiler does its job, only a small overhead should be expected and the
- functional .EXE. Code at run time should exhibit the same effects as before
- the additions. TV will however function a little differently as the void
- *idle is overridden in the new version. This again should only effect the
- dynamic properties and not the function.
-
- - second the addition of a pre-compiler switch in the listdlg.cpp to switch
- in and out the modified effective code. The switch is de-active as sent to
- you and can be activated by taking out the comment mark // before the
- #define _TVERRERRED at the top of the definition and declaration table in
- listdlg.cpp. These modifications are clearly marked in listdlg.cpp with
-
- /************* heap loss reduction *********/
- code changes with #ifdef and #ifndef
- /************** end heap loss reduction ************/
-
- Effectively the modifications consist of moving the local declaration TForm
- *f; from the TListDialog constructor to the class declaration then allowing
- it to be destroyed in the TListDialog destructor.
-
- The heap values indicated below are for my setup and will depend on DOS
- setups and compiler type used. I have DOS 5.0 all allocated HI, BorlandC++
- kernel and IDE.
-
- In the unmodified version the following can be seen:-
-
- 1. From the IDE compile and RUN the TVERRORS.PRJ.
- 2. With the about box up on the screen.
- HEAP 63760.
- 3. Press return and release the about box.
- HEAP 65232.
-
- ************
- Note 1:- I will call this BASE HEAP.
- ************
-
- 4. Select file - open - PARTS.TVF.
- HEAP 58368.
- 5. Select item 1.
- HEAP 54512.
- 6. Release item 1.
- HEAP 58352.
-
- ************
- Note 2:- From 4.-58368 to 6. 58352 a loss of 16 Bytes.
- This has been found to be due to the overhead of the Streamable TFORMS.CPP
- class which is not released. Under the scope rules for C++ de-allocation for
- classes and there internal objects, when a class/object goes out of scope it
- should be de-allocated. If this is not done in a systematic manner then HEAP
- fragmentation eventually causes a CRASH. Graceful exits can be arranged by
- careful HEAP and stack checks but should not be required for normal exits
- from an object.
- ************
-
- 7. Now repeat with item 2 on the list
- HEAP 54512.
- 8. Now release item 2.
- HEAP 58352.
-
- ************
- Note 3:- The heap is now as 6. - The streamable 16 Bytes was a one time
- loss.
- ************
-
- 9. Release the list box with AltF3.
- HEAP 63632.
-
- ************
- Note 4:- A total loss from Base heap 65232-63632 = 1600 bytes. Take 16 for
- the streamable that was never released and we have 1584 loss.
- ************
-
-
- Repeat the process above from 4. to prove stability in the numbers - call
- the HEAP at this point New base heap.
-
-
- 4. HEAP 65864.
- 5. HEAP 53024.
- 6. HEAP 56864.
-
- *************
- Note 5:- No further loss of 16 bytes streamable. Once declared and the loss
- undertaken it is active , even after it went out of scope when the calling
- class - listdlg.cpp went out of scope. listdlg went out of scope when the
- list box was released. All memory at that point declared in listdlg and its
- subclasses (including child processes) should have been released.
- **************
-
- 7. HEAP 53024.
- 8. HEAP 56864.
-
- **************
- Note 6:- No further loss of 16 bytes.
- **************
-
- 9. HEAP 62144.
-
- **************
- Note 7:- A loss from New base heap of 1488 bytes This does not match the
- first loss of 1584 lost in the first run.
- **************
-
- repeat the process and you will find that there has been another permanent
- loss of 1488 bytes.
-
- **************
- Note 8:-
- As can be seen these losses can be broken up into three groups from the
- experiment so far.
- 1. The one time 16 bytes streamable - lost when the TForms is
- selected from the list box by way of listdlg.cpp.
- 2. A one time loss of 96 Bytes - the difference of the loss in
- run 1 and run 2. 1584-1488 = 96.
- 3. A repeated loss of 1488 bytes each time the form is selected.
- ***************
-
- Repeating the procedure with PHONENUM:TVF exhibits the same pattern. loss
- type three is the only difference.
-
- This lead me to the conclusion that there were a minimum of three possible
- areas of concern.
-
- 1. Nonrecoverable heap due to streamable and as noted before this can lead
- to fragmented heap.
-
- 2. A nonrecoverable 96 bytes that is systematic irrespective of the size of
- the Resource file of element size. However 96 was devisable by 16 !! -
- perhaps TV uses 6 none recoverable streams also being used. If so this
- could be difficult to trace as the heap is not directly accessible.
- Further investigations show that there are indeed several streamable
- objects being used - Resource files- streamable disk access - collections
- just to name three.
-
- 3. What is causing the collection size dependent loss.
-
- Now re-compile this time removing the comment // from the #define
-
- This is now the pattern:-
-
- 2. HEAP 63712.
- 3. HEAP 55184.
- 4. HEAP 58304.
- 5. HEAP 54448.
- 6. HEAP 58288.
- 7. HEAP 54448.
- 8. HEAP 58288.
- 9. HEAP 64960.
-
- **************
- Note 9:- Note loss of 224 - 16 streamable = 208.
- **************
-
- Now repeat as before:-
- 3. HEAP 64960.
- 4. HEAP 58176.
- 5. HEAP 54336.
- 6. HEAP 58176.
- 7. HEAP 54336.
- 8. HEAP 58176.
- 9. HEAP 64848.
-
- **************
- Note 10:- Note loss of 112 bytes. Differance of 96 bytes.
- **************
-
- **************
- Note 11:- Now we have some data to go on. 112 is also devisable by 16. But
- so is a long int !!.
- **************
-
- repeat the process with the other file TPHONENUM.TVF
-
- This gives the following:-
-
- The memory regained after the modification is 1376 Bytes for each access to
- the listdlg.cpp.
-
- The modification made was to take a declaration pointer to an object in the
- constructor and move it to the public area where it can be released
- 'manually' by the Destructor.
-
- Again scope rules demand that local declarations are destroyed when an
- object goes out of scope. In deed but for the list box in TV all other items
- do destroy themselves - after all that was the intention. There is a clear
- declaration in the manuals that states that 'release Element;' is not good
- enough but that destroy(Element); shall be used in order to ensure that the
- objects are released in order and the memory recovered. For the list box it
- states that the users list shall be disposed of prior to releasing the box
- to ensure full recovery. It is the users responsibility to ensure this.
-
- Any attempt to dispose of the list by making new = 0 collection as
- recommended, only leads to heap corruption.
-
- Conclusion
- -----------
- The streamable objects can leave a heap fragmented with pointers hiding
- lower access - initializing the streamables at the front of the program can
- avoid this but requires that all are global and defeats the object of C++
- encapsulation and protection. In out test case only 16 bytes were lost to
- the declared streamable outside TV and this at a point before the real use
- of the heap begins.
-
- TV seems to suffer the same kind of problem with streamables that a user
- streamable has. TV seems to use streams for the majority of its
- communication channels. Some are declared for extern use such as the
- streamable collections other are for internal use. In our test case 96 bytes
- were allocated to the TV streamables.
-
- The permanent loss due to the destructor not de-allocating the pointer *f
- but rather only the pointer can be traced to the structure and nature of TV.
- As TV is a context program structure any allocation cannot be allowed to be
- automatically destroyed when the calling element goes out of scope. The desk
- top contents may be required by other objects. However when the owning
- object (in our case listdlg.cpp) goes out of scope it can be eliminated
- safely as it is the only object that can initiate and safely destroy objects
- owned by it. TV does not apparently release the memory allocation when the
- pointer to an object is de-allocated. This means that ALL objects shall be
- destroyed explicitly before the pointer is released. Some mention of this is
- made in the manuals but Borland did not take there own advice in this demo.
-
- The remaining permanent loss each time the list box is accessed still
- remains a mystery. There seems to be no decernable relation to the
- collection size or elements of the collection. However the only thing that
- can be said is that it has been found to grow with increased size of each
- record of the collection. This is also related to the complexity of the
- window. The other related fact is that the loss is always devisable by 16 no
- matter what the contents if the record are.
-
- I have built up a system with float, long, short, int signed and unsigned,
- matrix elements and so on ( fields.cpp ). No matter which type is used the
- same number of bytes are lost.
-
- Whether this is a mater of a problem with the memory manager inside TV I do
- not know. The TV memory manager is stable and repeatable results can be
- obtained. There is a de-allocation problem somewhere in the system be it TV
- or the C++ memory functions.
-
- I can only say that until I have found the answers I cannot to my customer
- that he uses the TV for his tool box yet. The tool box is to check safety
- software and MUST be itself error free.
-
- Borland Response: There is a problem of a memory leak with TVFORMS, but I
- am unable to find the source of the problem.
-
- Workaround: None.
-
- Additional Information: None.
-
- ================================================================
-
- Subject: Low memory condition and TFILLIST.
-
- Location: TV
-
- Item: # 00004
-
- Submitted by: Ken Vogel [74007,564]
-
- Problem: Your application will crash with an abnormal program termination
- if you try to do a file Dialog in a directory with too many
- files.
-
- Proposed Solution:
-
- Interestingly enough, TFILLIST has code to handle too many files. The loop
- to read a file diligently checks if the pointer (allocated via new) is NULL,
- and will terminate the loop. However, NEW.CPP provided with TurboVision will
- *terminate* when no more memory is available (after dipping into the safety
- pool). TFILLIST should check the Safety Pool (via lowMemory) while it is
- looping, NOT the pointer itself.
-
- In TFileList::readDirectory, change lines 167 and 179 as follows:
- **** FROM *****
- while( p != 0 && res == 0)
- ***** TO *****
- while( p != 0 && res == 0 && !lowMemory())
- ***** END *****
-
- Also, change line 214:
- **** FROM 4*****
- if( p == 0 )
- messageBox( tooManyFiles, mfOKButton | mfWarning );
- ***** TO *****
- if( p == 0 || lowMemory())
- messageBox( tooManyFiles, mfOKButton | mfWarning );
- ***** END *****
-
- Borland Response: This seems to be a problem. I have made a report and
- sent it to the Quality Assurance Group.
-
- Workaround: To following proposed solution.
-
- Additional Information: None.
-
- ================================================================
-
- Subject: TV and DOS less than 3.31
-
- Location: TV
-
- Item #: 0005
-
- Submitted by: Nancy Enright [72230,1443]
-
- Problem: Will you add the TSystemError bug to your list - see the "Print
- via System call" thread. The only clarification I need to add to the
- documentation which I have already provided is that the machines which
- displayed the error message and hung were NOT all running DOS 3.21, 2 were,
- and 3 were running DOS 5.0. Let me know if I can supply any additional
- information which will help you track down this problem.
-
- Borland Response: I have made an information request concerning this
- problem.
-
- Workaround: None.
-
- Additional Information: Work on reproducing the problem.
-
- ================================================================
-
- Subject: ofTopSelect flags vs firstMatch.
-
- Location: TV
-
- Item #: 0006
-
- Submitted by: John L. Swartzentruber [76646,1430]
-
- Problem: This program is intended to demonstrate the bug in
- TGroup::firstMatch(). It also shows some other areas that appear to be
- problems. It is created from a modified version of the TVGUID04 demo
- program. For simplicity, the new fields do not have associated palettes, so
- the input fields are flashing red on white.
-
- The main bug is seen by attempting to click the mouse on one of the non-
- current fields. The selection does not change. The problem can be seen by
- tracing from the TView::select() of the field that you click on. This will
- eventually call firstMatch() which will return the wrong field. because of
- this, setCurrent() does nothing.
-
- Other potential problems (marked with !!) are:
-
- 1) buffering does not work for embedded windows. Writes seem to go to the
- parent window, but the current buffer containing garbage is what is painted.
-
- 2) TGroup::draw() does not call TView::draw(). This means that if the
- group is not completely covered by subviews, the rest of the group is
- covered with garbage.
-
- Attached information:
- ------------------------ Cut Here ------------------------
- #define Uses_TEvent
- #define Uses_TApplication
- #define Uses_TKeys
- #define Uses_TRect
- #define Uses_TMenuBar
- #define Uses_TSubMenu
- #define Uses_TMenuItem
- #define Uses_TStatusLine
- #define Uses_TStatusItem
- #define Uses_TStatusDef
- #define Uses_TDeskTop
- #define Uses_TWindow
- #define Uses_TInputLine
- #include <tv.h>
-
- const int cmMyNewWin = 201;
-
- class TMyApp : public TApplication
- {
-
- public:
- TMyApp();
- static TStatusLine *initStatusLine( TRect r );
- static TMenuBar *initMenuBar( TRect r );
- virtual void handleEvent( TEvent& event);
- void myNewWindow();
- };
-
-
- static short winNumber = 0; // initialize window number
-
- class TDemoWindow : public TWindow // define a new window class
- {
-
- public:
-
- TDemoWindow( const TRect& r, const char *aTitle, short aNumber );
- };
-
-
- class TInnerWindow : public TGroup
- {
- public:
- TInnerWindow( const TRect& r );
- virtual void draw();
- }
-
-
- TMyApp::TMyApp() :
- TProgInit( &TMyApp::initStatusLine,
- &TMyApp::initMenuBar,
- &TMyApp::initDeskTop
- )
- {
- TEvent event;
-
- // Put up the demo window
- event.what = evCommand;
- event.message.command = cmMyNewWin;
- putEvent(event);
- }
-
- TStatusLine *TMyApp::initStatusLine(TRect r)
- {
- r.a.y = r.b.y - 1; // move top to 1 line above bottom
- return new TStatusLine( r,
- *new TStatusDef( 0, 0xFFFF ) +
- // set range of help contexts
- *new TStatusItem( 0, kbF10, cmMenu ) +
- // define an item
- *new TStatusItem( "~Alt-X~ Exit", kbAltX, cmQuit ) +
- // define an item
- *new TStatusItem( "~Alt-F3~ Close", kbAltF3, cmClose )
- // and another one
- );
- }
-
- TMenuBar *TMyApp::initMenuBar( TRect r )
- {
-
- r.b.y = r.a.y + 1; // set bottom line 1 line below top line
- return new TMenuBar( r,
- *new TSubMenu( "~F~ile", kbAltF )+
- *new TMenuItem( "~N~ew", cmMyNewWin, kbF4, hcNoContext, "F4" )+
- newLine()+
- *new TMenuItem( "E~x~it", cmQuit, cmQuit, hcNoContext, "Alt-X" )+
- *new TSubMenu( "~W~indow", kbAltW )+
- *new TMenuItem( "~N~ext", cmNext, kbF6, hcNoContext, "F6" )+
- *new TMenuItem( "~Z~oom", cmZoom, kbF5, hcNoContext, "F5" )
- );
- }
-
- void TMyApp::handleEvent(TEvent& event)
- {
- TApplication::handleEvent(event); // act like base!
- if( event.what == evCommand )
- {
- switch( event.message.command )
- {
- case cmMyNewWin: // but respond to additional commands
- myNewWindow(); // define action for cmMyNewWin
- break;
- default:
- return;
- }
- clearEvent( event ); // clear event after handling
- }
- }
-
- void TMyApp::myNewWindow()
- {
- TRect r( 0, 0, 65, 18 ); // set initial size and position
- r.move(winNumber*3, winNumber*3);
- TDemoWindow *window = new TDemoWindow ( r, "Bug Demo Window", ++winNumber);
- deskTop->insert(window); // put window into desktop and draw it
- }
-
-
- TDemoWindow::TDemoWindow( const TRect& r, const char *aTitle, short aNumber):
- TWindow( r, aTitle, aNumber),
- TWindowInit( &TDemoWindow::initFrame
- )
-
- {
- // Create and insert the inside group. It is used so that nothing
- // overwrites the frame.
- TRect b = getExtent();
- b.grow(-1,-1);
- TView* inside = new TInnerWindow(b);
- insert(inside);
- }
-
-
- TInnerWindow::TInnerWindow( const TRect& r ) : TGroup(r)
- {
- options &= ~ofBuffered; // Buffering doesn't work in embedded windows !!
-
- TView* views[3];
-
- // Insert three input fields
- for (int i=0; i < 3; ++i) {
- TRect bounds(i * 17 + (i+1)*3, 1, (i+1) * 20, 2);
- views[i] = new TInputLine(bounds, 16);
- views[i]->options |= ofTopSelect; // This is what causes problem to
- // happen !!
- insert(views[i]);
- }
- }
-
- void TInnerWindow::draw()
- {
- // This is needed because the TGroup::draw() function never
- // calls TView::draw() !!
- TView::draw();
- TGroup::draw();
-
- // Put instructions under the input fields
- int color = 1;
- writeStr(1, 3, "This demonstrates the bug in TGroup::firstMatch()", color);
- writeStr(1, 5, "Click the mouse in the second or third field.", color);
- writeStr(1, 6, "Observe that nothing happens.", color);
- writeStr(1, 8, "The problem is that select() calls makeFirst()", color);
- writeStr(5, 9, "which calls putInFrontOf()", color);
- writeStr(5, 10, "which calls resetCurrent()", color);
- writeStr(5, 11, "which calls firstMatch()", color);
- writeStr(5, 12, "which has a bug and returns the last view, not the first",
- color);
- }
-
- int main()
- {
- TMyApp myApp;
- myApp.run();
- return 0;
- }
- ------------------------ Cut Here ------------------------
-
- Borland Response: I was able to reproduce the describe behavior. I am
- not sure why it does not work for this case, but works for TWindow. There
- is some limitation to the use of ofTopSelect. I will let the designers
- decide the issue. I am not sure what the issues are for other
- problems. When I get more information I will attach.
-
- Workarounds: None.
-
- Additional information: None.
-
- ================================================================
-
- Subject: Invalid cmReceivedFocus and cmReleasedFocus problems
-
- Location: TV
-
- Item #: 0007
-
- Submitted by: Bill Morrison [70403,3224]
-
-
- Problem: I mentioned that the cmReleasedFocus and cmReceivedFocus messages
- (R&R msgs) were being broadcast numerous times for no valid reason.
- Yesterday, I discovered some if not all of the bugs that cause this. Realize
- that it is these invalid broadcasts that make processing these events
- unreliable at best.
-
- Invalid R&R's are sent each time TView::setState is called to change the
- focus when in fact the focus state is already set to the desired value. In
- other words, a call to set the focus to True on a view whose focus is
- already True; and to False when already False. Again, pause a moment and
- realize: 1) the overhead of routing the message, 2) a handleEvent trying to
- discriminate a valid R&R from an invalid one. Maybe we can live with the
- overhead, and the modified TView::select is a valid alternative to R&R's.
- Personally, I prefer to have my cake and eat it too, so I will fix the bugs
- and modify select.
-
- In fixing the bugs, I saw and weighed several alternative solutions. The
- following are the ones I'm using today. They are all in the TGROUP module.
-
- Bug 1:
-
- void TGroup::focusView( TView* p, Boolean enable )
- {
- if( (state & sfFocused) != 0 && p != 0 )
- p->setState( sfFocused, enable );
- }
-
- My Fix to Bug 1:
-
- void TGroup::focusView( TView* p, Boolean enable )
- {
- if ( (p != 0)
- && ((state & sfFocused) != 0)
- )
- {
- Boolean isFocused=(Boolean) (p->state & sfFocused);
- if (isFocused != enable)
- p->setState(sfFocused,enable);
- }
- }
-
- The original TGroup::focusView says: if the group's present state is focused
- and p is a pointer to a subview, then set the focus state of p to enable,
- which can be True or False.
-
- The corrected TGroup::focusView applys the additional requirement that p's
- present focus state be different than enable. This prevents invalid R&R's.
-
- Bug 2: within TGroup::setState method you'll find:
-
- if( (aState & sfFocused) != 0 )
- {
- if( current != 0 )
- current->setState( sfFocused, enable );
- }
-
- My fix to Bug 2:
-
- if ((aState & sfFocused) != 0 )
- {
- if (current !=0)
- { Boolean isFocused = (Boolean) (current->state & sfFocused);
- if (isFocused != enable)
- current->setState(sfFocused,enable);
- }
- }
-
- Here again, I require the present focus state to be different than the
- requested focus state.
-
- Bug 3: within TGroup::setCurrent method you'll find:
-
- if( (state & sfFocused) != 0 && p != 0 )
- p->setState( sfFocused, True );
-
- My fix to Bug 3:
-
- if ( (p!=0)
- && ((state & sfFocused) !=0)
- && ((p->state & sfFocused) == 0)
- )
- {
- if (current!=0)
- {
- if ((current->state & sfFocused) == 0)
- p->setState(sfFocused,True);
- }
- else
- p->setState(sfFocused,True);
- }
-
- Again, the original does require p to be a subview of the focused group.
-
- The fix also requires that the present focus state of p to be False prior to
- setting it to True. In addition, if current is a pointer to another
- subview, that subview's focus state must also be False prior to setting p's
- focus to True.
-
- From page 105 of the User's Guide: "Within each group of views, one and only
- one subview is selected at any given moment.", and a little later: "Within
- the active window, the selected subview is called the focused view.".
-
- Notice also, that TGroup::setCurrent is also responsible for maintaining the
- sfSelected status for both the current and p subviews. It is likely that
- this logic needs a little fix as well. Perhaps more on that later.
-
- With the above fixes applied, you can recognize legitimate R&R messages
- within you subview's handleEvent. The approach that I have taken is to
- optionally have the subview handleEvent place a command in the event queue
- via putEvent. That command can tell the group's handleEvent to select a
- particular subview (previous, next, this, or whatever). For example:
-
- void MyField1::handleEvent(TEvent& event)
- {
- TInputLine::handleEvent(event);
-
- if ( ((event.what & evBroadcast)!=0)
- && (event.message.command==cmReleasedFocus)
- && (event.message.infoPtr==this)
- && ((state & sfExposed)!=0)
- )
- {
- if (valid(cmLeaving)!=True)
- { event.what=evCommand;
- event.message.command=cmSelectThis;
- event.message.infoPtr=this;
- putEvent(event);
- clearEvent(event); // important! and not on page 401
- }
- }
- }
-
- void MyDialog::handleEvent(TEvent& event)
- {
- TDialog::handleEvent(event);
- if ((event.what & evCommand)!=0)
- switch (event.message.command)
- { case cmSelectNext:
- clearEvent(event);
- selectNext(False);
- break;
-
- case cmSelectThis:
- setCurrent((TView *)event.message.infoPtr, normalSelect);
- clearEvent(event);
- break;
- }
-
- }
-
- Note, that for this to work with the mouse, I have set ofFirstClick for
- MyField1 to False. The reason that this is necessary is that the
- TView::mouseEvent method will dispose of the queued command while waiting
- for the evMouseUp. I think I could have ofFirstClick set to True if
- MyField1 handleEvent was smart enough.
-
- Borland Response: I have made a report that documents the problems with
- cmReleaseFocus & cmReceivedFocus. I have tried to supplied fixes and they
- seem to be a possible workaround.
-
- Workarounds: Apply the above fixes.
-
- Additional information: None.
-
- ================================================================
-
- Subject: MSGBOX.CPP & MessageBoxRect
-
- Location: TV
-
- Item #: 0008
-
- Submitted by: John L. Swartzentruber [76646,1430]
-
- Problem: Replaced variable argument list MessageBoxRect(...) to make it
- work. The output string was created with vsprintf(), then the fixed
- parameter version of MessageBoxRect() was called.
-
- Fixed a memory leak in the first messageBoxRect() function by calling
- shutDown() for the dialog.
-
- Borland Response: I have made a report that documents the fixes purpose for
- MessageBoxRect(...) and MessageBoxRect(). I have tried the supplied fixes
- and they seem to be a possible workaround.
-
- Workarounds: None.
-
- Additional information: None.
-
- ================================================================
-
- Subject: SYSERR and critical error reporter
-
- Location: TV
-
- Item #: 0009
-
- Submitted by: John L. Swartzentruber [76646,1430]
-
-
- Problem: The sysErr() critical error reporting function assumed that the
- output screen was 80 columns wide. If it is wider, garbage will appear at
- the end of the line, and the text will not be justified properly. Since the
- width of the screen is known, and is stored in TScreen::screenWidth, I used
- the actual screen width value. This was fixed by replacing the hardcoded
- numbers (80 and 79) with TScreen::screenWidth and TScreen::screenWidth-1.
-
- Borland Response: I have made a report that documents the fixes purpose for
- critical error handler. I have tried the supplied fixes and seem to be a
- possible workaround.
-
- Workarounds: None.
-
- Additional information: None.
-
- ================================================================
-
- Subject: KeyBufOrg and KeyBufEnd
-
- Location: TV
-
- Item #: 0010
-
- Submitted by: John L. Swartzentruber [76646,1430]
-
- Problem: The keyboard interrupt handler was assuming that everyone's
- keyboard buffer was at the default location. The handler can reset some of
- the BIOS pointers to incorrect locations because of this. Since the BIOS
- data area contains fields that point to the real keyboard buffer, I used
- those fields. Again, it was a very simple fix. The EQU's for KeyBufOrg and
- KeyBufEnd were changed to 80H and 82H respectively. These are the locations
- in the BIOS data area of words that point to the beginning and end of the
- keyboard buffer. Everywhere in the code where these EQU's were used, was
- changed to use the segment override (DS contains the BIOS data segment). For
- example:
-
- MOV AX,OFFSET KeyBufOrg
-
- was changed to
-
- MOV AX,DS:KeyBufOrg
-
- Borland Response: I have made a report that documents the problems with
- cmReleaseFocus & cmReceivedFocus. I have tried to supplied fixes and they
- seem to be a good workaround.
-
- Workarounds: None.
-
- Additional information: None.
-
- ================================================================
-
- Subject: iSqr and TDesktop.
-
- Location: TV
-
- Item #: 0011
-
- Submitted by: John L. Swartzentruber [76646,1430]
-
- Problem: The iSqr() function found a floating point square root, and then
- cast it to an integer. This works fine, except that it pulls in the
- floating point code, even if it isn't used anywhere else (15 - 20K worth I
- think). I rewrote the iSqr() function using Pascal code posted by J.W.
- Rider as its base, but making it fit the existing iSqr() interface.
-
- Borland Response: I have made a report that documents the problems with
- iSqr. I have tried to supplied fixes and they seem to be a good workaround.
-
- Workarounds: None.
-
- Additional information: None.
-
- ================================================================
-
- Subject: setBufSize and Tfiledtr
-
- Location: TV
-
- Item #: 0012
-
- Submitted by: John L. Swartzentruber [76646,1430]
-
- Problem: When the buffer size was being decreased, the setBufSize() function
- was losing data that was located after the gap. Also, when the buffer size
- increased, the gap was the wrong size, and data was lost and garbage was
- gained. The solution was to require that all setBufSize() functions should
- resize the gap when the buffer size is changed. To avoid duplication of
- effort in the future, I wrote a new function in the TEditor class to handle
- the changes. This new function is in module TEDITOR3.CPP.
-
- Borland Response: I have made a report that documents the problems with
- setBufSize. I have tried to supplied fixes and they seem to be a good
- workaround.
-
- Workarounds: None.
-
- Additional information: None.
-
- ================================================================
-
- Subject: TEditor, TFiledtr, and setBufSize.
-
- Location: TV
-
- Item #: 0013
-
- Submitted by: John L. Swartzentruber [76646,1430]
-
- Problem: This is a new module that I wrote to solve the problem in
- TFilEdtr.cpp. Since I did not want to have all the code duplicated in every
- class that overrides setBufSize(), I wrote this helper function and put it
- in the base class. I also added the proper declaration for this function in
- EDITORS.H. The new function declaration looks like this:
-
- int TEditor::normalizeBuffer(ushort newSize);
-
- The name isn't the best, but "setBufSize()" was already being used. This
- function's purpose is the make sure that all the internal pointers are set
- correctly and that the gap is the proper size for the new buffer size. This
- function should only be called from a setBufSize() function in a class
- derived from TEditor. If the buffer is growing (i.e., newSize is greater
- than bufSize), this function should be called after the new buffer is
- allocated and the old data is copied to the buffer. If the new size is less
- than the old size, this function should be called before allocating the new
- buffer. See the setBufSize() function in TFileEditor for an example.
-
- Borland Response: I have place this suggestion in the data base.
-
- Workarounds: None.
-
- Additional information: A this unofficial improvement to TEditor
- class.
-
- ================================================================
-
- Subject: Memory Leak in fpstream.
-
- Location: TV
-
- Item #: 0014
-
- Submitted by: Ken Vogel [74004,564]
-
- Problem:
- A descendent of TNSCollection which sets the shouldDelete
- member to false will leave one unfreed pointer if it is
- destroyed when items are contained within it.
-
- The TOBJSTRM module creates just such a collection during
- a read of a stream containing TStreamable *.
-
- Proposed Solution:
- The problem appears to be as follows. The shutDown function
- of TNSCollection calls the member function setLimit(0) to
- free all object pointers. setLimit *never* lets limit fall
- below count. Thus, if you destroy a TNSCollection with
- count > 0, the items member of TNSCollection will never be
- freed. (Note that I'm not talking about the contained pointers,
- which are obviously the responsibility of the caller in a
- shouldDelete = False situation).
-
- To fix the problem, change line 57 of TCOLLECT.CPP:
- **** FROM *****
- void TNSCollection::shutDown()
- {
- if( shouldDelete )
- freeAll();
- setLimit(0);
- TObject::shutDown();
- }
- ***** TO *****
- void TNSCollection::shutDown()
- {
- if( shouldDelete )
- freeAll();
- else
- count = 0;
- setLimit(0);
- TObject::shutDown();
- }
- ***** END *****
-
- Borland Response: I have place this suggestion in the data base.
-
- Workarounds: None.
-
- Additional information: A this unofficial improvement to TEditor
- class.
-
- ================================================================
-
- Subject: TFILEDTR.CPP and cmCancel
-
- Location: TV
-
- Item #: 0015
-
- Submitted by: Steve Goulet [72421,2733]
-
- Problem:
- This is causing windows to be shut down or programs to be terminated when a
- user presses Cancel on a mfYesNoCancel dialog box when an editor window is
- being closed.
-
- **** FROM ****
- Line number 267 of file TFILEDTR.CPP reads:
-
- cmCancel:
-
- when it should read
-
- case cmCancel:
- **** END ****
-
- Borland Response: I have made a report that documents the problems with
- TFILEDTR. I have tried the supplied fix and it seem to be a good
- workaround.
-
- Workarounds: Apply above workaround.
-
- Additional information: None.
-
- ================================================================
-