pcb 4.1.1
An interactive printed circuit board layout editor.

gui-trackball.c

Go to the documentation of this file.
00001 /*
00002  *                            COPYRIGHT
00003  *
00004  *  PCB, interactive printed circuit board design
00005  *  Copyright (C) 2009 PCB Contributors (See ChangeLog for details)
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License along
00018  *  with this program; if not, write to the Free Software Foundation, Inc.,
00019  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00020  *
00021  *  Contact addresses for paper mail and Email:
00022  *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
00023  *  Thomas.Nau@rz.uni-ulm.de
00024  *
00025  */
00026 
00027 #ifdef HAVE_CONFIG_H
00028 #include "config.h"
00029 #endif
00030 
00031 #include "global.h"
00032 
00033 #include "gui.h"
00034 
00035 #include "copy.h"
00036 #include "data.h"
00037 #include "draw.h"
00038 #include "mymem.h"
00039 #include "move.h"
00040 #include "rotate.h"
00041 #include "hid/common/trackball.h"
00042 #include "gui-trackball.h"
00043 
00044 #ifdef HAVE_LIBDMALLOC
00045 #include <dmalloc.h>
00046 #endif
00047 
00048 enum {
00049   ROTATION_CHANGED,
00050   VIEW_2D_CHANGED,
00051   LAST_SIGNAL
00052 };
00053 
00054 
00055 static guint ghid_trackball_signals[ LAST_SIGNAL ] = { 0 };
00056 static GObjectClass *ghid_trackball_parent_class = NULL;
00057 
00058 
00059 static gboolean
00060 button_press_cb (GtkWidget *widget, GdkEventButton *ev, gpointer userdata)
00061 {
00062   GhidTrackball *ball = GHID_TRACKBALL (userdata);
00063   float axis[3];
00064 
00065   /* Only respond to left mouse button for now */
00066   if (ev->button != 1)
00067     return TRUE;
00068 
00069   switch (ev->type) {
00070 
00071     case GDK_BUTTON_PRESS:
00072       ball->x1 = 2. * ev->x / widget->allocation.width - 1.;
00073       ball->y1 = 2. * ev->y / widget->allocation.height - 1.;
00074       ball->dragging = TRUE;
00075       break;
00076 
00077     case GDK_2BUTTON_PRESS:
00078       /* Reset the rotation of the trackball */
00079       /* TODO: Would be nice to animate this! */
00080       axis[0] = 1.; axis[1] = 0.; axis[2] = 0.;
00081       axis_to_quat (axis, 0, ball->quart1);
00082       axis_to_quat (axis, 0, ball->quart2);
00083       g_signal_emit (ball, ghid_trackball_signals[ROTATION_CHANGED], 0,
00084                      ball->quart2);
00085       break;
00086 
00087     default:
00088       break;
00089   }
00090 
00091   return TRUE;
00092 }
00093 
00094 
00095 static gboolean
00096 button_release_cb (GtkWidget *widget, GdkEventButton *ev, gpointer userdata)
00097 {
00098   GhidTrackball *ball = GHID_TRACKBALL (userdata);
00099 
00100   /* Only respond to left mouse button for now */
00101   if (ev->button != 1)
00102     return TRUE;
00103 
00104   ball->quart1[0] = ball->quart2[0];
00105   ball->quart1[1] = ball->quart2[1];
00106   ball->quart1[2] = ball->quart2[2];
00107   ball->quart1[3] = ball->quart2[3];
00108 
00109   ball->dragging = FALSE;
00110 
00111   return TRUE;
00112 }
00113 
00114 
00115 static gboolean
00116 motion_notify_cb (GtkWidget *widget, GdkEventMotion *ev, gpointer userdata)
00117 {
00118   GhidTrackball *ball = GHID_TRACKBALL (userdata);
00119   double x1, y1;
00120   double x2, y2;
00121   float q[4];
00122 
00123   if (!ball->dragging) {
00124     gdk_event_request_motions (ev);
00125     return TRUE;
00126   }
00127 
00128   x1 = ball->x1;
00129   y1 = ball->y1;
00130 
00131   x2 = 2. * ev->x / widget->allocation.width - 1.;
00132   y2 = 2. * ev->y / widget->allocation.height - 1.;
00133 
00134   /* Trackball computation */
00135   trackball (q, x1, y1, x2, y2);
00136   add_quats (q, ball->quart1, ball->quart2);
00137 
00138   g_signal_emit (ball, ghid_trackball_signals[ROTATION_CHANGED], 0,
00139                  ball->quart2);
00140 
00141   gdk_event_request_motions (ev);
00142   return TRUE;
00143 }
00144 
00145 static gboolean
00146 ghid_trackball_expose (GtkWidget * widget, GdkEventExpose * ev)
00147 {
00148   cairo_t *cr;
00149   cairo_pattern_t *pattern;
00150   GtkAllocation allocation;
00151   GdkColor color;
00152   double radius;
00153 
00154   cr = gdk_cairo_create (gtk_widget_get_window (widget));
00155 
00156           /* set a clip region for the expose event */
00157   cairo_rectangle (cr,
00158                    ev->area.x, ev->area.y,
00159                    ev->area.width, ev->area.height);
00160   cairo_clip (cr);
00161 
00162   gtk_widget_get_allocation (widget, &allocation);
00163 
00164   radius = (MIN (allocation.width, allocation.height) - 5) / 2.;
00165   pattern = cairo_pattern_create_radial (2 * radius * 0.8,
00166                                          2 * radius * 0.3,
00167                                          0.,
00168                                          2 * radius * 0.50,
00169                                          2 * radius * 0.50,
00170                                          2 * radius * 0.71);
00171 
00172   color = widget->style->fg[gtk_widget_get_state (widget)];
00173 
00174   cairo_pattern_add_color_stop_rgb (pattern, 0.0,
00175                                     (color.red   / 65535. * 0.5 + 4.5) / 5.,
00176                                     (color.green / 65535. * 0.5 + 4.5) / 5.,
00177                                     (color.blue  / 65535. * 0.5 + 4.5) / 5.);
00178   cairo_pattern_add_color_stop_rgb (pattern, 0.2,
00179                                     (color.red   / 65535. * 1.5 + 3.7) / 5.,
00180                                     (color.green / 65535. * 1.5 + 3.7) / 5.,
00181                                     (color.blue  / 65535. * 1.5 + 3.7) / 5.);
00182   cairo_pattern_add_color_stop_rgb (pattern, 1.0,
00183                                     (color.red   / 65535. * 5. + 0.) / 5.,
00184                                     (color.green / 65535. * 5. + 0.) / 5.,
00185                                     (color.blue  / 65535. * 5. + 0.) / 5.);
00186   cairo_set_source (cr, pattern);
00187   cairo_pattern_destroy (pattern);
00188 
00189   cairo_save (cr);
00190   cairo_translate (cr, allocation.width / 2., allocation.height / 2.);
00191   cairo_scale (cr, radius, radius);
00192   cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI);
00193   cairo_restore (cr);
00194 
00195   cairo_fill_preserve (cr);
00196 
00197   gdk_cairo_set_source_color (cr, &widget->style->bg[gtk_widget_get_state (widget)]);
00198   cairo_set_line_width (cr, 0.4);
00199   cairo_stroke (cr);
00200 
00201   cairo_destroy (cr);
00202 
00203   return FALSE;
00204 }
00205 
00206 static gboolean
00207 view_2d_toggled_cb (GtkToggleButton *toggle, gpointer userdata)
00208 {
00209   GhidTrackball *ball = GHID_TRACKBALL (userdata);
00210   float axis[3];
00211   float quart[4];
00212   gboolean view_2d;
00213 
00214   view_2d = gtk_toggle_button_get_active (toggle);
00215   if (view_2d)
00216     {
00217       axis[0] = 1.; axis[1] = 0.; axis[2] = 0.;
00218       axis_to_quat (axis, 0, quart);
00219 
00220       g_signal_emit (ball, ghid_trackball_signals[ROTATION_CHANGED], 0, quart);
00221       gtk_widget_set_sensitive (ball->drawing_area, FALSE);
00222     }
00223   else
00224     {
00225       g_signal_emit (ball, ghid_trackball_signals[ROTATION_CHANGED], 0, ball->quart1);
00226       gtk_widget_set_sensitive (ball->drawing_area, TRUE);
00227     }
00228 
00229   g_signal_emit (ball, ghid_trackball_signals[VIEW_2D_CHANGED], 0, view_2d);
00230 
00231   return TRUE;
00232 }
00245 static GObject *
00246 ghid_trackball_constructor (GType type,
00247                             guint n_construct_properties,
00248                             GObjectConstructParam *construct_properties)
00249 {
00250   GhidTrackball *ball;
00251   float axis[3];
00252 
00253   /* chain up to constructor of parent class */
00254   ball = GHID_TRACKBALL (G_OBJECT_CLASS (ghid_trackball_parent_class)->
00255     constructor (type, n_construct_properties, construct_properties));
00256 
00257   gtk_widget_set_size_request (GTK_WIDGET (ball), 140, 140);
00258 
00259   ball->view_2d = gtk_toggle_button_new_with_label (_("2D View"));
00260   gtk_box_pack_start (GTK_BOX (ball), ball->view_2d, FALSE, FALSE, 0);
00261   gtk_widget_show (ball->view_2d);
00262   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ball->view_2d), TRUE);
00263 
00264   ball->drawing_area = gtk_drawing_area_new ();
00265   gtk_box_pack_start (GTK_BOX (ball), ball->drawing_area, TRUE, TRUE, 0);
00266   gtk_widget_set_sensitive (ball->drawing_area, FALSE);
00267   gtk_widget_show (ball->drawing_area);
00268 
00269   axis[0] = 1.; axis[1] = 0.; axis[2] = 0.;
00270   axis_to_quat (axis, 0, ball->quart1);
00271   axis_to_quat (axis, 0, ball->quart2);
00272 
00273   g_signal_connect (ball->view_2d, "toggled",
00274                     G_CALLBACK (view_2d_toggled_cb), ball);
00275 
00276   g_signal_connect (ball->drawing_area, "expose-event",
00277                     G_CALLBACK (ghid_trackball_expose), ball);
00278   g_signal_connect (ball->drawing_area, "button-press-event",
00279                     G_CALLBACK (button_press_cb), ball);
00280   g_signal_connect (ball->drawing_area, "button-release-event",
00281                     G_CALLBACK (button_release_cb), ball);
00282   g_signal_connect (ball->drawing_area, "motion-notify-event",
00283                     G_CALLBACK (motion_notify_cb), ball);
00284 
00285   gtk_widget_add_events (ball->drawing_area, GDK_BUTTON_PRESS_MASK   |
00286                                              GDK_BUTTON_RELEASE_MASK |
00287                                              GDK_POINTER_MOTION_MASK |
00288                                              GDK_POINTER_MOTION_HINT_MASK);
00289 
00290   return G_OBJECT (ball);
00291 }
00292 
00293 
00294 
00303 static void
00304 ghid_trackball_finalize (GObject * object)
00305 {
00306   G_OBJECT_CLASS (ghid_trackball_parent_class)->finalize (object);
00307 }
00308 
00309 
00322 static void
00323 ghid_trackball_set_property (GObject * object, guint property_id,
00324                                   const GValue * value, GParamSpec * pspec)
00325 {
00326   switch (property_id)
00327     {
00328     default:
00329       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
00330     }
00331 }
00332 
00333 
00346 static void
00347 ghid_trackball_get_property (GObject * object, guint property_id,
00348                                   GValue * value, GParamSpec * pspec)
00349 {
00350   switch (property_id)
00351     {
00352     default:
00353       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
00354     }
00355 
00356 }
00357 
00358 
00367 static void
00368 ghid_trackball_class_init (GhidTrackballClass * klass)
00369 {
00370   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
00371 
00372   gobject_class->constructor  = ghid_trackball_constructor;
00373   gobject_class->finalize     = ghid_trackball_finalize;
00374   gobject_class->set_property = ghid_trackball_set_property;
00375   gobject_class->get_property = ghid_trackball_get_property;
00376 
00377   ghid_trackball_parent_class = g_type_class_peek_parent (klass);
00378 
00379   ghid_trackball_signals[ROTATION_CHANGED] =
00380     g_signal_new ("rotation-changed",
00381                   G_OBJECT_CLASS_TYPE( gobject_class ),
00382                   G_SIGNAL_RUN_FIRST,     /*signal_flags */
00383                   G_STRUCT_OFFSET( GhidTrackballClass, rotation_changed ),
00384                   NULL, /* accumulator */
00385                   NULL, /* accu_data */
00386                   g_cclosure_marshal_VOID__POINTER,
00387                   G_TYPE_NONE,
00388                   1,    /* n_params */
00389                   G_TYPE_POINTER
00390                  );
00391 
00392   ghid_trackball_signals[VIEW_2D_CHANGED] =
00393     g_signal_new ("view-2d-changed",
00394                   G_OBJECT_CLASS_TYPE( gobject_class ),
00395                   G_SIGNAL_RUN_FIRST,     /*signal_flags */
00396                   G_STRUCT_OFFSET( GhidTrackballClass, view_2d_changed ),
00397                   NULL, /* accumulator */
00398                   NULL, /* accu_data */
00399                   g_cclosure_marshal_VOID__BOOLEAN,
00400                   G_TYPE_NONE,
00401                   1,    /* n_params */
00402                   G_TYPE_BOOLEAN
00403                  );
00404 }
00405 
00406 
00416 GType
00417 ghid_trackball_get_type ()
00418 {
00419   static GType ghid_trackball_type = 0;
00420 
00421   if (!ghid_trackball_type)
00422     {
00423       static const GTypeInfo ghid_trackball_info = {
00424         sizeof (GhidTrackballClass),
00425         NULL,                   /* base_init */
00426         NULL,                   /* base_finalize */
00427         (GClassInitFunc) ghid_trackball_class_init,
00428         NULL,                   /* class_finalize */
00429         NULL,                   /* class_data */
00430         sizeof (GhidTrackball),
00431         0,                      /* n_preallocs */
00432         NULL,                   /* instance_init */
00433       };
00434 
00435       ghid_trackball_type =
00436         g_type_register_static (GTK_TYPE_VBOX, "GhidTrackball",
00437                                 &ghid_trackball_info, 0);
00438     }
00439 
00440   return ghid_trackball_type;
00441 }
00442 
00443 
00451 GtkWidget *
00452 ghid_trackball_new (void)
00453 {
00454   GhidTrackball *ball;
00455 
00456   ball = g_object_new (GHID_TYPE_TRACKBALL, NULL);
00457 
00458   return GTK_WIDGET (ball);
00459 }