//
// GWList.cc
//
// (c) Copyright 1993, San Diego State University -- College of Sciences
//       (See the COPYRIGHT file for more Copyright information)
//
//   This  file  contains  the  routines  of the GWList class which
//   deal with the opening of windows.
//
#include "GWList.h"
#include "xvgopher.h"
#include "cursor.h"
#include "icons.h"
#include "GWBookmarks.h"
#include "GWInfo.h"
#include <stream.h>
#include <string.h>
#include <xview/svrimage.h>
#include <xview/icon.h>
#include <xview/defaults.h>


static long	double_click_timeout = defaults_get_integer("openwindows.multiclicktimeout", "OpenWindows.MultiClickTimeout", 4) * 100;

//***************************************************************************
// GWList::GWList()
//   Do generic initialization.  This should be called from any derived constructor.
//
GWList::GWList()
{
	parent = NULL;
	dir_list = NULL;
	dir_quit = NULL;
	current_selected = -1;
}


//***************************************************************************
// void GWList::frame_event(Xv_Window window, Event *event, Notify_arg)
//   This is where we will take care of window resize events.  This is rather
//   messy because XView does not allow for arbitrarily sized scrolling lists.
//   Instead we have to specify the height in rows, not in pixels like the width
//
void GWList::frame_event(Xv_Window window, Event *event, Notify_arg)
{
	if (event_action(event) == WIN_RESIZE)
	{
		//
		//   The  window  has  been  resized. We need to resize the list and
		//   relocate the quit button.
		//
		GWList *gwindow = (GWList *) xv_get(window, XV_KEY_DATA, KEY_GWINDOW);
		int width = (int) xv_get(window, XV_WIDTH);
		int height = (int) xv_get(window, XV_HEIGHT);

		//
		//   If  you  can figure out the formula for the number of rows, you
		//   deserve a raise!!!
		//
		xv_set(gwindow->dir_list,
			PANEL_LIST_WIDTH,				width - 30,
			PANEL_LIST_DISPLAY_ROWS,		(int) ((height - 61.69) / 18.91) + 1,
			NULL);

		//
		//   Ok,  we  have resized the list. Now relocate the quit or dismiss button if
		//   it is present.
		//
		if (gwindow->dir_quit)
			xv_set(gwindow->dir_quit,
				XV_X,							width - 10 - (gwindow->parent ? 69 : 47),
				NULL);
	}
}


//***************************************************************************
// int GWList::open(Response *resp)
// PURPOSE:
//   This will create a window with a panel at the top and a scrolling list
//   at the bottom.  The panel will be empty.
//   The only special thing that is taken care of is that when there is no
//   parent, the window will be a real window, while if there is a parent,
//   the window will be a command window.
//
int GWList::open(Response *resp)
{
	info = resp;
	if (!parent)
	{
		//
		// This is the main window
		//
		char version[200];
		sprintf(version, "XvGopher %s.  Root Directory", VERSION);
		frame = (Frame) xv_create(NULL, FRAME,
			XV_WIDTH,					WINDOW_WIDTH,
			XV_HEIGHT,					WINDOW_HEIGHT,
			FRAME_LABEL,				version,
			NULL);
		panel = (Panel) xv_create(frame, PANEL,
			NULL);
		assign_icon(frame);

		//
		// The main window has a quit button
		//
		dir_quit = xv_create(panel, PANEL_BUTTON,
			XV_X,						468,	// It will be relocated anyway...
			XV_Y,						8,
			PANEL_LABEL_STRING,			"Quit",
			PANEL_NOTIFY_PROC,			quit_proc,
			XV_KEY_DATA,				KEY_GWINDOW, this,
			NULL);
	}
	else
	{
		//
		// We are creating a child window
		//
		compute_location(WINDOW_WIDTH, WINDOW_HEIGHT);
		frame = xv_create(parent, FRAME_CMD,
			XV_X,						next_x,
			XV_Y,						next_y,
			XV_WIDTH,					WINDOW_WIDTH,
			XV_HEIGHT,					WINDOW_HEIGHT,
			FRAME_LABEL,				info->get_title(),
			FRAME_CMD_PIN_STATE,		FRAME_CMD_PIN_IN,
			FRAME_DONE_PROC,			done_proc,
			FRAME_SHOW_RESIZE_CORNER,	TRUE,
			NULL);
		panel = (Panel) xv_get(frame, FRAME_CMD_PANEL);

		//
		// Child windows have a close button
		//
		dir_quit = xv_create(panel, PANEL_BUTTON,
			XV_X,						468,	// It will be relocated anyway...
			XV_Y,						8,
			PANEL_LABEL_STRING,			"Dismiss",
			PANEL_NOTIFY_PROC,			quit_proc,
			XV_KEY_DATA,				KEY_GWINDOW, this,
			NULL);
	}

	//
	//   Now  some  things  which  need  to  be  set for both FRAMEs and
	//   FRAME_CMDs
	//
	xv_set(frame,
		FRAME_SHOW_FOOTER,				TRUE,
		XV_KEY_DATA,					KEY_GWINDOW,	this,
		FRAME_MIN_SIZE,					280, 118,
		FRAME_MAX_SIZE,					0, 0,
		WIN_EVENT_PROC,					frame_event,
		WIN_CONSUME_EVENTS,
				WIN_NO_EVENTS,
				WIN_RESIZE,
				NULL,
		NULL);

	//
	// Create the scrolling list.  Make it so that 0 or 1 rows can be selected.
	//
	dir_list = (Panel_item) xv_create(panel, PANEL_LIST,
		XV_X,							8,
		XV_Y,							36,
		PANEL_LIST_WIDTH,				500,
		PANEL_LIST_DISPLAY_ROWS,		15,
		PANEL_NOTIFY_PROC,				list_notify,
		PANEL_READ_ONLY,				TRUE,
		PANEL_CHOOSE_ONE,				TRUE,
		PANEL_CHOOSE_NONE,				TRUE,
		XV_KEY_DATA,					KEY_GWINDOW,	this,
		NULL);

	return OK;
}


//***************************************************************************
// void GWList::list_notify(Panel_item item, char *, caddr_t cd, Panel_list_op op, Event *event, int row)
//   This is called when the mouse is clicked in the scrolling list.
//   We will determine if this is a double click or not.  If it is,
//   we need to create a new window to display the data.
//
void GWList::list_notify(Panel_item item, char *, caddr_t cd, Panel_list_op op, Event *event, int row)
{
	static long		prev_click_time = 0;
	static int		previous_row = -1;
	long			click_time = event->ie_time.tv_sec * 1000 +
								 event->ie_time.tv_usec / 1000;
	GWList			*gwindow = (GWList *) xv_get(item, XV_KEY_DATA, KEY_GWINDOW);

	if (op == 0)
	{
		//
		// The currently selected row was deselected.
		//
		gwindow->current_selected = -1;
		gwindow->row_deselect(row, (Response *) cd);
	}
	else
	{
		//
		// A new row was selected.
		//
		gwindow->current_selected = row;
		gwindow->row_select(row, (Response *) cd);
	}

	if (row == previous_row)
	{
		//
		// Check if this is a double click
		//
		if (click_time - prev_click_time < double_click_timeout)
		{
			//
			// It is.  Start a new window.
			//
			Response	*r = new Response((Response *) cd);
			GWindow	*sub;
			if (preferences.get_remove_children())
				sub = CreateWindow(r, gwindow->frame);
			else
				sub = CreateWindow(r, !gwindow->parent ? gwindow->frame : gwindow->parent);

			prev_click_time = 0;
			previous_row = -1;

			//
			// Make sure that the current item is now selected.
			// It can happen that the user double clicked on an item which was
			// not yet selected.  The first click will selected and the second
			// click will deselect it.  After the second click, we will fall through
			// to this point, so now we want to select the row again.
			xv_set(item, PANEL_LIST_SELECT, row, TRUE, NULL);
			gwindow->row_select(row, (Response *) cd);
			return;
		}
	}
	previous_row = row;
	prev_click_time = click_time;
}


static unsigned short icon_bits[] = {
#include "icons/gopher.icon"
};
static unsigned short icon_mask_bits[] = {
#include "icons/gopher.icon.mask"
};

//***************************************************************************
// void GWList::assign_icon(Frame frame)
//
void GWList::assign_icon(Frame frame)
{
	//
	//   The main frame needs an icon. Give it a good one!
	//
	Icon			ic;
	Server_image	icon, icon_mask;

	icon = (Server_image) xv_create(NULL, SERVER_IMAGE,
		XV_WIDTH,					64,
		XV_HEIGHT,					64,
		SERVER_IMAGE_BITS,			icon_bits,
		NULL);
	icon_mask = (Server_image) xv_create(NULL, SERVER_IMAGE,
		XV_WIDTH,					64,
		XV_HEIGHT,					64,
		SERVER_IMAGE_BITS,			icon_mask_bits,
		NULL);
	ic = (Icon) xv_create(frame, ICON,
		ICON_IMAGE,					icon,
		ICON_MASK_IMAGE,			icon_mask,
		NULL);
	xv_set(frame,
		FRAME_ICON,					ic,
		NULL);
}


//***************************************************************************
// void GWList::row_deselect(int, Response *)
//
void GWList::row_deselect(int, Response *)
{
}


//***************************************************************************
// void GWList::row_select(int, Response *)
//
void GWList::row_select(int, Response *)
{
}


//***************************************************************************
// void GWList::show_item_info_proc(Menu, Menu_item mi)
//   Create a little popup which displays the information about the currently
//   selected item.
//
void GWList::show_item_info_proc(Menu, Menu_item mi)
{
	GWList		*gwindow = (GWList *) xv_get(mi, XV_KEY_DATA, KEY_GWINDOW);

	GWInfo		*list = new GWInfo(gwindow->frame);
	if (gwindow->current_selected == -1)
	{
		//
		// No row is selected.  We will assume we want to display info
		// about the window itself.
		//
		list->open(new Response(gwindow->info));
	}
	else
	{
		//
		// Some row was selected.  Display info about this item.
		//
		Response		*r;
		r = (Response *) xv_get(gwindow->dir_list, PANEL_LIST_CLIENT_DATA, gwindow->current_selected);
		list->open(r);
	}
}
