#line 2 "os_win.c"		/* So compiler knows original name of this file.*/
/*
 * $Id: os_win.c,v 4.8 1994/09/27 15:59:37 mikes Exp $
 *
 * Program:	Operating system dependent routines - MS Windows 3.1
 *
 *
 * Tom Unger
 * Networks and Distributed Computing
 * Computing and Communications
 * University of Washington
 * Administration Builiding, AG-44
 * Seattle, Washington, 98195, USA
 * Internet: mikes@cac.washington.edu
 *
 * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
 *
 * Copyright 1991-1994  University of Washington
 *
 *  Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee to the University of
 * Washington is hereby granted, provided that the above copyright notice
 * appears in all copies and that both the above copyright notice and this
 * permission notice appear in supporting documentation, and that the name
 * of the University of Washington not be used in advertising or publicity
 * pertaining to distribution of the software without specific, written
 * prior permission.  This software is made available "as is", and
 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Pine and Pico are trademarks of the University of Washington.
 * No commercial use of these trademarks may be made without prior
 * written permission of the University of Washington.
 *
 *
 * Notes:
 *      - mouse support added (mss, 921215)
 *
 *  Portions of this code derived from MicroEMACS 3.10:
 *
 *	MSDOS.C:	Operating specific I/O and Spawning functions
 *			under the MS/PCDOS operating system
 *			for MicroEMACS 3.10
 *			(C)opyright 1988 by Daniel M. Lawrence
 *
 */

/*#include	<windows.h>*/

#include 	<stdio.h>
#include	<errno.h>
#include	<signal.h>
#include	<setjmp.h>
#include	<time.h>
#include	<fcntl.h>
/*#include	<io.h>*/
/*#include	<bios.h>*/

#include	"osdep.h"
#include	"estruct.h"
#include	"efunc.h"
#include        "edef.h"
#include        "pico.h"
#include	"dos_gen.h"

#ifdef	MOUSE

#define MOUSE_BUTTONS		3
					 
#endif	/* MOUSE */

/*
 * Internal functions...
 */
static int	MapMSKEYtoPK (int c);
static int	ProcessMouse(MEvent *, unsigned int *);
static void	invert_label(int, MENUITEM *);
int		ttopen ();
int		ttclose ();


/*
 * Include generic DOS/Windows routines
 */
#include	"dos_gen.c"


#define	MARGIN			8	/* size of minimim margin and	*/
#define	SCRSIZ			64	/* scroll size for extended lines */



/*
 * Standard terminal interface dispatch table. Fields point to functions
 * that operate the terminal.  All these functions live in mswin.c, but
 * this structure is defined here because it is specific to pico.
 */
TERM    term    = {
        0,
        0,
	MARGIN,
	SCRSIZ,
        ttopen,
        ttclose,
        mswin_getc,
	mswin_putc,
        mswin_flush,
        mswin_move,
        mswin_eeol,
        mswin_eeop,
        mswin_beep,
	mswin_rev
};



int ttresize (row, col)
{
    resize_pico (row-1, col);
    return (0);
}



/*
 * This function is called once to set up the terminal device streams.
 */
int
ttopen()
{
    int rows, columns;

    
    mswin_getscreensize (&rows, &columns);
    term.t_nrow = rows - 1;
    term.t_ncol = columns;
    term.t_scrsiz = (columns * 2) / 3;
    
    /*
     * Do we implement optimized character insertion and deletion?
     * o_insert() and o_delete()
     */
    inschar  = delchar = FALSE;
    revexist = TRUE;
    
    mswin_setresizecallback (ttresize);
    
    

#if	MOUSE
    init_mouse();
#else	/* !MOUSE */
    mexist = 0;
#endif	/* MOUSE */
    return(1);
}


#ifdef	MOUSE
/* 
 * init_mouse - check for and initialize mouse driver...
 */
int
init_mouse()
{
    nbuttons = MOUSE_BUTTONS;
    return (mexist = TRUE);		/* Mouse always exists under windows */
}


/*
 * mouseon - call made available for programs calling pico to turn ON the
 *           mouse cursor.
 */
void
mouseon()
{
}


/*
 * mouseoff - call made available for programs calling pico to turn OFF the
 *           mouse cursor.
 */
void
mouseoff()
{
}
#endif



/*
 * This function gets called just before we go back home to the command
 * interpreter.
 */
int
ttclose()
{
    mswin_clearresizecallback (ttresize);
    return(1);
}



/*
 * Flush terminal buffer. Does real work where the terminal output is buffered
 * up. A no-operation on systems where byte at a time terminal I/O is done.
 */
int
ttflush()
{
    return(1);
}



/*
 * Read in a key.
 * Do the standard keyboard preprocessing. Convert the keys to the internal
 * character set.  Resolves escape sequences and returns no-op if global
 * timeout value exceeded.
 */
int
GetKey ()
{
    int			ch = 0;
    unsigned int	lch;
    long		timein;
    int			controlKey;
    MEvent		mouse;
    
    
    controlKey = FALSE;			/* xxx Don't know how to tell. */
    ch = NODATA;

    
    timein = time(0L);


    /*
     * Main character processing loop.
     */
    while((ch = mswin_getc ()) == MSWIN_KEY_NODATA) {

#if MOUSE
	/* Check Mouse.  If we get a mouse event, convert to char
	 * event and return that. */
	if (checkmouse (&ch)) {
	    curwp->w_flag |= WFHARD;
	    return (ch);
	}
#endif /* MOUSE */


	/* Check Timeout. */
	if(time(0L) >= timein+timeout) 
	    return(NODATA);
    }

    
    return (MapMSKEYtoPK (ch));
}



static int
MapMSKEYtoPK (int c)
{
    switch (c) {
	case MSWIN_KEY_UP:		return (K_PAD_UP);
	case MSWIN_KEY_DOWN:		return (K_PAD_DOWN);
	case MSWIN_KEY_RIGHT:		return (K_PAD_RIGHT);
	case MSWIN_KEY_LEFT:		return (K_PAD_LEFT);
	case MSWIN_KEY_PREVPAGE:	return (K_PAD_PREVPAGE);
	case MSWIN_KEY_NEXTPAGE:	return (K_PAD_NEXTPAGE);
	case MSWIN_KEY_HOME:		return (K_PAD_HOME);
	case MSWIN_KEY_END:		return (K_PAD_END);
	case MSWIN_KEY_DELETE:		return (K_PAD_DELETE);
	case MSWIN_KEY_F1:		return (F1);
	case MSWIN_KEY_F2:		return (F2);
	case MSWIN_KEY_F3:		return (F3);
	case MSWIN_KEY_F4:		return (F4);
	case MSWIN_KEY_F5:		return (F5);
	case MSWIN_KEY_F6:		return (F6);
	case MSWIN_KEY_F7:		return (F7);
	case MSWIN_KEY_F8:		return (F8);
	case MSWIN_KEY_F9:		return (F9);
	case MSWIN_KEY_F10:		return (F10);
	case MSWIN_KEY_F11:		return (F11);
	case MSWIN_KEY_F12:		return (F12);
	case MSWIN_KEY_NODATA:		return (NODATA);
    }
    
    /* Control keys. */
    if (c < ' ') 
	return (CTRL | (c + '@'));
    
    /* Normal keys. */
    return (c);
}





#if	MOUSE
/* 
 * checkmouse - Check mouse and return maped command.
 *
 *	EXPORTED to pico.
 */
int	
checkmouse (unsigned int *ch)
{
    static int	oindex;		/* Index of previous mouse down. */
    int		k;		/* current bit/button of mouse */
    int		mcol;		/* current mouse column */
    int		mrow;		/* current mouse row */
    int		down;		/* TRUE when mouse down event. */
    int		rv = 0;		/* TRUE when we have something to return. */
    MEvent      mouse;

    
    
    *ch = 0;
    
    /* Mouse installed? */
    if (!mexist)
	return (FALSE);

    if (!WinGetMEvent (&mouse)) 
	return (FALSE);


    /* Location of mouse event. */
    mcol = mouse.nColumn;
    mrow = mouse.nRow;


    /* Only pay attation to button 1. */
    if (mouse.button == 1) {
	int i = 0;

	/* Mouse down or up? */
	down = FALSE;
	if (mouse.event == MEVENT_MOUSEDOWN) {	/* button down */
	    down = TRUE;
	    oindex = -1;	/* No Previous mouse down. */
        }

	
	/* In special screen region? */
	if(mfunc.action && M_ACTIVE(mrow, mcol, &mfunc)){
	    unsigned long r;

	    r = (*mfunc.action)(down, mrow, mcol);
	    if (r & 0xffff){
		*ch = (unsigned) ((r>>16) & 0xffff);
		rv  = TRUE;
	    }
	}
	else{
		
	    /* In any of the menuitems? */
	    while(1){	/* see if we understand event */
		if(i >= 12){
		    i = -1;	/* Not Found. */
		    break;
		}

		if(M_ACTIVE(mrow, mcol, &menuitems[i]))
		    break;	/* Found. */

		i++;		/* Next. */
	    }

	    /* Now, was that a mouse down or mouse up? */
	    if (down) {				/* button down */
		oindex = i;			/* remember where */
		if(i != -1)			/* invert label */
		    invert_label (1, &menuitems[i]);
	    }
	    else {				/* button up */
		if (oindex != -1) {		/* If up in menu item. */
		    if (i == oindex){		/* And same item down in. */
			*ch = menuitems[i].val; /* Return menu character. */
			rv = 1;
		    }
		}
	    }
	}

	/* If this is mouse up AND there was a mouse down in a menu item
	 * then uninvert that menu item */
	if(!down && oindex != -1)
	  invert_label(0, &menuitems[oindex]);	/* restore label */
    }
    return(rv);
}


/*
 * invert_label - highlight the label of the given menu item.
 */
static void
invert_label(state, m)
int state;
MENUITEM *m;
{
    int			i, j, r, c, p;
    char		*lp;
    int			old_state;
	int			wasShown;

    if(m->val == mnoop)
      return;

  
	mswin_getpos (&r, &c);			/* get cursor position */
	wasShown = mswin_showcursor (0);
    old_state = mswin_getrevstate ();
    (*term.t_move)(m->tl.r, m->tl.c);
    (*term.t_rev)(state);

	for(i = m->tl.r; i <= m->br.r; i++) {
      for(j = m->tl.c; j <= m->br.c; j++) {
        if(i == m->lbl.r && j == m->lbl.c){	/* show label?? */
	      lp = m->label;
	      while(*lp && j++ < m->br.c)
	        (*term.t_putchar)(*lp++);

	      continue;
	    }
	    else
	      (*term.t_putchar)(' ');
      }
    }

    (*term.t_rev)(old_state);
	mswin_move (r, c);
	mswin_showcursor (wasShown);
}
#endif	/* MOUSE */



/*
 * alt_editor - fork off an alternate editor for mail message composition
 *
 */
int
alt_editor(int f, int n)
{
    char   eb[NLINE];				/* buf holding edit command */
    char   *fn;					/* tmp holder for file name */
    char   *writetmp();
    int	   status;
    int	   done;
    int	   rc;


    if(Pmaster == NULL)
      return(-1);

    if(gmode&MDSCUR){
	emlwrite("Alternate editor not available in restricted mode", NULL);
	return(-1);
    }

    if(Pmaster->alt_ed == NULL){
	if(!(gmode&MDADVN)){
	    emlwrite("\007Unknown Command",NULL);
	    return(-1);
	}
	
	/* Guess which editor they want. */
	if(getenv("EDITOR"))
	  strcpy(eb, (char *)getenv("EDITOR"));
	else
	  *eb = '\0';
  
	done = FALSE;
	while(!done){
	    rc = mlreplyd("Which alternate editor ? ",eb,NLINE,QDEFLT,NULL);

	    switch(rc){
	      case ABORT:
		return(-1);
	      case HELPCH:
		emlwrite("no alternate editor help yet", NULL);

/* take sleep and break out after there's help */
		sleep(3);
		break;
	      case (CTRL|'L'):
		sgarbf = TRUE;
		update();
		break;
	      case TRUE:
	      case FALSE:			/* does editor exist ? */
		if(*eb == '\0'){		/* leave silently? */
		    mlerase();
		    return(-1);
		}

		done++;
		break;
	      default:
		break;
	    }
	}
    }
    else
      strcpy(eb, Pmaster->alt_ed);

    if((fn=writetmp(0, 1)) == NULL){		/* get temp file */
	emlwrite("Problem writing temp file for alt editor", NULL);
	return(-1);
    }

    strcat(eb, " ");
    strcat(eb, fn);
    
    emlwrite("Waiting for alternate editor to finish...", NULL);
    
    status = mswin_exec_and_wait ("alternate editor", eb);

    switch (status) {

    case 0:
	/*
	 * Success:  replace edited text with new text 
	 */
	curbp->b_flag &= ~BFCHG;	/* make sure old text gets blasted */
	readin(fn, 0);			/* read new text overwriting old */
	unlink(fn);			/* blast temp file */
	curbp->b_flag |= BFCHG;		/* mark dirty for packbuf() */
	ttopen();			/* reset the signals */
	refresh(0, 1);			/* redraw */
	return(0);

	
	/*
	 * Possible errors.
	 */
    case -1:
	/* Failed to map return from WinExec to a HTASK. */
	emlwrite("Problem finding alternet editor task handle.", NULL);
	return (-1);
	
    case -2:
	/* User decided to abandon the alternate editor.*/
	emlwrite("Alternate editor abandoned.", NULL);
	return (-1);

	
	/*
	 * Positive error codes are from WinExec.  The _really_ common
	 * ones get interpreted.
	 */
    case 2:
    case 3:
	emlwrite("Alternate editor %s not found.", eb);
	return (-1);
	
    case 8:
	emlwrite("Not enough memory to run alternate editor.", NULL);
	return (-1);
	
    default:
	emlwrite("Error %d starting alternate editor.",(void *)status);
	return (-1);
    }
    return (-1);
}




/*
 *  bktoshell - suspend and wait to be woken up
 *
 *  NOTE:  Not yet used under WINDOWS.
 */
int
bktoshell()
{
    return (0);
}


/*
 * P_open - run the given command in a sub-shell returning a file pointer
 *	    from which to read the output
 *
 * note:
 *	For OS's other than unix, you will have to rewrite this function.
 *	Hopefully it'll be easy to exec the command into a temporary file, 
 *	and return a file pointer to that opened file or something.
 *
 * xxx Need to figure out how to do this in windows.
 */
FILE *P_open(c)
char *c;
{
    return (NULL);
}


/*
 * P_close - close the given descriptor
 *
 */
P_close(fp)
FILE *fp;
{
}







/*
 * FileBrowse - display contents of given directory dir
 *
 *	    intput:  
 *		     dir points to initial dir to browse.
 *		     fn  initial file name. 
 *		     flags
 *
 *         returns:
 *                   dir points to currently selected directory (without
 *			 trailing seperator character).
 *                   fn  points to currently selected file
 *                   sz  points to size of file if ptr passed was non-NULL
 *
 *                   1 if a file's been selected
 *                   0 if no files seleted
 *                  -1 if there where problems
 */
FileBrowse(dir, fn, sz, flags)
char *dir, *fn, *sz;			/* dir, name and optional size */
int  flags;
{
    struct stat sbuf;
    int			rc;
    char		lfn[NFILEN];
    
    if (flags & FB_SAVE) {
	rc = mswin_savefile (dir, fn, NFILEN);
    }
    else {
	*fn = '\0';				/* No initial file names for 
						 * open. */
	rc = mswin_openfile (dir, fn, NFILEN);
    }
    if (rc == 0) {
	if (sz != NULL) {
	    /* build full path to stat file. */
	    strcpy (lfn, dir);
	    strcat (lfn, S_FILESEP);
	    strcat (lfn, fn);
	    if (stat (fn, &sbuf) < 0) 
		 strcpy (sz, "0");
	    else 
		 strcpy (sz, prettysz (sbuf.st_size));
	}
	return (1);
    }
    return (0);
}



ResizeBrowser ()
{
    return (0);
}
