/*-
 * Copyright (c) 2003-2004 Seth Kingsley.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgment:
 *    This product includes software developed by Seth Kingsley and
 *    contributors.
 * 4. Neither the author's name, nor any of the contributors names may be
 *    used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include    <sys/cdefs.h>
#include    <sys/types.h>

#include    <gtk/gtkclist.h>

#include    <assert.h>
#include    <string.h>
#include    <strings.h>
#ifdef DEBUG
#include    <err.h>
#endif /* DEBUG */

#include    <xmk-gtk.h>

#include    "support.h"
#include    "portsman.h"
#include    "portcell.h"
#include    "portlist.h"

#define STATE_MARGIN_HGT    1
#define STATE_MARGIN_LEFT   4
#define STATE_MARGIN_RIGHT  2
#define STATE_ARROW_ASPECT  0.85

__RCSID("$Id: portlist.c,v 1.27 2004/06/01 06:17:43 sethk Exp $");

struct portlist_pixbuf
{
    GdkPixmap	*plp_pixmap;
    GdkBitmap	*plp_mask;
};

struct portlist_data
{
    struct descview_data    *pld_ddatap;
    struct install_data	    *pld_idatap;
    GtkCList		    *pld_portview;
    GtkArrow		    *pld_sort_arr;
    struct portlist_pixbuf  pld_pixbufs[PSTATE_NSTATE];
    struct cat_desc	    *pld_curcat;
    struct port_desc	    *pld_curport;
};

enum portlist_col
{
    PLCOL_STATE_NAME,
    PLCOL_VERSION,
    PLCOL_DESCR,
    PLCOL_NCOL
};

struct portlist_data	*pldatap;   /* For callbacks */

static gint
_portlist_cmprow_byname(GtkCList *clist, GtkCListRow *row1, GtkCListRow *row2)
{
    return strcasecmp(((struct port_desc *)(row1->data))->pd_name,
	    ((struct port_desc *)(row2->data))->pd_name);
}

static void
_portlist_set(struct portlist_data *pldp,
	gchar *text[],
	struct port_desc *pdescp,
	GdkPixmap **pixp,
	GdkBitmap **maskp)
{
    text[PLCOL_STATE_NAME] = pdescp->pd_name;
    text[PLCOL_VERSION] = pdescp->pd_version;
    text[PLCOL_DESCR] = pdescp->pd_comm;
    text[PLCOL_NCOL] = NULL;

    *pixp = pldp->pld_pixbufs[pdescp->pd_state].plp_pixmap;
    *maskp = pldp->pld_pixbufs[pdescp->pd_state].plp_mask;
}

static gboolean
_portlist_append(gchar *name,
	struct port_desc *pdescp,
	struct portlist_data *pldp)
{
    gchar	*text[PLCOL_NCOL + 1];
    gint	row;
    GdkPixmap	*pix;
    GdkBitmap	*mask;

    _portlist_set(pldp, text, pdescp, &pix, &mask);
    row = gtk_clist_append(pldp->pld_portview, text);
    gtk_clist_set_row_data(pldp->pld_portview, row, pdescp);
    gtk_clist_set_pixtext(pldp->pld_portview,
	    row, PLCOL_STATE_NAME,
	    text[PLCOL_STATE_NAME],
	    STATE_MARGIN_RIGHT,
	    pix, mask);

    return FALSE;
}

struct portlist_data *
portlist_init(GtkWindow *app,
	struct refresh_data *rdp,
	struct catlist_data *cdp)
{
    static struct portlist_data	pldata;
    u_int			i;
    guint			pwidth, pheight,
				awidth, aheight;
    GdkDrawable			*root,
				*draw;
    guint			depth;
    GtkStyle			*style;

    pldata.pld_ddatap = descview_init(app, rdp, cdp, &pldata);
    pldata.pld_idatap = install_init(app, rdp, &pldata);

    pldata.pld_portview = GTK_CLIST(
	    lookup_widget(GTK_WIDGET(app), "portlist"));
    gtk_clist_set_sort_column(pldata.pld_portview, PLCOL_STATE_NAME);
    gtk_clist_set_compare_func(pldata.pld_portview,
	    (GtkCListCompareFunc)_portlist_cmprow_byname);
    gtk_clist_set_sort_type(pldata.pld_portview, GTK_SORT_ASCENDING);
    pldata.pld_sort_arr = GTK_ARROW(
	    lookup_widget(GTK_WIDGET(pldata.pld_portview),
		"portlist_sort_arr"));
    gtk_clist_column_title_passive(pldata.pld_portview, PLCOL_VERSION);
    gtk_clist_column_title_passive(pldata.pld_portview, PLCOL_DESCR);
    gtk_clist_set_column_auto_resize(pldata.pld_portview, PLCOL_DESCR, TRUE);

    gtk_widget_realize(GTK_WIDGET(pldata.pld_portview));
    pheight = pldata.pld_portview->row_height;
    aheight = pheight - (STATE_MARGIN_HGT * 2);
    if (!(aheight % 1))
	--aheight;
    awidth = (float)aheight * STATE_ARROW_ASPECT;
    if (!(awidth % 1))
	++awidth;
    pwidth = STATE_MARGIN_LEFT + awidth + 2;
    root = gtk_widget_get_root_window(GTK_WIDGET(pldata.pld_portview));
    draw = GDK_DRAWABLE(GTK_WIDGET(pldata.pld_portview)->window);
    depth = gdk_drawable_get_depth(draw);
    style = GTK_WIDGET(pldata.pld_portview)->style;
    for (i = 0; i < PSTATE_NSTATE; ++i)
    {
	gboolean    filled;
	GdkGCValues gcv;
	GdkGC	    *black, *white;

	pldata.pld_pixbufs[i].plp_pixmap = gdk_pixmap_new(draw,
		pwidth, pheight,
		depth);
	pldata.pld_pixbufs[i].plp_mask = gdk_pixmap_new(root,
		pwidth, pheight,
		1);

	gcv.foreground.pixel = 0;
	black = gdk_gc_new_with_values(pldata.pld_pixbufs[i].plp_mask,
		&gcv,
		GDK_GC_FOREGROUND);
	gcv.foreground.pixel = 1;
	white = gdk_gc_new_with_values(pldata.pld_pixbufs[i].plp_mask,
		&gcv,
		GDK_GC_FOREGROUND);

	gdk_draw_rectangle(pldata.pld_pixbufs[i].plp_pixmap,
		style->base_gc[GTK_STATE_NORMAL],
		TRUE,
		0, 0,
		pwidth, pheight);
	gdk_draw_rectangle(pldata.pld_pixbufs[i].plp_mask,
		black,
		TRUE,
		0, 0,
		pwidth, pheight);

	filled = TRUE;
	switch ((enum port_state)i)
	{
	    GdkPoint    pts[4];

	    case PSTATE_STALE:
		filled = FALSE;
		/*FALLTHROUGH*/
	    case PSTATE_INST:
		pts[0].x = pts[2].x = pts[3].x = STATE_MARGIN_LEFT;
		pts[0].y = pts[3].y = STATE_MARGIN_HGT + ((filled) ? 0 : 1);
		pts[1].x = STATE_MARGIN_LEFT + awidth - ((filled) ? 0 : 1);
		pts[1].y = STATE_MARGIN_HGT + ((float)aheight / 2);
		pts[2].y = STATE_MARGIN_HGT + aheight - ((filled) ? 0 : 1);
		gdk_draw_polygon(
			GDK_DRAWABLE(pldata.pld_pixbufs[i].plp_pixmap),
			style->fg_gc[GTK_STATE_INSENSITIVE],
			filled,
			pts, sizeof(pts) / sizeof(pts[0]));
		gdk_draw_polygon(
			GDK_DRAWABLE(pldata.pld_pixbufs[i].plp_mask),
			white,
			filled,
			pts, sizeof(pts) / sizeof(pts[0]));

		break;

	    default:
		break;
	}

	gdk_gc_unref(black);
	gdk_gc_unref(white);
    }

    pldata.pld_curcat = NULL;
    pldata.pld_curport = NULL;

    return (pldatap = &pldata);
}

void
portlist_fini(struct portlist_data *pldp)
{
    u_int   i;

    descview_fini(pldp->pld_ddatap);

    install_fini(pldp->pld_idatap);

    for (i = 0; i < PSTATE_NSTATE; ++i)
    {
	gdk_pixmap_unref(pldp->pld_pixbufs[i].plp_pixmap);
	gdk_pixmap_unref(pldp->pld_pixbufs[i].plp_mask);
    }
}

void
portlist_populate(struct portlist_data *pldp, struct cat_desc *cdescp)
{
#ifdef DEBUG_VIEWS
    warnx("%s(%p)", __func__, cdescp);
#endif /* DEBUG_VIEWS */

    gtk_clist_freeze(pldp->pld_portview);
    g_tree_foreach(cdescp->cd_pdtree, (GTraverseFunc)_portlist_append, pldp);
    gtk_clist_thaw(pldp->pld_portview);

    gtk_widget_set_sensitive(
	    gtk_widget_get_parent(GTK_WIDGET(pldp->pld_portview)), TRUE);

    pldp->pld_curcat = cdescp;
}

void
portlist_select(struct portlist_data *pldp, struct port_desc *pdescp)
{
    xmk_clist_scroll_select_bydata(pldp->pld_portview,
	    PLCOL_STATE_NAME,
	    pdescp);
}

void
portlist_select_row(struct portlist_data *pldp,
	gint row,
	GdkEvent *event)
{
    struct port_desc    *pdescp;

    pdescp = gtk_clist_get_row_data(pldp->pld_portview, row);

    xmk_busy_push(
	    gtk_widget_get_parent_window(GTK_WIDGET(pldp->pld_portview)));

    if (pldp->pld_curport)
	descview_clear(pldp->pld_ddatap);
    descview_populate(pldp->pld_ddatap, pldp->pld_curcat, pdescp);

    install_enable(pldp->pld_idatap, pdescp);

    pldp->pld_curport = pdescp;

    xmk_busy_pop(
	    gtk_widget_get_parent_window(GTK_WIDGET(pldp->pld_portview)));
}

void
portlist_resort(struct portlist_data *pldp)
{
    GtkSortType	order;

    xmk_busy_push(
	    gtk_widget_get_parent_window(GTK_WIDGET(pldp->pld_portview)));

    order = (pldp->pld_portview->sort_type == GTK_SORT_ASCENDING) ?
	GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
    gtk_clist_set_sort_type(pldp->pld_portview, order);
    gtk_clist_sort(pldp->pld_portview);
    gtk_arrow_set(pldp->pld_sort_arr,
	    (order == GTK_SORT_ASCENDING) ? GTK_ARROW_DOWN : GTK_ARROW_UP,
	    GTK_SHADOW_ETCHED_IN);

    xmk_busy_pop(
	    gtk_widget_get_parent_window(GTK_WIDGET(pldp->pld_portview)));
}

void
portlist_refresh(struct portlist_data *pldp)
{
    gint    i;

    for (i = 0; i < pldp->pld_portview->rows; ++i)
    {
	struct port_desc    *pdescp;
	gchar		    *text[PLCOL_NCOL + 1];
	GdkPixmap	    *pix;
	GdkBitmap	    *mask;
	gint		    j;

	pdescp = gtk_clist_get_row_data(pldp->pld_portview, i);
	_portlist_set(pldp, text, pdescp, &pix, &mask);
	gtk_clist_set_pixtext(pldp->pld_portview,
		i, PLCOL_STATE_NAME,
		text[PLCOL_STATE_NAME],
		STATE_MARGIN_RIGHT,
		pix, mask);
	for (j = 1; j < PLCOL_NCOL; ++j)
	    gtk_clist_set_text(pldp->pld_portview, i, j, text[j]);
    }
}

void
portlist_clear(struct portlist_data *pldp)
{
#ifdef DEBUG_VIEWS
    warnx("%s", __func__);
#endif /* DEBUG_VIEWS */

    if (pldp->pld_curport)
	descview_clear(pldp->pld_ddatap);

    install_disable(pldp->pld_idatap);

    gtk_clist_clear(pldp->pld_portview);

    gtk_widget_set_sensitive(
	    gtk_widget_get_parent(GTK_WIDGET(pldp->pld_portview)),
	    FALSE);

    pldp->pld_curcat = NULL;
    pldp->pld_curport = NULL;
}

gboolean
portlist_popup(GtkWidget *widget,
	GdkEventButton *event,
	struct portlist_data *pldp)
{
    gint    row, col;

    if (gtk_clist_get_selection_info(pldp->pld_portview,
		event->x, event->y,
		&row, &col))
    {
	gtk_clist_select_row(pldp->pld_portview, row, col);
	install_menu_popup(pldp->pld_idatap, event);
    }

#ifdef NOT_CLIST
    gtk_tree_view_get_path_at_pos(pldp->pld_portview,
	    event->x,
	    event->y,
	    &path,
	    NULL,
	    NULL,
	    NULL);
    gtk_tree_selection_select_path(
	    gtk_tree_view_get_selection(pldp->pld_portview),
	    path);
    gtk_tree_path_free(path);
#endif /* NOT_CLIST */

    return TRUE;
}

const gchar *
portlist_getpath(struct portlist_data *pldp)
{
    if (pldp->pld_curport)
	return pldp->pld_curport->pd_origin;
    else
	return NULL;
}

void
portlist_action(struct portlist_data *pldp, enum install_action ia)
{
    assert(pldp->pld_curport);
    install_action(pldp->pld_idatap, pldp->pld_curport, ia);
}

