Group selection to move and resize.
authordanigm <dani@danigm.net>
Sun, 7 Nov 2010 18:46:38 +0000 (19:46 +0100)
committerdanigm <dani@danigm.net>
Sun, 21 Nov 2010 10:17:02 +0000 (11:17 +0100)
src/Makefile.am
src/tbo-object-base.c
src/tbo-object-base.h
src/tbo-object-group.c [new file with mode: 0644]
src/tbo-object-group.h [new file with mode: 0644]
src/tbo-tool-selector.c

index e004c7c..b0ddf4b 100644 (file)
@@ -50,6 +50,8 @@ SOURCES = \
        tbo-object-text.c \
        tbo-object-pixmap.h \
        tbo-object-pixmap.c \
+       tbo-object-group.h \
+       tbo-object-group.c \
        tbo-tool-base.h \
        tbo-tool-base.c \
        tbo-tool-selector.h \
index b8e3d27..90dd37e 100644 (file)
@@ -39,6 +39,49 @@ save (TboObjectBase *self, FILE *file)
 {
 }
 
+static void
+move (TboObjectBase *self, enum MOVE_OPT type)
+{
+    switch (type)
+    {
+        case MOVE_UP:
+            self->y -= MOVING_OFFSET;
+            break;
+        case MOVE_DOWN:
+            self->y += MOVING_OFFSET;
+            break;
+        case MOVE_LEFT:
+            self->x -= MOVING_OFFSET;
+            break;
+        case MOVE_RIGHT:
+            self->x += MOVING_OFFSET;
+            break;
+        default:
+            break;
+    }
+}
+
+static void
+resize (TboObjectBase *self, enum RESIZE_OPT type)
+{
+    switch (type)
+    {
+        case RESIZE_LESS:
+            if (self->width > 10 && self->height > 10)
+            {
+                self->width *= 0.95;
+                self->height *= 0.95;
+            }
+            break;
+        case RESIZE_GREATER:
+            self->width *= 1.05;
+            self->height *= 1.05;
+            break;
+        default:
+            break;
+    }
+}
+
 static TboObjectBase *
 clone (TboObjectBase *self)
 {
@@ -61,6 +104,9 @@ tbo_object_base_init (TboObjectBase *self)
     self->draw = draw;
     self->save = save;
     self->clone = clone;
+
+    self->move = move;
+    self->resize = resize;
 }
 
 static void
@@ -145,42 +191,11 @@ tbo_object_base_order_up (TboObjectBase *self, Frame *frame)
 void
 tbo_object_base_move (TboObjectBase *self, enum MOVE_OPT type)
 {
-    switch (type)
-    {
-        case MOVE_UP:
-            self->y -= MOVING_OFFSET;
-            break;
-        case MOVE_DOWN:
-            self->y += MOVING_OFFSET;
-            break;
-        case MOVE_LEFT:
-            self->x -= MOVING_OFFSET;
-            break;
-        case MOVE_RIGHT:
-            self->x += MOVING_OFFSET;
-            break;
-        default:
-            break;
-    }
+    self->move (self, type);
 }
 
 void
 tbo_object_base_resize (TboObjectBase *self, enum RESIZE_OPT type)
 {
-    switch (type)
-    {
-        case RESIZE_LESS:
-            if (self->width > 10 && self->height > 10)
-            {
-                self->width *= 0.95;
-                self->height *= 0.95;
-            }
-            break;
-        case RESIZE_GREATER:
-            self->width *= 1.05;
-            self->height *= 1.05;
-            break;
-        default:
-            break;
-    }
+    self->resize (self, type);
 }
index c877cef..d643114 100644 (file)
 typedef struct _TboObjectBase      TboObjectBase;
 typedef struct _TboObjectBaseClass TboObjectBaseClass;
 
+enum MOVE_OPT
+{
+    MOVE_UP,
+    MOVE_DOWN,
+    MOVE_LEFT,
+    MOVE_RIGHT,
+};
+
+enum RESIZE_OPT
+{
+    RESIZE_LESS,
+    RESIZE_GREATER,
+};
+
 struct _TboObjectBase
 {
     GObject parent_instance;
@@ -53,6 +67,8 @@ struct _TboObjectBase
     void (*draw) (TboObjectBase *, Frame *, cairo_t *);
     void (*save) (TboObjectBase *, FILE *);
     TboObjectBase * (*clone) (TboObjectBase *);
+    void (*move) (TboObjectBase *, enum MOVE_OPT type);
+    void (*resize) (TboObjectBase *, enum RESIZE_OPT type);
 };
 
 struct _TboObjectBaseClass
@@ -69,20 +85,6 @@ GType tbo_object_base_get_type (void);
  * Method definitions.
  */
 
-enum MOVE_OPT
-{
-    MOVE_UP,
-    MOVE_DOWN,
-    MOVE_LEFT,
-    MOVE_RIGHT,
-};
-
-enum RESIZE_OPT
-{
-    RESIZE_LESS,
-    RESIZE_GREATER,
-};
-
 GObject * tbo_object_base_new ();
 void tbo_object_base_flipv (TboObjectBase *self);
 void tbo_object_base_fliph (TboObjectBase *self);
diff --git a/src/tbo-object-group.c b/src/tbo-object-group.c
new file mode 100644 (file)
index 0000000..0f871b1
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * This file is part of TBO, a gnome comic editor
+ * Copyright (C) 2010  Daniel Garcia Moreno <dani@danigm.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <glib.h>
+#include <cairo.h>
+#include <stdio.h>
+#include "tbo-types.h"
+#include "tbo-object-group.h"
+
+G_DEFINE_TYPE (TboObjectGroup, tbo_object_group, TBO_TYPE_OBJECT_BASE);
+
+static TboObjectBase * clone (TboObjectBase *);
+
+static TboObjectBase *
+clone (TboObjectBase *self)
+{
+}
+
+static void tbo_object_group_get_base (TboObjectGroup *self, gint *minx, gint *miny, gint *maxx, gint *maxy);
+
+static void
+resize (TboObjectBase *self, enum RESIZE_OPT type)
+{
+    GList *o;
+    TboObjectBase *obj;
+    gdouble scale = 1.0;
+    gint minx=-1, miny=-1, maxx=0, maxy=0;
+    tbo_object_group_get_base (TBO_OBJECT_GROUP (self), &minx, &miny, &maxx, &maxy);
+
+    switch (type)
+    {
+        case RESIZE_LESS:
+            scale -= 0.05;
+            break;
+        case RESIZE_GREATER:
+            scale += 0.05;
+            break;
+        default:
+            break;
+    }
+
+    for (o=g_list_first (TBO_OBJECT_GROUP (self)->objs); o; o=g_list_next(o))
+    {
+        obj = TBO_OBJECT_BASE (o->data);
+
+        if ((obj->width < 10 || obj->height < 10) && scale < 1)
+            break;
+
+        obj->width *= scale;
+        obj->x = minx + (obj->x - minx) * scale;
+        obj->height *= scale;
+        obj->y = miny + (obj->y - miny) * scale;
+    }
+}
+
+static void
+move (TboObjectBase *self, enum MOVE_OPT type)
+{
+    TBO_OBJECT_GROUP (self)->parent_move (self, type);
+    tbo_object_group_update_status (TBO_OBJECT_GROUP (self));
+}
+
+/* init methods */
+
+static void
+tbo_object_group_init (TboObjectGroup *self)
+{
+    self->objs = NULL;
+    self->parent_move = self->parent_instance.move;
+
+    self->parent_instance.clone = clone;
+    self->parent_instance.resize = resize;
+    self->parent_instance.move = move;
+}
+
+static void
+tbo_object_group_finalize (GObject *self)
+{
+    if (TBO_IS_OBJECT_GROUP (self))
+    {
+        if ((TBO_OBJECT_GROUP (self)->objs))
+            g_list_free (TBO_OBJECT_GROUP (self)->objs);
+    }
+    /* Chain up to the parent class */
+    G_OBJECT_CLASS (tbo_object_group_parent_class)->finalize (self);
+}
+
+static void
+tbo_object_group_class_init (TboObjectGroupClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    gobject_class->finalize = tbo_object_group_finalize;
+}
+
+/* object functions */
+
+GObject *
+tbo_object_group_new ()
+{
+    GObject *tbo_object;
+    tbo_object = g_object_new (TBO_TYPE_OBJECT_GROUP, NULL);
+
+    return tbo_object;
+}
+
+void
+tbo_object_group_add (TboObjectGroup *self, TboObjectBase *obj)
+{
+    if (TBO_IS_OBJECT_GROUP (obj))
+        return;
+    if (!g_list_find (self->objs, obj))
+        self->objs = g_list_append (self->objs, obj);
+}
+
+void
+tbo_object_group_del (TboObjectGroup *self, TboObjectBase *obj)
+{
+    self->objs = g_list_remove (g_list_first (self->objs), obj);
+}
+
+void
+tbo_object_group_get_base (TboObjectGroup *self,
+                           gint *minx, gint *miny,
+                           gint *maxx, gint *maxy)
+{
+    GList *o;
+    TboObjectBase *obj;
+
+    for (o=g_list_first (self->objs); o; o=g_list_next(o))
+    {
+        obj = TBO_OBJECT_BASE (o->data);
+        if (*minx < 0 || obj->x < *minx)
+            *minx = obj->x;
+        if (*miny < 0 || obj->y < *miny)
+            *miny = obj->y;
+        if ((obj->x + obj->width) > *maxx)
+            *maxx = (obj->x + obj->width);
+        if ((obj->y + obj->height) > *maxy)
+            *maxy = (obj->y + obj->height);
+    }
+}
+
+void
+tbo_object_group_set_vars (TboObjectBase *obj)
+{
+    if (!TBO_IS_OBJECT_GROUP (obj))
+        return;
+
+    gint minx=-1, miny=-1, maxx=0, maxy=0;
+    tbo_object_group_get_base (TBO_OBJECT_GROUP (obj), &minx, &miny, &maxx, &maxy);
+
+    obj->x = minx;
+    obj->y = miny;
+    obj->width = maxx - minx;
+    obj->height = maxy - miny;
+}
+
+void
+tbo_object_group_unset_vars (TboObjectBase *obj)
+{
+    if (!TBO_IS_OBJECT_GROUP (obj))
+        return;
+
+    obj->x = 0;
+    obj->y = 0;
+    obj->width = 0;
+    obj->height = 0;
+}
+
+void
+tbo_object_group_update_status (TboObjectGroup *self)
+{
+    GList *o;
+    gdouble scale = 1.0;
+    TboObjectBase *obj, *tbo_object;
+    gint minx=-1, miny=-1, maxx=0, maxy=0;
+
+    tbo_object = TBO_OBJECT_BASE (self);
+    tbo_object_group_get_base (self, &minx, &miny, &maxx, &maxy);
+
+    for (o=g_list_first (self->objs); o; o=g_list_next(o))
+    {
+        obj = TBO_OBJECT_BASE (o->data);
+        obj->x += tbo_object->x;
+        obj->y += tbo_object->y;
+        // resizing
+        scale = (obj->width + tbo_object->width) / (double) obj->width;
+        obj->width += tbo_object->width;
+        obj->x = minx + (obj->x - minx) * scale;
+        scale =  (obj->height + tbo_object->height) / (double) obj->height;
+        obj->height += tbo_object->height;
+        obj->y = miny + (obj->y - miny) * scale;
+    }
+
+    tbo_object_group_unset_vars (tbo_object);
+}
+
diff --git a/src/tbo-object-group.h b/src/tbo-object-group.h
new file mode 100644 (file)
index 0000000..779302f
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file is part of TBO, a gnome comic editor
+ * Copyright (C) 2010  Daniel Garcia Moreno <dani@danigm.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __TBO_OBJECT_GROUP_H__
+#define __TBO_OBJECT_GROUP_H__
+
+#include <glib.h>
+#include "tbo-object-base.h"
+#include "tbo-object-group.h"
+
+#define TBO_TYPE_OBJECT_GROUP            (tbo_object_group_get_type ())
+#define TBO_OBJECT_GROUP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TBO_TYPE_OBJECT_GROUP, TboObjectGroup))
+#define TBO_IS_OBJECT_GROUP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TBO_TYPE_OBJECT_GROUP))
+#define TBO_OBJECT_GROUP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TBO_TYPE_OBJECT_GROUP, TboObjectGroupClass))
+#define TBO_IS_OBJECT_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TBO_TYPE_OBJECT_GROUP))
+#define TBO_OBJECT_GROUP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TBO_TYPE_OBJECT_GROUP, TboObjectGroupClass))
+
+typedef struct _TboObjectGroup      TboObjectGroup;
+typedef struct _TboObjectGroupClass TboObjectGroupClass;
+
+struct _TboObjectGroup
+{
+    TboObjectBase parent_instance;
+
+    /* instance members */
+    GList *objs;
+    void (*parent_move) (TboObjectBase *, enum MOVE_OPT type);
+};
+
+struct _TboObjectGroupClass
+{
+    TboObjectBaseClass parent_class;
+
+    /* class members */
+};
+
+/* used by TBO_TYPE_OBJECT_GROUP */
+GType tbo_object_group_get_type (void);
+
+/*
+ * Method definitions.
+ */
+
+GObject * tbo_object_group_new ();
+void tbo_object_group_add (TboObjectGroup *self, TboObjectBase *obj);
+void tbo_object_group_del (TboObjectGroup *self, TboObjectBase *obj);
+void tbo_object_group_set_vars (TboObjectBase *self);
+void tbo_object_group_unset_vars (TboObjectBase *self);
+void tbo_object_group_update_status (TboObjectGroup *self);
+
+#endif /* __TBO_OBJECT_GROUP_H__ */
+
index a33d3f6..ce14e91 100644 (file)
@@ -26,6 +26,7 @@
 #include "tbo-ui-utils.h"
 #include "tbo-tool-selector.h"
 #include "tbo-drawing.h"
+#include "tbo-object-group.h"
 
 G_DEFINE_TYPE (TboToolSelector, tbo_tool_selector, TBO_TYPE_TOOL_BASE);
 
@@ -305,8 +306,8 @@ frame_view_on_move (TboToolBase *tool, GtkWidget *widget, GdkEventMotion *event)
             // resizing object
             if (self->resizing)
             {
-                self->selected_object->width = abs (self->start_m_w - offset_x);
-                self->selected_object->height = abs (self->start_m_h - offset_y);
+                self->selected_object->width = self->start_m_w - offset_x;
+                self->selected_object->height = self->start_m_h - offset_y;
             }
             else if (self->rotating)
             {
@@ -320,6 +321,20 @@ frame_view_on_move (TboToolBase *tool, GtkWidget *widget, GdkEventMotion *event)
             }
         }
 
+        // updating group object
+        if (TBO_IS_OBJECT_GROUP (self->selected_object))
+        {
+            tbo_object_group_update_status (TBO_OBJECT_GROUP (self->selected_object));
+
+            self->start_x = x;
+            self->start_y = y;
+            self->start_m_x = self->selected_object->x;
+            self->start_m_y = self->selected_object->y;
+            self->start_m_w = self->selected_object->width;
+            self->start_m_h = self->selected_object->height;
+        }
+
+        tbo_object_group_set_vars (self->selected_object);
         // over resizer
         if (over_resizer_obj (self, self->selected_object, x, y))
         {
@@ -338,6 +353,8 @@ frame_view_on_move (TboToolBase *tool, GtkWidget *widget, GdkEventMotion *event)
         {
             self->over_rotater = FALSE;
         }
+        tbo_object_group_unset_vars (self->selected_object);
+
     }
 }
 
@@ -348,7 +365,8 @@ frame_view_on_click (TboToolBase *tool, GtkWidget *widget, GdkEventButton *event
     int x, y;
     GList *obj_list;
     Frame *frame;
-    TboObjectBase *obj;
+    TboObjectBase *obj, *obj2;
+    TboObjectGroup *group;
     TboDrawing *drawing = TBO_DRAWING (tool->tbo->drawing);
     gboolean found = FALSE;
 
@@ -356,6 +374,7 @@ frame_view_on_click (TboToolBase *tool, GtkWidget *widget, GdkEventButton *event
     y = (int)event->y;
 
     // resizing
+    tbo_object_group_set_vars (self->selected_object);
     if (self->selected_object && over_resizer_obj (self, self->selected_object, x, y))
     {
         self->resizing = TRUE;
@@ -367,19 +386,41 @@ frame_view_on_click (TboToolBase *tool, GtkWidget *widget, GdkEventButton *event
     else
     {
         frame = tbo_drawing_get_current_frame (drawing);
+
         for (obj_list = g_list_first (frame->objects); obj_list; obj_list = obj_list->next)
         {
             obj = TBO_OBJECT_BASE (obj_list->data);
+            tbo_object_group_set_vars (obj);
             if (tbo_frame_point_inside_obj (obj, x, y))
             {
                 // Selecting last occurrence.
-                tbo_tool_selector_set_selected_obj (self, obj);
+                obj2 = obj;
                 found = TRUE;
             }
+            tbo_object_group_unset_vars (obj);
         }
+
         if (!found)
             tbo_tool_selector_set_selected_obj (self, NULL);
+        else
+        {
+            if ((event->state & GDK_SHIFT_MASK) && self->selected_object) {
+                if (!TBO_IS_OBJECT_GROUP (self->selected_object))
+                {
+                    group = TBO_OBJECT_GROUP (tbo_object_group_new ());
+                    tbo_frame_add_obj (frame, TBO_OBJECT_BASE (group));
+                    tbo_object_group_add (group, self->selected_object);
+                }
+                else
+                    group = TBO_OBJECT_GROUP (self->selected_object);
+
+                tbo_object_group_add (group, obj2);
+                obj2 = TBO_OBJECT_BASE (group);
+            }
+            tbo_tool_selector_set_selected_obj (self, obj2);
+        }
     }
+    tbo_object_group_unset_vars (self->selected_object);
 
     self->start_x = x;
     self->start_y = y;
@@ -407,12 +448,16 @@ frame_view_drawing (TboToolBase *tool, cairo_t *cr)
     Color *rotater_fill;
     int x, y;
     float r_size;
+    gboolean group = FALSE;
     TboToolSelector *self = TBO_TOOL_SELECTOR (tool);
     TboObjectBase *current_obj = self->selected_object;
     TboDrawing *drawing = TBO_DRAWING (tool->tbo->drawing);
 
     if (current_obj != NULL)
     {
+        if (TBO_IS_OBJECT_GROUP (current_obj))
+            tbo_object_group_set_vars (current_obj);
+
         cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
         cairo_set_line_width (cr, 1);
         cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0);
@@ -487,6 +532,9 @@ frame_view_drawing (TboToolBase *tool, cairo_t *cr)
             cairo_line_to (cr, self->x, self->y);
             cairo_stroke (cr);
         }
+
+        if (TBO_IS_OBJECT_GROUP (current_obj))
+            tbo_object_group_unset_vars (current_obj);
     }
 }
 
@@ -810,6 +858,12 @@ tbo_tool_selector_set_selected (TboToolSelector *self, Frame *frame)
 void
 tbo_tool_selector_set_selected_obj (TboToolSelector *self, TboObjectBase *obj)
 {
+    if (!obj && TBO_IS_OBJECT_GROUP (self->selected_object))
+    {
+        TboDrawing *drawing = TBO_DRAWING (TBO_TOOL_BASE (self)->tbo->drawing);
+        Frame *frame = tbo_drawing_get_current_frame (drawing);
+        tbo_frame_del_obj (frame, self->selected_object);
+    }
     self->selected_object = obj;
     update_menubar (TBO_TOOL_BASE (self)->tbo);
 }