/* This is -*- C -*- */
/* $Id: guppi-seq.c,v 1.28 2002/01/14 05:01:17 trow Exp $ */
/*
* guppi-seq.c
*
* Copyright (C) 2000 EMC Capital Management, Inc.
* Copyright (C) 2001 The Free Software Foundation
*
* Developed by Jon Trowbridge <trow@gnu.org> and
* Havoc Pennington <hp@pobox.com>.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include "guppi-seq.h"
#include <string.h>
#include <gtk/gtksignal.h>
#include <guppi-memory.h>
#include <guppi-debug.h>
#include <guppi-convenient.h>
#include "guppi-seq-boolean.h"
#include "../libguppidataimpl/guppi-seq-boolean-core.h" /* Ack! */
static GtkObjectClass *parent_class = NULL;
enum {
CHANGED_SHIFT_INDICES,
CHANGED_SET,
CHANGED_INSERT,
CHANGED_GROW,
CHANGED_DELETE,
LAST_SIGNAL
};
static guint seq_signals[LAST_SIGNAL] = { 0 };
/***************************************************************************/
/* Possible Operations */
typedef struct _GuppiDataOp_Seq GuppiDataOp_Seq;
struct _GuppiDataOp_Seq {
GuppiDataOp op;
gint i;
gint i1;
gsize N;
};
static void
op_shift (GuppiData *d, GuppiDataOp *op)
{
GuppiSeq *seq = GUPPI_SEQ (d);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (d)->klass);
GuppiDataOp_Seq *seq_op = (GuppiDataOp_Seq *) op;
g_assert (klass->shift_indices);
klass->shift_indices (seq, seq_op->i);
}
static void
op_grow_to_include (GuppiData *d, GuppiDataOp *op)
{
GuppiSeq *seq = GUPPI_SEQ (d);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (d)->klass);
GuppiDataOp_Seq *seq_op = (GuppiDataOp_Seq *) op;
gint i0, i1, j0, j1;
g_assert (klass->insert_generic);
j0 = seq_op->i;
j1 = seq_op->i1;
guppi_seq_bounds (seq, &i0, &i1);
if (guppi_seq_size (seq) == 0) {
klass->insert_generic (seq, 0, j1 - j0 + 1);
klass->shift_indices (seq, j0 - i0);
return;
}
/* Grow to the front */
if (j0 < i0) {
klass->insert_generic (seq, i0, i0 - j0);
klass->shift_indices (seq, j0 - i0);
}
/* Grow to the back */
if (i1 < j1) {
klass->insert_generic (seq, i1+1, j1 - i1);
}
}
static void
op_set_missing (GuppiData *d, GuppiDataOp * op)
{
GuppiSeq *seq = GUPPI_SEQ (d);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (d)->klass);
GuppiDataOp_Seq *seq_op = (GuppiDataOp_Seq *) op;
g_assert (klass->set_missing);
klass->set_missing (seq, seq_op->i, TRUE);
}
static void
op_insert_missing (GuppiData *d, GuppiDataOp *op)
{
GuppiSeq *seq = GUPPI_SEQ (d);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (d)->klass);
GuppiDataOp_Seq *seq_op = (GuppiDataOp_Seq *) op;
g_assert (klass->insert_missing && klass->insert_generic);
klass->insert_generic (seq, seq_op->i, 1);
klass->set_missing (seq, seq_op->i, TRUE);
}
static void
op_delete (GuppiData *d, GuppiDataOp *op)
{
GuppiSeq *seq = GUPPI_SEQ (d);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (d)->klass);
GuppiDataOp_Seq *seq_op = (GuppiDataOp_Seq *) op;
g_assert (klass->delete_many);
klass->delete_many (seq, seq_op->i, seq_op->N);
}
/***************************************************************************/
/**
* guppi_seq_size_hint
* @seq: The sequence
* @expected_size: The expected maximum size of the sequence
*
* Inform the implementation of the expected maximum size of the sequence.
* In some cases, this information can be used to allocate memory in a
* more efficient fashion.
*/
void
guppi_seq_size_hint (GuppiSeq *seq, gsize expected_size)
{
GuppiSeqClass *klass;
g_return_if_fail (GUPPI_IS_SEQ (seq));
/* Read-only data silently ignores size hints. It likes the size it
is just fine, thank you. */
if (guppi_data_is_read_only (GUPPI_DATA (seq)))
return;
klass = GUPPI_SEQ_CLASS (GTK_OBJECT (seq)->klass);
if (klass->size_hint)
klass->size_hint (seq, expected_size);
}
/**
* guppi_seq_indices
* @seq: The sequence
* @min: A pointer to a variable to contain the lowest index value
* @max: A pointer to a variable to contain the highest index bound
*
* Get the minimum and maximum legal values of the sequence's
* position index. Either @min or @max may be %NULL.
*
* guppi_seq_indices() is a synonym for guppi_seq_bounds().
*/
/**
* guppi_seq_bounds
* @seq: The sequence
* @min: A pointer to a variable to contain the lowest index value
* @max: A pointer to a variable to contain the highest index bound
*
* Get the minimum and maximum legal values of the sequence's
* position index. Either @min or @max may be %NULL.
*
* guppi_seq_bounds() is a synonym for guppi_seq_indices().
*/
void
guppi_seq_indices (const GuppiSeq *seq, gint *min, gint *max)
{
GuppiSeqClass *klass;
g_return_if_fail (GUPPI_IS_SEQ (seq));
klass = GUPPI_SEQ_CLASS (GTK_OBJECT (seq)->klass);
g_assert (klass->get_bounds);
klass->get_bounds ((GuppiSeq *) seq, min, max);
}
/**
* guppi_seq_min_index
* @seq: The sequence
*
* Returns: The sequence's lowest legal index value.
*/
gint
guppi_seq_min_index (const GuppiSeq *seq)
{
gint min = 0;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), 0);
guppi_seq_indices (seq, &min, NULL);
return min;
}
/**
* guppi_seq_max_index
* @seq: The sequence
*
* Returns: The sequence's highest legal index value.
*/
gint
guppi_seq_max_index (const GuppiSeq *seq)
{
gint max = -1;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), -1);
guppi_seq_indices (seq, NULL, &max);
return max;
}
/**
* guppi_seq_size
* @seq: The sequence
*
* Computes the length of the sequence.
*
* Returns: The number of elements in the sequence.
*/
gsize
guppi_seq_size (const GuppiSeq *seq)
{
gint min, max;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), 0);
guppi_seq_indices (seq, &min, &max);
g_assert (max + 1 >= min);
return max + 1 - min;
}
/**
* guppi_seq_count
* @seq: The sequence
*
* Counts how many elements of @seq are known values.
*
* Returns: The number of known/non-missing elements in the sequence.
*/
gsize
guppi_seq_count (const GuppiSeq *seq)
{
gsize size, missing;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), 0);
size = guppi_seq_size (seq);
missing = guppi_seq_missing_count ((GuppiSeq *) seq);
g_return_val_if_fail (size >= missing, 0);
return size - missing;
}
/**
* guppi_seq_empty
* @seq: The sequence
*
* Checks if @seq is of zero length.
*
* Returns: %TRUE if @seq contains any elements, %FALSE otherwise.
*/
gboolean
guppi_seq_empty (const GuppiSeq *seq)
{
g_return_val_if_fail (GUPPI_IS_SEQ (seq), TRUE);
return guppi_seq_size (seq) == 0;
}
/**
* guppi_seq_nonempty
* @seq: The sequence
*
* Checks if @seq contains any elements.
*
* Returns: %TRUE if @seq contains no elements whatsoever, %FALSE otherwise.
*/
gboolean
guppi_seq_nonempty (const GuppiSeq *seq)
{
g_return_val_if_fail (GUPPI_IS_SEQ (seq), FALSE);
return guppi_seq_size (seq) > 0;
}
/**
* guppi_seq_absent
* @seq: The sequence
*
* Checks if @seq consists only of missing values.
*
* Returns: %TRUE is @seq is either empty or contains only missing values,
* %FALSE otherwise.
*/
gboolean
guppi_seq_absent (const GuppiSeq *seq)
{
g_return_val_if_fail (GUPPI_IS_SEQ (seq), TRUE);
return guppi_seq_count (seq) == 0;
}
/**
* guppi_seq_present
* @seq: The sequence
*
* Checks if @seq contains any known values.
*
* Returns: %TRUE is @seq contains at least one non-missing value,
* %FALSE otherwise.
*/
gboolean
guppi_seq_present (const GuppiSeq *seq)
{
g_return_val_if_fail (GUPPI_IS_SEQ (seq), FALSE);
return guppi_seq_count (seq) > 0;
}
/**
* guppi_seq_in_bounds
* @seq: The sequence
* @i: An index value
*
* Checks if @i lies within the sequence's bounds.
*
* Returns: %TRUE if @i is a legal index value for @seq.
*/
gboolean
guppi_seq_in_bounds (const GuppiSeq *seq, gint i)
{
gint min = 0, max = -1;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), FALSE);
guppi_seq_indices (seq, &min, &max);
return min <= i && i <= max;
}
/**
* guppi_seq_contains_bounds
* @seq: A sequence
* @seq2: Another sequence
*
* Check if a sequence's index bounds completely contain those
* of another sequence.
*
* Returns: %TRUE if the bounds of @seq2 are contained entirely within
* those of @seq.
*/
gboolean
guppi_seq_contains_bounds (const GuppiSeq *seq, const GuppiSeq *seq2)
{
gint amin = 0, amax = -1, bmin = 0, bmax = -1;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), FALSE);
g_return_val_if_fail (GUPPI_IS_SEQ (seq2), FALSE);
guppi_seq_indices (seq, &amin, &amax);
guppi_seq_indices (seq2, &bmin, &bmax);
return amin <= bmin && bmax <= amax;
}
/**
* guppi_seq_equal_bounds
* @seq: A sequence
* @seq2: Another sequence
*
* Check if two sequences have the same upper and lower index bounds.
*
* Returns: %TRUE if the bounds of @seq and @seq2 are identical.
*/
gboolean
guppi_seq_equal_bounds (const GuppiSeq *seq, const GuppiSeq *seq2)
{
gint amin = 0, amax = -1, bmin = 0, bmax = -1;
g_return_val_if_fail (seq != NULL, FALSE);
g_return_val_if_fail (seq2 != NULL, FALSE);
guppi_seq_indices (seq, &amin, &amax);
guppi_seq_indices (seq2, &bmin, &bmax);
return amin == bmin && bmax == amax;
}
/**
* guppi_seq_common_bounds
* @seq: A sequences
* @seq2: Another sequences
* @min: A pointer to a variable to contain the lower index value
* @max: A pointer to a variable to contain the upper index value
*
* Compute the largest range of index values that are valid
* for both @seq and @seq2.
*/
void
guppi_seq_common_bounds (const GuppiSeq *seq, const GuppiSeq *seq2,
gint *min, gint *max)
{
gint amin = 0, amax = -1, bmin = 0, bmax = -1;
g_return_if_fail (seq != NULL);
g_return_if_fail (seq != NULL);
guppi_seq_indices (seq, &amin, &amax);
guppi_seq_indices (seq, &bmin, &bmax);
if (min)
*min = MAX (amin, bmin);
if (max)
*max = MIN (amax, bmax);
}
/**
* guppi_seq_shift_indices
* @seq: A sequences
* @delta: The amount of shift the indices by.
*
* Shift the sequence's indices by @delta.
*/
void
guppi_seq_shift_indices (GuppiSeq *seq, gint delta)
{
GuppiDataOp_Seq op;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
if (delta == 0)
return;
/* I confess... I love typing op.op.op! */
op.op.op = op_shift;
op.i = delta;
guppi_seq_changed_shift_indices (seq, delta, (GuppiDataOp *) &op);
}
/**
* guppi_seq_set_min_index
* @seq: A sequence
* @min: The new lower index value
*
* Adjust the sequence's index values such that
* the lower bound becomes @min.
*/
void
guppi_seq_set_min_index (GuppiSeq *seq, gint min)
{
gint old_min;
g_return_if_fail (GUPPI_IS_SEQ (seq));
old_min = guppi_seq_min_index (seq);
guppi_seq_shift_indices (seq, min - old_min);
}
/**
* guppi_seq_set_max_index
* @seq: A sequence
* @max: The new upper index value
*
* Adjust the sequence's index values such that
* the lower bound becomes @max.
*/
void
guppi_seq_set_max_index (GuppiSeq *seq, gint max)
{
gint old_max;
g_return_if_fail (GUPPI_IS_SEQ (seq));
old_max = guppi_seq_max_index (seq);
guppi_seq_shift_indices (seq, max - old_max);
}
/**
* guppi_seq_delete
* @seq: A sequence
* @i: A position index
*
* Remove the @i-th element of sequence @seq. The position index
* of all elements above the @i-th position are decremented.
* @seq must be writeable and @i must be in-bounds.
*/
void
guppi_seq_delete (GuppiSeq *seq, gint i)
{
GuppiDataOp_Seq op;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
g_return_if_fail (guppi_seq_in_bounds (seq, i));
op.op.op = op_delete;
op.i = i;
op.N = 1;
guppi_seq_changed_delete (seq, i, 1, (GuppiDataOp *) & op);
}
/**
* guppi_seq_delete_many
* @seq: A sequence
* @i: A position index
* @N: A count
*
* Remove @N elements from sequence @seq, starting at the @i-th. The
* position index of all elements following the removed elements are
* decreased accordingly.
* @seq must be writeable and @i must be in-bounds.
*/
void
guppi_seq_delete_many (GuppiSeq *seq, gint i, gsize N)
{
GuppiDataOp_Seq op;
gint i1;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
g_return_if_fail (guppi_seq_in_bounds (seq, i));
if (N == 0)
return;
i1 = guppi_seq_max_index (seq);
if (i + N - 1 > i1)
N = i1 - i + 1;
op.op.op = op_delete;
op.i = i;
op.N = N;
guppi_seq_changed_delete (seq, i, N, (GuppiDataOp *) & op);
}
/**
* guppi_seq_delete_range
* @seq: A sequence
* @i0: The lower index bound
* @i1: The upper index bound
*
* Remove the elements at positions @i0 through @i1 from sequence
* @seq. The position index of all elements following the removed
* elements are decreased accordingly. @seq must be writeable and both
* @i0 and @i1 must be in-bounds.
*/
void
guppi_seq_delete_range (GuppiSeq *seq, gint i0, gint i1)
{
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
g_return_if_fail (guppi_seq_in_bounds (seq, i0));
g_return_if_fail (guppi_seq_in_bounds (seq, i1));
guppi_2sort_i (&i0, &i1);
guppi_seq_delete_many (seq, i0, i1 - i0 + 1);
}
/**
* guppi_seq_grow_to_include
* @seq: A sequence
* @i: A position index
*
* Expand the bounds of the sequence @seq sufficiently to
* contain @i. If @i is already in-bounds, this operation
* does nothing. @seq must be writeable.
*
* The default values assigned to any elements added to @seq
* are type-dependent.
*/
void
guppi_seq_grow_to_include (GuppiSeq *seq, gint i)
{
g_return_if_fail (GUPPI_IS_SEQ (seq));
guppi_seq_grow_to_include_range (seq, i, i);
}
/**
* guppi_seq_grow_to_include_range
* @seq: A sequence
* @i0: The lower index bound
* @i1: The upper index bound
*
* Expand the bounds of the sequence @seq sufficiently to contain the
* range @i0 to @i1. If that range is already in-bounds, this
* operation does nothing. @seq must be writeable.
*
* The default values assigned to any elements added to @seq
* are type-dependent.
*/
void
guppi_seq_grow_to_include_range (GuppiSeq *seq, gint i0, gint i1)
{
GuppiDataOp_Seq op;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
if (guppi_seq_in_bounds (seq, i0) && guppi_seq_in_bounds (seq, i1))
return;
guppi_2sort_i (&i0, &i1);
op.op.op = op_grow_to_include;
op.i = i0;
op.i1 = i1;
guppi_seq_changed_grow (seq, i0, i1, (GuppiDataOp *) &op);
}
/**
* guppi_seq_grow_to_overlap
* @seq: A sequence
* @seq_to_overlap: The sequence that @seq is to overlap
*
* Expand the bounds of the sequence @seq sufficiently to contain the
* full range of indices of @seq_to_overlap. If that range is already
* in-bounds, this operation does nothing. @seq must be writeable.
*
* The default values assigned to any elements added to @seq
* are type-dependent.
*/
void
guppi_seq_grow_to_overlap (GuppiSeq *seq, const GuppiSeq *seq_to_overlap)
{
gint i0, i1;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (GUPPI_IS_SEQ (seq_to_overlap));
if (guppi_seq_size (seq_to_overlap) > 0) {
guppi_seq_indices (seq_to_overlap, &i0, &i1);
guppi_seq_grow_to_include_range (seq, i0, i1);
}
}
/**************************************************************************/
/**
* guppi_seq_has_missing
* @seq: The sequence
*
* Checks whether or not the sequence contains any missing values.
*
* Returns: %TRUE if @seq contains missing values.
*/
gboolean
guppi_seq_has_missing (GuppiSeq *seq)
{
GuppiSeqClass *klass;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), FALSE);
klass = GUPPI_SEQ_CLASS (GTK_OBJECT (seq)->klass);
if (! klass->support_missing_values)
return FALSE;
g_assert (klass->has_missing);
return klass->has_missing (seq);
}
/**
* guppi_seq_missing_count
* @seq: The sequence
*
* Counts the number of missing values in the sequence.
*
* Returns: The number of missing values in @seq.
*/
gsize
guppi_seq_missing_count (GuppiSeq *seq)
{
GuppiSeqClass *klass;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), 0);
klass = GUPPI_SEQ_CLASS (GTK_OBJECT (seq)->klass);
if (! klass->support_missing_values)
return 0;
g_assert (klass->missing_count);
return klass->missing_count (seq);
}
/**
* guppi_seq_missing
* @seq: The sequence
* @i: An index value
*
* Check to see if a particular element of a sequence is a missing value.
*
* Returns: %TRUE is @i is a missing value.
*/
gboolean
guppi_seq_missing (GuppiSeq *seq, gint i)
{
GuppiSeqClass *klass;
g_return_val_if_fail (GUPPI_IS_SEQ (seq), FALSE);
g_return_val_if_fail (guppi_seq_in_bounds (seq, i), FALSE);
klass = GUPPI_SEQ_CLASS (GTK_OBJECT (seq)->klass);
if (! klass->support_missing_values)
return FALSE;
g_assert (klass->missing);
return klass->missing (seq, i);
}
/**
* guppi_seq_available
* @seq: The sequence
* @i: An index value
*
* Check to see if a particular element of a sequence is a known value.
*
* Returns: %TRUE is @i is not a missing value.
*/
gboolean
guppi_seq_available (GuppiSeq *seq, gint i)
{
return !guppi_seq_missing (seq, i);
}
/**
* guppi_seq_set_missing
* @seq: The sequence
* @i: An index value
*
* Marks an element of the sequence as a missing value.
* @i must be a valid index for @seq.
* @seq must be writeable.
*/
void
guppi_seq_set_missing (GuppiSeq *seq, gint i)
{
gboolean x;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
g_return_if_fail (guppi_seq_in_bounds (seq, i));
x = guppi_seq_missing (seq, i);
if (!x) {
GuppiDataOp_Seq op;
op.op.op = op_set_missing;
op.i = i;
guppi_seq_changed_set (seq, i, i, (GuppiDataOp *) & op);
}
}
/**
* guppi_seq_insert_missing
* @seq: The sequence
* @i: An index value
*
* Inserts a missing value into a sequence at a particular position.
* @i must be a valid index for @seq.
* @seq must be writeable.
*/
void
guppi_seq_insert_missing (GuppiSeq *seq, gint i)
{
GuppiDataOp_Seq op;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
op.op.op = op_insert_missing;
op.i = i;
guppi_seq_changed_insert (seq, i, 1, (GuppiDataOp *) & op);
}
/**
* guppi_seq_prepend_missing
* @seq: The sequence
*
* Prepend a missing value to the sequence.
* @seq must be writeable.
*/
void
guppi_seq_prepend_missing (GuppiSeq *seq)
{
gint first;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
first = guppi_seq_min_index (seq);
guppi_seq_insert_missing (seq, first);
}
/**
* guppi_seq_append_missing
* @seq: The sequence
*
* Append a missing value to the sequence.
* @seq must be writeable.
*/
void
guppi_seq_append_missing (GuppiSeq *seq)
{
gint last;
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (guppi_data_can_change (GUPPI_DATA (seq)));
last = guppi_seq_max_index (seq);
guppi_seq_insert_missing (seq, last + 1);
}
/**************************************************************************/
void
guppi_seq_changed_shift_indices (GuppiSeq *seq, gint i, GuppiDataOp * op)
{
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (op != NULL);
guppi_data_add_pending_op (GUPPI_DATA (seq), op);
gtk_signal_emit (GTK_OBJECT (seq), seq_signals[CHANGED_SHIFT_INDICES], i);
}
void
guppi_seq_changed_set (GuppiSeq *seq, gint i0, gint i1, GuppiDataOp * op)
{
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (op != NULL);
guppi_data_add_pending_op (GUPPI_DATA (seq), op);
gtk_signal_emit (GTK_OBJECT (seq), seq_signals[CHANGED_SET], i0, i1);
}
void
guppi_seq_changed_insert (GuppiSeq *seq, gint i, gsize N, GuppiDataOp * op)
{
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (op != NULL);
guppi_data_add_pending_op (GUPPI_DATA (seq), op);
gtk_signal_emit (GTK_OBJECT (seq), seq_signals[CHANGED_INSERT], i, N);
}
void
guppi_seq_changed_grow (GuppiSeq *seq, gint i0, gint i1, GuppiDataOp *op)
{
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (op != NULL);
guppi_data_add_pending_op (GUPPI_DATA (seq), op);
gtk_signal_emit (GTK_OBJECT (seq), seq_signals[CHANGED_GROW], i0, i1);
}
void
guppi_seq_changed_delete (GuppiSeq *seq, gint i, gsize N, GuppiDataOp * op)
{
g_return_if_fail (GUPPI_IS_SEQ (seq));
g_return_if_fail (op != NULL);
guppi_data_add_pending_op (GUPPI_DATA (seq), op);
gtk_signal_emit (GTK_OBJECT (seq), seq_signals[CHANGED_DELETE], i, N);
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
/*
* Base implementation of missing values.
* This could be optimized substantially.
*/
static GuppiSeqBoolean *
get_missing (GuppiSeq *seq)
{
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (seq)->klass);
if (! klass->support_missing_values)
return NULL;
/* Magic hack to disable missing data */
if (seq->missing_data == get_missing)
return NULL;
if (seq->missing_data == NULL) {
seq->missing_data = guppi_seq_boolean_core_new ();
GUPPI_SEQ (seq->missing_data)->missing_data = get_missing; /* avoid infinite recursion */
g_assert (seq->missing_data);
}
return seq->missing_data;
}
static void
missing_sanity_check (GuppiSeq *seq)
{
GuppiSeqBoolean *miss = get_missing (seq);
gint seq_i0, seq_i1, miss_i0, miss_i1;
if (miss != NULL) {
guppi_seq_bounds (seq, &seq_i0, &seq_i1);
guppi_seq_bounds (GUPPI_SEQ (miss), &miss_i0, &miss_i1);
if (seq_i0 != miss_i0 || seq_i1 != miss_i1) {
g_warning ("seq: [%d, %d] miss: [%d, %d]", seq_i0, seq_i1, miss_i0, miss_i1);
g_assert (0);
}
}
}
static void
shift_indices (GuppiSeq *seq, gint delta)
{
GuppiSeqBoolean *miss = get_missing (seq);
if (miss) {
guppi_seq_shift_indices (GUPPI_SEQ (miss), delta);
}
}
static void
delete_many (GuppiSeq *seq, gint i, gsize N)
{
GuppiSeqBoolean *miss = get_missing (seq);
if (miss) {
guppi_seq_delete_many (GUPPI_SEQ (miss), i, N);
}
}
static gboolean
has_missing (GuppiSeq *seq)
{
GuppiSeqBoolean *miss = get_missing (seq);
return miss && guppi_seq_boolean_first_true (miss) <= guppi_seq_max_index (GUPPI_SEQ (miss));
}
static gsize
missing_count (GuppiSeq *seq)
{
GuppiSeqBoolean *miss = get_missing (seq);
return miss ? guppi_seq_boolean_true_count (miss) : 0;
}
static gboolean
missing (GuppiSeq *seq, gint i)
{
GuppiSeqBoolean *miss = get_missing (seq);
return miss ? guppi_seq_boolean_get (miss, i) : FALSE;
}
static void
set_missing (GuppiSeq *seq, gint i, gboolean x)
{
GuppiSeqBoolean *miss = get_missing (seq);
if (miss) {
guppi_seq_boolean_set (miss, i, x);
}
missing_sanity_check (seq);
}
static void
set_range_missing (GuppiSeq *seq, gint i0, gint i1, gboolean x)
{
GuppiSeqBoolean *miss = get_missing (seq);
if (miss) {
gint i;
for (i = i0; i <= i1; ++i) {
guppi_seq_boolean_set (miss, i, x);
}
}
missing_sanity_check (seq);
}
static void
set_many_missing (GuppiSeq *seq, gint *ind, gsize N, gboolean x)
{
GuppiSeqBoolean *miss = get_missing (seq);
if (miss) {
guppi_seq_boolean_set_many (miss, ind, N, x);
}
missing_sanity_check (seq);
}
static void
set_all_missing (GuppiSeq *seq, gboolean x)
{
GuppiSeqBoolean *miss = get_missing (seq);
if (miss) {
guppi_seq_boolean_set_all (miss, x);
}
}
static void
insert_missing (GuppiSeq *seq, gint i, gboolean x, gsize N)
{
GuppiSeqBoolean *miss = get_missing (seq);
if (miss) {
guppi_seq_boolean_insert_many (miss, i, x, N);
}
missing_sanity_check (seq);
}
static void
copy_missing (GuppiSeq *dest, GuppiSeq *src)
{
GuppiSeq *src_miss;
g_return_if_fail (guppi_seq_equal_bounds (dest, src));
guppi_unref0 (dest->missing_data);
src_miss = (GuppiSeq *) get_missing (src);
if (src_miss) {
dest->missing_data = guppi_data_copy (GUPPI_DATA (src_miss));
}
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
static void
export_xml (GuppiData *data, GuppiXMLDocument *doc, xmlNodePtr node)
{
GuppiSeq *seq = GUPPI_SEQ (data);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (data)->klass);
gint i, i0, i1;
xmlNodePtr seq_node;
gboolean has_missing;
g_return_if_fail (klass->export_xml_element != NULL);
guppi_seq_bounds (seq, &i0, &i1);
has_missing = guppi_seq_has_missing (seq);
seq_node = guppi_xml_new_node (doc, "Sequence");
if (i0 <= i1) {
guppi_xml_set_property_int (seq_node, "min_index", i0);
guppi_xml_set_property_int (seq_node, "max_index", i1);
}
guppi_xml_set_property_bool (seq_node, "has_missing", has_missing);
xmlAddChild (node, seq_node);
for (i = i0; i <= i1; ++i) {
xmlNodePtr el_node;
if (has_missing && guppi_seq_missing (seq, i)) {
el_node = guppi_xml_new_node (doc, "missing_value");
} else {
el_node = klass->export_xml_element (seq, i, doc);
}
xmlAddChild (seq_node, el_node);
}
if (GUPPI_DATA_CLASS (parent_class)->export_xml)
GUPPI_DATA_CLASS (parent_class)->export_xml (data, doc, node);
}
static gboolean
import_xml (GuppiData *data, GuppiXMLDocument *doc, xmlNodePtr node)
{
GuppiSeq *seq = GUPPI_SEQ (data);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (data)->klass);
if (! strcmp (node->name, "Sequence")) {
gint i, i0, i1;
gboolean has_missing;
i0 = guppi_xml_get_property_int (node, "min_index", 0);
i1 = guppi_xml_get_property_int (node, "max_index", -1);
has_missing = guppi_xml_get_property_bool (node, "has_missing", TRUE);
if (i0 > i1)
return TRUE;
node = node->xmlChildrenNode;
i = i0;
while (node != NULL && i <= i1) {
if (has_missing && ! strcmp (node->name, "missing_value")) {
guppi_seq_append_missing (seq);
} else if (! klass->import_xml_element (seq, doc, node))
return FALSE;
node = node->next;
++i;
}
return TRUE;
}
/* Pass along any non-Sequence nodes. */
if (GUPPI_DATA_CLASS (parent_class)->import_xml)
return GUPPI_DATA_CLASS (parent_class)->import_xml (data, doc, node);
else
return FALSE;
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
static gchar *
get_size_info (GuppiData *d)
{
gint i0, i1;
guppi_seq_bounds (GUPPI_SEQ (d), &i0, &i1);
if (i0 > i1)
return g_strdup ("empty");
else
return g_strdup_printf ("%d to %d", i0, i1);
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
static void
guppi_seq_finalize (GtkObject * obj)
{
GuppiSeq *seq = GUPPI_SEQ (obj);
if (seq->missing_data != get_missing)
guppi_unref (seq->missing_data);
if (parent_class->finalize)
parent_class->finalize (obj);
}
static void
changed_shift_indices (GuppiSeq *seq, gint i)
{
guppi_data_changed (GUPPI_DATA (seq));
}
static void
changed_set (GuppiSeq *seq, gint i0, gint i1)
{
guppi_data_changed (GUPPI_DATA (seq));
}
static void
changed_insert (GuppiSeq *seq, gint i, gsize N)
{
guppi_data_changed (GUPPI_DATA (seq));
}
static void
changed_grow (GuppiSeq *seq, gint i0, gint i1)
{
guppi_data_changed (GUPPI_DATA (seq));
}
static void
changed_delete (GuppiSeq *seq, gint i, gsize N)
{
guppi_data_changed (GUPPI_DATA (seq));
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
static gboolean
validate_class (GuppiDataClass *klass)
{
GuppiSeqClass *seq_klass = GUPPI_SEQ_CLASS (klass);
gboolean ok = TRUE;
if (seq_klass->get_bounds == NULL) {
g_warning ("Method GuppiSeq::get_bounds not defined.");
ok = FALSE;
}
if (seq_klass->insert_generic == NULL) {
g_warning ("Method GuppiSeq::insert_generic not defined.");
ok = FALSE;
}
if (seq_klass->shift_indices == shift_indices) {
g_warning ("Method GuppiSeq::shift_indices not defined.");
ok = FALSE;
}
if (seq_klass->export_xml_element == NULL) {
g_warning ("Method GuppiSeq::export_xml_element not defined.");
ok = FALSE;
}
if (seq_klass->import_xml_element == NULL) {
g_warning ("Method GuppiSeq::export_xml_element not defined.");
ok = FALSE;
}
if (GUPPI_DATA_CLASS (parent_class)->validate_class
&& ! GUPPI_DATA_CLASS (parent_class)->validate_class (klass))
ok = FALSE;
return ok;
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
static void
guppi_seq_class_init (GuppiSeqClass *klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass);
parent_class = gtk_type_class (GUPPI_TYPE_DATA);
object_class->finalize = guppi_seq_finalize;
data_class->validate_class = validate_class;
data_class->import_xml = import_xml;
data_class->export_xml = export_xml;
data_class->get_size_info = get_size_info;
klass->shift_indices = shift_indices;
klass->delete_many = delete_many;
klass->has_missing = has_missing;
klass->missing_count = missing_count;
klass->missing = missing;
klass->set_missing = set_missing;
klass->set_range_missing = set_range_missing;
klass->set_many_missing = set_many_missing;
klass->set_all_missing = set_all_missing;
klass->insert_missing = insert_missing;
klass->copy_missing = copy_missing;
klass->changed_shift_indices = changed_shift_indices;
klass->changed_set = changed_set;
klass->changed_insert = changed_insert;
klass->changed_grow = changed_grow;
klass->changed_delete = changed_delete;
seq_signals[CHANGED_SHIFT_INDICES] =
gtk_signal_new ("changed_shift_indices",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (GuppiSeqClass, changed_shift_indices),
gtk_marshal_NONE__INT, GTK_TYPE_NONE, 1, GTK_TYPE_INT);
seq_signals[CHANGED_SET] =
gtk_signal_new ("changed_set",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (GuppiSeqClass, changed_set),
gtk_marshal_NONE__INT_INT,
GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
/* I've used the NONE__INT_INT marshaller here, even though the
correct thing would be do use NONE__INT_UINT. I'm just being
lazy and using the predefined marshallers, though. This should
work fine --- Famous last words :-) */
seq_signals[CHANGED_INSERT] =
gtk_signal_new ("changed_insert",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (GuppiSeqClass, changed_insert),
gtk_marshal_NONE__INT_INT,
GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_UINT);
seq_signals[CHANGED_GROW] =
gtk_signal_new ("changed_grow",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (GuppiSeqClass, changed_grow),
gtk_marshal_NONE__INT_INT,
GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_UINT);
seq_signals[CHANGED_DELETE] =
gtk_signal_new ("changed_delete",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (GuppiSeqClass, changed_delete),
gtk_marshal_NONE__INT_INT,
GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_UINT);
gtk_object_class_add_signals (object_class, seq_signals, LAST_SIGNAL);
}
static void
guppi_seq_init (GuppiSeq *obj)
{
}
GtkType guppi_seq_get_type (void)
{
static GtkType guppi_seq_type = 0;
if (!guppi_seq_type) {
static const GtkTypeInfo guppi_seq_info = {
"GuppiSeq",
sizeof (GuppiSeq),
sizeof (GuppiSeqClass),
(GtkClassInitFunc) guppi_seq_class_init,
(GtkObjectInitFunc) guppi_seq_init,
NULL, NULL, (GtkClassInitFunc) NULL
};
guppi_seq_type = gtk_type_unique (GUPPI_TYPE_DATA, &guppi_seq_info);
}
return guppi_seq_type;
}
/* $Id: guppi-seq.c,v 1.28 2002/01/14 05:01:17 trow Exp $ */
syntax highlighted by Code2HTML, v. 0.9.1