#define PRTMAC_C

/* PrintMacros - Version 1.0 07-10-93
   Copywrite 1993 by Brian Moats

   This program will print MachV macros to the printer, a file, or a window.
   It requires MachV 5.0 or later to be running.

   PrintMacros is called from SetMachV when you click on 'Print Macros'. The
   configuration that is current at that time is the one whose macros will be
   printed. It may also be called from the shell with one argument - the name
   of the desired configuration. If no name is supplied, the current
   configuration will be printed.

   You may also create a macro that will execute this program. Example:

   Exe_Cmd"PrintMacros \"GNU Emacs#?\""

   This program may also serve as an example of how to interact with MachV.
   It is not an elegant example of coding, however ;-).

   If you have the appropriate catalog, PrintMacros will adapt to that
   language. deutsch and français are provided.

   Because MachV is locked by ObtainSemaphore() while printing, MachV can
   not do anything. This may be inconvenient, but it is proper.


   sc PrintMacros
   slink lib:cback.o PrintMacros.o lib lib:sc.lib lib:debug.lib lib:amiga.lib

   or to make smaller:

   slink lib:cback.o PrintMacros.o sprintf.o lib lib:sc.lib lib:debug.lib \
         lib:amiga.lib
   
       (requires sprintf.a)

*/

#include <string.h>
#include <stdio.h>
#include <proto/asl.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <clib/keymap_protos.h>
#include <clib/locale_protos.h>
#include <exec/execbase.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/gadtools.h>

#define STRINGARRAY

#include "prtstr.h" /* created by catcomp */
#include "m43:headers/mach.h"
#include "m43:headers/machlib.h"
#include "m43:headers/machlib_protos.h"
#include "m43:headers/machlib_pragmas.h"

#define WIN_WIDTH    150
#define WIN_HEIGHT    76

#define WIN_FLAGS  WFLG_ACTIVATE | WFLG_SMART_REFRESH | WFLG_CLOSEGADGET | \
				   WFLG_NOCAREREFRESH | WFLG_DEPTHGADGET | WFLG_DRAGBAR
	
#define IDCMP_FLAGS IDCMP_CLOSEWINDOW | IDCMP_GADGETUP | IDCMP_REFRESHWINDOW
	
#define WHERE_GID   100
#define GO_GID      101
#define ABORT_GID   102

#define PRINT_TO_CONSOLE	 0
#define PRINT_TO_PRINTER	 1
#define PRINT_TO_FILE		 2	

/* ============== Local Protos ============== */

static void handle_msg(void);
static BOOL main_window_open(void);
static void main_window_close(void);
static void set_language(void);
static void print_macros(void);
static void get_keytext(UWORD key,UWORD qual);
static BOOL XLateKeyQual(UWORD code,UWORD qual,UBYTE *buf);
static BOOL FileByRequest(void);
static void FreeFileRequestStruct(void);

static void writemacro(void);
static void writestr(char *s);
static void writehdr(void);
static void writedash(void);
static void writepadded(char *str,long max);
static void pause(void);
static void getquals(void);


/* ============= Globals ============== */

struct MachLibrary *MachBase;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *GadToolsBase, *LocaleBase, *KeymapBase, *AslBase;
APTR Catalog;

/* detach stuff */

long __stack = 4000;
long __priority = 0;
long __BackGroundIO = 1;
char *__procname = "PRINTMACROS";

extern BPTR _Backstdout;   /* file handle we will write to with */

/* ============== Locals ============== */

static struct Window *mainw;

static struct TextAttr Topaz80 = {(STRPTR)"topaz.font",8,0,FPF_ROMFONT};
static struct VisualInfo  *VI; 

static char *where_labels[] = {NULL,NULL,NULL,NULL};

static struct Gadget *print_glist;

static WORD wle,conh,conw;
static WORD where_to;
static BOOL print_done,wb_to_back;
static LONG maxlines,maxconsolelines,linecnt;

static BPTR fh;
static char printdirname[80],printfilename[40];

static char blanks[43] = "                                          ";
static char pressreturn[] = "Press Return";

static char curname[16]; /* name of macro currently being printed */

static char constr[50] = "Con:0/0/640/200/Macros/CLOSE";

static char qualtext[16],keytext[10];

static struct MacroObject *mo; /* macro currently being printed */
static struct MachCfg  *cfg;

static ULONG toppix;


main(int argc, char **argv)
{
	IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",37L);
	GfxBase	= (struct GfxBase *) OpenLibrary("graphics.library",37L);
	GadToolsBase = OpenLibrary("gadtools.library",37L);
	KeymapBase		= (struct Library*)OpenLibrary("keymap.library",0L);
	MachBase = (struct MachLibrary*)OpenLibrary("mach.library",37L);

	if (cfg = MachBase->ml_CurConfig) { /* then MachV is running */
		set_language();
		where_to = PRINT_TO_CONSOLE;
		if (argc > 1) {
			argv++;
			cfg = FindConfigByName(*argv);
		}
		if (cfg) {
			if (main_window_open()) {
				while (mainw) {
					Wait(1L << mainw->UserPort->mp_SigBit);
					handle_msg();
				}
			}
		}
		else
			Write(_Backstdout,"Can't find configuration!\n",26L);
		CloseLibrary((struct Library*)MachBase);
	}
	else
		Write(_Backstdout,"MachV must be active first!\n",30L);
	if (wb_to_back)
		WBenchToBack();
	if (IntuitionBase)
		CloseLibrary((struct Library*)IntuitionBase);
	if (GfxBase)
		CloseLibrary((struct Library*)GfxBase);
	if (GadToolsBase)
		CloseLibrary(GadToolsBase);
	if (KeymapBase)
		CloseLibrary(KeymapBase);
}

static void print_macros(void)
{
	UBYTE            fullpath[160];
	ULONG			 match_type;

	switch (where_to) {
    case PRINT_TO_CONSOLE:
		sprintf(constr,"Con:0/0/640/%ld/Macros/CLOSE",conh);
		fh = Open((STRPTR)constr,MODE_OLDFILE);
		maxlines = maxconsolelines;
		break;
    case PRINT_TO_PRINTER:
		fh = Open((STRPTR)"prt:",MODE_OLDFILE);
		maxlines = 99999;
		break;
    case PRINT_TO_FILE:
		if (AslBase = OpenLibrary((UBYTE*)"asl.library",37L)) {
			if (FileByRequest()) {
				strcpy((char*)fullpath,printdirname);
				AddPart(fullpath,(UBYTE*)printfilename,160);
				fh = Open((STRPTR)fullpath,MODE_NEWFILE);
				FreeFileRequestStruct();
			}
			CloseLibrary(AslBase);
		}
		maxlines = 99999;
		break;
	}
	print_done = 0;
	linecnt = 0;
	if (fh) {
		writestr(cfg->mc_Title);
		writehdr();
		writedash();
		ObtainSemaphore(&MachBase->ml_BaseSemaphore);
		while (!print_done) {
			match_type = MT_WILDCARDS; /* find all macros */
			while (mo = FindByName(cfg,"#?",match_type)) {
				match_type |= MT_MATCH_NEXT; 	/* keep going */
				writemacro(); /* write to printer, file or window */
				if (print_done) /* writestr() can set this */
					break;
				handle_msg();  /* check for abort gadget hit */
			}
			if (mo == NULL) /* did all, now pause */
				pause();
			break;
		}
		ReleaseSemaphore(&MachBase->ml_BaseSemaphore);
		Close(fh);
		fh = NULL;
	}
}

static void handle_msg(void)
{
	struct IntuiMessage *imsg;
	struct Gadget *gadget;
	ULONG iclass;
	WORD icode;

	while (mainw && (imsg = GT_GetIMsg(mainw->UserPort))) {
		iclass = imsg->Class;
		icode = imsg->Code;
		gadget = (struct Gadget *) imsg->IAddress;
		GT_ReplyIMsg(imsg);
		if (iclass == IDCMP_GADGETUP) {
			switch (gadget->GadgetID) {
			case WHERE_GID:
				where_to = icode;
				break;
			case GO_GID:
				if (fh == NULL) /* not already printing? */
					print_macros();
				break;
			case ABORT_GID:
				print_done = 1;
				break;
			}
		}
		if (iclass == IDCMP_CLOSEWINDOW)
			main_window_close();
	}
}

static BOOL main_window_open(void)
{
	struct Screen *public_screen;
	struct Gadget *gad;
	struct NewGadget ng;
	int i,w,width;

	where_labels[0] = AppStrings[MSG_DEST_WINDOW_GAD].as_Str;
	where_labels[1] = AppStrings[MSG_DEST_PRINTER_GAD].as_Str;
	where_labels[2] = AppStrings[MSG_DEST_FILE_GAD].as_Str;

	width = 0; /* get max width for cycle gadget */
	for (i=0;i<3;i++)
		if ((w = strlen(where_labels[i])) > width)
			width = w;

	if ((public_screen = LockPubScreen(NULL)) != NULL) {
		VI = GetVisualInfoA(public_screen,TAG_END);
		toppix = public_screen->FirstWindow->BorderTop + 1;
		wle = ((GfxBase->NormalDisplayColumns >> 1) - public_screen->LeftEdge - (WIN_WIDTH / 2));
		conh = GfxBase->NormalDisplayRows;
		conw = GfxBase->NormalDisplayColumns;
		if (public_screen->ViewPort.Modes & LACE)
			conh <<= 1;
		maxconsolelines = (conh - toppix) / public_screen->RastPort.TxHeight - 4;
		wb_to_back = public_screen != IntuitionBase->FirstScreen;
		UnlockPubScreen(NULL,public_screen);
	}
	ng.ng_VisualInfo = VI;
	ng.ng_TextAttr = &Topaz80;
	ng.ng_Height = 12;
	ng.ng_Flags = PLACETEXT_IN;
	
	gad = CreateContext(&print_glist);
	
	/* =============== CYCLE_KIND ================= */
	ng.ng_GadgetText = NULL;
	ng.ng_Width = width * 8 + 32;
	ng.ng_LeftEdge = (WIN_WIDTH - ng.ng_Width) / 2;
	ng.ng_TopEdge = toppix+2;
	ng.ng_GadgetID = WHERE_GID;
	gad = CreateGadget(CYCLE_KIND, gad, &ng, GTCY_Labels, (ULONG)where_labels, 
						GTCY_Active, 0,TAG_DONE);
	
	/* =============== Go BUTTON_KIND ================= */
	ng.ng_GadgetText = AppStrings[MSG_GO_GAD].as_Str;
	ng.ng_Width = strlen((char*)ng.ng_GadgetText)*8 + 8;
	ng.ng_LeftEdge = (WIN_WIDTH - ng.ng_Width) / 2;
	ng.ng_TopEdge = toppix+24;
	ng.ng_GadgetID = GO_GID;
	gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);
	
	/* =============== Abort BUTTON_KIND ================= */
	ng.ng_TopEdge = toppix + 46;
	ng.ng_GadgetText = AppStrings[MSG_ABORT_PRINT_GAD].as_Str;
	ng.ng_Width = strlen(ng.ng_GadgetText)*8 + 8;
	ng.ng_LeftEdge = (WIN_WIDTH - ng.ng_Width) / 2;
	ng.ng_GadgetID = ABORT_GID;
	gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);

	if (gad) {
		if (mainw = OpenWindowTags(NULL, WA_Left, wle,
								   WA_Top,30,
								   WA_Width, WIN_WIDTH,
								   WA_Height, WIN_HEIGHT,
								   WA_IDCMP,IDCMP_FLAGS,
								   WA_Flags,WIN_FLAGS,
								   WA_Title,(ULONG)AppStrings[MSG_TITLE_STR].as_Str,
								   WA_AutoAdjust,TRUE,TAG_DONE)) {
			AddGList(mainw, print_glist, -1, -1, NULL);
			RefreshGList(print_glist, mainw, NULL, -1);
			GT_RefreshWindow(mainw, NULL);
			WBenchToFront();
		}
		else {
			main_window_close(); /* frees glist */
		}
	}
	return((BOOL)(gad != NULL));
}

static void main_window_close(void)
{
	print_done = 1;
	if (mainw)
		CloseWindow(mainw);
	mainw = NULL;
	if (print_glist)
		FreeGadgets(print_glist);
	if (VI)
		FreeVisualInfo(VI);
}

/*=================================
  Set AppStrings if foreign tongue.
  =================================*/

static void set_language(void)
{
	int i;

	if (LocaleBase = OpenLibrary("locale.library",38)) {
		Catalog = OpenCatalog(NULL,"machv/printmacros.catalog",TAG_DONE);
		if (Catalog) {
			for (i=0;i < LAST_LINE_OF_TEXT;i++) {
				AppStrings[i].as_Str = GetCatalogStr(Catalog,i,AppStrings[i].as_Str);
			}
		}
	}
}

/*===========================================
  write: keytext  qualtext  curname  |  macro
         ----------------------------+-------
  ===========================================*/
static void writemacro(void)
{
	char *mac;
	int len;

	mac = mo->mo_Macro;
	len = mo->mo_Size;
	strcpy(curname,mo->mo_Name);
	get_keytext(mo->mo_Code,mo->mo_Qual);
	if (mo->mo_Code != UNKEYED) /* no qualifiers with unkeyed macros */
		getquals();
	while (len > 0) { /* do 42 chars of macro on each line */
		writepadded((char*)keytext,8);
		writepadded(qualtext,9);
		writepadded(curname,14 );
		writestr(" | ");
		writepadded(mac,42 );
		writestr("\n");
		linecnt++;
		*keytext = '\0'; /* don't want key qual and name on extra lines */
		*qualtext = '\0';
		*curname = '\0';
		mac += 42;
		len -= 42;
	}
	if (linecnt > maxlines) { /* this can only be true if output to window */
		pause(); /* shows 'Press Return' and waits for input */
		linecnt = 0;
		writehdr();
	}
	writedash();
}

static void writestr(char *s)
{
	if (!print_done)
		if (FPuts(fh,s) == -1)
			print_done = 1;
}

static void writehdr(void)
{
	writestr("\nKey   Qualifier    Name           Macro\n");
	linecnt++;
}

static void writedash(void)
{
	writestr("--------------------------------+-------------------------------------------\n" );
	linecnt++;
}

/*=========================================================
  Write a string. If length less than max, pad with blanks.
  If more than max, write only max chars. 
  =========================================================*/
static void writepadded(char *str,long max)
{
	long len;
	char savechr;

	len = strlen(str);
	if (len > max) {
		savechr = str[max];
		str[max] = '\0'; /* writestr needs null terminated string */
	}
	writestr(str);
	if (len > max) {
		str[max] = savechr;
	}
	if ((len = max-len) > 0) {
		blanks[len] = '\0';
		writestr(blanks);
		blanks[len] = ' ';
	}
}

/*=================================================================
  Print 'Press Return' and wait for input only if output to window.
  =================================================================*/
static void pause(void)
{
	if (where_to == PRINT_TO_CONSOLE) {
		writestr(pressreturn);
		Flush(fh);
		if (FGetC(fh) == -1) { /* close gadget or ctrl-c */
			print_done = 1;
		}
	}
}

/*=====================================================================
  Set qualtext to the letters C A S L R for the appropriate qualifiers.
  =====================================================================*/

static struct table {WORD  qual;
					 char let;} qualTab[] = {
					IEQUALIFIER_CONTROL,'C',
					IEQUALIFIER_LALT | IEQUALIFIER_RALT,'A',
					IEQUALIFIER_LCOMMAND,'L',
					IEQUALIFIER_RCOMMAND,'R',
					IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT,'S'
};

static void getquals(void)
{
	WORD i, idx;
	
	i = 0;
	for (idx=0;idx<5;idx++) {
		if (mo->mo_Qual & qualTab[idx].qual) {
			qualtext[i++] = qualTab[idx].let;
			qualtext[i++] = '+';
		}
	}
	if (i > 0)
		qualtext[--i] = 0;
}

/*====================================
  Set keytext for the appropriate key.
  ====================================*/

#define N0	     0x0f
#define N1	     0x1d
#define N2	     0x1e
#define N3	     0x1f
#define N4	     0x2d
#define N5	     0x2e
#define N6	     0x2f
#define N7	     0x3d
#define N8	     0x3e
#define N9	     0x3f
#define NDP	     0x3c

static short nptab[] = {
	N7, N8, N9, N4, N5, N6,
	N1, N2, N3, N0, NDP,
	0};

/* these are in keycode order */
static char *speckeys[] = {"SPACE ", "BACKSP", "TAB   ", "ENTER ", "RETURN",
						   "ESC   ", "DEL   ", "", "", "", "KP -  ", "",
						   "UP    ", "DOWN  ", "RIGHT ", "LEFT  ", "F1    ",
						   "F2    ", "F3    ", "F4    ", "F5    ", "F6    ",
						   "F7    ", "F8    ", "F9    ", "F10   ", "KP (  ",
						   "KP )  ", "KP /  ", "KP *  ", "KP +  ", "HELP  ",
						   "", "", "", "", "", "", "", "",
						   "L Butn", "R Butn", "M Butn"
};

static void get_keytext(UWORD key,UWORD qual)
{
	WORD i;
	
	/* if special key */
	if ((key >= 0x40) && (key < UNKEYED)) {  
		i = key - 0x40;
		if ((i >= (sizeof(speckeys)/4)) || (*speckeys[i] == '\0')) {
			sprintf(keytext,"0x%02lx",(ULONG)key);
		}
		else
			strcpy((char *)keytext,speckeys[i]);
	}
	else {    /* 1st check for numeric pad keys */
		i = 0;
		if (key < 128) {
			qual &= IEQUALIFIER_RSHIFT | IEQUALIFIER_LSHIFT;
			while (nptab[i] && (key != nptab[i]))
				i++;
			if (nptab[i]) {
				strcpy((char*)keytext,"KP ");
				qual |= IEQUALIFIER_NUMERICPAD;
				i = 3;
			}
			else {
				strcpy((char*)keytext,"  ");
				i = 2;
			}
			if (!XLateKeyQual(key,qual,&keytext[i])) {
				sprintf(keytext,"0x%02x",(UWORD)key);
				i = 3;
			}
			i++;
			while (i < 6)
				keytext[i++] = ' ';
			keytext[i] = '\0';
		}
		else
			strcpy((char*)keytext,"NO KEY");
	}
}

/*=========================================================
  Translate a key code and qualifier to ascii, if possible.
  =========================================================*/

static BOOL XLateKeyQual(UWORD code,UWORD qual,UBYTE *buf)
{
	struct InputEvent ev;

	memset(&ev,0,sizeof(struct InputEvent));
	ev.ie_Class = IECLASS_RAWKEY;
	ev.ie_Code = code;
	ev.ie_Qualifier = qual;
	return((BOOL)(MapRawKey(&ev,buf,1L,NULL) != -1));
}

/*============================
  Simple file request routine.
  ============================*/

static struct FileRequester *file_requester;

static BOOL FileByRequest(void)
{
  if (file_requester = (APTR)AllocAslRequest(ASL_FileRequest,TAG_DONE)) {
    if (AslRequest(file_requester,TAG_DONE)) {
      strcpy(printdirname,(char*)file_requester->fr_Drawer);
      strcpy(printfilename,(char*)file_requester->fr_File);
      return(TRUE);
    }
  }
  return(FALSE);
}

static void FreeFileRequestStruct(void)
{
  if (file_requester != NULL)
    FreeAslRequest(file_requester);
}

