From: gordie@cyclesoft.com (Gordie Freedman)
Subject: Bug in subclassed Text object in scrollview? (Long)
Keywords: text subclass appkit.555 scrollview font
Reply-To: gordie@cyclesoft.com
Organization: Dolphin Software
Date: Sun, 27 Dec 1992 20:15:06 GMT

Hi,

(On a NeXTStation Color (non turbo) running 3.0, 16/400, 21" color monitor.)

I have a problem with subclassing the Text object, and than replacing the Text  
object in a scrollview with my custom subclass, as explained in NeXTanswer  
appkit.555.

For the most part it works as advertised, however if I read ascii into the Text  
object (after setting it to MonoFont:YES), and than try to change the font from  
the Font Panel - the old text (with the old font) is not cleared, and the new  
text (with the new font) is painted over it. Not a pretty sight. As well, I  
have to do some mucking around when reading in a new document into the Text,  
otherwise it does the same thing (paints the new text over the old instead of  
replacing it).

I don't have these problems with the same code/nib keeping the original text  
object.

In the nib, I have a Controller object (which inherits from Object, and doesn't  
really do a whole lot in this example). The controller is the delegate for the  
Application, and there is one window (besides the main menu). The Controller  
has three outlets related to the window, an id for the window itself (set in  
the nib), an id for the scrollview (set in the nib), and an id for the text  
object (set in appDidInit: after getting the new custom Text object). The  
controller is also set to be the delegate of the window from IB, and  
programmatically set to be the delegate of the subclassed Text object.

I don't know if I am doing something wrong here (possibly with font stuff, see  
code example), or if there is a problem with the Text object that this usage  
uncovers.

Here's the code in the implementation of the Controller object:

------------------------------------------------

#import "Controller.h"
#import <strings.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/file.h>

//
// This app illustrates a problem using the code from NeXTAnswers
// appkit.555 - replacing a Text object in a ScrollView with a custom
// Text object (in this case a PText object - see below). 
//

// This app is for NeXTSTEP 3.0 using all standard dev tools/etc., and
// can be built using project builder.

// The Controller is set up as the delegate for the Window with the 
// ScrollView and the Text, and is also set as the delegate for the
// Application object via IB.

// To see the problem, launch the App, use the Display menu item to
// display the Text window, than use the Open... menu item to open a file.
// Select a file that is ascii (not rich text). Bring up the Font panel
// (Command-t), set focus in the Text in the Text Window, than select
// a different font from the font panel. The result will be that the
// old text is left in the window and the new text is painted over it.

// Note that this does not happen with Rich Text, although without the code
// in [Controller clearText] reading in a new document has similar problems.

// Questions:
//
// 1. Why doesn't setting the font from the Font Panel work correctly on
//	non-rich text?
//
// 2. Why is the "clearText" method needed when reading in new text?
//

/*---------------------------------------------------------------------------*/

// This is the subclassed PText - it doesn't do anything now,
// but would be used to specialize methods such as addToPageSetup
// When it replaces the standard text object in a ScrollView, modifying
// the font for a MonoFont document does not work correctly. It paints
// the new text (with the new font setting) on top of the previous text,
// rather than replacing the old text. When this program is run without
// replacing the Text object with the PText, the problems go away.

@interface PText: Text
{
}

@end

@implementation PText

- addToPageSetup
{	
	[super addToPageSetup];
		
	return self;

}

@end


// Some private methods for the Controller object.

@interface Controller (Private)

- clearText;
- (BOOL) openTextFile:(const char*) fileName;
- doit:(const char*) name;

@end

@implementation Controller (Private)

// This method is needed when reading in new text from a stream -
// Without it, the old text is still present in the window, and 
// the new text is painted over it. This is not needed when a standard
// Text object is used.

- clearText
{
	NXRect r;
	[text getFrame:&r];
	
	[text setTextGray:NX_WHITE];
	[text display];
	[text setTextGray:NX_BLACK];
	[text renewRuns:NULL text:"" frame:&r tag:[text tag]];	
	return self;
}

#define	BUF_SIZE	5
#define	RTF_SIG		"{\\rtf"
#define NUM_TABS	10
static char buf[BUF_SIZE];

// Method to open a text file. Checks to see if it is rich text,
// and if so, sets MonoFont:NO and reads in as such. Otherwise, sets
// MonoFont:YES and reads in ascii text.
//
- (BOOL) openTextFile:(const char*) fileName
{
	int        fd ;
	NXStream  *stream ;

	fd = open (fileName, O_RDONLY) ;
	if (fd == 0)
	{
		return NO ;
	}
	stream = NXOpenFile (fd, NX_READONLY) ;
	if (stream == 0)
	{
		close (fd) ;
		return NO ;
	}

	// read in the beginning of the text to see if rtf. Seek back to
	// the beginning of the file before reading from the stream.
	
	buf [0] = '\0';
	read (fd, buf, BUF_SIZE);
	lseek (fd, 0, L_SET);
	[self clearText];
	if (!strncmp (buf, RTF_SIG, BUF_SIZE))
	{
		[text setMonoFont:NO];
		[text readRichText:stream];
	}
	else
	{
		[text setMonoFont:YES];
		[text readText:stream];		
		// Set whatever font the user last selected in Font Panel
//		[text setFont:font];
	}
	NXClose (stream);
	close (fd);
	return YES;
}

// 
// Method to find out if the text file is rtfd or not. Gets the
// text into the PText object via openTextFile (if not rtfd), or
// openRTFDFrom (if rtfd).
//
- doit:(const char*) name
{
	id newFont;
	const char* type;
	NXSize oldSize;
	
	// Save the old size to see if the paper size changes
	oldSize.width = pageSize.width;
	oldSize.height = pageSize.height;
	
	if ((!name) || (*name == '\0'))
	{
		return nil;
	}

	// Get whatever font is in the font panel, free previous
	// font if not the same ...	
	newFont = [fontPanel panelConvertFont:font];
	if (font != newFont)
	{
		[font free];
		font = newFont;
	}

	// Get the type of the file
	if ((type = rindex (name, '.')) == NULL)
	{
		type = "";
	}
	else
	{
		type ++;
	}
	if (!strcmp (type, "rtfd"))
	{
		[text setMonoFont:NO];
		[self clearText];
		if ([text openRTFDFrom:name] != NX_RTFDErrorNone)
		{
			return nil;
		}
	}
	else
	{
		if ([self openTextFile:name] == NO)
		{
			return nil;
		}
	}
	
	// If the paper size changed, resize the window
	if (pageSize.width != oldSize.width)
	{
		NXSize size;
		[scrollView getContentSize:&size];
		[textWindow sizeWindow:pageSize.width+NX_SCROLLERWIDTH
			:size.height];
	}
	// Set the window title and return
	[textWindow setTitle:name];
	return self;
}

@end

@implementation Controller

//
// appDidInit - gets size from old text object, and sets up
// new PText object in its place in the scrollview. Makes the
// Controller the delegate of the PText to get the textDidRead msg.
//
- appDidInit:sender
{
	id	stdText;
	NXRect	r;

	// Get a font opanel and an initial font
	fontPanel = [FontPanel new];
	font = [Font newFont:"Helvetica" size:12.0];
	
	// Straight out of appkit.555, only the names were
	// changed to protect the innocent	
	
	// If this code is skipped, everything works as advertised ...
	// If skipping it, have to do:
	// [text setDelegate:self];
	
	stdText = [scrollView docView];
	[stdText getFrame:&r];
	
	[scrollView setVertScrollerRequired:YES];
	[scrollView setHorizScrollerRequired:NO];
	[scrollView setDynamicScrolling:YES]; 
	
	text = [[PText alloc] initFrame:&r];
	[text moveTo:0.0:0.0];
	[text notifyAncestorWhenFrameChanged: YES];
	[text setVertResizable:YES];
	[text setSelectable:YES];
	[text setEditable: YES];
	[text setAutosizing:NX_WIDTHSIZABLE];
	[text setMinSize:&r.size];
	r.size.height = 1.0e30;
	[text setMaxSize:&r.size];
	[scrollView setDocView:text];
	[stdText free];

	// Set oursleves as the delegate for the new PText object
	[text setDelegate:self];

	return self;
}


// Invoked in response to user selecting Display menu item
// Displays the window with text in it.
- showText:sender
{

	[textWindow makeKeyAndOrderFront:self];
	return self;
}

// Window delegate method for the text window, invoked when
// the window resizes. Saves the new size
- windowDidResize:sender
{
	NXSize size;
	[scrollView getContentSize:&size];
	pageSize.width = size.width;
	pageSize.height = size.height;
	return self;
}

// PText delegate method invoked when reading an rtf stream
// and the papersize directives are read. Saves the size for later.
- textDidRead:sender paperSize:(NXSize*) psize
{
	pageSize.width = psize->width;
	pageSize.height = psize->height;
	return self;
}

// Private routine to make a full path name out of dir and file name
static char* 
makeFileName (const char* const file, const char* const dir)
{
	char* name ;
	name = calloc (strlen (dir) + strlen (file) + 2, 1) ;
	strcpy (name, dir) ;
	strcat (name, "/") ;
	strcat (name, file) ;
	return name ;
}

// Method invoked in reponse to user selecting "Open..." menu item.
// Displays the standard open panel, and opens files selected.
const char* typeList[] = { NULL };

- openFile:sender
{
	OpenPanel* op ;
	const char* const* filenames ;
	const char* directory ;
	char* name ;
	int rc;
	
	op = [OpenPanel new] ;
	[op allowMultipleFiles:YES] ;
	rc = [op runModalForTypes:typeList] ;
	
	if (rc == NX_CANCELTAG)
	{
		return self;
	}
	filenames = [op filenames] ;
	directory = [op directory] ;

	if ((! directory) || (! filenames))
	{
		return self;
	}
		
	while (*filenames)
	{
		name = makeFileName (*filenames, directory);
		[self doit:name];
		free (name);
		filenames++;
	}
	return self;
}

@end

----------------------------

I can supply the .nib as well, I didn't want to make the post too huge, and  
hopefully the description is enough to recreate the problem. I have a full  
project (around 25KBytes uncompiled) that illustrates this problem. I can  
NeXTMail it if this post does not give enough of a description to recreate, and  
anyone would like to explore this further.

Thanks,

Gordie Freedman
-- 
Gordie Freedman gordie@cyclesoft.com   ->   NextMail encouraged

Newsgroups: comp.sys.next.bugs