/**********************************************
 **************    notes.c   ******************
 **********************************************/

#define INTUI_V36_NAMES_ONLY

#include <intuition/intuition.h>
#include <exec/exec.h>
#include <intuition/gadgetclass.h>

#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>
#include <clib/diskfont_protos.h>
#include <clib/gadtools_protos.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "stickit2.h"

#include "consts.h"
#include "structs.h"
#include "proto.h"

#ifndef GTMN_NewLookMenus
#define GTMN_NewLookMenus	GT_TagBase+67
#endif

extern prj_p prj;

extern struct NewMenu noteMenuNewMenu[];

#ifdef _DCC
extern __chip UWORD WaitPointer[];
#else
extern UWORD __chip WaitPointer[];
#endif

/*
Function : note_p note_create(int note_position)
Purpose : Allocates the memory needed for a note, sets all the values to the
	default. Returns a pointer to the new note.
*/

note_p note_create(int note_position)
{
	struct Gadget gad_resize = {
		NULL,
		-7,-7,7,7,
		GFLG_GADGHNONE | GFLG_GADGIMAGE | GFLG_RELRIGHT |
GFLG_RELBOTTOM,
		GACT_IMMEDIATE,
		GTYP_SIZING,
		NULL,NULL,NULL,NULL,NULL,
		0};

	note_p new_note;

	/* Sort out the storing of the note pointer */

	new_note = (note_p)malloc(sizeof(struct note));

	if (!new_note)
		error("Can't allocate new note",ERR_MALLOC,__LINE__,__FILE__);

	/* Set up defaults */

	new_note->win = NULL;
	new_note->font = NULL;
	new_note->visualinfo = NULL;
	new_note->menustrip = NULL;

	strncpy(new_note->fontname,prj->prefs.notefont,STRLEN_FONTNAME);

	new_note->textattr.ta_Name = new_note->fontname;
	new_note->textattr.ta_YSize = prj->prefs.textattr.ta_YSize;
	new_note->textattr.ta_Style = prj->prefs.textattr.ta_Style;
	new_note->textattr.ta_Flags = prj->prefs.textattr.ta_Flags;

	build_fontstring(new_note);

	new_note->topedge = DEFAULT_TOPEDGE;
	new_note->leftedge = DEFAULT_LEFTEDGE;
	new_note->width = prj->prefs.notewidth;
	new_note->height = prj->prefs.noteheight;

	new_note->backcolour = prj->prefs.backcolour;
	new_note->textcolour = prj->prefs.textcolour;
	new_note->caratcolour = prj->prefs.caratcolour;

	new_note->title[0] = '\0';
	strcpy(new_note->text,"Empty");
	strcpy(new_note->pubscreen,prj->prefs.pubscreen);

	/* Copy gadget into note's gadget */

	new_note->gad = gad_resize;

	/* Add to note array */

	prj->notes[note_position] = new_note;

	/* Make an entry in the linked list */

	new_note->node.ln_Name = new_note->text;
	new_note->node.ln_Type = NT_STICKIT2;
	new_note->node.ln_Pri = - (BYTE)note_position;

	/* Clear the blocking requester */

	InitRequester(&new_note->blockreq);

	/* If commodities window is open, detach the list before altering it */

	if (commod)
		GT_SetGadgetAttrs(commodGadgets[commod_listview],commod,NULL,
GTLV_Labels,~0,TAG_END);

	Enqueue(&prj->commodlist,&new_note->node);

	if (commod)
		GT_SetGadgetAttrs(commodGadgets[commod_listview],commod,NULL,
GTLV_Labels,&prj->commodlist,TAG_END);

	return(new_note);
}

/*
Function : void note_open(note_p curr_note)
Purpose : Given a note will open the window and draw the text.
*/

void note_open(note_p curr_note)
{
	struct RastPort *curr_rport;
	struct Window *curr_win;

	/* If already open, bring to front */

	if (curr_note->win) {
		WindowToFront(curr_note->win);
		return;
	}

	curr_note->win = OpenWindowTags(NULL,
		WA_Left,curr_note->leftedge,
		WA_Top,curr_note->topedge,
		WA_Width,curr_note->width,
		WA_Height,curr_note->height,
		WA_MinWidth,DEFAULT_MINWIDTH,
		WA_MaxWidth,DEFAULT_MAXWIDTH,
		WA_MinHeight,DEFAULT_MINHEIGHT,
		WA_MaxHeight,DEFAULT_MAXHEIGHT,
		WA_Gadgets,&curr_note->gad,
		WA_Flags,WFLG_CLOSEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET |
WFLG_SIMPLE_REFRESH,
		WA_IDCMP,NULL,
		WA_Title,curr_note->title,
		WA_ScreenTitle,"StickIt2 ©1994 Andy Dean",
		WA_PubScreenName,curr_note->pubscreen,
		WA_PubScreenFallBack,TRUE,
		WA_Dummy+0x30, TRUE, /* NewLookMenus */
		TAG_END);

	if (!curr_note->win)
		error("Can't open note",ERR_OPENNOTE,__LINE__,__FILE__);

	curr_win = curr_note->win;

	/* Set up menus */

	curr_note->visualinfo = GetVisualInfo(curr_win->WScreen,TAG_END);

	if (curr_note->visualinfo) {
		curr_note->menustrip = CreateMenus(noteMenuNewMenu,TAG_END);

		if (curr_note->menustrip) {
			if (LayoutMenus(curr_note->menustrip,
curr_note->visualinfo,GTMN_NewLookMenus,TRUE,TAG_END))
				SetMenuStrip(curr_win,curr_note->menustrip);
		}
	}

	/* Set up some easy references */

	curr_rport = curr_note->win->RPort;

	/* Connect window to message port */

	curr_note->win->UserPort = prj->main_msg_port;

	ModifyIDCMP(curr_note->win,IDCMP_REFRESHWINDOW|IDCMP_CLOSEWINDOW|
IDCMP_NEWSIZE|IDCMP_INACTIVEWINDOW|IDCMP_MOUSEBUTTONS|IDCMP_VANILLAKEY|
IDCMP_RAWKEY|IDCMP_CHANGEWINDOW|IDCMP_MENUPICK);

	/* Set font, if different than the system default */

	if ((!curr_note->font) && (curr_note->fontname[0] != '\0'))
		curr_note->font = OpenDiskFont(&curr_note->textattr);

	/* Add any font styles */

	if (curr_note->font) {
		SetFont(curr_note->win->RPort,curr_note->font);
		SetSoftStyle(curr_rport,curr_note->textattr.ta_Style ^
curr_note->font->tf_Style,(FSF_BOLD | FSF_UNDERLINED | FSF_ITALIC));
	}
	else	/* Cancel font name */
		curr_note->fontname[0] = '\0';

	/* No carat */

	curr_note->carat.text = NULL;

	curr_note->carat.xpos = curr_note->carat.ypos = 0;
	curr_note->carat.oldxpos = curr_note->carat.oldypos = 0;
}

/*
Function : void note_close(note_p curr_note,bool_e kill)
Purpose : Closes window. Copies last position and size of window. If kill
	== FALSE, it doesn't dellocate the gadget memory or close the font,
	as the window may be opened up again sometime later. If kill == TRUE
	then all resources are deallocated. The memory for the note itself
	is also dellocated if kill == TRUE.
*/

void note_close(note_p curr_note,bool_e kill)
{
	/* If already closed, return */

	if (curr_note->win) {

		/* Store window info */

		curr_note->leftedge = curr_note->win->LeftEdge;
		curr_note->topedge = curr_note->win->TopEdge;
		curr_note->width = curr_note->win->Width;
		curr_note->height = curr_note->win->Height;

		/* Handle menu stuff */

		if (curr_note->menustrip) {
			ClearMenuStrip(curr_note->win);

		FreeMenus(curr_note->menustrip);
		}

		if (curr_note->visualinfo)
			FreeVisualInfo(curr_note->visualinfo);

		/* Actually close the window */

		andysCloseWindowSafely(curr_note->win);
		curr_note->win = NULL;
	}

	if (kill) {
		if (curr_note->font) {
			CloseFont(curr_note->font);
			curr_note->font = NULL;
		}

		/* If the listview is up, disconnect it and remove the node */

		if (commod) {
			GT_SetGadgetAttrs(commodGadgets[commod_listview],
commod,NULL,GTLV_Labels,~0,TAG_END);

			Remove(&curr_note->node);

			GT_SetGadgetAttrs(commodGadgets[commod_listview],
commod,NULL,GTLV_Labels,&prj->commodlist,TAG_END);
		}

		/* Free the memory */

		free(curr_note);
	}
}

/*
Function : void note_refresh(note_p curr_note)
Purpose : Redraws the background and the text in the note.
*/

void note_refresh(note_p curr_note)
{
	struct Window *curr_win;
	struct RastPort *curr_rport;
	struct TextExtent textextent_result;

	char *text_ptr;

	ULONG remaininglength;
	LONG textbaseline,topoffset;
	LONG textfit;

	int l;

	/* Set up some easy references */

	curr_win = curr_note->win;
	curr_rport = curr_win->RPort;

	textbaseline = curr_rport->TxBaseline + NO_INTERLINE_PIXELS;
	topoffset = curr_win->WScreen->BarHeight + 1;

	/* Draw backdrop */

	SetDrMd(curr_rport,JAM1);
	SetAPen(curr_rport,curr_note->backcolour);
	RectFill(curr_rport,(LONG)curr_win->BorderLeft,
(LONG)curr_win->BorderTop,curr_win->Width - (curr_win->BorderRight + 1),
curr_win->Height - (curr_win->BorderBottom + 1));

	/* Set drawing mode */

	SetAPen(curr_rport,curr_note->textcolour);
	SetBPen(curr_rport,curr_note->backcolour);

	/* Set up the text pointer */

	text_ptr = curr_note->text;

	/* Move to position */

	Move(curr_rport,(LONG)curr_win->BorderLeft,
topoffset + textbaseline);

	while(strlen(text_ptr)) {

		/* Have we reached the bottom ? */

		if ((curr_rport->cp_y + (curr_rport->TxHeight - textbaseline))>
(curr_win->Height - (curr_win->BorderBottom + 2)))
			break;

		/* Remove any space */

		for (l = 0; (text_ptr[l] == ' ') &&(l < strlen(text_ptr)); l++);

		text_ptr += l;

		/* How much text will fit on ? */

		textfit = TextFit(curr_rport,text_ptr,strlen(text_ptr),
&textextent_result,NULL,1,(LONG)curr_win->Width -
(curr_rport->cp_x + curr_win->BorderLeft + curr_win->BorderRight + CARAT_WIDTH),
curr_rport->TxHeight + 1);

		/* If no text will be printed, try next line */

		if (textfit == 0) {
			Move(curr_rport,curr_win->BorderLeft,
curr_rport->cp_y + curr_rport->TxHeight + NO_INTERLINE_PIXELS);
			continue;
		}

		/* Will I print all the remainining text ? */

		remaininglength = strlen(text_ptr);

		if (textfit != remaininglength) {
			for (l = textfit;((l > 0) &&(text_ptr[l] != ' ')); l--);

			/* If we can successfully wrap it */

			if (l != 0)
				textfit = l + 1;
		}

		/* textfit now holds the number of characters to be printed */

		SetAPen(curr_rport,curr_note->textcolour);
		Text(curr_rport,text_ptr,textfit);

		/* Next line */

		Move(curr_rport,curr_win->BorderLeft,
curr_rport->cp_y +curr_rport->TxHeight + NO_INTERLINE_PIXELS);

		/* If we've finished */

		if (textfit == remaininglength)
			break;

		/* Else, set up next line pointer */

		text_ptr += textfit;
	}

	/* If there was a carat in the window, redraw it. Clear old one first */

	curr_note->carat.oldxpos = curr_note->carat.oldypos = 0;
	carat_draw(curr_note);
}

/*
Function : BOOL note_event(struct IntuiMessage *curr_msg)
Purpose : Handles any events related to the notes on the screen.
*/

BOOL note_event(struct IntuiMessage *curr_msg)
{
	note_p curr_note;

	char *temp_carat;

	int notepos;

	curr_note = note_from_window(curr_msg->IDCMPWindow);

	/* If stray message from closed window */

	if (!curr_note)
		return(FALSE);

	switch(curr_msg->Class) {
		case IDCMP_MENUPICK:
			note_menuevent(curr_note,curr_msg);
			break;
		case IDCMP_MOUSEBUTTONS:
			switch(curr_msg->Code) {
				case SELECTDOWN:
					/* Select in listview if open */
					if (commod) {
						notepos = positionfromnote(
curr_note);
						if (notepos != NOT_IN_ARRAY)
							commodlistview(
notepos,0,0);
						GT_SetGadgetAttrs(
commodGadgets[commod_listview],commod,NULL,GTLV_Selected,notepos,TAG_END);
					}

					/* Where's carat ? */
					carat_from_coords(curr_note,
curr_msg->MouseX,curr_msg->MouseY);
					break;
				default:
					break;
			}
			break;
		case IDCMP_REFRESHWINDOW:
			BeginRefresh(curr_note->win);
			note_refresh(curr_note);
			EndRefresh(curr_note->win,TRUE);
			break;
		case IDCMP_NEWSIZE:
			/* Store window info */

			curr_note->leftedge = curr_note->win->LeftEdge;
			curr_note->topedge = curr_note->win->TopEdge;
			curr_note->width = curr_note->win->Width;
			curr_note->height = curr_note->win->Height;

			/* Store carat to place back */

			temp_carat = curr_note->carat.text;

			/* Remove carat */

			carat_clear(curr_note);

			note_refresh(curr_note);

			/* Place carat back */

			if (temp_carat) {
				curr_note->carat.text = temp_carat;
				carat_from_ptr(curr_note,PTRCHANGE_CURSOR);
			}

			break;
		case IDCMP_CHANGEWINDOW:
			/* Store window info */

			curr_note->leftedge = curr_note->win->LeftEdge;
			curr_note->topedge = curr_note->win->TopEdge;
			curr_note->width = curr_note->win->Width;
			curr_note->height = curr_note->win->Height;

			break;
		case IDCMP_INACTIVEWINDOW:
			/* Remove carat */

			carat_clear(curr_note);

			/* If commodities window open, refresh list */

			if (commod) {
				GT_SetGadgetAttrs(
commodGadgets[commod_listview],commod,NULL,GTLV_Labels,~0,TAG_END);
				GT_SetGadgetAttrs(
commodGadgets[commod_listview],commod,NULL,GTLV_Labels,&prj->commodlist,TAG_END);
			}

			break;
		case IDCMP_CLOSEWINDOW:
			note_close(curr_note,FALSE);
			break;
		case IDCMP_VANILLAKEY:
			/* Is there a carat in the window ? */

			if (curr_note->carat.text)
				carat_vanillakey(curr_note,curr_msg);

			break;
		case IDCMP_RAWKEY:
			/* Is there a carat in the window ? */

			if (curr_note->carat.text)
				carat_rawkey(curr_note,curr_msg);

			break;
		default:
			break;
	}

	return(FALSE);
}

/*
Function : note_p note_from_window(struct Window *curr_win)
Purpose : Given a window pointer, searches the array of notes and if any
	note has that window pointer, returns a pointer to that note. Returns
	NULL if no valid window is found (ie stray message from closed
	window).
*/
note_p note_from_window(struct Window *curr_win)
{
	int l;

	for (l = 0; l < NO_NOTES; l++) {
		if (!prj->notes[l])
			continue;

		if (prj->notes[l]->win == curr_win)
			return(prj->notes[l]);
	}

	/* Couldn't find the window */

	return(NULL);
}

/*
Function : void note_block(note_p curr_note)
Purpose : Blocks the current note to mouse input and puts up a wait pointer.
*/

void note_block(note_p curr_note)
{
	/* Only do it if window open */

	if (!curr_note->win)
		return;

	InitRequester(&curr_note->blockreq);

	if (Request(&curr_note->blockreq,curr_note->win))
		SetPointer(curr_note->win,WaitPointer,16,16,-6,0);
}

/*
Function : void note_blockclear(note_p curr_note)
Purpose : Clears the current note for mouse input and returns to the normal
	pointer.
*/

void note_blockclear(note_p curr_note)
{
	/* Only do it if window open */

	if (!curr_note->win)
		return;

	ClearPointer(curr_note->win);
	EndRequest(&curr_note->blockreq,curr_note->win);
}

/*
Function : void note_blockall()
Purpose : Blocks all the open notes.
*/

void note_blockall()
{
	note_p curr_note;

	int l;

	for (l = 0; l < NO_NOTES; l++) {
		if (prj->notes[l]) {
			curr_note = prj->notes[l];

			if (curr_note->win)
				note_block(curr_note);
		}
	}
}

/*
Function : void note_blockclearall()
Purpose : Unblocks all the open notes.
*/

void note_blockclearall()
{
	note_p curr_note;

	int l;

	for (l = 0; l < NO_NOTES; l++) {
		if (prj->notes[l]) {
			curr_note = prj->notes[l];

			if (curr_note->win)
				note_blockclear(curr_note);
		}
	}
}

/*
Function : void note_menuevent(note_p curr_note,struct IntuiMessage *curr_msg)
Purpose : Handles menu events from a note.
*/

void note_menuevent(note_p curr_note,struct IntuiMessage *curr_msg)
{
	struct MenuItem *menuitem;

	char *temp_carat = NULL;

	UWORD menunumber;
	UWORD menunum;
	UWORD itemnumber;
	UWORD subnumber;

	BOOL done = FALSE;

	menunumber = curr_msg->Code;

	while ((menunumber != MENUNULL) && (!done)) {
		menuitem = ItemAddress(curr_note->menustrip,menunumber);
		menunum = MENUNUM(menunumber);
		itemnumber = ITEMNUM(menunumber);
		subnumber = SUBNUM(menunumber);

		switch (menunum) {
			case NOMENU:
				break;

			case noteMenu_project:
				switch (itemnumber) {
					case NOITEM:
						break;
					case noteMenu_project_save:
						file_writenotes();
						break;
					case noteMenu_project_panel:
						openwindowcommod();
						break;
					default:
						break;
				}
				break;
			case noteMenu_edit:
				switch(itemnumber) {
					case NOITEM:
						break;
					case noteMenu_edit_copy:
						notemenu_editcopy(curr_note);
						break;
					case noteMenu_edit_cut:
						if (notemenu_editcopy(
curr_note)) {
							temp_carat =
curr_note->carat.text;
							if (temp_carat)
								carat_clear(curr_note);

							curr_note->text[0] =
'\0';
							note_refresh(curr_note);

							if (temp_carat) {
								curr_note->carat.text =
curr_note->text;
								carat_from_ptr(
curr_note,PTRCHANGE_CURSOR);
							}
						}

						prj->projectchanged = TRUE;

						break;
					case noteMenu_edit_delete:
						temp_carat = curr_note->carat.text;

						if (temp_carat)
							carat_clear(curr_note);

						curr_note->text[0] = '\0';

						note_refresh(curr_note);

						if (temp_carat) {
							curr_note->carat.text =
curr_note->text;
							carat_from_ptr(
curr_note,PTRCHANGE_CURSOR);
						}

						prj->projectchanged = TRUE;

						break;
					case noteMenu_edit_paste:
						notemenu_editpaste(curr_note);
						break;
					default:
						break;
				}
				break;
			default:
				break;
		}

	menunumber = menuitem->NextSelect;
	}
}

