/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
    chunk_view.c
    Copyright (C) 2006 Sebastien Granjoux

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

/*
 * It is a GtkTextView with an associated GtkAjustment, allowing to put in the
 * GtkTextView only a part of a big amount of text. Cursor movements are not
 * limited by the GtkTextView content but by the GtkAjustment.
 *---------------------------------------------------------------------------*/
 
#include "chunk_view.h"

struct _DmaChunkView
{
	GtkTextView parent;
	
	GtkAdjustment *vadjustment;
};

struct _DmaChunkViewClass
{
	GtkTextViewClass parent_class;
};

/* Used in dispose and finalize */
static GtkWidgetClass *parent_class = NULL;

/* Helper functions
 *---------------------------------------------------------------------------*/

static void
set_adjustment_clamped (GtkAdjustment *adj, gdouble value)
{
	gdouble lower, upper, page_size;

	lower = gtk_adjustment_get_lower (adj);
	upper = gtk_adjustment_get_upper (adj);
	page_size = gtk_adjustment_get_page_size (adj);

	if (value < lower)
	{
		value = lower;
	}
	if (value > (upper - page_size))
	{
		value = upper - page_size;
	}
	gtk_adjustment_set_value (adj, value);
}

/* Private functions
 *---------------------------------------------------------------------------*/

static void
dma_chunk_view_move_cursor (GtkTextView *text_view,
			     GtkMovementStep step,
			     gint            count,
			     gboolean        extend_selection)
{
	DmaChunkView *view = DMA_CHUNK_VIEW (text_view);
	GtkTextMark *mark;
	GtkTextBuffer *buffer;
	GtkTextIter cur;
	gint line;
	gdouble value = 0, step_increment;
	
	switch (step)
	{
	case GTK_MOVEMENT_LOGICAL_POSITIONS:
	case GTK_MOVEMENT_VISUAL_POSITIONS:
	case GTK_MOVEMENT_WORDS:
	case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
	case GTK_MOVEMENT_HORIZONTAL_PAGES:
		break;
	case GTK_MOVEMENT_DISPLAY_LINES:
	case GTK_MOVEMENT_PARAGRAPHS:
	case GTK_MOVEMENT_PARAGRAPH_ENDS:
		buffer = gtk_text_view_get_buffer (text_view);
		mark = gtk_text_buffer_get_insert (buffer);
		gtk_text_buffer_get_iter_at_mark (buffer, &cur, mark);
		line = gtk_text_iter_get_line (&cur);
		step_increment = gtk_adjustment_get_step_increment (view->vadjustment);
	
		if ((count < 0) && (line == 0))
		{
			value += count * step_increment;
			set_adjustment_clamped (view->vadjustment, value);
			return;
		}
		else if ((count > 0) && (line == gtk_text_buffer_get_line_count(buffer) - 1))
		{
			value += count * step_increment;
			set_adjustment_clamped (view->vadjustment, value);
			return;
		}
		break;
	case GTK_MOVEMENT_PAGES:
		value += count * gtk_adjustment_get_page_increment (view->vadjustment);
		set_adjustment_clamped (view->vadjustment, value);
		return;
	case GTK_MOVEMENT_BUFFER_ENDS:
		set_adjustment_clamped (view->vadjustment,
					count < 0 ? gtk_adjustment_get_lower (view->vadjustment) : gtk_adjustment_get_upper (view->vadjustment));
		return;
	default:
		break;
    }
	
	GTK_TEXT_VIEW_CLASS (parent_class)->move_cursor (text_view,
								 step, count,
								 extend_selection);
}

/* Public functions
 *---------------------------------------------------------------------------*/

void dma_chunk_view_set_scroll_adjustment (DmaChunkView *this, GtkAdjustment* vadjustment)
{
	this->vadjustment = vadjustment;
}

/* GObject functions
 *---------------------------------------------------------------------------*/

/* dispose is the first destruction step. It is used to unref object created
 * with instance_init in order to break reference counting cycles. This
 * function could be called several times. All function should still work
 * after this call. It has to called its parents.*/

static void
dma_chunk_view_dispose (GObject *object)
{
	/*DmaChunkView *this = DMA_CHUNK_VIEW (obj);*/
	
	G_OBJECT_CLASS (parent_class)->dispose (object);
}

/* finalize is the last destruction step. It must free all memory allocated
 * with instance_init. It is called only one time just before releasing all
 * memory */

static void
dma_chunk_view_finalize (GObject *object)
{
	/*DmaChunkView *view = DMA_CHUNK_VIEW (object);*/
	
	G_OBJECT_CLASS (parent_class)->finalize (object);
}

/* instance_init is the constructor. All functions should work after this
 * call. */

static void
dma_chunk_view_instance_init (DmaChunkView *view)
{
	// GtkAdjustment *adj;
	// GtkWidget* wid;
	// PangoFontDescription *font_desc;

}

/* class_init intialize the class itself not the instance */

static void
dma_chunk_view_class_init (DmaChunkViewClass * klass)
{
	GObjectClass *gobject_class;
	GtkTextViewClass *text_view_class;

	g_return_if_fail (klass != NULL);
	
	gobject_class = G_OBJECT_CLASS (klass);
	text_view_class = GTK_TEXT_VIEW_CLASS (klass);
	parent_class = GTK_WIDGET_CLASS (g_type_class_peek_parent (klass));
	
	gobject_class->dispose = dma_chunk_view_dispose;
	gobject_class->finalize = dma_chunk_view_finalize;
	
	text_view_class->move_cursor = dma_chunk_view_move_cursor;
}

GType
dma_chunk_view_get_type (void)
{
	static GType type = 0;

	if (!type)
	{
		static const GTypeInfo type_info = 
		{
			sizeof (DmaChunkViewClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) dma_chunk_view_class_init,
			(GClassFinalizeFunc) NULL,
			NULL,           /* class_data */
			sizeof (DmaChunkView),
			0,              /* n_preallocs */
			(GInstanceInitFunc) dma_chunk_view_instance_init,
			NULL            /* value_table */
		};

		type = g_type_register_static (GTK_TYPE_TEXT_VIEW,
		                            "DmaChunkView", &type_info, 0);
	}
	
	return type;
}

/* Creation and Destruction
 *---------------------------------------------------------------------------*/

GtkWidget*
dma_chunk_view_new (void)
{
	GtkWidget *this;

	this = g_object_new (DMA_CHUNK_VIEW_TYPE, NULL);
	g_assert (this != NULL);
	
	return this;
}
	
void
dma_chunk_view_free (DmaChunkView *this)
{
	g_object_unref (this);
}

