/*****************************************************
 * This source demonstrates how to use drag and      *
 * drop to drag data between listboxes, frames and   *
 * windows.  It should work on any Amiga.  On amigas *
 * with Intuition V36 and above it will open a       *
 * screen with the same screen mode as your WB       *
 * screen.  The program, when compiled and linked    *
 * can be run from the WB or a shell with a stack    *
 * size of about 40k.                                *
 ****************************************************/

/*
 * To compile with SAS/C type:
 * sc UNSCHAR STRMERGE StructureEquivalence DragDropTest
 * Then link as follows:
 * slink from LIB:c.o+DragDropTest.o to DragDropTest lib FoxLib:FoxGui.lib+LIB:scm.lib+LIB:sc.lib+LIB:amiga.lib
 */

#include <stdlib.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <string.h>
#include <exec/memory.h>

#include "foxinclude/FoxGui.h"

GuiScreen *gscreen;
GuiWindow *gw1, *gw2;
Frame *frame1, *frame2;
ListBox *lb1;
struct TextAttr pub_screen_font;
struct TextFont *opened_font;

void FreeRMScreenBits(void)
	{
	if (opened_font)
		{
		CloseFont(opened_font);
		opened_font = NULL;
		}
	if (pub_screen_font.ta_Name)
		{
		GuiFree(pub_screen_font.ta_Name);
		pub_screen_font.ta_Name = NULL;
		}
	}

static GuiScreen *cloneScreen(UBYTE *pub_screen_name)
	{
	GuiScreen *retval = NULL;
	ULONG  screen_modeID;
	UBYTE *pub_scr_font_name;
	struct Screen   *pub_screen = NULL;
	struct DrawInfo *screen_drawinfo = NULL;

	// name is a (UBYTE *) pointer to the name of the public screen to clone
	pub_screen = LockPubScreen(pub_screen_name);
	if (pub_screen != NULL)
		{
		// Get the DrawInfo structure from the locked screen.  This returns pen, depth and font info.
		screen_drawinfo = GetScreenDrawInfo(pub_screen);
		if (screen_drawinfo != NULL)
			{
			screen_modeID = GetVPModeID(&(pub_screen->ViewPort));
			if( screen_modeID != INVALID_ID )
				{
            /* Get a copy of the font.  The name of the font must be copied as the public screen may go
					away at any time after we unlock it.  Allocate enough memory to copy the font name, create
					a TextAttr that matches the font, and open the font. */
				pub_scr_font_name = screen_drawinfo->dri_Font->tf_Message.mn_Node.ln_Name;
				pub_screen_font.ta_Name = GuiMalloc((strlen(pub_scr_font_name) + 1) * sizeof(char), MEMF_CLEAR);
				if (pub_screen_font.ta_Name != NULL)
					{
					strcpy(pub_screen_font.ta_Name, pub_scr_font_name);
					pub_screen_font.ta_YSize = screen_drawinfo->dri_Font->tf_YSize;
					pub_screen_font.ta_Style = screen_drawinfo->dri_Font->tf_Style;
					pub_screen_font.ta_Flags = screen_drawinfo->dri_Font->tf_Flags;

					opened_font = OpenFont(&pub_screen_font);
					if (opened_font != NULL)
						{
						/* screen_modeID may now be used in a call to OpenScreenTagList() with the tag
							SA_DisplayID. */
						retval = OpenGuiScreen(screen_drawinfo->dri_Depth, 2, 1,
								&pub_screen_font, "Drag-drop Test", NULL, GS_AUTOSCROLL |
								GS_DISPLAY_ID | GS_OVERSCAN | GS_PENS, screen_modeID, OSCAN_TEXT,
								screen_drawinfo->dri_Pens);
						if (retval)
							SetGuiPens(screen_drawinfo->dri_Pens[SHINEPEN], screen_drawinfo->dri_Pens[SHADOWPEN]);
						else
							/*	This way we can check whether the WB screen was cloned from anywhere in the code
								by checking the value of opened_font */
							FreeRMScreenBits();
						}
					}
				}
			}
		}

	// Free the drawinfo and public screen as we don't need them any more.  We now have our own screen.
	if (screen_drawinfo)
		FreeScreenDrawInfo(pub_screen,screen_drawinfo);
	if (pub_screen)
		UnlockPubScreen(pub_screen_name,pub_screen);

	return retval;
	}

void CleanUp(void)
{
	FreeRMScreenBits();
	EndGui();
	exit(0);
}

int CloseWinFn(GuiWindow *w)
{
	return GUI_END;
}

int WinDropFn(GuiWindow *w, int x, int y, void *DragData)
{
	char text[100];
	sprintf(text, "From: %s", DragData);
	AddListBoxItem(lb1, text, TRUE);
	sprintf(text, "To:   Window %d at %3d, %3d", w == gw1 ? 1 : 2, x, y);
	AddListBoxItem(lb1, text, TRUE);
	return GUI_CONTINUE;
}

char DragText[100];

int Frame1Fn(Frame *fm, short action, short x, short y, void **DragData)
{
	if (action == FM_RBUT)
	{
		char message[100];
		sprintf(message, "RClick in Frame 1 at %3d, %3d", x, y);
		AddListBoxItem(lb1, message, TRUE);
	}
	else if (action == FM_DRAG)
	{
		sprintf(DragText, "Frame 1  at %3d, %3d", x, y);
		*DragData = DragText;
	}
	else if (action == FM_DROP)
	{
		char text[100];
		sprintf(text, "From: %s", *DragData);
		AddListBoxItem(lb1, text, TRUE);
		sprintf(text, "To:   Frame 1  at %3d, %3d", x, y);
		AddListBoxItem(lb1, text, TRUE);
	}
	else if (action == FM_LBUT)
	{
		char message[100];
		sprintf(message, "LClick in Frame 1 at %3d, %3d", x, y);
		AddListBoxItem(lb1, message, TRUE);
	}
	return GUI_CONTINUE;
}

int Frame2Fn(Frame *fm, short action, short x, short y, void **DragData)
{
	if (action == FM_RBUT)
	{
		char message[100];
		sprintf(message, "RClick in Frame 2 at %3d, %3d", x, y);
		AddListBoxItem(lb1, message, TRUE);
	}
	else if (action == FM_DRAG)
	{
		sprintf(DragText, "Frame 2  at %3d, %3d", x, y);
		*DragData = DragText;
	}
	else if (action == FM_DROP)
	{
		char text[100];
		sprintf(text, "From: %s", *DragData);
		AddListBoxItem(lb1, text, TRUE);
		sprintf(text, "To:   Frame 2  at %3d, %3d", x, y);
		AddListBoxItem(lb1, text, TRUE);
	}
	else if (action == FM_LBUT)
	{
		char message[100];
		sprintf(message, "LClick in Frame 2 at %3d, %3d", x, y);
		AddListBoxItem(lb1, message, TRUE);
	}
	return GUI_CONTINUE;
}

int LBeventfn(ListBox *lb, short Event, int lbitemnum, void **EventData)
{
	char ItemNumStr[20];

	if (lbitemnum)
		sprintf(ItemNumStr, "(item %d)", lbitemnum);
	else
		sprintf(ItemNumStr, "(No Item)");

	if (Event == LB_DROP && *EventData)
	{
		char text[100];
		sprintf(text, "From: %s", *EventData);
		AddListBoxItem(lb, text, TRUE);
		sprintf(text, "To:   List Box %s", ItemNumStr);
		AddListBoxItem(lb, text, TRUE);
	}
	else if (Event == LB_DRAG)
	{
		sprintf(DragText, "List Box %s", ItemNumStr);
		*EventData = DragText;
	}
	return GUI_CONTINUE;
}

void main(void)
{
	short IntuitionVersion = InitGui(FAST_MALLOCS, NULL);

	if (IntuitionVersion == 0)
		return;

	/* Open a FoxGui screen using the same screen mode as the current workbench screen if possible */

	if (IntuitionVersion >= 36) // Screen cloning only works with intuition V36 or greater.
		gscreen = cloneScreen("Workbench");
	else
		gscreen = OpenGuiScreen(2, 2, 1, NULL, "Drag-drop Test", NULL, GS_AUTOSCROLL);
		
	if (!gscreen)
		CleanUp();

	/* Open a couple of windows */

	gw1 = OpenGuiWindow(gscreen, 0, 10, 320, 235, 0, 0, 8, 2, 6, "Window 1", GW_DRAG | GW_DEPTH | GW_SIZE | GW_CLOSE | GW_DROP, CloseWinFn, WinDropFn);
	if (!gw1)
	{
		CloseGuiScreen(gscreen);
		CleanUp();
	}
	gw2 = OpenGuiWindow(gscreen, 320, 10, 320, 235, 0, 0, 8, 2, 6, "Window 2", GW_DRAG | GW_DEPTH | GW_SIZE | GW_DROP | GW_CONSOLE, NULL, WinDropFn);
	if (!gw2)
	{
		CloseGuiWindow(gw1);
		CloseGuiScreen(gscreen);
		CleanUp();
	}

	/* Window gw2 has a console so it will get an unsightly cursor unless we hide it! */
	WinHideCursor(gw2);
	WinPrintTab(gw2, 1, 15, "Try dragging data:");
	WinPrintTab(gw2, 3, 16, "From one frame to another,");
	WinPrintTab(gw2, 3, 17, "From a frame to the list box,");
	WinPrintTab(gw2, 3, 18, "From the list box to a frame,");
	WinPrintTab(gw2, 3, 19, "From a frame to a window,");
	WinPrintTab(gw2, 3, 20, "From the list box to a window.");

	/* Create a frame in each window and a list box in which to list the drag/drop and button
		events. */

	frame1 = MakeFrame(gw1, "Frame 1", 20, 30, 260, 40, 1, 1, NULL, Frame1Fn, FM_CLEAR | FM_DRAG | FM_DROP | FM_RBUT | FM_LBUT);
	frame2 = MakeFrame(gw2, "Frame 2", 20, 30, 260, 40, 1, 1, NULL, Frame2Fn, FM_CLEAR | FM_DRAG | FM_DROP | FM_RBUT | FM_LBUT);
	lb1 = MakeListBox(gw1, 20, 90, 260, 100, 2, 1, 1, NULL, NULL, LB_DRAG | LB_DROP, LBeventfn);
	if (lb1)
		AddListBoxTitle(lb1, "Event List:", 1, TRUE);

	GuiLoop();

	CloseGuiWindow(gw2);
	CloseGuiWindow(gw1);
	CloseGuiScreen(gscreen);
	CleanUp();
}
