
/* MyApp.m */

#import <objc/List.h>
#import <appkit/Window.h>
#import <appkit/Bitmap.h>
#import <appkit/Listener.h>
#import <appkit/Bitmap.h>
#import <appkit/graphics.h>
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import <streams/streams.h>
#import <libc.h>
#import <strings.h>
#import <pwd.h>
#import "IconView.h"
#import "DockSpeaker.h"
#import "MyApp.h"


#define READ 0
#define DOCK_WINDOW_LEVEL (NX_MAINMENULEVEL - 1)

char *mySalloc(string)
/* returns a copy of string */
char *string;
{
    char *newString;
    
    newString = (char *)malloc(strlen(string) + 1);
    if (!newString) {
      return("");
    }
    strcpy(newString, string);
    return newString;
}

@implementation MyApp

/* factory methods */

+ new
{
    self = [super new];
    
    /* we want to be our own delegate */
    [self setDelegate:self];
    
    return self;
}


/* class methods */

- createDockWindows:(int *)minX :(int *)minY :(int *)maxX :(int *)maxY
/* creates the dock windows based on the .altdock file */
{
    id		   dockWindowList, appBitmap;
    NXStream	   *fileStream;
    int		   fd, scanResult, success, xCoordinate, yCoordinate;
    char	   *homeDir, *dockFile, *appFileName;
    struct passwd  *passwdEntry;
    
    /* initialize the bounding box */
    *minX = *minY = *maxX = *maxY = 0;
    
    /* create a list to hold the dock's windows */
    dockWindowList = [List new];
    
    /* get ready to ask the WorkSpace for icons */
    [[self appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];

    /* get the user's home directory and create the .altdock file's abs path */
    passwdEntry = getpwuid((int)getuid());
    homeDir = passwdEntry->pw_dir;
    dockFile = (char *)malloc(strlen(homeDir) + 10);
    sprintf(dockFile, "%s/.altdock", homeDir);
    
    /* open a stream on the .altdock file */
    fd = open(dockFile, READ);
    if (fd < 0) {
      NXRunAlertPanel(NULL, "Couldn't open .altdock file", NULL, NULL, NULL);
      free(dockFile);
      return dockWindowList;
    }
    fileStream = NXOpenFile(fd, NX_READONLY);

    while (YES) {
      /* get the window's x and y coordinates, then the app's path */
      scanResult = NXScanf(fileStream, "%d %d\n", &xCoordinate, &yCoordinate);
      if (scanResult == EOF) {
        break;
      } else if (scanResult < 2) {
        NXRunAlertPanel(NULL, "Error reading .altdock file", NULL, NULL, NULL);
	break;
      }
      appFileName = [self stringFromStream:fileStream ok:&success];
      if (!success) {
        /* we've reached the end of the .altdock file */
        NXRunAlertPanel(NULL, "Error reading .altdock file", NULL, NULL, NULL);
        break;
      }
      
      /* get the app's bitmap */
      appBitmap = [self bitmapFor:appFileName];
      
      /* create the window */
      [dockWindowList addObject:[self newIconWindowFor:appBitmap
      						      :xCoordinate
						      :yCoordinate
						      :appFileName]];
      /* adjust the bounding box */
      if (xCoordinate < *minX) *minX = xCoordinate;
      if (yCoordinate < *minY) *minY = yCoordinate;
      if (xCoordinate > *maxX) *maxX = xCoordinate;
      if (yCoordinate > *maxY) *maxY = yCoordinate;
      free(appFileName);
    }
    
    /* clean up */
    NXClose(fileStream);
    free(dockFile);
    
    return dockWindowList;
}

- bitmapFor:(char *)appFileName
{
    id        bitmap;
    char      tiffDataBuffer[3000], *tiffDataBufferPtr;
    int       length, flag;
    NXStream  *tiffDataStream;
    
    if (!strcmp("Dock", appFileName)) {
      /* assign the dock window's bitmap */
      bitmap = [Bitmap findBitmapFor:"TXeN"];
    } else {
      /* ask the WorkSpace for the app's bitmap */
      tiffDataBufferPtr = tiffDataBuffer;
      [[self appSpeaker] getFileIconFor:appFileName
			 TIFF:&tiffDataBufferPtr
			 TIFFLength:&length
			 ok:&flag];
      /* get a stream on memory buffer holding the TIFF data */
      tiffDataStream = NXOpenMemory(tiffDataBufferPtr, length, NX_READONLY);
      bitmap = [Bitmap newFromStream:tiffDataStream];
      NXClose(tiffDataStream);
    }

    return bitmap;
}

- newIconWindowFor:bitmap :(int)xCoord :(int)yCoord :(char *)applicationName
{
    NXRect  windowRect;
    id      iconWindow, iconView;
    
    /* compute the new window's screen location */
    windowRect.origin.x = 4.0 + 64.0 * xCoord;
    windowRect.origin.y = 0.0 + 64.0 * yCoord;
    windowRect.size.height = windowRect.size.width = 64.0;
    
    /* create a new window */
    iconWindow = [Window newContent:&windowRect
    			 style:NX_PLAINSTYLE
			 backing:NX_BUFFERED
			 buttonMask:0
			 defer:YES];

    /* create an iconView to go in this window */		 
    iconView = [IconView new:applicationName :bitmap :xCoord :yCoord];
    
    /* stick the iconView in the window */
    [iconWindow setContentView:iconView];
    
    /* make the view the window's delegate and first responder */
    [iconWindow setDelegate:iconView];
    [iconWindow makeFirstResponder:iconView];
    
    /* create the PostScript window and stick it in the screen list */
    [iconWindow makeKeyAndOrderFront:self];
 
    /* set the window's level in the screen list to be just below menus */
    _NXSetWindowLevel([iconWindow windowNum], DOCK_WINDOW_LEVEL);
    
    return iconWindow;
}

- (char *)stringFromStream:(NXStream *)fileStream ok:(int *)success
/* reads a newline-terminated string from a stream */
{
    char   stringBuffer[1024];
    int    nextChar, characterNumber = 0;

    while ((nextChar = NXGetc(fileStream)) != '\n') {
      /* make sure we haven't reached the end of the file */
      if (nextChar == EOF) {
        *success = 0;
	return "";
      }
      stringBuffer[characterNumber++] = (char)nextChar;
    }
    stringBuffer[characterNumber] = '\0';
    *success = 1;

    /* create space for the string, copy it, and return the new string */
    return mySalloc(stringBuffer);
}


/* delegation methods */

- appDidInit:sender
{
    id     dockWindowList, dockWindow;
    int    minX, minY, maxX, maxY, i;
    
    /* we need to use our own special Speaker class object */
    [self setAppSpeaker:[DockSpeaker new]];
    
    /* create the dock's windows */
    dockWindowList = [self createDockWindows:&minX :&minY :&maxX :&maxY];

    /* find the dock window (the one with the backward NeXT) */
    i = [dockWindowList count];
    while (i--) {
      dockWindow = [dockWindowList objectAt:i];
      if ([[dockWindow contentView] theDock]) {
        break;
      }
    }
    
    if ([[dockWindow contentView] theDock]) {
      /* give the dock window the bounding box of all of the windows */
      [[dockWindow contentView] initialize:dockWindowList
      					  :minX
					  :minY
					  :maxX
					  :maxY];
    }
    
    return self;
}

@end
