X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Flabel.c;h=efa117f0b8a33c3920f94c9d3661fc1d5993e622;hb=ef59f87e1a43b44e49eef3dc50c596bfda37ff4a;hp=c886529bf629ffed94539570f579581423b90d1c;hpb=b13649a65b7dfa46ba1ed7fe9e8afa4aaef70f79;p=glabels diff --git a/src/label.c b/src/label.c index c886529b..efa117f0 100644 --- a/src/label.c +++ b/src/label.c @@ -1,25 +1,21 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ - /* - * (GLABELS) Label and Business Card Creation program for GNOME - * - * label.c: GLabels label module + * label.c + * Copyright (C) 2001-2009 Jim Evins . * - * Copyright (C) 2001-2007 Jim Evins . + * This file is part of gLabels. * - * This program is free software; you can redistribute it and/or modify + * gLabels 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 + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * gLabels 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 + * along with gLabels. If not, see . */ #include @@ -27,17 +23,24 @@ #include "label.h" #include +#include -#include "marshal.h" +#include "template-history.h" +#include "file-util.h" +#include "xml-label.h" #include "prefs.h" -#include "util.h" +#include "label-text.h" +#include "label-image.h" +#include "marshal.h" #include "debug.h" + /*========================================================*/ /* Private macros and constants. */ /*========================================================*/ + /*========================================================*/ /* Private types. */ /*========================================================*/ @@ -46,24 +49,80 @@ struct _glLabelPrivate { gchar *filename; gint compression; - gboolean modified_flag; gint untitled_instance; + gboolean modified_flag; + GTimeVal time_stamp; + + lglTemplate *template; + gboolean rotate_flag; + + GList *object_list; + glMerge *merge; GHashTable *pixbuf_cache; + GHashTable *svg_cache; + + /* Delay changed signals while operating on selections of multiple objects. */ + gboolean selection_op_flag; + gboolean delayed_change_flag; + + /* Default object text properties */ + gchar *default_font_family; + gdouble default_font_size; + PangoWeight default_font_weight; + gboolean default_font_italic_flag; + guint default_text_color; + PangoAlignment default_text_alignment; + gdouble default_text_line_spacing; + + /* Default object line properties */ + gdouble default_line_width; + guint default_line_color; + + /* Default object fill properties */ + guint default_fill_color; + + /* Undo/Redo state */ + GQueue *undo_stack; + GQueue *redo_stack; + gboolean cp_cleared_flag; + gchar *cp_desc; }; +typedef struct { + gchar *xml_buffer; + gchar *text; + GdkPixbuf *pixbuf; +} ClipboardData; + enum { + SELECTION_CHANGED, CHANGED, NAME_CHANGED, MODIFIED_CHANGED, MERGE_CHANGED, SIZE_CHANGED, - OBJECT_ADDED, LAST_SIGNAL }; +typedef struct { + gchar *description; + + gboolean modified_flag; + GTimeVal time_stamp; + + lglTemplate *template; + gboolean rotate_flag; + + GList *object_list; + + glMerge *merge; + +} State; + + /*========================================================*/ /* Private globals. */ /*========================================================*/ @@ -72,6 +131,7 @@ static guint signals[LAST_SIGNAL] = {0}; static guint untitled = 0; + /*========================================================*/ /* Private function prototypes. */ /*========================================================*/ @@ -79,18 +139,58 @@ static guint untitled = 0; static void gl_label_finalize (GObject *object); static void object_changed_cb (glLabelObject *object, - glLabel *label); + glLabel *label); static void object_moved_cb (glLabelObject *object, - gdouble x, - gdouble y, - glLabel *label); + glLabel *label); + +static void do_modify (glLabel *label); + +static void begin_selection_op (glLabel *label); +static void end_selection_op (glLabel *label); + +static void clipboard_get_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + ClipboardData *data); + +static void clipboard_clear_cb (GtkClipboard *clipboard, + ClipboardData *data); + +static void receive_targets_cb (GtkClipboard *clipboard, + GdkAtom *targets, + gint n_targets, + glLabel *label); + +static void paste_xml_received_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + glLabel *label); + +static void paste_text_received_cb (GtkClipboard *clipboard, + const gchar *text, + glLabel *label); + +static void paste_image_received_cb(GtkClipboard *clipboard, + GdkPixbuf *pixbuf, + glLabel *label); + +static void stack_clear (GQueue *stack); +static void stack_push_state (GQueue *stack, + State *state); +static State *stack_pop_state (GQueue *stack); + +static State *state_new (glLabel *this, + const gchar *description); +static void state_free (State *state); +static void state_restore (State *state, + glLabel *this); + - /*****************************************************************************/ /* Boilerplate object stuff. */ /*****************************************************************************/ -G_DEFINE_TYPE (glLabel, gl_label, G_TYPE_OBJECT); +G_DEFINE_TYPE (glLabel, gl_label, G_TYPE_OBJECT) + static void gl_label_class_init (glLabelClass *class) @@ -103,6 +203,15 @@ gl_label_class_init (glLabelClass *class) object_class->finalize = gl_label_finalize; + signals[SELECTION_CHANGED] = + g_signal_new ("selection_changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (glLabelClass, selection_changed), + NULL, NULL, + gl_marshal_VOID__VOID, + G_TYPE_NONE, + 0); signals[CHANGED] = g_signal_new ("changed", G_OBJECT_CLASS_TYPE (object_class), @@ -148,58 +257,83 @@ gl_label_class_init (glLabelClass *class) gl_marshal_VOID__VOID, G_TYPE_NONE, 0); - signals[OBJECT_ADDED] = - g_signal_new ("object_added", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (glLabelClass, object_added), - NULL, NULL, - gl_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, G_TYPE_OBJECT); gl_debug (DEBUG_LABEL, "END"); } + static void gl_label_init (glLabel *label) { gl_debug (DEBUG_LABEL, "START"); - label->template = NULL; - label->rotate_flag = FALSE; - label->objects = NULL; - label->priv = g_new0 (glLabelPrivate, 1); - label->priv->filename = NULL; - label->priv->merge = NULL; - label->priv->pixbuf_cache = gl_pixbuf_cache_new (); + label->priv->template = NULL; + label->priv->rotate_flag = FALSE; + label->priv->object_list = NULL; + + label->priv->filename = NULL; + label->priv->modified_flag = FALSE; + label->priv->compression = 9; + + label->priv->merge = NULL; + label->priv->pixbuf_cache = gl_pixbuf_cache_new (); + label->priv->svg_cache = gl_svg_cache_new (); + + label->priv->undo_stack = g_queue_new (); + label->priv->redo_stack = g_queue_new (); + + /* + * Defaults from preferences + */ + label->priv->default_font_family = gl_prefs_model_get_default_font_family (gl_prefs); + label->priv->default_font_size = gl_prefs_model_get_default_font_size (gl_prefs); + label->priv->default_font_weight = gl_prefs_model_get_default_font_weight (gl_prefs); + label->priv->default_font_italic_flag = gl_prefs_model_get_default_font_italic_flag (gl_prefs); + label->priv->default_text_color = gl_prefs_model_get_default_text_color (gl_prefs); + label->priv->default_text_alignment = gl_prefs_model_get_default_text_alignment (gl_prefs); + label->priv->default_text_line_spacing = gl_prefs_model_get_default_text_line_spacing (gl_prefs); + label->priv->default_line_width = gl_prefs_model_get_default_line_width (gl_prefs); + label->priv->default_line_color = gl_prefs_model_get_default_line_color (gl_prefs); + label->priv->default_fill_color = gl_prefs_model_get_default_fill_color (gl_prefs); gl_debug (DEBUG_LABEL, "END"); } + static void gl_label_finalize (GObject *object) { glLabel *label = GL_LABEL (object); - GList *p, *p_next; + GList *p; gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (object && GL_IS_LABEL (object)); - for (p = label->objects; p != NULL; p = p_next) { - p_next = p->next; /* NOTE: p will be left dangling */ + for (p = label->priv->object_list; p != NULL; p = p->next) + { g_object_unref (G_OBJECT(p->data)); } + g_list_free (label->priv->object_list); - lgl_template_free (label->template); + lgl_template_free (label->priv->template); g_free (label->priv->filename); - if (label->priv->merge != NULL) { + if (label->priv->merge != NULL) + { g_object_unref (G_OBJECT(label->priv->merge)); } + g_free (label->priv->default_font_family); + + stack_clear (label->priv->undo_stack); + stack_clear (label->priv->redo_stack); + + g_queue_free (label->priv->undo_stack); + g_queue_free (label->priv->redo_stack); + gl_pixbuf_cache_free (label->priv->pixbuf_cache); + gl_svg_cache_free (label->priv->svg_cache); g_free (label->priv); @@ -208,6 +342,10 @@ gl_label_finalize (GObject *object) gl_debug (DEBUG_LABEL, "END"); } + +/*****************************************************************************/ +/* New label. */ +/*****************************************************************************/ GObject * gl_label_new (void) { @@ -217,166 +355,235 @@ gl_label_new (void) label = g_object_new (gl_label_get_type(), NULL); - label->priv->compression = 9; - - label->priv->modified_flag = FALSE; - gl_debug (DEBUG_LABEL, "END"); return G_OBJECT (label); } -/*****************************************************************************/ -/* Add object to label. */ -/*****************************************************************************/ +/****************************************************************************/ +/* Set filename. */ +/****************************************************************************/ void -gl_label_add_object (glLabel *label, - glLabelObject *object) +gl_label_set_filename (glLabel *label, + const gchar *filename) { - gl_debug (DEBUG_LABEL, "START"); + label->priv->filename = g_strdup (filename); - g_return_if_fail (label && GL_IS_LABEL (label)); - g_return_if_fail (object && GL_IS_LABEL_OBJECT (object)); + g_signal_emit (G_OBJECT(label), signals[NAME_CHANGED], 0); +} - object->parent = label; - label->objects = g_list_append (label->objects, g_object_ref (object)); - label->priv->modified_flag = TRUE; +/****************************************************************************/ +/* return filename. */ +/****************************************************************************/ +gchar * +gl_label_get_filename (glLabel *label) +{ + gl_debug (DEBUG_LABEL, ""); - g_signal_emit (G_OBJECT(label), signals[OBJECT_ADDED], 0, object); - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); - g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); + return g_strdup ( label->priv->filename ); +} - g_signal_connect (G_OBJECT(object), "changed", - G_CALLBACK(object_changed_cb), label); - g_signal_connect (G_OBJECT(object), "moved", - G_CALLBACK(object_moved_cb), label); +/****************************************************************************/ +/* return short filename. */ +/****************************************************************************/ +gchar * +gl_label_get_short_name (glLabel *label) +{ + gl_debug (DEBUG_LABEL, ""); - gl_debug (DEBUG_LABEL, "END"); -} + if ( label->priv->filename == NULL ) + { -/*****************************************************************************/ -/* Remove object from label. */ -/*****************************************************************************/ -void -gl_label_remove_object (glLabel *label, - glLabelObject *object) -{ - gl_debug (DEBUG_LABEL, "START"); + if ( label->priv->untitled_instance == 0 ) + { + label->priv->untitled_instance = ++untitled; + } - g_return_if_fail (label && GL_IS_LABEL (label)); - g_return_if_fail (GL_IS_LABEL_OBJECT (object)); + return g_strdup_printf ( "%s %d", _("Untitled"), + label->priv->untitled_instance ); + + } + else + { + gchar *temp_name, *short_name; + + temp_name = g_path_get_basename ( label->priv->filename ); + short_name = gl_file_util_remove_extension (temp_name); + g_free (temp_name); - object->parent = NULL; - label->objects = g_list_remove (label->objects, object); + return short_name; + } +} - if ( G_OBJECT(label)->ref_count /* not finalized */ ) { - g_signal_handlers_disconnect_by_func (object, - G_CALLBACK(object_changed_cb), - label); - g_signal_handlers_disconnect_by_func (object, - G_CALLBACK(object_moved_cb), - label); +/****************************************************************************/ +/* Is label untitled? */ +/****************************************************************************/ +gboolean +gl_label_is_untitled (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "return %d",(label->priv->filename == NULL)); + return (label->priv->filename == NULL); +} - label->priv->modified_flag = TRUE; - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); - g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); +/****************************************************************************/ +/* Set compression level. */ +/****************************************************************************/ +void +gl_label_set_compression (glLabel *label, + gint compression) +{ + gl_debug (DEBUG_LABEL, "set %d", compression); + /* Older versions of libxml2 always return a -1 for documents "read in," so + * default to 9. Also, default to 9 for anything else out of range. */ + if ((compression < 0) || (compression >9)) + { + compression = 9; } - gl_debug (DEBUG_LABEL, "END"); + gl_debug (DEBUG_LABEL, "actual set %d", compression); + label->priv->compression = compression; } -/*---------------------------------------------------------------------------*/ -/* PRIVATE. Object changed callback. */ -/*---------------------------------------------------------------------------*/ -static void -object_changed_cb (glLabelObject *object, - glLabel *label) + +/****************************************************************************/ +/* Get compression level. */ +/****************************************************************************/ +gint +gl_label_get_compression (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "return %d", label->priv->compression); + return label->priv->compression; +} + + +/****************************************************************************/ +/* Set modified flag. */ +/****************************************************************************/ +void +gl_label_set_modified (glLabel *label) { - if ( !label->priv->modified_flag ) { + if ( !label->priv->modified_flag ) + { label->priv->modified_flag = TRUE; g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); } - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); } -/*---------------------------------------------------------------------------*/ -/* PRIVATE. Object moved callback. */ -/*---------------------------------------------------------------------------*/ -static void -object_moved_cb (glLabelObject *object, - gdouble x, - gdouble y, - glLabel *label) + +/****************************************************************************/ +/* Clear modified flag. */ +/****************************************************************************/ +void +gl_label_clear_modified (glLabel *label) { - if ( !label->priv->modified_flag ) { + if ( label->priv->modified_flag ) + { - label->priv->modified_flag = TRUE; + g_get_current_time (&label->priv->time_stamp); + label->priv->modified_flag = FALSE; g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); } - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); } + /****************************************************************************/ -/* Bring label object to front/top. */ +/* Is label modified? */ /****************************************************************************/ -void -gl_label_raise_object_to_top (glLabel *label, - glLabelObject *object) +gboolean +gl_label_is_modified (glLabel *label) { - gl_debug (DEBUG_LABEL, "START"); + gl_debug (DEBUG_LABEL, "return %d", label->priv->modified_flag); + return label->priv->modified_flag; +} - /* Move to end of list, representing front most object */ - label->objects = g_list_remove (label->objects, object); - label->objects = g_list_append (label->objects, object); - label->priv->modified_flag = TRUE; +/****************************************************************************/ +/* Object "changed" callback. */ +/****************************************************************************/ +static void +object_changed_cb (glLabelObject *object, + glLabel *label) +{ + do_modify (label); +} - g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); - gl_debug (DEBUG_LABEL, "END"); +/****************************************************************************/ +/* Object "moved" callback. */ +/****************************************************************************/ +static void +object_moved_cb (glLabelObject *object, + glLabel *label) +{ + do_modify (label); } + /****************************************************************************/ -/* Send label object to rear/bottom. */ +/* Do modify. */ /****************************************************************************/ -void -gl_label_lower_object_to_bottom (glLabel *label, - glLabelObject *object) +static void +do_modify (glLabel *label) { - gl_debug (DEBUG_LABEL, "START"); + if ( label->priv->selection_op_flag ) + { + label->priv->delayed_change_flag = TRUE; + } + else + { + label->priv->modified_flag = TRUE; - /* Move to front of list, representing rear most object */ - label->objects = g_list_remove (label->objects, object); - label->objects = g_list_prepend (label->objects, object); + g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); + g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); + } +} - label->priv->modified_flag = TRUE; - g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); +/****************************************************************************/ +/* Begin selection operation. */ +/****************************************************************************/ +static void +begin_selection_op (glLabel *label) +{ + label->priv->selection_op_flag = TRUE; +} - gl_debug (DEBUG_LABEL, "END"); + +/****************************************************************************/ +/* End selection operation. */ +/****************************************************************************/ +static void +end_selection_op (glLabel *label) +{ + label->priv->selection_op_flag = FALSE; + if ( label->priv->delayed_change_flag ) + { + label->priv->delayed_change_flag = FALSE; + do_modify (label); + } } + /****************************************************************************/ /* set template. */ /****************************************************************************/ -extern void -gl_label_set_template (glLabel *label, - lglTemplate *template) +void +gl_label_set_template (glLabel *label, + const lglTemplate *template, + gboolean checkpoint) { gchar *name; @@ -385,52 +592,79 @@ gl_label_set_template (glLabel *label, g_return_if_fail (label && GL_IS_LABEL (label)); g_return_if_fail (template); - if ((label->template == NULL) || - !lgl_template_do_templates_match (template, label->template)) { + if ((label->priv->template == NULL) || + !lgl_template_do_templates_match (template, label->priv->template)) + { - lgl_template_free (label->template); - label->template = lgl_template_dup (template); + if ( checkpoint ) + { + gl_label_checkpoint (label, _("Label properties")); + } - label->priv->modified_flag = TRUE; + lgl_template_free (label->priv->template); + label->priv->template = lgl_template_dup (template); - g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); + do_modify (label); g_signal_emit (G_OBJECT(label), signals[SIZE_CHANGED], 0); - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); name = lgl_template_get_name (template); - gl_prefs_add_recent_template (name); + gl_template_history_model_add_name (gl_template_history, name); g_free (name); } gl_debug (DEBUG_LABEL, "END"); } + +/****************************************************************************/ +/* get template. */ +/****************************************************************************/ +const lglTemplate * +gl_label_get_template (glLabel *label) +{ + return label->priv->template; +} + + /****************************************************************************/ /* set rotate flag. */ /****************************************************************************/ -extern void +void gl_label_set_rotate_flag (glLabel *label, - gboolean rotate_flag) + gboolean rotate_flag, + gboolean checkpoint) { gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (label && GL_IS_LABEL (label)); - if (rotate_flag != label->rotate_flag) { - - label->rotate_flag = rotate_flag; + if (rotate_flag != label->priv->rotate_flag) + { + if ( checkpoint ) + { + gl_label_checkpoint (label, _("Label properties")); + } - label->priv->modified_flag = TRUE; + label->priv->rotate_flag = rotate_flag; - g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); + do_modify (label); g_signal_emit (G_OBJECT(label), signals[SIZE_CHANGED], 0); - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); - } gl_debug (DEBUG_LABEL, "END"); } + +/****************************************************************************/ +/* Get rotate flag. */ +/****************************************************************************/ +gboolean +gl_label_get_rotate_flag (glLabel *label) +{ + return label->priv->rotate_flag; +} + + /****************************************************************************/ /* Get label size. */ /****************************************************************************/ @@ -446,48 +680,58 @@ gl_label_get_size (glLabel *label, g_return_if_fail (label && GL_IS_LABEL (label)); - template = label->template; - if ( !template ) { + template = label->priv->template; + if ( !template ) + { gl_debug (DEBUG_LABEL, "END -- template NULL"); *w = *h = 0; return; } frame = (lglTemplateFrame *)template->frames->data; - if (!label->rotate_flag) { + if (!label->priv->rotate_flag) + { lgl_template_frame_get_size (frame, w, h); - } else { + } + else + { lgl_template_frame_get_size (frame, h, w); } gl_debug (DEBUG_LABEL, "END"); } + /****************************************************************************/ /* set merge information structure. */ /****************************************************************************/ -extern void +void gl_label_set_merge (glLabel *label, - glMerge *merge) + glMerge *merge, + gboolean checkpoint) { gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (label && GL_IS_LABEL (label)); - if ( label->priv->merge != NULL ) { + if ( checkpoint ) + { + gl_label_checkpoint (label, _("Merge properties")); + } + + if ( label->priv->merge != NULL ) + { g_object_unref (G_OBJECT(label->priv->merge)); } label->priv->merge = gl_merge_dup (merge); - label->priv->modified_flag = TRUE; - + do_modify (label); g_signal_emit (G_OBJECT(label), signals[MERGE_CHANGED], 0); - g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); - g_signal_emit (G_OBJECT(label), signals[CHANGED], 0); gl_debug (DEBUG_LABEL, "END"); } + /****************************************************************************/ /* Get merge information structure. */ /****************************************************************************/ @@ -503,81 +747,2481 @@ gl_label_get_merge (glLabel *label) return gl_merge_dup (label->priv->merge); } + /****************************************************************************/ -/* return filename. */ +/* Get pixbuf cache. */ /****************************************************************************/ -gchar * -gl_label_get_filename (glLabel *label) +GHashTable * +gl_label_get_pixbuf_cache (glLabel *label) { + return label->priv->pixbuf_cache; +} + + +/****************************************************************************/ +/* Get svg cache. */ +/****************************************************************************/ +GHashTable * +gl_label_get_svg_cache (glLabel *label) +{ + return label->priv->svg_cache; +} + + +/*****************************************************************************/ +/* Add object to label. */ +/*****************************************************************************/ +void +gl_label_add_object (glLabel *label, + glLabelObject *object) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + g_return_if_fail (object && GL_IS_LABEL_OBJECT (object)); + + gl_label_object_set_parent (object, label); + label->priv->object_list = g_list_append (label->priv->object_list, object); + + g_signal_connect (G_OBJECT (object), "changed", + G_CALLBACK (object_changed_cb), label); + g_signal_connect (G_OBJECT (object), "moved", + G_CALLBACK (object_moved_cb), label); + + do_modify (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Delete object from label. */ +/*****************************************************************************/ +void +gl_label_delete_object (glLabel *label, + glLabelObject *object) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + g_return_if_fail (object && GL_IS_LABEL_OBJECT (object)); + + label->priv->object_list = g_list_remove (label->priv->object_list, object); + + g_signal_handlers_disconnect_by_func (G_OBJECT (object), + G_CALLBACK (object_changed_cb), label); + g_signal_handlers_disconnect_by_func (G_OBJECT (object), + G_CALLBACK (object_moved_cb), label); + g_object_unref (object); + + do_modify (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Get object list. */ +/*****************************************************************************/ +const GList * +gl_label_get_object_list (glLabel *label) +{ + return label->priv->object_list; +} + + +/*****************************************************************************/ +/* Select object. */ +/*****************************************************************************/ +void +gl_label_select_object (glLabel *label, + glLabelObject *object) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + g_return_if_fail (object && GL_IS_LABEL_OBJECT (object)); + + gl_label_object_select (object); + + label->priv->cp_cleared_flag = TRUE; + g_signal_emit (G_OBJECT(label), signals[SELECTION_CHANGED], 0); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Unselect object. */ +/*****************************************************************************/ +void +gl_label_unselect_object (glLabel *label, + glLabelObject *object) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + g_return_if_fail (object && GL_IS_LABEL_OBJECT (object)); + + gl_label_object_unselect (object); + + label->priv->cp_cleared_flag = TRUE; + g_signal_emit (G_OBJECT(label), signals[SELECTION_CHANGED], 0); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Select all objects. */ +/*****************************************************************************/ +void +gl_label_select_all (glLabel *label) +{ + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + for ( p = label->priv->object_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_select (object); + } + + label->priv->cp_cleared_flag = TRUE; + g_signal_emit (G_OBJECT(label), signals[SELECTION_CHANGED], 0); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Unselect all objects. */ +/*****************************************************************************/ +void +gl_label_unselect_all (glLabel *label) +{ + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + for ( p = label->priv->object_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_unselect (object); + } + + label->priv->cp_cleared_flag = TRUE; + g_signal_emit (G_OBJECT(label), signals[SELECTION_CHANGED], 0); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Select all objects contained in region. */ +/*****************************************************************************/ +void +gl_label_select_region (glLabel *label, + glLabelRegion *region) +{ + GList *p; + glLabelObject *object; + gdouble r_x1, r_y1; + gdouble r_x2, r_y2; + glLabelRegion obj_extent; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + r_x1 = MIN (region->x1, region->x2); + r_y1 = MIN (region->y1, region->y2); + r_x2 = MAX (region->x1, region->x2); + r_y2 = MAX (region->y1, region->y2); + + for (p = label->priv->object_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT(p->data); + + gl_label_object_get_extent (object, &obj_extent); + if ((obj_extent.x1 >= r_x1) && + (obj_extent.x2 <= r_x2) && + (obj_extent.y1 >= r_y1) && + (obj_extent.y2 <= r_y2)) + { + gl_label_object_select (object); + } + } + + label->priv->cp_cleared_flag = TRUE; + g_signal_emit (G_OBJECT(label), signals[SELECTION_CHANGED], 0); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Is selection empty? */ +/*****************************************************************************/ +gboolean +gl_label_is_selection_empty (glLabel *label) +{ + GList *p; + glLabelObject *object; + + for ( p = label->priv->object_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + if ( gl_label_object_is_selected (object) ) + { + return FALSE; + } + } + + return TRUE; +} + + +/*****************************************************************************/ +/* Is selection atomic? */ +/*****************************************************************************/ +gboolean +gl_label_is_selection_atomic (glLabel *label) +{ + GList *p; + glLabelObject *object; + gint n_selected = 0; + + for ( p = label->priv->object_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + if ( gl_label_object_is_selected (object) ) + { + n_selected++; + + if (n_selected > 1) + { + return FALSE; + } + } + } + + return (n_selected == 1); +} + + +/*****************************************************************************/ +/* Get first selected object. */ +/*****************************************************************************/ +glLabelObject * +gl_label_get_1st_selected_object (glLabel *label) +{ + GList *p; + glLabelObject *object; + + for ( p = label->priv->object_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + if ( gl_label_object_is_selected (object) ) + { + return object; + } + } + + return NULL; +} + + +/*****************************************************************************/ +/* Get list of selected objects. */ +/*****************************************************************************/ +GList * +gl_label_get_selection_list (glLabel *label) +{ + GList *selection_list = NULL; + GList *p; + glLabelObject *object; + + for ( p = label->priv->object_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + if ( gl_label_object_is_selected (object) ) + { + selection_list = g_list_append (selection_list, object); + } + } + + return (selection_list); +} + + +/*****************************************************************************/ +/* Can text properties be set for selection? */ +/*****************************************************************************/ +gboolean +gl_label_can_selection_text (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, ""); + + g_return_val_if_fail (label && GL_IS_LABEL (label), FALSE); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + if (gl_label_object_can_text (object)) + { + g_list_free (selection_list); + return TRUE; + } + } + + g_list_free (selection_list); + return FALSE; +} + + +/*****************************************************************************/ +/* Can fill properties be set for selection? */ +/*****************************************************************************/ +gboolean +gl_label_can_selection_fill (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, ""); + + g_return_val_if_fail (label && GL_IS_LABEL (label), FALSE); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + if (gl_label_object_can_fill (object)) + { + g_list_free (selection_list); + return TRUE; + } + + } + + g_list_free (selection_list); + return FALSE; +} + + +/*****************************************************************************/ +/* Can line color properties be set for selection? */ +/*****************************************************************************/ +gboolean +gl_label_can_selection_line_color (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, ""); + + g_return_val_if_fail (label && GL_IS_LABEL (label), FALSE); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + if (gl_label_object_can_line_color (object)) + { + g_list_free (selection_list); + return TRUE; + } + } + + g_list_free (selection_list); + return FALSE; +} + + +/*****************************************************************************/ +/* Can line width properties be set for selection? */ +/*****************************************************************************/ +gboolean +gl_label_can_selection_line_width (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gl_debug (DEBUG_LABEL, ""); - return g_strdup ( label->priv->filename ); + g_return_val_if_fail (label && GL_IS_LABEL (label), FALSE); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + if (gl_label_object_can_line_width (object)) + { + g_list_free (selection_list); + return TRUE; + } + + } + + g_list_free (selection_list); + return FALSE; +} + + +/*****************************************************************************/ +/* Delete selection from label. */ +/*****************************************************************************/ +void +gl_label_delete_selection (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + gl_label_checkpoint (label, _("Delete")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for ( p = selection_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_delete_object (label, object); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Bring selection object to front/top. */ +/****************************************************************************/ +void +gl_label_raise_selection_to_top (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + gl_label_checkpoint (label, _("Bring to front")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for ( p = selection_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + label->priv->object_list = g_list_remove (label->priv->object_list, object); + } + + /* Move to end of list, representing front most object */ + label->priv->object_list = g_list_concat (label->priv->object_list, selection_list); + + do_modify (label); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Send selection to rear/bottom. */ +/****************************************************************************/ +void +gl_label_lower_selection_to_bottom (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + gl_label_checkpoint (label, _("Send to back")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for ( p = selection_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + label->priv->object_list = g_list_remove (label->priv->object_list, object); + } + + /* Move to front of list, representing rear most object */ + label->priv->object_list = g_list_concat (selection_list, label->priv->object_list); + + do_modify (label); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Rotate selected objects by given angle. */ +/*****************************************************************************/ +void +gl_label_rotate_selection (glLabel *label, + gdouble theta_degs) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + gl_label_checkpoint (label, _("Rotate")); + + selection_list = gl_label_get_selection_list (label); + + for ( p = selection_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_rotate (object, theta_degs); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Rotate selected objects 90 degrees left. */ +/*****************************************************************************/ +void +gl_label_rotate_selection_left (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + gl_label_checkpoint (label, _("Rotate left")); + + selection_list = gl_label_get_selection_list (label); + + for ( p = selection_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_rotate (object, -90.0); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Rotate selected objects 90 degrees right. */ +/*****************************************************************************/ +void +gl_label_rotate_selection_right (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + gl_label_checkpoint (label, _("Rotate right")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for ( p = selection_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_rotate (object, 90.0); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Flip selected objects horizontally. */ +/*****************************************************************************/ +void +gl_label_flip_selection_horiz (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + gl_label_checkpoint (label, _("Flip horizontally")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for ( p = selection_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_flip_horiz (object); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Flip selected objects vertically. */ +/*****************************************************************************/ +void +gl_label_flip_selection_vert (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + gl_label_checkpoint (label, _("Flip vertically")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for ( p = selection_list; p != NULL; p = p->next ) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_flip_vert (object); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Align selected objects to left most edge. */ +/*****************************************************************************/ +void +gl_label_align_selection_left (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gdouble dx, x1_min; + glLabelRegion obj_extent; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_return_if_fail (!gl_label_is_selection_empty (label) && + !gl_label_is_selection_atomic (label)); + + gl_label_checkpoint (label, _("Align left")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + /* find left most edge */ + p = selection_list; + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + x1_min = obj_extent.x1; + for (p = p->next; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + if ( obj_extent.x1 < x1_min ) x1_min = obj_extent.x1; + } + + /* now adjust the object positions to line up the left edges */ + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dx = x1_min - obj_extent.x1; + gl_label_object_set_position_relative (object, dx, 0.0, FALSE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Align selected objects to right most edge. */ +/*****************************************************************************/ +void +gl_label_align_selection_right (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gdouble dx, x2_max; + glLabelRegion obj_extent; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_return_if_fail (!gl_label_is_selection_empty (label) && + !gl_label_is_selection_atomic (label)); + + gl_label_checkpoint (label, _("Align right")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + /* find left most edge */ + p = selection_list; + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + x2_max = obj_extent.x2; + for (p = p->next; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + if ( obj_extent.x2 > x2_max ) x2_max = obj_extent.x2; + } + + /* now adjust the object positions to line up the left edges */ + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dx = x2_max - obj_extent.x2; + gl_label_object_set_position_relative (object, dx, 0.0, FALSE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Align selected objects to horizontal center of objects. */ +/*****************************************************************************/ +void +gl_label_align_selection_hcenter (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gdouble dx; + gdouble dxmin; + gdouble xsum, xavg; + glLabelRegion obj_extent; + gdouble xcenter; + gint n; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_return_if_fail (!gl_label_is_selection_empty (label) && + !gl_label_is_selection_atomic (label)); + + gl_label_checkpoint (label, _("Align horizontal center")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + /* find average center of objects */ + xsum = 0.0; + n = 0; + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + xsum += (obj_extent.x1 + obj_extent.x2) / 2.0; + n++; + } + xavg = xsum / n; + + /* find center of object closest to average center */ + p = selection_list; + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dxmin = fabs (xavg - (obj_extent.x1 + obj_extent.x2)/2.0); + xcenter = (obj_extent.x1 + obj_extent.x2)/2.0; + for (p = p->next; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dx = fabs (xavg - (obj_extent.x1 + obj_extent.x2)/2.0); + if ( dx < dxmin ) + { + dxmin = dx; + xcenter = (obj_extent.x1 + obj_extent.x2)/2.0; + } + } + + /* now adjust the object positions to line up this center */ + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dx = xcenter - (obj_extent.x1 + obj_extent.x2)/2.0; + gl_label_object_set_position_relative (object, dx, 0.0, FALSE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Align selected objects to top most edge. */ +/*****************************************************************************/ +void +gl_label_align_selection_top (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gdouble dy, y1_min; + glLabelRegion obj_extent; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_return_if_fail (!gl_label_is_selection_empty (label) && + !gl_label_is_selection_atomic (label)); + + gl_label_checkpoint (label, _("Align tops")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + /* find top most edge */ + p = selection_list; + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + y1_min = obj_extent.y1; + for (p = p->next; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + if ( obj_extent.y1 < y1_min ) y1_min = obj_extent.y1; + } + + /* now adjust the object positions to line up the top edges */ + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dy = y1_min - obj_extent.y1; + gl_label_object_set_position_relative (object, 0.0, dy, FALSE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Align selected objects to bottom most edge. */ +/*****************************************************************************/ +void +gl_label_align_selection_bottom (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gdouble dy, y2_max; + glLabelRegion obj_extent; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_return_if_fail (!gl_label_is_selection_empty (label) && + !gl_label_is_selection_atomic (label)); + + gl_label_checkpoint (label, _("Align bottoms")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + /* find bottom most edge */ + p = selection_list; + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + y2_max = obj_extent.y2; + for (p = p->next; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + if ( obj_extent.y2 > y2_max ) y2_max = obj_extent.y2; + } + + /* now adjust the object positions to line up the bottom edges */ + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dy = y2_max - obj_extent.y2; + gl_label_object_set_position_relative (object, 0.0, dy, FALSE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Align selected objects to viertical center of objects. */ +/*****************************************************************************/ +void +gl_label_align_selection_vcenter (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gdouble dy; + gdouble dymin; + gdouble ysum, yavg; + glLabelRegion obj_extent; + gdouble ycenter; + gint n; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_return_if_fail (!gl_label_is_selection_empty (label) && + !gl_label_is_selection_atomic (label)); + + gl_label_checkpoint (label, _("Align vertical center")); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + /* find average center of objects */ + ysum = 0.0; + n = 0; + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + ysum += (obj_extent.y1 + obj_extent.y2) / 2.0; + n++; + } + yavg = ysum / n; + + /* find center of object closest to average center */ + p = selection_list; + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dymin = fabs (yavg - (obj_extent.y1 + obj_extent.y2)/2.0); + ycenter = (obj_extent.y1 + obj_extent.y2)/2.0; + for (p = p->next; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dy = fabs (yavg - (obj_extent.y1 + obj_extent.y2)/2.0); + if ( dy < dymin ) + { + dymin = dy; + ycenter = (obj_extent.y1 + obj_extent.y2)/2.0; + } + } + + /* now adjust the object positions to line up this center */ + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + dy = ycenter - (obj_extent.y1 + obj_extent.y2)/2.0; + gl_label_object_set_position_relative (object, 0.0, dy, FALSE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Center selected objects to in center of label. */ +/*****************************************************************************/ +void +gl_label_center_selection_horiz (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gdouble dx; + gdouble x_label_center; + gdouble x_obj_center; + glLabelRegion obj_extent; + gdouble w, h; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_return_if_fail (!gl_label_is_selection_empty (label)); + + gl_label_checkpoint (label, _("Center horizontally")); + + begin_selection_op (label); + + gl_label_get_size (label, &w, &h); + x_label_center = w / 2.0; + + /* adjust the object positions */ + selection_list = gl_label_get_selection_list (label); + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + x_obj_center = (obj_extent.x1 + obj_extent.x2) / 2.0; + dx = x_label_center - x_obj_center; + gl_label_object_set_position_relative (object, dx, 0.0, FALSE); + } + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Center selected objects to in center of label. */ +/*****************************************************************************/ +void +gl_label_center_selection_vert (glLabel *label) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + gdouble dy; + gdouble y_label_center; + gdouble y_obj_center; + glLabelRegion obj_extent; + gdouble w, h; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_return_if_fail (!gl_label_is_selection_empty (label)); + + gl_label_checkpoint (label, _("Center vertically")); + + begin_selection_op (label); + + gl_label_get_size (label, &w, &h); + y_label_center = h / 2.0; + + /* adjust the object positions */ + selection_list = gl_label_get_selection_list (label); + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_get_extent (object, &obj_extent); + y_obj_center = (obj_extent.y1 + obj_extent.y2) / 2.0; + dy = y_label_center - y_obj_center; + gl_label_object_set_position_relative (object, 0.0, dy, FALSE); + } + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Move selected objects */ +/*****************************************************************************/ +void +gl_label_move_selection (glLabel *label, + gdouble dx, + gdouble dy) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_object_set_position_relative (object, dx, dy, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set font family for all text contained in selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_font_family (glLabel *label, + const gchar *font_family) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_font_family (object, font_family, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set font size for all text contained in selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_font_size (glLabel *label, + gdouble font_size) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_font_size (object, font_size, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set font weight for all text contained in selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_font_weight (glLabel *label, + PangoWeight font_weight) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_font_weight (object, font_weight, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set font italic flag for all text contained in selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_font_italic_flag (glLabel *label, + gboolean font_italic_flag) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_font_italic_flag (object, font_italic_flag, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set text alignment for all text contained in selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_text_alignment (glLabel *label, + PangoAlignment text_alignment) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_text_alignment (object, text_alignment, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set text line spacing for all text contained in selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_text_line_spacing (glLabel *label, + gdouble text_line_spacing) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_text_line_spacing (object, text_line_spacing, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set text color for all text contained in selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_text_color (glLabel *label, + glColorNode *text_color_node) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_text_color (object, text_color_node, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set fill color for all selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_fill_color (glLabel *label, + glColorNode *fill_color_node) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_fill_color (object, fill_color_node, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set line color for all selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_line_color (glLabel *label, + glColorNode *line_color_node) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_line_color (object, line_color_node, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Set line width for all selected objects. */ +/*****************************************************************************/ +void +gl_label_set_selection_line_width (glLabel *label, + gdouble line_width) +{ + GList *selection_list; + GList *p; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + begin_selection_op (label); + + selection_list = gl_label_get_selection_list (label); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + gl_label_object_set_line_width (object, line_width, TRUE); + } + + g_list_free (selection_list); + + end_selection_op (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* "Cut" selected items and place on clipboard. */ +/*****************************************************************************/ +void +gl_label_cut_selection (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + gl_label_copy_selection (label); + gl_label_delete_selection (label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* "Copy" selected items to clipboard. */ +/*****************************************************************************/ +void +gl_label_copy_selection (glLabel *label) +{ + GtkClipboard *clipboard; + GList *selection_list; + glLabel *label_copy; + GList *p; + glLabelObject *object; + glXMLLabelStatus status; + + ClipboardData *data; + + static GtkTargetEntry glabels_targets[] = { { "application/glabels", 0, 0 }, + { "text/xml", 0, 0 }, + }; + + GtkTargetList *target_list; + GtkTargetEntry *target_table; + gint n_targets; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + selection_list = gl_label_get_selection_list (label); + + if (selection_list) + { + + data = g_new0 (ClipboardData, 1); + + target_list = gtk_target_list_new (glabels_targets, G_N_ELEMENTS(glabels_targets)); + + /* + * Serialize selection by encoding as an XML label document. + */ + label_copy = GL_LABEL(gl_label_new ()); + + gl_label_set_template (label_copy, label->priv->template, FALSE); + gl_label_set_rotate_flag (label_copy, label->priv->rotate_flag, FALSE); + + for (p = selection_list; p != NULL; p = p->next) + { + object = GL_LABEL_OBJECT (p->data); + + gl_label_add_object (label_copy, gl_label_object_dup (object, label_copy)); + } + + data->xml_buffer = gl_xml_label_save_buffer (label_copy, &status); + + g_object_unref (G_OBJECT (label_copy)); + + + /* + * Is it an atomic text selection? If so, also make available as text. + */ + if ( gl_label_is_selection_atomic (label) && + GL_IS_LABEL_TEXT (selection_list->data) ) + { + glLabelText *text_object = GL_LABEL_TEXT (selection_list->data); + + gtk_target_list_add_text_targets (target_list, 1); + + data->text = gl_label_text_get_text (text_object); + } + + + /* + * Is it an atomic image selection? If so, also make available as pixbuf. + */ + if ( gl_label_is_selection_atomic (label) && + GL_IS_LABEL_IMAGE (selection_list->data) ) + { + glLabelImage *image_object = GL_LABEL_IMAGE (selection_list->data); + GdkPixbuf *pixbuf = gl_label_image_get_pixbuf (image_object, NULL); + + if (pixbuf) + { + gtk_target_list_add_image_targets (target_list, 2, TRUE); + data->pixbuf = pixbuf; + } + } + + + target_table = gtk_target_table_new_from_list (target_list, &n_targets); + + gtk_clipboard_set_with_data (clipboard, + target_table, n_targets, + (GtkClipboardGetFunc)clipboard_get_cb, + (GtkClipboardClearFunc)clipboard_clear_cb, + data); + + gtk_target_table_free (target_table, n_targets); + gtk_target_list_unref (target_list); + } + + + g_list_free (selection_list); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* "Paste" from clipboard. */ +/*****************************************************************************/ +void +gl_label_paste (glLabel *label) +{ + GtkClipboard *clipboard; + + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + gtk_clipboard_request_targets (clipboard, + (GtkClipboardTargetsReceivedFunc)receive_targets_cb, + label); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/*****************************************************************************/ +/* Is there anything that can be pasted? */ +/*****************************************************************************/ +gboolean +gl_label_can_paste (glLabel *label) +{ + GtkClipboard *clipboard; + gboolean can_flag; + + gl_debug (DEBUG_LABEL, "START"); + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + can_flag = gtk_clipboard_wait_is_target_available (clipboard, + gdk_atom_intern("application/glabels", TRUE)) + || gtk_clipboard_wait_is_text_available (clipboard) + || gtk_clipboard_wait_is_image_available (clipboard); + + gl_debug (DEBUG_LABEL, "END"); + return can_flag; +} + + +/****************************************************************************/ +/* Clipboard "Get" function. */ +/****************************************************************************/ +static void +clipboard_get_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + ClipboardData *data) +{ + gl_debug (DEBUG_LABEL, "START"); + + switch (info) + { + + case 0: + gtk_selection_data_set (selection_data, + gtk_selection_data_get_target (selection_data), + 8, + (guchar *)data->xml_buffer, strlen (data->xml_buffer)); + break; + + case 1: + gtk_selection_data_set_text (selection_data, data->text, -1); + break; + + case 2: + gtk_selection_data_set_pixbuf (selection_data, data->pixbuf); + break; + + default: + g_assert_not_reached (); + break; + + } + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Clipboard "Clear" function. */ +/****************************************************************************/ +static void +clipboard_clear_cb (GtkClipboard *clipboard, + ClipboardData *data) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_free (data->xml_buffer); + g_free (data->text); + if (data->pixbuf) + { + g_object_unref (data->pixbuf); + } + + g_free (data); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Deal with clipboard data. */ +/****************************************************************************/ +static void +receive_targets_cb (GtkClipboard *clipboard, + GdkAtom *targets, + gint n_targets, + glLabel *label) +{ + gint i; + + /* + * Application/glabels + */ + for ( i = 0; i < n_targets; i++ ) + { + if ( strcmp(gdk_atom_name(targets[i]), "application/glabels") == 0 ) + { + gtk_clipboard_request_contents (clipboard, + gdk_atom_intern("application/glabels", TRUE), + (GtkClipboardReceivedFunc)paste_xml_received_cb, + label); + return; + } + } + + /* + * Text + */ + if ( gtk_targets_include_text (targets, n_targets) ) + { + gtk_clipboard_request_text (clipboard, + (GtkClipboardTextReceivedFunc)paste_text_received_cb, + label); + return; + } + + /* + * Image + */ + if ( gtk_targets_include_image (targets, n_targets, TRUE) ) + { + gtk_clipboard_request_image (clipboard, + (GtkClipboardImageReceivedFunc)paste_image_received_cb, + label); + return; + } +} + + +/****************************************************************************/ +/* Paste received glabels XML callback. */ +/****************************************************************************/ +static void +paste_xml_received_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + glLabel *label) +{ + gchar *xml_buffer; + glLabel *label_copy; + glXMLLabelStatus status; + GList *p; + glLabelObject *object, *newobject; + + gl_debug (DEBUG_LABEL, "START"); + + gl_label_checkpoint (label, _("Paste")); + + xml_buffer = (gchar *)gtk_selection_data_get_data (selection_data); + + /* + * Deserialize XML label document and extract objects. + */ + label_copy = gl_xml_label_open_buffer (xml_buffer, &status); + if ( label_copy ) + { + gl_label_unselect_all (label); + + for (p = label_copy->priv->object_list; p != NULL; p = p->next) + { + object = (glLabelObject *) p->data; + newobject = gl_label_object_dup (object, label); + gl_label_add_object( label, newobject ); + + gl_label_select_object (label, newobject); + + gl_debug (DEBUG_LABEL, "object pasted"); + } + + g_object_unref (G_OBJECT (label_copy)); + } + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Paste received text callback. */ +/****************************************************************************/ +static void +paste_text_received_cb (GtkClipboard *clipboard, + const gchar *text, + glLabel *label) +{ + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + gl_label_checkpoint (label, _("Paste")); + + gl_label_unselect_all (label); + + object = GL_LABEL_OBJECT (gl_label_text_new (label, FALSE)); + gl_label_text_set_text (GL_LABEL_TEXT (object), text, FALSE); + gl_label_object_set_position (object, 18, 18, FALSE); + + gl_label_select_object (label, object); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Paste received image callback. */ +/****************************************************************************/ +static void +paste_image_received_cb (GtkClipboard *clipboard, + GdkPixbuf *pixbuf, + glLabel *label) +{ + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + gl_label_checkpoint (label, _("Paste")); + + gl_label_unselect_all (label); + + object = GL_LABEL_OBJECT (gl_label_image_new (label, FALSE)); + gl_label_image_set_pixbuf (GL_LABEL_IMAGE (object), pixbuf, FALSE); + gl_label_object_set_position (object, 18, 18, FALSE); + + gl_label_select_object (label, object); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default font family. */ +/****************************************************************************/ +void +gl_label_set_default_font_family (glLabel *label, + const gchar *font_family) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + g_free (label->priv->default_font_family); + label->priv->default_font_family = g_strdup (font_family); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default font size. */ +/****************************************************************************/ +void +gl_label_set_default_font_size (glLabel *label, + gdouble font_size) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_font_size = font_size; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default font weight. */ +/****************************************************************************/ +void +gl_label_set_default_font_weight (glLabel *label, + PangoWeight font_weight) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_font_weight = font_weight; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default font italic flag. */ +/****************************************************************************/ +void +gl_label_set_default_font_italic_flag (glLabel *label, + gboolean font_italic_flag) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_font_italic_flag = font_italic_flag; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default text color. */ +/****************************************************************************/ +void +gl_label_set_default_text_color (glLabel *label, + guint text_color) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_text_color = text_color; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default text alignment. */ +/****************************************************************************/ +void +gl_label_set_default_text_alignment (glLabel *label, + PangoAlignment text_alignment) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_text_alignment = text_alignment; + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default text line spacing. */ +/****************************************************************************/ +void +gl_label_set_default_text_line_spacing (glLabel *label, + gdouble text_line_spacing) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_text_line_spacing = text_line_spacing; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default line width. */ +/****************************************************************************/ +void +gl_label_set_default_line_width (glLabel *label, + gdouble line_width) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_line_width = line_width; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default line color. */ +/****************************************************************************/ +void +gl_label_set_default_line_color (glLabel *label, + guint line_color) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_line_color = line_color; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Set default fill color. */ +/****************************************************************************/ +void +gl_label_set_default_fill_color (glLabel *label, + guint fill_color) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_if_fail (label && GL_IS_LABEL (label)); + + label->priv->default_fill_color = fill_color; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Get default font family. */ +/****************************************************************************/ +gchar * +gl_label_get_default_font_family (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_val_if_fail (label && GL_IS_LABEL (label), NULL); + + gl_debug (DEBUG_LABEL, "END"); + + return g_strdup (label->priv->default_font_family); +} + + +/****************************************************************************/ +/* Get default font size. */ +/****************************************************************************/ +gdouble +gl_label_get_default_font_size (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_val_if_fail (label && GL_IS_LABEL (label), 12.0); + + gl_debug (DEBUG_LABEL, "END"); + + return label->priv->default_font_size; +} + + +/****************************************************************************/ +/* Get default font weight. */ +/****************************************************************************/ +PangoWeight +gl_label_get_default_font_weight (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_val_if_fail (label && GL_IS_LABEL (label), PANGO_WEIGHT_NORMAL); + + gl_debug (DEBUG_LABEL, "END"); + + return label->priv->default_font_weight; +} + + +/****************************************************************************/ +/* Get default font italic flag. */ +/****************************************************************************/ +gboolean +gl_label_get_default_font_italic_flag (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_val_if_fail (label && GL_IS_LABEL (label), FALSE); + + gl_debug (DEBUG_LABEL, "END"); + + return label->priv->default_font_italic_flag; +} + + +/****************************************************************************/ +/* Get default text color. */ +/****************************************************************************/ +guint +gl_label_get_default_text_color (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_val_if_fail (label && GL_IS_LABEL (label), 0); + + gl_debug (DEBUG_LABEL, "END"); + + return label->priv->default_text_color; +} + + +/****************************************************************************/ +/* Get default text alignment. */ +/****************************************************************************/ +PangoAlignment +gl_label_get_default_text_alignment (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_val_if_fail (label && GL_IS_LABEL (label), PANGO_ALIGN_LEFT); + + gl_debug (DEBUG_LABEL, "END"); + + return label->priv->default_text_alignment; +} + + +/****************************************************************************/ +/* Get default text line spacing. */ +/****************************************************************************/ +gdouble +gl_label_get_default_text_line_spacing (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_val_if_fail (label && GL_IS_LABEL (label), 1.0); + + gl_debug (DEBUG_LABEL, "END"); + + return label->priv->default_text_line_spacing; +} + + +/****************************************************************************/ +/* Get default line width. */ +/****************************************************************************/ +gdouble +gl_label_get_default_line_width (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); + + g_return_val_if_fail (label && GL_IS_LABEL (label), 1.0); + + gl_debug (DEBUG_LABEL, "END"); + + return label->priv->default_line_width; } + /****************************************************************************/ -/* return short filename. */ +/* Get default line color. */ /****************************************************************************/ -gchar * -gl_label_get_short_name (glLabel *label) +guint +gl_label_get_default_line_color (glLabel *label) { - gl_debug (DEBUG_LABEL, ""); + gl_debug (DEBUG_LABEL, "START"); - if ( label->priv->filename == NULL ) { + g_return_val_if_fail (label && GL_IS_LABEL (label), 0); - if ( label->priv->untitled_instance == 0 ) { - label->priv->untitled_instance = ++untitled; - } + gl_debug (DEBUG_LABEL, "END"); - return g_strdup_printf ( "%s %d", _("Untitled"), - label->priv->untitled_instance ); + return label->priv->default_line_color; +} - } else { - gchar *temp_name, *short_name; - temp_name = g_path_get_basename ( label->priv->filename ); - short_name = gl_util_remove_extension (temp_name); - g_free (temp_name); +/****************************************************************************/ +/* Get default fill color. */ +/****************************************************************************/ +guint +gl_label_get_default_fill_color (glLabel *label) +{ + gl_debug (DEBUG_LABEL, "START"); - return short_name; + g_return_val_if_fail (label && GL_IS_LABEL (label), 0); + + gl_debug (DEBUG_LABEL, "END"); + + return label->priv->default_fill_color; +} + + +/****************************************************************************/ +/* Draw label. */ +/****************************************************************************/ +void +gl_label_draw (glLabel *label, + cairo_t *cr, + gboolean screen_flag, + glMergeRecord *record) +{ + GList *p_obj; + glLabelObject *object; + + g_return_if_fail (label && GL_IS_LABEL (label)); + + for (p_obj = label->priv->object_list; p_obj != NULL; p_obj = p_obj->next) + { + object = GL_LABEL_OBJECT (p_obj->data); + + gl_label_object_draw (object, cr, screen_flag, record); } } + /****************************************************************************/ -/* Get pixbuf cache. */ +/* Get object located at coordinates. */ /****************************************************************************/ -GHashTable * -gl_label_get_pixbuf_cache (glLabel *label) +glLabelObject *gl_label_object_at (glLabel *label, + cairo_t *cr, + gdouble x_pixels, + gdouble y_pixels) { - return label->priv->pixbuf_cache; + GList *p_obj; + glLabelObject *object; + + g_return_val_if_fail (label && GL_IS_LABEL (label), NULL); + + for (p_obj = g_list_last (label->priv->object_list); p_obj != NULL; p_obj = p_obj->prev) + { + object = GL_LABEL_OBJECT (p_obj->data); + + if (gl_label_object_is_located_at (object, cr, x_pixels, y_pixels)) + { + return object; + } + + } + + return NULL; } + /****************************************************************************/ -/* Is label modified? */ +/* Return handle and associated object at coordinates. */ /****************************************************************************/ -gboolean -gl_label_is_modified (glLabel *label) +glLabelObject * +gl_label_get_handle_at (glLabel *label, + cairo_t *cr, + gdouble x_pixels, + gdouble y_pixels, + glLabelObjectHandle *handle) { - gl_debug (DEBUG_LABEL, "return %d", label->priv->modified_flag); - return label->priv->modified_flag; + GList *selection_list; + GList *p_obj; + glLabelObject *object; + + g_return_val_if_fail (label && GL_IS_LABEL (label), NULL); + + selection_list = gl_label_get_selection_list (label); + + for (p_obj = g_list_last (selection_list); p_obj != NULL; p_obj = p_obj->prev) + { + + object = GL_LABEL_OBJECT (p_obj->data); + + if ((*handle = gl_label_object_handle_at (object, cr, x_pixels, y_pixels))) + { + g_list_free (selection_list); + return object; + } + + } + + g_list_free (selection_list); + + *handle = GL_LABEL_OBJECT_HANDLE_NONE; + return NULL; } + /****************************************************************************/ -/* Is label untitled? */ +/* Checkpoint state. */ /****************************************************************************/ -gboolean -gl_label_is_untitled (glLabel *label) +void +gl_label_checkpoint (glLabel *this, + const gchar *description) { - gl_debug (DEBUG_LABEL, "return %d",(label->priv->filename == NULL)); - return (label->priv->filename == NULL); + State *state; + + gl_debug (DEBUG_LABEL, "START"); + + /* + * Do not perform consecutive checkpoints that are identical. + * E.g. moving an object by dragging, would produce a large number + * of incremental checkpoints -- what we really want is a single + * checkpoint so that we can undo the entire dragging effort with + * one "undo" + */ + if ( this->priv->cp_cleared_flag + || (this->priv->cp_desc == NULL) + || (strcmp (description, this->priv->cp_desc) != 0) ) + { + + /* Sever old redo "thread" */ + stack_clear (this->priv->redo_stack); + + /* Save state onto undo stack. */ + state = state_new (this, description); + stack_push_state (this->priv->undo_stack, state); + + /* Track consecutive checkpoints. */ + this->priv->cp_cleared_flag = FALSE; + this->priv->cp_desc = g_strdup (description); + } + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Undo. */ +/****************************************************************************/ +void +gl_label_undo (glLabel *this) +{ + State *state_old; + State *state_now; + + gl_debug (DEBUG_LABEL, "START"); + + state_old = stack_pop_state (this->priv->undo_stack); + state_now = state_new (this, state_old->description); + + stack_push_state (this->priv->redo_stack, state_now); + + state_restore (state_old, this); + state_free (state_old); + + this->priv->cp_cleared_flag = TRUE; + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Redo. */ +/****************************************************************************/ +void +gl_label_redo (glLabel *this) +{ + State *state_old; + State *state_now; + + gl_debug (DEBUG_LABEL, "START"); + + state_old = stack_pop_state (this->priv->redo_stack); + state_now = state_new (this, state_old->description); + + stack_push_state (this->priv->undo_stack, state_now); + + state_restore (state_old, this); + state_free (state_old); + + this->priv->cp_cleared_flag = TRUE; + + gl_debug (DEBUG_LABEL, "END"); } + /****************************************************************************/ /* Can undo? */ /****************************************************************************/ gboolean -gl_label_can_undo (glLabel *label) +gl_label_can_undo (glLabel *this) { - return FALSE; + return (!g_queue_is_empty (this->priv->undo_stack)); } @@ -585,90 +3229,231 @@ gl_label_can_undo (glLabel *label) /* Can redo? */ /****************************************************************************/ gboolean -gl_label_can_redo (glLabel *label) +gl_label_can_redo (glLabel *this) { - return FALSE; + return (!g_queue_is_empty (this->priv->redo_stack)); } /****************************************************************************/ -/* Set filename. */ +/* Get undo description string. */ /****************************************************************************/ -void -gl_label_set_filename (glLabel *label, - const gchar *filename) +gchar * +gl_label_get_undo_description (glLabel *this) { - label->priv->filename = g_strdup (filename); + State *state; + gchar *description; - g_signal_emit (G_OBJECT(label), signals[NAME_CHANGED], 0); + state = g_queue_peek_head (this->priv->undo_stack); + if ( state ) + { + description = g_strdup (state->description); + } + else + { + description = g_strdup (""); + } + + return description; } + /****************************************************************************/ -/* Clear modified flag. */ +/* Get redo description string. */ /****************************************************************************/ -void -gl_label_clear_modified (glLabel *label) +gchar * +gl_label_get_redo_description (glLabel *this) { + State *state; + gchar *description; + + state = g_queue_peek_head (this->priv->redo_stack); + if ( state ) + { + description = g_strdup (state->description); + } + else + { + description = g_strdup (""); + } - if ( label->priv->modified_flag ) { + return description; +} - label->priv->modified_flag = FALSE; - g_signal_emit (G_OBJECT(label), signals[MODIFIED_CHANGED], 0); - } +/****************************************************************************/ +/* Clear undo or redo stack. */ +/****************************************************************************/ +static void +stack_clear (GQueue *stack) +{ + State *state; + + gl_debug (DEBUG_LABEL, "START"); + while ( (state = g_queue_pop_head (stack)) != NULL ) + { + state_free (state); + } + + gl_debug (DEBUG_LABEL, "END"); } /****************************************************************************/ -/* Set compression level. */ +/* Push state onto stack. */ /****************************************************************************/ -void -gl_label_set_compression (glLabel *label, - gint compression) +static void +stack_push_state (GQueue *stack, + State *state) { - gl_debug (DEBUG_LABEL, "set %d", compression); + gl_debug (DEBUG_LABEL, "START"); - /* Older versions of libxml2 always return a -1 for documents "read in," so - * default to 9. Also, default to 9 for anything else out of range. */ - if ((compression < 0) || (compression >9)) { - compression = 9; - } + g_queue_push_head( stack, state ); - gl_debug (DEBUG_LABEL, "actual set %d", compression); - label->priv->compression = compression; + gl_debug (DEBUG_LABEL, "END"); } /****************************************************************************/ -/* Get compression level. */ +/* Pop state from stack. */ /****************************************************************************/ -gint -gl_label_get_compression (glLabel *label) +static State * +stack_pop_state (GQueue *stack) { - gl_debug (DEBUG_LABEL, "return %d", label->priv->compression); - return label->priv->compression; + State *state; + + gl_debug (DEBUG_LABEL, "START"); + + state = g_queue_pop_head (stack); + + gl_debug (DEBUG_LABEL, "END"); + return state; } /****************************************************************************/ -/* Draw label. */ +/* New state from label. */ /****************************************************************************/ -void -gl_label_draw (glLabel *label, - cairo_t *cr, - gboolean screen_flag, - glMergeRecord *record) +static State * +state_new (glLabel *this, + const gchar *description) { - GList *p_obj; - glLabelObject *object; + State *state; + GList *p_obj; + glLabelObject *object; - g_return_if_fail (label && GL_IS_LABEL (label)); + gl_debug (DEBUG_LABEL, "START"); + + state = g_new0 (State, 1); + + state->description = g_strdup (description); + + state->template = lgl_template_dup (this->priv->template); + state->rotate_flag = this->priv->rotate_flag; - for (p_obj = label->objects; p_obj != NULL; p_obj = p_obj->next) + for ( p_obj = this->priv->object_list; p_obj != NULL; p_obj = p_obj->next ) { - object = GL_LABEL_OBJECT (p_obj->data); + object = GL_LABEL_OBJECT (p_obj->data); - gl_label_object_draw (object, cr, screen_flag, record); - } + state->object_list = g_list_append (state->object_list, + gl_label_object_dup (object, this)); + } + + state->merge = gl_merge_dup (this->priv->merge); + + state->modified_flag = this->priv->modified_flag; + state->time_stamp = this->priv->time_stamp; + + + gl_debug (DEBUG_LABEL, "END"); + return state; +} + + +/****************************************************************************/ +/* Restore label from saved state. */ +/****************************************************************************/ +static void +state_free (State *state) +{ + GList *p_obj; + + gl_debug (DEBUG_LABEL, "START"); + + g_free (state->description); + + lgl_template_free (state->template); + if ( state->merge ) + { + g_object_unref (G_OBJECT (state->merge)); + } + + for ( p_obj = state->object_list; p_obj != NULL; p_obj = p_obj->next ) + { + g_object_unref (G_OBJECT (p_obj->data)); + } + g_list_free (state->object_list); + + g_free (state); + + gl_debug (DEBUG_LABEL, "END"); +} + + +/****************************************************************************/ +/* Restore label from saved state. */ +/****************************************************************************/ +static void +state_restore (State *state, + glLabel *this) + +{ + GList *p_obj, *p_next; + glLabelObject *object; + + gl_debug (DEBUG_LABEL, "START"); + + gl_label_set_rotate_flag (this, state->rotate_flag, FALSE); + gl_label_set_template (this, state->template, FALSE); + + for ( p_obj = this->priv->object_list; p_obj != NULL; p_obj = p_next ) + { + p_next = p_obj->next; /* Hold on to next; delete is destructive */ + object = GL_LABEL_OBJECT (p_obj->data); + + gl_label_delete_object (this, object); + } + + for ( p_obj = state->object_list; p_obj != NULL; p_obj = p_obj->next ) + { + object = GL_LABEL_OBJECT (p_obj->data); + + gl_label_add_object (this, gl_label_object_dup (object, this)); + } + g_signal_emit (G_OBJECT(this), signals[SELECTION_CHANGED], 0); + + gl_label_set_merge (this, state->merge, FALSE); + + + if ( !state->modified_flag && + (state->time_stamp.tv_sec == this->priv->time_stamp.tv_sec) && + (state->time_stamp.tv_usec == this->priv->time_stamp.tv_usec) ) + { + gl_label_clear_modified (this); + } + + gl_debug (DEBUG_LABEL, "END"); } + + + + +/* + * Local Variables: -- emacs + * mode: C -- emacs + * c-basic-offset: 8 -- emacs + * tab-width: 8 -- emacs + * indent-tabs-mode: nil -- emacs + * End: -- emacs + */