/* 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 and * Havoc Pennington . * * 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 #include "guppi-seq.h" #include #include #include #include #include #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 $ */