/* ASCIITable **** Version 2.00, R.Florac, 14 novembre 1999

   This program is freeware, do what you want with it, but
   please, send me your ideas for improvements or bug reports.

   Compiles with SASC :
   sc link NOSTACKCHECK OPT STRINGMERGE ASCIITable.c */

#include <stdio.h>			    /* for sprintf function */
#include <string.h>
#include <libraries/gtlayout.h>
#include <libraries/iffparse.h>
#include <libraries/locale.h>
#include <libraries/commodities.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/commodities.h>
#include <proto/icon.h>
#include <proto/intuition.h>
#include <proto/locale.h>
#include <proto/iffparse.h>
#include <proto/gtlayout_protos.h>	    /* you need the gtlayout stuff */
#include <pragmas/gtlayout_pragmas.h>


#define ID_FTXT MAKE_ID('F','T','X','T')
#define ID_CHRS MAKE_ID('C','H','R','S')
#define EVT_HOTKEY 1

#define CATALOG_VERSION 1


/* Gadgets ID (have to be greater than 255) */
enum { STRING = 256, PAGE, CANCEL, ABOUT, QUIT, COPY, CLEAR };

struct Library * CxBase, * GTLayoutBase, * IFFParseBase, * IconBase, * LocaleBase;
struct IntuitionBase * IntuitionBase;
APTR catalogue;
CxObj * commodity = 0, * filter, * sender, * traductor;
struct LayoutHandle * handle = 0;
unsigned char * hotkeys = "lcommand a";
unsigned char lettres[(128-32)*2], texte[32] = "", window_title[40];

char info_title[] = "ASCIITable 2.00 (©) R.Florac 14/10/99";

/* Localization tables *******************************************************/
enum { COMMODITY_TITLE, DESCRIPTION,
    PAGE_ID, COPY_ID, DELETE_ID, INFO_ID, QUIT_ID,
    OPERATION_FAILED, OPERATION_SUCCESS, CHARACTER, HEXA_STRING, DECIMAL_STRING
};

char * strings[] = {
    "ASCII table commodity", "Uses the clipboard.device",
    "Page", "Copy", "Clear", "Info", "Quit", "Operation failed", "Operation succeeded",
    "Char:", ", Hexa:", ", Decimal:"
};


/* Commodity structure *******************************************************/
struct NewBroker newbroker = {
    NB_VERSION,     /* nb_Version - Version of the NewBroker structure */
    "ASCIITable",   /* nb_Name - Name Commodities uses to identify this commodity */
    0,		    /* nb_Title - Title of commodity that appears in CXExchange */
    0,		    /* nb_Descr - Description of the commodity */
    NBU_UNIQUE | NBU_NOTIFY,	    /* nb_Unique - Tells CX not to launch another commodity with same name */
    COF_SHOW_HIDE,  /* nb_Flags - Tells CX if this commodity has a window */
    0,		    /* nb_Pri - This commodity's priority */
    0,		    /* nb_Port - MsgPort CX talks to */
    0		    /* nb_ReservedChannel - reserved for later use */
};


void __regargs init_textes_gadgets (unsigned char first_letter)
{   long i = 0, j;
    for (j = 32;  j < 128;  j++)
    {	lettres[i++] = first_letter++;	lettres[i++]=0; }
}

void __regargs gadget_letter (char * label, long id)
{
    LT_New (handle, LA_Type, BUTTON_KIND, LA_NoKey, TRUE, LA_LabelText, label, LA_ID, id, TAG_DONE);
}

void __regargs groupe_horizontal (char * letters, long line)
{   long i;
    LT_New (handle, LA_Type, HORIZONTAL_KIND, TAG_DONE);
    for (i = 0;  i < 16;  i++)
	gadget_letter (&letters[2*i], line+i);
    LT_EndGroup (handle);
}

unsigned char * __regargs GetString (long i)
{   unsigned char * def = strings[i];
    return (catalogue ? GetCatalogStr (catalogue, i, def) : def);
}

__saveds __asm unsigned char * LocaleHookFunc (register __a0 struct Hook * UnusedHook, register __a2 APTR Unused, register __a1 long ID)
{
    return GetString(ID);
}

struct Hook LocaleHook = { 0, 0, (HOOKFUNC) LocaleHookFunc, 0, 0 };

void __regargs set_string (char * t)
{
    LT_SetAttributes (handle, STRING, GTST_String, t, TAG_END);
}

void close_window (void)
{
    LT_DeleteHandle (handle);
    handle = 0;
}

void __regargs set_title (char * t)
{
    SetWindowTitles (handle->Window, t, (char *) -1);
}

void __regargs open_window (void)
{
    long i;

    handle = LT_CreateHandleTags (0, LAHN_LocaleHook, &LocaleHook,TAG_DONE);
    if (handle)
    {	LT_New (handle, LA_Type, VERTICAL_KIND, TAG_DONE);
	{   for (i = 0;  i < 6;  i++)
		groupe_horizontal (lettres + i * 32, 16 * i);
	    LT_New (handle, LA_Type, XBAR_KIND, TAG_DONE);
	    LT_New (handle, LA_Type, STRING_KIND, GTST_String, texte, GTST_MaxChars, 30, LA_ID, STRING, TAG_DONE);
	    LT_New (handle, LA_Type, HORIZONTAL_KIND, LAGR_SameSize, TRUE, LAGR_Spread, TRUE, TAG_DONE);
	    {
		LT_New (handle, LA_Type, BUTTON_KIND, LA_LabelID, PAGE_ID, LA_ID, PAGE, TAG_DONE);
		LT_New (handle, LA_Type, BUTTON_KIND, LA_LabelID, COPY_ID, LA_ID, COPY, TAG_DONE);
		LT_New (handle, LA_Type, BUTTON_KIND, LA_LabelID, DELETE_ID, LA_ID, CLEAR, TAG_DONE);
		LT_New (handle, LA_Type, BUTTON_KIND, LA_LabelID, INFO_ID, LA_ID, ABOUT, TAG_DONE);
		LT_New (handle, LA_Type, BUTTON_KIND, LA_LabelID, QUIT_ID, LA_ID, QUIT, TAG_DONE);
		LT_EndGroup (handle);
	    }
	    LT_EndGroup (handle);
	}
	if (! LT_Build (handle,
	    LAWN_Title, 	info_title,
	    LAWN_BelowMouse,	TRUE,
	    WA_DepthGadget,	TRUE,
	    WA_DragBar, 	TRUE,
	    WA_Activate,	TRUE,
	    WA_CloseGadget,	TRUE,
	    WA_RMBTrap, 	TRUE,
	    LAWN_IDCMP, 	IDCMP_CLOSEWINDOW | CHECKBOXIDCMP,
	TAG_DONE))
	    close_window ();
    }
}

void __regargs copy (char * t)
{   long error = 1;
    struct IFFHandle * iff;

    if (IFFParseBase = OpenLibrary ("iffparse.library", 0L))
    {	if (iff = AllocIFF ())
	{   if (iff->iff_Stream = (ULONG) OpenClipboard (0))
	    {	InitIFFasClip (iff);
		if (! OpenIFF (iff, IFFF_WRITE))
		{   if (! PushChunk (iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN))
		    {	if (! PushChunk (iff, 0, ID_CHRS, IFFSIZE_UNKNOWN))
			{   error = strlen (t);
			    if (WriteChunkBytes (iff, t, error) == error)
				error = 0;
			}
			if (error == 0) error = PopChunk (iff);
		    }
		    if (error == 0) error = PopChunk (iff);
		    CloseIFF(iff);
		}
		CloseClipboard ((struct ClipboardHandle *) iff->iff_Stream);
	    }
	    FreeIFF (iff);
	}
	CloseLibrary (IFFParseBase);
    }
    if (error)
	set_title (GetString(OPERATION_FAILED));
    else
	set_title (GetString(OPERATION_SUCCESS));
}

BOOL open_all (void)
{   if (LocaleBase = OpenLibrary ("locale.library",38))     /* Perhaps some library versions to change... */
	catalogue = OpenCatalog (NULL, "ASCIITable.catalog", OC_Version, CATALOG_VERSION, TAG_DONE);
    if (IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 37))
	if (GTLayoutBase = OpenLibrary ("gtlayout.library", 39L))
	    if (CxBase = OpenLibrary ("commodities.library", 37) )
	    {	newbroker.nb_Title = GetString (COMMODITY_TITLE);
		newbroker.nb_Descr = GetString (DESCRIPTION);
		return 1;
	    }
    return 0;
}

void close_all (void)
{
    if (catalogue)
	CloseCatalog (catalogue);
    if (LocaleBase)
	CloseLibrary (LocaleBase);
    if (commodity)
	DeleteCxObjAll (commodity);
    if (newbroker.nb_Port)
    {	struct Message * message;
	while (message = GetMsg (newbroker.nb_Port))
	    ReplyMsg (message);
	DeletePort (newbroker.nb_Port);
    }
    if (CxBase)
	CloseLibrary (CxBase);
    if (GTLayoutBase)
	CloseLibrary (GTLayoutBase);
    if (IntuitionBase)
	CloseLibrary ((struct Library *) IntuitionBase);
}

long __regargs handle_commodity (CxMsg * message)
{   ULONG type_message = CxMsgType (message), identificateur = CxMsgID (message);
    long end = 0;
    ReplyMsg ((struct Message *) message);
    switch (type_message)
    {	case CXM_IEVENT:
	    if (identificateur == EVT_HOTKEY)        /* Combinaison touches d'appel */
		if (handle)
		    close_window ();
		else
		    open_window ();
	    break;
	case CXM_COMMAND:
	    switch (identificateur)
	    {	case CXCMD_DISABLE:	    /* Désactivation de la commodité */
		    ActivateCxObj (commodity, 0);  break;
		case CXCMD_ENABLE:	    /* Activation de la commodité */
		    ActivateCxObj (commodity, 1);  break;
		case CXCMD_DISAPPEAR:
		    close_window ();
		    break;
		case CXCMD_KILL:
		    end = 1;	    break;
		case CXCMD_UNIQUE:	    /* On a tenté de relancer... */
		    if (handle)
		    {	end = 1;    break;  }
		case CXCMD_APPEAR:
		    if (handle)
			WindowToFront (handle->Window);
		    else
			open_window ();
	    }
    }
    return end;
}

unsigned char * addspace (unsigned char * t)
{   long l =strlen (t);
    t += l;
    *t = ' ';
    t++;
    *t = 0;
    return t;
}

long handle_window (void)
{
    struct IntuiMessage *message;
    struct Gadget * gad;
    long end = 0;
    static long page = 1, rang = 0;
    ULONG classe;
    unsigned char * lettre, * t;

    while (message = LT_GetIMsg (handle))
    {	classe = message->Class;
	gad = (struct Gadget *) message->IAddress;
	LT_ReplyIMsg (message);
	switch (classe)
	{   case IDCMP_CLOSEWINDOW:
		close_window ();    return 0;
	    case IDCMP_GADGETUP:
		/* Each ASCII char gadget has an ID corresponding to its ASCII code */
		if (gad->GadgetID < 256)
		{   lettre = lettres + 2 * gad->GadgetID;
		    /* and then, better than sprintf but a bit harder... (2k bytes saved) */
		    strcpy (window_title, GetString(CHARACTER));
		    t = addspace (window_title);
		    * t= * lettre;
		    strcpy (++t, GetString (HEXA_STRING));
		    t = addspace (t);
		    stcl_h (t, * lettre);
		    strcat (t, GetString(DECIMAL_STRING));
		    t = addspace (t);
		    stcl_d (t, * lettre);
		    set_title (window_title);
		    if (rang < 30)
		    {	texte[rang++] = * lettre;
			texte[rang] = 0;
			set_string (texte);
		    }
		}
		else
		{   switch (gad->GadgetID)
		    {	case PAGE:
			    page ^= 1;
			    init_textes_gadgets (page ? 160 : ' ');
			    RefreshGList (handle->Window->FirstGadget, handle->Window, 0, -1);
			    break;
			case COPY:
			    if (rang)           /* NEVER copy a null string ! */
				copy (texte);
			    break;
			case CLEAR:
			    texte[rang = 0] = 0;
			    set_string (texte);
			case ABOUT:
			    set_title (info_title);
			    break;
			case QUIT:
			    end = 1;	break;
			case STRING:
			    lettre = (unsigned char *) LT_GetAttributes (handle, STRING, TAG_DONE);
			    strcpy (texte, lettre);
			    rang = strlen(texte);
			    break;
		    }
		}
		break;
	}
    }
    if (end)
	close_window ();
    return end;
}

void handle_events (void)
{
    long end = 0;
    ULONG signal_reçu;
    struct Message * message;

    while (! end)
    {	ULONG signaux = 0;
	if (handle)
	    signaux = 1 << handle->Window->UserPort->mp_SigBit;
	signaux |= SIGBREAKF_CTRL_C | 1L << newbroker.nb_Port->mp_SigBit;
	signal_reçu = Wait (signaux);
	if (handle)
	    end = handle_window ();
	if (signal_reçu & SIGBREAKF_CTRL_C)
	    end = 1;
	if (signal_reçu & 1L << newbroker.nb_Port->mp_SigBit)
	    while (message = GetMsg (newbroker.nb_Port))
		end |= handle_commodity ((CxMsg *) message);
    }
    if (handle);
	close_window ();
}

long init_commodity (void)
{   if (newbroker.nb_Port = CreateMsgPort())
    {	newbroker.nb_Pri = 0;
	if (commodity = CxBroker (&newbroker, 0))
	{   if (filter = CxFilter (hotkeys))
	    {	AttachCxObj (commodity, filter);
		if (sender = CxSender (newbroker.nb_Port, EVT_HOTKEY) )
		{   AttachCxObj (filter, sender);
		    if (traductor = CxTranslate (0))
		    {	AttachCxObj (filter, traductor);
			if (! CxObjError (filter))
			{   ActivateCxObj (commodity, 1);
			    return 1;
			}
		    }
		}
	    }
	}
    }
    return 0;
}

void main (long argc, char ** argv)
{
    BOOL open = 1;

    if (open_all ())
    {	if (argc == 0)
	{   struct WBStartup * WBenchMsg;
	    struct WBArg * wbarg;
	    struct DiskObject * dobj;
	    char ** toolarray, * s;
	    BPTR olddir = -1;
	    if (IconBase = OpenLibrary ("icon.library", 33))
	    {	WBenchMsg = (struct WBStartup *) argv;
		wbarg = WBenchMsg->sm_ArgList;
		if ((wbarg->wa_Lock)  &&  (*wbarg->wa_Name))
		    olddir = CurrentDir (wbarg->wa_Lock);
		if ((*wbarg->wa_Name)  &&  (dobj = GetDiskObject (wbarg->wa_Name)))
		{   toolarray = (char **) dobj->do_ToolTypes;
		    if (s = (char *) FindToolType (toolarray, "CX_POPKEY"))
			hotkeys = s;
		    if (s = (char *) FindToolType (toolarray, "CX_POPUP"))
			if (MatchToolValue (s, "NO"))
			    open = 0;
		    FreeDiskObject (dobj);
		}
		if (olddir != -1)
		    CurrentDir (olddir);
		CloseLibrary (IconBase);
	    }
	}
	if (argc > 1)
	    if (stricmp (argv[1], "NOWIN") == 0)
		open = 0;
	init_textes_gadgets (160);
	if (init_commodity ())
	{
	    if (open)
		open_window ();
	    handle_events ();
	}
    }
    close_all ();
}
