X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktextmark.c;h=33b0649a6f66f050b9e73b03b07ec16bb38b4efc;hb=HEAD;hp=739504732b7f401dfa1be548d719e54f43f97753;hpb=9ae724bf4d72d1eed98f3e55a640b31ff552e8f1;p=~andy%2Fgtk diff --git a/gtk/gtktextmark.c b/gtk/gtktextmark.c index 739504732..33b0649a6 100644 --- a/gtk/gtktextmark.c +++ b/gtk/gtktextmark.c @@ -1,117 +1,421 @@ -/* - * tkTextMark.c -- - * - * This file contains the procedure that implement marks for - * text widgets. +/* gtktextmark.c - mark segments * * Copyright (c) 1994 The Regents of the University of California. * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * Copyright (c) 2000 Red Hat, Inc. + * Tk -> Gtk port by Havoc Pennington + * + * This software is copyrighted by the Regents of the University of + * California, Sun Microsystems, Inc., and other parties. The + * following terms apply to all files associated with the software + * unless explicitly disclaimed in individual files. + * + * The authors hereby grant permission to use, copy, modify, + * distribute, and license this software and its documentation for any + * purpose, provided that existing copyright notices are retained in + * all copies and that this notice is included verbatim in any + * distributions. No written agreement, license, or royalty fee is + * required for any of the authorized uses. Modifications to this + * software may be copyrighted by their authors and need not follow + * the licensing terms described here, provided that the new terms are + * clearly indicated on the first page of each file where they apply. + * + * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL + * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, + * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE + * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * GOVERNMENT USE: If you are acquiring this software on behalf of the + * U.S. government, the Government shall have only "Restricted Rights" + * in the software and related documentation as defined in the Federal + * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + * are acquiring the software on behalf of the Department of Defense, + * the software shall be classified as "Commercial Computer Software" + * and the Government shall have only "Restricted Rights" as defined + * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the + * foregoing, the authors grant the U.S. Government and others acting + * in its behalf permission to use and distribute the software in + * accordance with the terms specified in this license. * - * RCS: @(#) $Id$ */ +#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API +#include "config.h" #include "gtktextbtree.h" +#include "gtkprivate.h" +#include "gtkintl.h" + + +/** + * SECTION:gtktextmark + * @Short_description: A position in the buffer preserved across buffer modifications + * @Title: GtkTextMark + * + * You may wish to begin by reading the text widget + * conceptual overview which gives an overview of all the objects and data + * types related to the text widget and how they work together. + * + * A #GtkTextMark is like a bookmark in a text buffer; it preserves a position in + * the text. You can convert the mark to an iterator using + * gtk_text_buffer_get_iter_at_mark(). Unlike iterators, marks remain valid across + * buffer mutations, because their behavior is defined when text is inserted or + * deleted. When text containing a mark is deleted, the mark remains in the + * position originally occupied by the deleted text. When text is inserted at a + * mark, a mark with left gravity will be moved to the + * beginning of the newly-inserted text, and a mark with right + * gravity will be moved to the end. + * + * + * "left" and "right" here refer to logical direction (left is the toward the start + * of the buffer); in some languages such as Hebrew the logically-leftmost text is + * not actually on the left when displayed. + * + * + * Marks are reference counted, but the reference count only controls the validity + * of the memory; marks can be deleted from the buffer at any time with + * gtk_text_buffer_delete_mark(). Once deleted from the buffer, a mark is + * essentially useless. + * + * Marks optionally have names; these can be convenient to avoid passing the + * #GtkTextMark object around. + * + * Marks are typically created using the gtk_text_buffer_create_mark() function. + */ + + +static void gtk_text_mark_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_text_mark_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gtk_text_mark_finalize (GObject *object); + +static GtkTextLineSegment *gtk_mark_segment_new (GtkTextMark *mark_obj); + +G_DEFINE_TYPE (GtkTextMark, gtk_text_mark, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_NAME, + PROP_LEFT_GRAVITY +}; + +static void +gtk_text_mark_class_init (GtkTextMarkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gtk_text_mark_finalize; + object_class->set_property = gtk_text_mark_set_property; + object_class->get_property = gtk_text_mark_get_property; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + P_("Name"), + P_("Mark name"), + NULL, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_LEFT_GRAVITY, + g_param_spec_boolean ("left-gravity", + P_("Left gravity"), + P_("Whether the mark has left gravity"), + FALSE, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gtk_text_mark_init (GtkTextMark *mark) +{ + mark->segment = gtk_mark_segment_new (mark); +} + +static void +gtk_text_mark_finalize (GObject *obj) +{ + GtkTextMark *mark; + GtkTextLineSegment *seg; + + mark = GTK_TEXT_MARK (obj); + + seg = mark->segment; + + if (seg) + { + if (seg->body.mark.tree != NULL) + g_warning ("GtkTextMark being finalized while still in the buffer; " + "someone removed a reference they didn't own! Crash " + "impending"); + + g_free (seg->body.mark.name); + g_free (seg); + + mark->segment = NULL; + } + + /* chain parent_class' handler */ + G_OBJECT_CLASS (gtk_text_mark_parent_class)->finalize (obj); +} + +static void +gtk_text_mark_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + gchar *tmp; + GtkTextMark *mark = GTK_TEXT_MARK (object); + GtkTextLineSegment *seg = mark->segment; + + switch (prop_id) + { + case PROP_NAME: + tmp = seg->body.mark.name; + seg->body.mark.name = g_value_dup_string (value); + g_free (tmp); + break; + + case PROP_LEFT_GRAVITY: + if (g_value_get_boolean (value)) + seg->type = >k_text_left_mark_type; + else + seg->type = >k_text_right_mark_type; + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtk_text_mark_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkTextMark *mark = GTK_TEXT_MARK (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, gtk_text_mark_get_name (mark)); + break; + + case PROP_LEFT_GRAVITY: + g_value_set_boolean (value, gtk_text_mark_get_left_gravity (mark)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} +/** + * gtk_text_mark_new: + * @name: (allow-none): mark name or %NULL + * @left_gravity: whether the mark should have left gravity + * + * Creates a text mark. Add it to a buffer using gtk_text_buffer_add_mark(). + * If @name is %NULL, the mark is anonymous; otherwise, the mark can be + * retrieved by name using gtk_text_buffer_get_mark(). If a mark has left + * gravity, and text is inserted at the mark's current location, the mark + * will be moved to the left of the newly-inserted text. If the mark has + * right gravity (@left_gravity = %FALSE), the mark will end up on the + * right of newly-inserted text. The standard left-to-right cursor is a + * mark with right gravity (when you type, the cursor stays on the right + * side of the text you're typing). + * + * Return value: new #GtkTextMark + * + * Since: 2.12 + **/ +GtkTextMark * +gtk_text_mark_new (const gchar *name, + gboolean left_gravity) +{ + return g_object_new (GTK_TYPE_TEXT_MARK, + "name", name, + "left-gravity", left_gravity, + NULL); +} + +/** + * gtk_text_mark_get_visible: + * @mark: a #GtkTextMark + * + * Returns %TRUE if the mark is visible (i.e. a cursor is displayed + * for it). + * + * Return value: %TRUE if visible + **/ gboolean -gtk_text_mark_is_visible(GtkTextMark *mark) +gtk_text_mark_get_visible (GtkTextMark *mark) { GtkTextLineSegment *seg; - seg = (GtkTextLineSegment*)mark; + seg = mark->segment; return seg->body.mark.visible; } -char * +/** + * gtk_text_mark_get_name: + * @mark: a #GtkTextMark + * + * Returns the mark name; returns NULL for anonymous marks. + * + * Return value: mark name + **/ +const char * gtk_text_mark_get_name (GtkTextMark *mark) { GtkTextLineSegment *seg; - seg = (GtkTextLineSegment*)mark; + seg = mark->segment; - return g_strdup (seg->body.mark.name); + return seg->body.mark.name; +} + +/** + * gtk_text_mark_get_deleted: + * @mark: a #GtkTextMark + * + * Returns %TRUE if the mark has been removed from its buffer + * with gtk_text_buffer_delete_mark(). See gtk_text_buffer_add_mark() + * for a way to add it to a buffer again. + * + * Return value: whether the mark is deleted + **/ +gboolean +gtk_text_mark_get_deleted (GtkTextMark *mark) +{ + GtkTextLineSegment *seg; + + g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), FALSE); + + seg = mark->segment; + + if (seg == NULL) + return TRUE; + + return seg->body.mark.tree == NULL; +} + +/** + * gtk_text_mark_get_buffer: + * @mark: a #GtkTextMark + * + * Gets the buffer this mark is located inside, + * or %NULL if the mark is deleted. + * + * Return value: (transfer none): the mark's #GtkTextBuffer + **/ +GtkTextBuffer* +gtk_text_mark_get_buffer (GtkTextMark *mark) +{ + GtkTextLineSegment *seg; + + g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), NULL); + + seg = mark->segment; + + if (seg->body.mark.tree == NULL) + return NULL; + else + return _gtk_text_btree_get_buffer (seg->body.mark.tree); +} + +/** + * gtk_text_mark_get_left_gravity: + * @mark: a #GtkTextMark + * + * Determines whether the mark has left gravity. + * + * Return value: %TRUE if the mark has left gravity, %FALSE otherwise + **/ +gboolean +gtk_text_mark_get_left_gravity (GtkTextMark *mark) +{ + GtkTextLineSegment *seg; + + g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), FALSE); + + seg = mark->segment; + + return seg->type == >k_text_left_mark_type; } /* * Macro that determines the size of a mark segment: */ -#define MSEG_SIZE ((unsigned) (G_STRUCT_OFFSET(GtkTextLineSegment, body) \ - + sizeof(GtkTextMarkBody))) +#define MSEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \ + + sizeof (GtkTextMarkBody))) -GtkTextLineSegment* -mark_segment_new(GtkTextBTree *tree, - gboolean left_gravity, - const gchar *name) +static GtkTextLineSegment * +gtk_mark_segment_new (GtkTextMark *mark_obj) { GtkTextLineSegment *mark; - mark = (GtkTextLineSegment *) g_malloc0(MSEG_SIZE); - mark->body.mark.name = g_strdup(name); - - if (left_gravity) - mark->type = >k_text_left_mark_type; - else - mark->type = >k_text_right_mark_type; + mark = (GtkTextLineSegment *) g_malloc0 (MSEG_SIZE); + mark->body.mark.name = NULL; + mark->type = >k_text_right_mark_type; mark->byte_count = 0; mark->char_count = 0; - mark->body.mark.tree = tree; + mark->body.mark.obj = mark_obj; + mark_obj->segment = mark; + + mark->body.mark.tree = NULL; mark->body.mark.line = NULL; mark->next = NULL; - mark->body.mark.refcount = 1; - mark->body.mark.visible = FALSE; - + mark->body.mark.not_deleteable = FALSE; + return mark; } void -mark_segment_ref(GtkTextLineSegment *mark) +_gtk_mark_segment_set_tree (GtkTextLineSegment *mark, + GtkTextBTree *tree) { - g_return_if_fail(mark != NULL); - g_return_if_fail(mark->type == >k_text_right_mark_type || - mark->type == >k_text_left_mark_type); - g_return_if_fail(mark->body.mark.refcount > 0); - - mark->body.mark.refcount += 1; -} + g_assert (mark->body.mark.tree == NULL); + g_assert (mark->body.mark.obj != NULL); -void -mark_segment_unref(GtkTextLineSegment *mark) -{ - g_return_if_fail(mark != NULL); - g_return_if_fail(mark->type == >k_text_right_mark_type || - mark->type == >k_text_left_mark_type); - g_return_if_fail(mark->body.mark.refcount > 0); + mark->byte_count = 0; + mark->char_count = 0; - mark->body.mark.refcount -= 1; + mark->body.mark.tree = tree; + mark->body.mark.line = NULL; + mark->next = NULL; - if (mark->body.mark.refcount == 0) - { - g_free(mark->body.mark.name); - - g_free(mark); - } + mark->body.mark.not_deleteable = FALSE; } -/* - * Forward references for procedures defined in this file: - */ +static int mark_segment_delete_func (GtkTextLineSegment *segPtr, + GtkTextLine *line, + int treeGone); +static GtkTextLineSegment *mark_segment_cleanup_func (GtkTextLineSegment *segPtr, + GtkTextLine *line); +static void mark_segment_check_func (GtkTextLineSegment *segPtr, + GtkTextLine *line); -static int mark_segment_delete_func (GtkTextLineSegment *segPtr, - GtkTextLine *line, - int treeGone); -static GtkTextLineSegment *mark_segment_cleanup_func (GtkTextLineSegment *segPtr, - GtkTextLine *line); -static void mark_segment_check_func (GtkTextLineSegment *segPtr, - GtkTextLine *line); /* * The following structures declare the "mark" segment types. @@ -120,220 +424,106 @@ static void mark_segment_check_func (GtkTextLineSegment *segPtr, * their gravity property. */ -GtkTextLineSegmentClass gtk_text_right_mark_type = { - "mark", /* name */ - FALSE, /* leftGravity */ - (GtkTextLineSegmentSplitFunc) NULL, /* splitFunc */ - mark_segment_delete_func, /* deleteFunc */ - mark_segment_cleanup_func, /* cleanupFunc */ - (GtkTextLineSegmentLineChangeFunc) NULL, /* lineChangeFunc */ - mark_segment_check_func /* checkFunc */ +const GtkTextLineSegmentClass gtk_text_right_mark_type = { + "mark", /* name */ + FALSE, /* leftGravity */ + NULL, /* splitFunc */ + mark_segment_delete_func, /* deleteFunc */ + mark_segment_cleanup_func, /* cleanupFunc */ + NULL, /* lineChangeFunc */ + mark_segment_check_func /* checkFunc */ }; -GtkTextLineSegmentClass gtk_text_left_mark_type = { - "mark", /* name */ - TRUE, /* leftGravity */ - (GtkTextLineSegmentSplitFunc) NULL, /* splitFunc */ - mark_segment_delete_func, /* deleteFunc */ - mark_segment_cleanup_func, /* cleanupFunc */ - (GtkTextLineSegmentLineChangeFunc) NULL, /* lineChangeFunc */ - mark_segment_check_func /* checkFunc */ +const GtkTextLineSegmentClass gtk_text_left_mark_type = { + "mark", /* name */ + TRUE, /* leftGravity */ + NULL, /* splitFunc */ + mark_segment_delete_func, /* deleteFunc */ + mark_segment_cleanup_func, /* cleanupFunc */ + NULL, /* lineChangeFunc */ + mark_segment_check_func /* checkFunc */ }; - /* *-------------------------------------------------------------- * * mark_segment_delete_func -- * - * This procedure is invoked by the text B-tree code whenever - * a mark lies in a range of characters being deleted. + * This procedure is invoked by the text B-tree code whenever + * a mark lies in a range of characters being deleted. * * Results: - * Returns 1 to indicate that deletion has been rejected. + * Returns 1 to indicate that deletion has been rejected, + * or 0 otherwise * * Side effects: - * None (even if the whole tree is being deleted we don't - * free up the mark; it will be done elsewhere). + * Frees mark if tree is going away * *-------------------------------------------------------------- */ - /* ARGSUSED */ -static int -mark_segment_delete_func(segPtr, line, treeGone) - GtkTextLineSegment *segPtr; /* Segment being deleted. */ - GtkTextLine *line; /* Line containing segment. */ - int treeGone; /* Non-zero means the entire tree is - * being deleted, so everything must - * get cleaned up. */ +static gboolean +mark_segment_delete_func (GtkTextLineSegment *seg, + GtkTextLine *line, + gboolean tree_gone) { - return 1; + if (tree_gone) + { + _gtk_text_btree_release_mark_segment (seg->body.mark.tree, seg); + return FALSE; + } + else + return TRUE; } - + /* *-------------------------------------------------------------- * * mark_segment_cleanup_func -- * - * This procedure is invoked by the B-tree code whenever a - * mark segment is moved from one line to another. + * This procedure is invoked by the B-tree code whenever a + * mark segment is moved from one line to another. * * Results: - * None. + * None. * * Side effects: - * The line field of the segment gets updated. + * The line field of the segment gets updated. * *-------------------------------------------------------------- */ static GtkTextLineSegment * -mark_segment_cleanup_func(markPtr, line) - GtkTextLineSegment *markPtr; /* Mark segment that's being moved. */ - GtkTextLine *line; /* Line that now contains segment. */ +mark_segment_cleanup_func (GtkTextLineSegment *seg, + GtkTextLine *line) { - markPtr->body.mark.line = line; - return markPtr; + /* not sure why Tk did this here and not in LineChangeFunc */ + seg->body.mark.line = line; + return seg; } -#if 0 - - -/* - *-------------------------------------------------------------- - * - * GtkTextInsertDisplayFunc -- - * - * This procedure is called to display the insertion - * cursor. - * - * Results: - * None. - * - * Side effects: - * Graphics are drawn. - * - *-------------------------------------------------------------- - */ - - /* ARGSUSED */ -void -GtkTextInsertDisplayFunc(chunkPtr, x, y, height, baseline, display, dst, screenY) - GtkTextDisplayChunk *chunkPtr; /* Chunk that is to be drawn. */ - int x; /* X-position in dst at which to - * draw this chunk (may differ from - * the x-position in the chunk because - * of scrolling). */ - int y; /* Y-position at which to draw this - * chunk in dst (x-position is in - * the chunk itself). */ - int height; /* Total height of line. */ - int baseline; /* Offset of baseline from y. */ - Display *display; /* Display to use for drawing. */ - Drawable dst; /* Pixmap or window in which to draw - * chunk. */ - int screenY; /* Y-coordinate in text window that - * corresponds to y. */ -{ - GtkTextView *tkxt = (GtkTextView *) chunkPtr->clientData; - int halfWidth = tkxt->insertWidth/2; - - if ((x + halfWidth) < 0) { - /* - * The insertion cursor is off-screen. Just return. - */ - - return; - } - - /* - * As a special hack to keep the cursor visible on mono displays - * (or anywhere else that the selection and insertion cursors - * have the same color) write the default background in the cursor - * area (instead of nothing) when the cursor isn't on. Otherwise - * the selection might hide the cursor. - */ - - if (tkxt->flags & INSERT_ON) { - Tk_Fill3DRectangle(tkxt->tkwin, dst, tkxt->insertBorder, - x - tkxt->insertWidth/2, y, tkxt->insertWidth, - height, tkxt->insertBorderWidth, TK_RELIEF_RAISED); - } else if (tkxt->selBorder == tkxt->insertBorder) { - Tk_Fill3DRectangle(tkxt->tkwin, dst, tkxt->border, - x - tkxt->insertWidth/2, y, tkxt->insertWidth, - height, 0, TK_RELIEF_FLAT); - } -} - -/* - *-------------------------------------------------------------- - * - * InsertUndisplayFunc -- - * - * This procedure is called when the insertion cursor is no - * longer at a visible point on the display. It does nothing - * right now. - * - * Results: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------- - */ - - /* ARGSUSED */ -static void -InsertUndisplayFunc(tkxt, chunkPtr) - GtkTextView *tkxt; /* Overall information about text - * widget. */ - GtkTextDisplayChunk *chunkPtr; /* Chunk that is about to be freed. */ -{ - return; -} - -#endif /* *-------------------------------------------------------------- * * mark_segment_check_func -- * - * This procedure is invoked by the B-tree code to perform - * consistency checks on mark segments. + * This procedure is invoked by the B-tree code to perform + * consistency checks on mark segments. * * Results: - * None. + * None. * * Side effects: - * The procedure panics if it detects anything wrong with - * the mark. + * The procedure panics if it detects anything wrong with + * the mark. * *-------------------------------------------------------------- */ static void -mark_segment_check_func(markPtr, line) - GtkTextLineSegment *markPtr; /* Segment to check. */ - GtkTextLine *line; /* Line containing segment. */ +mark_segment_check_func (GtkTextLineSegment *seg, + GtkTextLine *line) { - if (markPtr->body.mark.line != line) - g_error("mark_segment_check_func: markPtr->body.mark.line bogus"); - - /* No longer do this because we don't have access to btree - struct members */ -#if 0 - /* - * Make sure that the mark is still present in the text's mark - * hash table. - */ - for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.tkxt->markTable, - &search); hPtr != markPtr->body.mark.hPtr; - hPtr = Tcl_NextHashEntry(&search)) { - if (hPtr == NULL) { - panic("mark_segment_check_func couldn't find hash table entry for mark"); - } - } -#endif + if (seg->body.mark.line != line) + g_error ("mark_segment_check_func: seg->body.mark.line bogus"); }