home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Education Sampler 1992 [NeXTSTEP]
/
Education_1992_Sampler.iso
/
SoundAndMusic
/
Hyperupic
/
Hyperupic.app
/
Help.m
< prev
next >
Wrap
Text File
|
1992-08-10
|
12KB
|
381 lines
/*
* Help.m, a help object to manage and display RTF help files.
* The help object owns its own nib section "Help.nib" which has a
* Help panel - with an NXBrowser to display the help topics, and
* a scrolling text view to display the help files. The help files are
* all stored as RTF text files in a directory called Help within the
* app wrapper. At init time, the Help object loads the browser with
* names of all the files found in the Help directory. When a name is
* chosen from the browser, the help object opens a stream on that file
* and read the rich text into the text object. The help object also
* responds to request for context-sensitive help, by trying to find an
* appropriate help file for the view that was moused down in. See the
* helpForObject: method for more detailed explanation of that.
* This object is a useful addition to any program, because all users
* appreciate help!
*
* Author: Julie Zelenski, NeXT Developer Support
* You may freely copy, distribute and reuse the code in this example.
* NeXT disclaims any warranty of any kind, expressed or implied, as to
* its fitness for any particular use.
*/
#import "Help.h"
#import "HyperupicApp.h"
#import <appkit/Button.h>
#import <appkit/Cell.h>
#import <appkit/Matrix.h>
#import <appkit/MenuCell.h>
#import <appkit/NXBrowser.h>
#import <appkit/NXBrowserCell.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <dpsclient/wraps.h>
#import <sys/dir.h> //for getdirentries()
#import <libc.h>
@implementation Help:Object
- init
/* For newly created help object, loads the nib section with the Help
* panel which has the topics browser and a scrolling text view for
* displaying help files. Gets the appDirectory from NXApp, finds help
* directory in app wrapper.
*/
{
sprintf(helpDirectory,"%s/%s",[NXApp appDirectory],"Help");
sprintf(noHelpFile,"%s/%s",helpDirectory,"No Help.rtf");
helpPanel = [NXApp loadNibSection:"Help.nib" owner:self];
return self;
}
- setHelpBrowser:anObject;
/* Sets the helpBrowser outlet, and calls on the browser to load up.
*/
{
helpBrowser = anObject;
[helpBrowser setDelegate:self];
[helpBrowser loadColumnZero];
return self;
}
/* TARGET/ACTION METHODS */
- generalHelp:sender;
/* This is the target/action method for the "Help" menu item. This method
* will show the "general help" file.
*/
{
[self showHelpFile:"01-Introduction"];
return self;
}
- browserHit:sender
/* This is the target/action method from the help topics browser. When
* a help topic is selected, this method will show the help file for that
* topic.
*/
{
[self showHelpFile:[[[sender matrixInColumn:0] selectedCell] stringValue]];
return self;
}
- print:sender;
/* This method is called by the Print menu cell in the main menu. It will
* print the current help file.
*/
{
[[helpScrollView docView] printPSCode:sender];
return self;
}
/* HELP METHODS */
- helpForWindow:window;
/* If this window is the main menu, it will ask to display the help file
* for "Main Menu." (I didn't want to call the Main Menu help file "BusyBox
* Menu") Else it gets the help file for <window title> <window name>
* "Info Panel" or "Document Menu" for example.
*/
{
char filename[MAXPATHLEN];
if (window == [NXApp mainMenu])
sprintf(filename,"Main Menu");
else
sprintf(filename,"%s %s",[window title],[window name]);
[self showHelpFile:filename];
return self;
}
- helpForView:view atPoint:(NXPoint *)aPt;
/* Gives help for the specified view, which was hit with a Control-mouseDown
* at aPt. Gives feedback to the user which view was hit by framing the
* bounds of the view with a gray rectangle. This is done with instance
* drawing and erased after the helpfile is read and displayed. If the view
* is a matrix, the method makes the effort to find the particular cell which
* was hit and to only frame that cell.
* This method calls on the method helpForObject which will figure out which
* help file to display.
*/
{
int row,column;
NXRect b;
NXPoint p;
id cell = nil;
[view getBounds:&b];
if ([view isKindOf:[Matrix class]]) {
p = *aPt;
[view convertPoint:&p fromView:nil];
if (cell = [view getRow:&row andCol:&column forPoint:&p])
[view getCellFrame:&b at:row :column];
}
[view lockFocus];
PSnewinstance();
PSsetinstance(YES);
PSsetgray(NX_DKGRAY);
NXFrameRectWithWidth(&b,1.0);
[[view window] flushWindow];
PSsetinstance(NO);
if (cell)
[self helpForObject:cell];
else if ([[view window] contentView] == view)
[self helpForWindow:[view window]];
else
[self helpForObject:view];
PSnewinstance();
[view unlockFocus];
return self;
}
- helpForObject:object;
/* The method tries to cons together a file name that represents help
* for the given object. It makes no assumptions about the tags or
* titles of the views, rather it employs a general strategy. For many
* objects, the name is simply used ("ScrollView","TextField"). For Cells,
* it strips Cell from the name, for our purposes, TextFieldCell is
* the same as a TextField. For Buttons (and ButtonCells), it uses icon + name
* (radio Button, popUp Button). For MenuCells, it figures out where it is
* submenu or a item. Items display help for their parent menu, (using
* helpForWindow: method), submenus use title + menu (Format Menu, Find Menu).
* What is neat about this general scheme is that all views, windows, menu
* respond to the control-click, not just the ones I created. Bring up the
* Print panel and control-click on the Resolution popup, and you will see
* the help file for popups! Control-click on the Save Panel, you get help for
* Save Panel! This is probably not the way that a real application would
* implement help. It happens to work for mine, because I want the same
* help file to be displayed for every popup list, the same file for every
* button. In your app, different button have various functions, and you
* would want to display different help files for different buttons. You
* will probably devise a scheme using unique tags or titles, a more specific
* way to determine what help file to display.
*/
{
char filename[MAXPATHLEN];
char *suffix;
int len;
sprintf(filename,"%s",[object name]);
if ([object isKindOf:[Button class]] || [object isKindOf:[Cell class]]) {
if ([object icon])
sprintf(filename,"%s %s",[object icon],[object name]);
if ([object isKindOf:[Cell class]]) {
len = strlen(filename);
suffix = filename + (len-4)*sizeof(char);
if (strcmp("Cell",suffix)==0) {
filename[len-4] = '\0';
}
}
}
if ([object isKindOf:[MenuCell class]]) {
if ([object icon] && (strcmp([object icon],"menuArrow")==0))
sprintf(filename,"%s %s",[object title],"Menu");
else {
return [self helpForWindow:[[object controlView] window]];
}
}
[self showHelpFile:filename];
return self;
}
- showHelpFile:(const char*)filename;
/* Tries to open a stream for the specified RTF text file in the Help
* directory so the text object can readRichText. Also selects the
* filename in the browser of help topics. If the filename doesn't exist,
* it will select and display the "no help" file. It also brings the
* help panel to the front.
*/
{
NXStream *stream;
char helpFile[MAXPATHLEN];
static NXPoint origin = {0.0,0.0};
if (![self browser:helpBrowser selectCell:filename inColumn:0])
[self browser:helpBrowser selectCell:"No Help" inColumn:0];
sprintf(helpFile,"%s/%s.rtf",helpDirectory,filename);
if ((stream = NXMapFile(helpFile,NX_READONLY)) == NULL)
stream = NXMapFile(noHelpFile,NX_READONLY);
if (stream != NULL) {
[helpPanel disableFlushWindow];
[[helpScrollView docView] readRichText:stream];
[[helpScrollView docView] scrollPoint:&origin];
[[helpPanel reenableFlushWindow] flushWindow];
NXCloseMemory(stream,NX_FREEBUFFER);
}
[helpPanel orderFront:self];
return self;
}
/* BROWSER DELEGATE METHODS */
#define CHUNK 127
static char **addFile(const char *file, int length, char **list, int count)
/* Adds the specified filename to the list of filenames. It allocates
* more memory in chunks as needed.
*/
{
char *suffix;
if (!list) list = (char **)malloc(CHUNK*sizeof(char *));
if (suffix = rindex(file,'.'))
*suffix = '\0'; /* strip rtf suffix */
list[count] = (char *)malloc((length+1)*sizeof(char));
strcpy(list[count], file);
count++;
if (!(count% CHUNK)) {
list = (char **)realloc(list,(((count/CHUNK)+1)*CHUNK)*sizeof(char *));
}
list[count] = NULL;
return list;
}
static void freeList(char **list)
/* Frees the array of filenames
*/
{
char **strings;
if (list) {
strings = list;
while (*strings) free(*strings++);
free(list);
}
}
static BOOL isOk(const char *s)
/* checks to make sure the filename is not NULL and to verify that it is
* not a "dot"--hidden file.
*/
{
return (!s[0] || s[0] == '.') ? NO : YES;
}
static int caseInsensitiveCompare(void *arg1, void *arg2)
/* Compares the two arguments without regard for case using strcasecmp().
*/
{
char *string1, *string2;
string1 = *((char **)arg1);
string2 = *((char **)arg2);
return strcasecmp(string1,string2);
}
static char **fileList;
- (int)browser:sender fillMatrix:matrix inColumn:(int)column
/* This delegate method goes out to the help directory and gets a list
* of all the files in that directory. It creates a list of file names
* for the static variable fileList, and will load the filenames into the
* browser on demand (lazy loading).
*/
{
long basep;
char *buf;
struct direct *dp;
char **list = NULL;
int cc, fd, fileCount = 0;
char dirbuf[8192];
if ((fd = open(helpDirectory, O_RDONLY, 0644)) > 0) {
cc = getdirentries(fd, (buf = dirbuf), 8192, &basep);
while (cc) {
dp = (struct direct *)buf;
if (isOk(dp->d_name)) {
list = addFile(dp->d_name, dp->d_namlen, list, fileCount++);
}
buf += dp->d_reclen;
if (buf >= dirbuf + cc) {
cc = getdirentries(fd, (buf = dirbuf), 8192, &basep);
}
}
close(fd);
if (list) qsort(list,fileCount,sizeof(char *),caseInsensitiveCompare);
}
freeList(fileList);
fileList = list;
return fileCount;
}
- browser:sender loadCell:cell atRow:(int)row inColumn:(int)column
/* This delegate method loads the cell for a given row. The stringValue
* for that row comes from the fileList.
*/
{
if (fileList) {
[cell setStringValueNoCopy:fileList[row]];
[cell setLeaf:YES];
}
return self;
}
- (BOOL)browser:sender selectCell:(const char *)title inColumn:(int)column
/* This delegate method selects the cell with the given title. If it finds
* a cell with that title, it verifies that it has a file entry in the
* fileList, forces the loading of the cell, selects it (highlights) and
* scrolls the browser so the cell is visible. It returns a boolean value
* which indicates whether the cell was found.
*/
{
int row;
id matrix;
if (title) {
matrix = [sender matrixInColumn:column];
if (!fileList) return NO;
for (row = [matrix cellCount]-1; row >= 0; row--) {
if (fileList[row] && !strcmp(title, fileList[row])) {
[sender getLoadedCellAtRow:row inColumn:column];
[matrix selectCellAt:row :0];
[matrix scrollCellToVisible:row :0];
return YES;
}
}
}
return NO;
}
/* WINDOW DELEGATE METHODS */
- windowWillResize:sender toSize:(NXSize *)frameSize;
/* This method constrains the Help Panel to a reasonable minimum size
* when the user resizes the panel.
*/
{
frameSize->width = MAX(frameSize->width,400.0);
frameSize->height = MAX(frameSize->height,350.0);
return self;
}
@end