Logo Search packages:      
Sourcecode: vertex version File versions

viewcb.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include <GL/gl.h>
/* #include <GL/glu.h> */

#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <gtkgl/gtkglarea.h>

#include "msglist.h"
#include "guiutils.h"
#include "cdialog.h"
#include "fb.h"

#include "editor.h"
#include "editorselect.h"
#include "editorcb.h"
#include "editoridialog.h"
#include "editoridialogcb.h"

#include "view.h"
#include "viewbg.h"
#include "viewcb.h"
#include "viewdraw.h"

#include "editor.h"

#include "vmacfg.h"
#include "vmacfglist.h"
#include "vma.h"
#include "vmapixmaps.h"

#include "messages.h"

#ifdef MEMWATCH
# include "memwatch.h"
#endif


static void ViewIDialogCancelCB(void *idialog, void *data);

void View2DRecordPositionsCB(vma_view2d_struct *v);
void View3DRecordPositionsCB(vma_view3d_struct *v);

gint View2DViewEventCB(GtkWidget *widget, gpointer event, gpointer data);
gint View3DViewEventCB(GtkWidget *widget, gpointer event, gpointer data);

void View2DTextEnterCB(GtkWidget *widget, gpointer data);
void View3DTextEnterCB(GtkWidget *widget, gpointer data);

void View2DSpinChangeCB(GtkWidget *widget, gpointer data);
void View3DSpinChangeCB(GtkWidget *widget, gpointer data);

gint View2DEnabledDACB(GtkWidget *widget, GdkEvent *event, gpointer data);
gint View3DEnabledDACB(GtkWidget *widget, GdkEvent *event, gpointer data);

void View2DEnabledCB(GtkWidget *widget, gpointer data);
void View3DEnabledCB(GtkWidget *widget, gpointer data);

static void ViewMenuMapPositionCB(
        GtkMenu *menu, gint *x, gint *y, gpointer data
);
gint View2DMenuMapCB(GtkWidget *widget, gpointer event, gpointer data);
gint View3DMenuMapCB(GtkWidget *widget, gpointer event, gpointer data);

void View2DButtonMenuMapCB(GtkButton *button, gpointer data);
void View2DMenuHideCB(GtkWidget *widget, gpointer data);
void View3DButtonMenuMapCB(GtkButton *button, gpointer data);
void View3DMenuHideCB(GtkWidget *widget, gpointer data);

void View2DUndoCB(GtkWidget *widget, gpointer data);
void View2DRedoCB(GtkWidget *widget, gpointer data);
void View2DJumpToVertexCB(GtkWidget *widget, gpointer data);
void View2DSetToCursorCB(GtkWidget *widget, gpointer data);
static void View2DSetBGImageBrowseCB(void *widget, void *data);
static void View2DSetBGImageEntryChangedCB(GtkWidget *widget, gpointer data);
void View2DSetBGImageCB(GtkWidget *widget, gpointer data);
static void View2DBGImageSetCB(void *idialog, void *data);
void View2DClearBGImageCB(GtkWidget *widget, gpointer data);

void View2DTranslateCB(vma_view2d_struct *v);
void View2DZoomCB(vma_view2d_struct *v);
void View2DGridCB(vma_view2d_struct *v);
void View2DSelectRectangleCB(vma_view2d_struct *v, gbool report_event);
void View2DSetCursorCB(
      vma_view2d_struct *v, gbool report_event,
      gbool is_initial, gbool is_final
);
void View2DSetVertexCB(
      vma_view2d_struct *v,
      gint vertex_type, /* 0 = Vertex, 1 = Normal, 2 = Texcoord. */
      gbool report_event,
      gbool is_initial, gbool is_final
);
void View2DTranslateToW(vma_view2d_struct *v, gint wi, gint wj);
void View2DZoomToW(vma_view2d_struct *v, gint wi, gint wj);

void View3DMoveTurnCB(vma_view3d_struct *v);
void View3DMoveStrafeCB(vma_view3d_struct *v);
void View3DTurnCB(vma_view3d_struct *v);
void View3DMoveVerticalCB(vma_view3d_struct *v);
void View3DGridCB(vma_view3d_struct *v);
void View3DMoveRateCB(vma_view3d_struct *v);

void View3DMoveTurnToW(vma_view3d_struct *v, gint wi, gint wj);
void View3DMoveStrafeToW(vma_view3d_struct *v, gint wi, gint wj);
void View3DTurnToW(vma_view3d_struct *v, gint wi, gint wj);
void View3DMoveVerticalToW(vma_view3d_struct *v, gint wi, gint wj);

void View2DMaximizeCB(GtkWidget *widget, gpointer data);
void View2DRestoreCB(GtkWidget *widget, gpointer data);

void View3DMaximizeCB(GtkWidget *widget, gpointer data);
void View3DRestoreCB(GtkWidget *widget, gpointer data);

void View3DUndoCB(GtkWidget *widget, gpointer data);
void View3DRedoCB(GtkWidget *widget, gpointer data);
void View3DRenderActualToggleCB(GtkWidget *widget, gpointer data);
void View3DCullToggleCB(GtkWidget *widget, gpointer data);
void View3DTranslationsToggleCB(GtkWidget *widget, gpointer data);
void View3DEnableAlphaChannelToggleCB(GtkWidget *widget, gpointer data);
void View3DSetBGImageCB(GtkWidget *widget, gpointer data);
void View3DClearBGImageCB(GtkWidget *widget, gpointer data);

void View3DCameraPropertiesMapCB(GtkWidget *widget, gpointer data);
void View3DCameraPropertiesOKCB(void *idialog, void *data);
void View3DCameraPropertiesApplyCB(void *idialog, void *data);
void View3DCameraPropertiesCancelCB(void *idialog, void *data);



#ifndef GDK_BUTTON1
# define GDK_BUTTON1    1
#endif
#ifndef GDK_BUTTON2
# define GDK_BUTTON2    2
#endif
#ifndef GDK_BUTTON3
# define GDK_BUTTON3    3
#endif
#ifndef GDK_BUTTON4
# define GDK_BUTTON4    4
#endif
#ifndef GDK_BUTTON5
# define GDK_BUTTON5    5
#endif


#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))

#define DEGTORAD(d)     ((d) * PI / 180.0)
#define RADTODEG(r)     ((r) * 180.0 / PI)


/*
 *    All purpose input dialog cancel callback.
 */
static void ViewIDialogCancelCB(void *idialog, void *data)
{
      ma_editor_struct *editor;
      ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)idialog;
        if(d == NULL)
            return;

        editor = (ma_editor_struct *)d->editor_ptr;
        if(editor == NULL)
            return;

        /* Reset input dialog and unmap it. */
        EditorIDialogReset(editor, d, TRUE);
}

/*
 *    Records 2d view positions to global configuration options
 *    list.
 */
void View2DRecordPositionsCB(vma_view2d_struct *v)
{
      /* Do nothing. */
      return;
}

/*
 *    Records 2d view positions to global configuration options
 *    list.
 */
void View3DRecordPositionsCB(vma_view3d_struct *v)
{
        vma_cfg_item_struct *opt = option;
        u_int8_t ui8;
      u_int32_t ui32;
      double d;


        if(v == NULL)
            return;

      if(!v->initialized)
          return;

        /* Begin recording view values. */

      /* View position decimals. */
      ui32 = v->position_decimals;
      VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_DECIMALS_POSITION,
            &ui32, 0
        );
      /* View angle decimals. */
        ui32 = v->angle_decimals;
        VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_DECIMALS_ANGLE,
            &ui32, 0
        );

      /* Render state. */
        ui8 = v->render_state;
        VMACFGItemListMatchSetValue(
          opt, VMA_CFG_PARM_VIEW_RENDER,
          &ui8, 0
        );
        /* Cull state. */
        ui8 = v->cull_state;
        VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_CULL_FACES,
            &ui8, 0
        );
        /* Cull direction. */
        ui8 = v->cull_direction;
        VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_CULL_DIRECTION,
            &ui8, 0
        );
        /* Translations and rotations. */
        ui8 = v->translations_state;
        VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_TRANSLATIONS_STATE,
            &ui8, 0
        );

      /* Alpha channel. */
        ui8 = v->enable_alpha_channel;
        VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_ENABLE_ALPHA_CHANNEL,
            &ui8, 0
        );

      /* Grid spacing. */
/*
      d = v->grid_spacing;
        VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_GRID_SPACING,
            &d, 0
        );
 */
      /* Move rate. */
/*
      d = v->move_rate;
      VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_MOVE_RATE,
            &d, 0
        );
 */

      /* Camera clip near. */
      d = v->cam_clip_near;
      VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_CAM_CLIP_NEAR,
            &d, 0
        );
        /* Camera clip far. */
        d = v->cam_clip_far;
        VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_CAM_CLIP_FAR,
            &d, 0
        );
        /* Camera FOV. */
        d = v->cam_fov;
        VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_VIEW_CAM_FOV,
            &d, 0
        );


      return;
}


/*
 *    Event callback for 2d view. The given event pointer's
 *    type will be checked by the function, so an anonymous
 *    pointer to any valid gdk event structure is valid.
 *
 *    The given data pointer is assumed to be a 2d view.
 */
gint View2DViewEventCB(
        GtkWidget *widget, gpointer event, gpointer data  
)
{
      gbool status = FALSE;
      GdkCursor *cur;
      GtkWidget *w;
      gint etype, x, y;
      GdkModifierType mask;

      vma_view2d_struct *v = (vma_view2d_struct *)data;

      GdkEventConfigure *configure;
      GdkEventKey *key;
      guint state, keyval;
      gbool keystate;
      GdkEventCrossing *crossing;
      GdkEventButton *button;
        GdkEventMotion *motion;
 

        if((widget == NULL) || (event == NULL) || (v == NULL))
            return(status);

      /* Get pointer to view widget, event widget must match it. */
      w = v->view;
      if(w != widget)
          return(status);

      /* Handle by event type. */
      etype = (*(gint *)event);
      switch(etype)
      {
        case GDK_EXPOSE:
          View2DDraw(v, &v->palette, 0, 0, TRUE);
            status = TRUE;
          break;

          case GDK_CONFIGURE:
            configure = (GdkEventConfigure *)event;
          if(!v->view_realized)
            v->view_realized = TRUE;
          if(!View2DGLEnableContext(v))
                glViewport(
                    0, 0,
                    configure->width, configure->height
                );
            status = TRUE;
            break;

        case GDK_KEY_PRESS: case GDK_KEY_RELEASE:
          key = (GdkEventKey *)event;
          state = key->state;
          keyval = key->keyval;
          keystate = (((*(gint *)event) == GDK_KEY_PRESS) ? TRUE : FALSE);
          /* Begin handling by key value. */
          /* Escape. */
          if(keyval == GDK_Escape)
          {
            if(!keystate)
            {
                /* Ungrab pointer. */
                gdk_flush();
                gdk_pointer_ungrab(key->time);
                gdk_flush();
                while(gdk_pointer_is_grabbed())
                  { gdk_pointer_ungrab(GDK_CURRENT_TIME); gdk_flush(); }
            }
            status = TRUE;
          }
          else if((keyval == GDK_Alt_L) ||
                    (keyval == GDK_Alt_R)
          )
          {
            v->drag_state = (keystate ?
                VMA_VIEW2D_DRAG_ZOOM : VMA_VIEW2D_DRAG_NONE
            );
            status = TRUE;
          }
            else if((keyval == GDK_Control_L) ||
                    (keyval == GDK_Control_R)
            )
          {
                v->drag_state = (keystate ?
                    VMA_VIEW2D_DRAG_TRANSLATE : VMA_VIEW2D_DRAG_NONE
                );
                status = TRUE;
            }
            else if((keyval == GDK_Shift_L) ||
                    (keyval == GDK_Shift_R)
            )
          {
                v->drag_state = (keystate ?
                    VMA_VIEW2D_DRAG_SELECT_RECTANGLE : VMA_VIEW2D_DRAG_NONE
                );
                status = TRUE;
            }
          /* Change cursor depending on drag state. */
          switch(v->drag_state)
          {
            case VMA_VIEW2D_DRAG_SELECT_RECTANGLE:
            cur = v->select_cur;
            break;
            case VMA_VIEW2D_DRAG_TRANSLATE:
            cur = v->translate_cur;
            break;
            case VMA_VIEW2D_DRAG_ZOOM:
            cur = v->zoom_cur;
            break;
            default:    /* No drag. */
            cur = NULL; /* Set default cursor. */
            break;
          }
          if(!GTK_WIDGET_NO_WINDOW(w) && status)
            gdk_window_set_cursor(w->window, cur);
          break;

        case GDK_ENTER_NOTIFY:
          crossing = (GdkEventCrossing *)event;
          if(!crossing->send_event)
          {
            /* Check if no keyboard modifiers are held when pointer
             * re-enters the view widget.
             */
            if(!(crossing->state & (GDK_SHIFT_MASK |
                GDK_CONTROL_MASK | GDK_MOD1_MASK))
            )
            {
                /* If there was a prior drag state then the drag state
                 * needs to be reset to VMA_VIEW2D_DRAG_NONE.
                 */
                if(v->drag_state != VMA_VIEW2D_DRAG_NONE)
                {
                  v->drag_state = VMA_VIEW2D_DRAG_NONE;
                  if(!GTK_WIDGET_NO_WINDOW(w))
                      gdk_window_set_cursor(w->window, NULL);
                }
            }
            status = TRUE;
          }
          break;

          case GDK_BUTTON_PRESS:
          button = (GdkEventButton *)event;
          switch(button->button)
          {
            case GDK_BUTTON1:
            v->flags |= VMA_VIEW_FLAG_BUTTON1;
                status = TRUE;
            break;

              case GDK_BUTTON2:
            if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
            {
                    /* Emulating as 2 button pointer, so GDK_BUTTON2
                     * is ignored.
                     */
            }
            else
            {

                v->flags |= VMA_VIEW_FLAG_BUTTON2;
                status = TRUE;
            }
                break;

              case GDK_BUTTON3:
            if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
            {
                /* Emulating as 2 button pointer, so GDK_BUTTON3
                 * is mapped to button 2.
                 */
                v->flags |= VMA_VIEW_FLAG_BUTTON2;
                status = TRUE;
            }
            else
            {
                View2DMenuMapCB(widget, event, data);
                return(TRUE); /* Return now. */
            }
                break;

              case GDK_BUTTON4:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON) 
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON4
                     * is ignored.
                     */
                }
                else
                {
                    v->flags |= VMA_VIEW_FLAG_BUTTON4;
                    status = TRUE;
                }
                break;

              case GDK_BUTTON5:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON5
                     * is ignored.
                     */
                }
                else   
                {
                    v->flags |= VMA_VIEW_FLAG_BUTTON5;
                    status = TRUE;
                }
                break;
          }
            /* Grab pointer. */
            if(!GTK_WIDGET_NO_WINDOW(w))
            {
                gdk_pointer_grab( 
                    w->window,
                    FALSE,
                    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                        GDK_POINTER_MOTION_MASK,
                    /* w->window, */ NULL,
                    GDK_NONE,
                    button->time
                );
                gdk_flush();  
            }
          /* If not dragging, then start drag set vertex. */
          if((v->drag_state == VMA_VIEW2D_DRAG_NONE) &&
               (v->flags & (VMA_VIEW_FLAG_BUTTON1 | VMA_VIEW_FLAG_BUTTON2))
          )
          {
            /* Button1? */
            if(v->flags & VMA_VIEW_FLAG_BUTTON1)
                {
                    v->drag_state = VMA_VIEW2D_DRAG_SET_CURSOR;
                    /* Need to update last positions now. */
                    v->w_last_i = button->x;
                    v->w_last_j = button->y;
                    v->w_di = 0;                /* No movement. */
                    v->w_dj = 0;
                    View2DSetCursorCB(v, TRUE, TRUE, FALSE);
                }
            /* Button2? (could only be 1 or 2) */
            else
            {
                /* Check if shift (select rect) is held down, in which
                 * case we set normal.
                 */
                if(v->drag_state == VMA_VIEW2D_DRAG_SELECT_RECTANGLE)
                {
/* Note that this won't work since we checked if drag state was
 * VMA_VIEW2D_DRAG_NONE.
 */
                        v->drag_state = VMA_VIEW2D_DRAG_SET_NORMAL;
                        /* Need to update last positions now. */  
                        v->w_last_i = button->x;
                        v->w_last_j = button->y;
                        v->w_di = 0;            /* No movement. */
                        v->w_dj = 0;
                        View2DSetVertexCB(v, 1, TRUE, TRUE, FALSE);
                }
                /* Otherwise set vertex. */
                else
                {
                  v->drag_state = VMA_VIEW2D_DRAG_SET_VERTEX;
                  /* Need to update last positions now. */
                  v->w_last_i = button->x;
                  v->w_last_j = button->y;
                  v->w_di = 0;            /* No movement. */
                  v->w_dj = 0;
                  View2DSetVertexCB(v, 0, TRUE, TRUE, FALSE);
                }
            }
          }
          /* Modifier specify drag select rectangular? */
          else if(v->drag_state == VMA_VIEW2D_DRAG_SELECT_RECTANGLE)
          {
            v->w_sel_rect_i0 = button->x;
            v->w_sel_rect_j0 = button->y;
                v->w_sel_rect_i1 = button->x;
                v->w_sel_rect_j1 = button->y;
                View2DSelectRectangleCB(v, FALSE);
          }
          /* Record last button press position. */
            v->w_last_i = button->x;
            v->w_last_j = button->y;
          break;

          case GDK_BUTTON_RELEASE:
            button = (GdkEventButton *)event;
            switch(button->button)
            {
              case GDK_BUTTON1:
                v->flags &= ~VMA_VIEW_FLAG_BUTTON1;
            status = TRUE;
                break;
              
              case GDK_BUTTON2:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON2
                     * is ignored.
                     */
                }
                else
                {
                v->flags &= ~VMA_VIEW_FLAG_BUTTON2;
                status = TRUE;
            }
            break;

              case GDK_BUTTON3:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON3
                     * is mapped to button 2.
                     */
                    v->flags &= ~VMA_VIEW_FLAG_BUTTON2;
                    status = TRUE;
            }
            else
            {
                return(FALSE);
                }
                break;

              case GDK_BUTTON4:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON4
                     * is ignored.
                     */
                }
                else 
                {
                    v->flags &= ~VMA_VIEW_FLAG_BUTTON4;
                    status = TRUE;
                }
                break;

              case GDK_BUTTON5:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON5
                     * is ignored.
                     */
                }
                else
                {
                    v->flags &= ~VMA_VIEW_FLAG_BUTTON5;
                    status = TRUE;
                }
                break;
            }
            /* Record last button release position. */
            v->w_last_i = button->x;
            v->w_last_j = button->y;
            /* Ungrab pointer. */
          gdk_flush();
            gdk_pointer_ungrab(button->time);
          gdk_flush();
            while(gdk_pointer_is_grabbed())
                { gdk_pointer_ungrab(GDK_CURRENT_TIME); gdk_flush(); }
          /* Stop drag set cursor (since no key modifiers control that)? */
            if(v->drag_state == VMA_VIEW2D_DRAG_SET_CURSOR)
            {
                v->drag_state = VMA_VIEW2D_DRAG_NONE;
                v->w_di = 0;            /* No movement. */
                v->w_dj = 0;
                View2DSetCursorCB(v, TRUE, FALSE, TRUE);
          }
          /* Stop drag set vertex (since no key modifiers control that)? */
          else if(v->drag_state == VMA_VIEW2D_DRAG_SET_VERTEX)
          {
            v->drag_state = VMA_VIEW2D_DRAG_NONE;
                v->w_di = 0;            /* No movement. */
                v->w_dj = 0;
            View2DSetVertexCB(v, 0, TRUE, FALSE, TRUE);
          }
            /* Stop drag set normal. */
            else if(v->drag_state == VMA_VIEW2D_DRAG_SET_NORMAL)
            {
                v->drag_state = VMA_VIEW2D_DRAG_NONE;
                v->w_di = 0;            /* No movement. */
                v->w_dj = 0;
                View2DSetVertexCB(v, 1, TRUE, FALSE, TRUE);
            }
            /* Stop drag set texcoord. */
            else if(v->drag_state == VMA_VIEW2D_DRAG_SET_TEXCOORD)
            {
                v->drag_state = VMA_VIEW2D_DRAG_NONE;
                v->w_di = 0;            /* No movement. */
                v->w_dj = 0;
                View2DSetVertexCB(v, 2, TRUE, FALSE, TRUE);
            }
          /* Report rectangular select if drag is that type. */
          else if(v->drag_state == VMA_VIEW2D_DRAG_SELECT_RECTANGLE)
            {
            /* Do not set back drag state to none. */
                v->w_sel_rect_i1 = button->x;
                v->w_sel_rect_j1 = button->y;
                View2DSelectRectangleCB(v, TRUE);
            }
            break;

        case GDK_MOTION_NOTIFY:
          motion = (GdkEventMotion *)event;
          if(motion->is_hint)
          {
            if((v->drag_state != VMA_VIEW2D_DRAG_NONE) &&
                   (v->flags & (VMA_VIEW_FLAG_BUTTON1 | VMA_VIEW_FLAG_BUTTON2))
            )
                gdk_window_get_pointer(
                  motion->window, &x, &y, &mask
                );
          }
          else
          {
            x = motion->x;
            y = motion->y;
            mask = motion->state;
          }
          /* Button1 or Button2 pressed? */
          if(v->flags & (VMA_VIEW_FLAG_BUTTON1 | VMA_VIEW_FLAG_BUTTON2))
          {
              switch(v->drag_state)
              {
              case VMA_VIEW2D_DRAG_TRANSLATE:
                View2DTranslateToW(v, x, y);
                View2DTranslateCB(v);
                    status = TRUE;
                break;

              case VMA_VIEW2D_DRAG_ZOOM:
                View2DZoomToW(v, x, y);
                View2DZoomCB(v);
                    status = TRUE;
                break;

              case VMA_VIEW2D_DRAG_SELECT_RECTANGLE:
                v->w_sel_rect_i1 = motion->x;
                    v->w_sel_rect_j1 = motion->y;
                View2DSelectRectangleCB(v, FALSE);
                    status = TRUE;
                    break;

                  case VMA_VIEW2D_DRAG_SET_CURSOR:
                    /* Need to update last pointer position now for
                     * View2DSetCursorCB().
                     */
                    v->w_di = motion->x - v->w_last_i;
                    v->w_dj = motion->y - v->w_last_j;
                    v->w_last_i = motion->x;
                    v->w_last_j = motion->y;
                    View2DSetCursorCB(v, TRUE, FALSE, FALSE);
                    status = TRUE;
                break;

              case VMA_VIEW2D_DRAG_SET_VERTEX:
                /* Need to update last pointer position now for
                 * View2DSetVertexCB().
                 */
                v->w_di = motion->x - v->w_last_i;
                v->w_dj = motion->y - v->w_last_j;
                v->w_last_i = motion->x;
                v->w_last_j = motion->y;
                    View2DSetVertexCB(v, 0, TRUE, FALSE, FALSE);
                    status = TRUE;
                break;

                  case VMA_VIEW2D_DRAG_SET_NORMAL:
                    /* Need to update last pointer position now for
                     * View2DSetVertexCB().
                     */
                    v->w_di = motion->x - v->w_last_i;
                    v->w_dj = motion->y - v->w_last_j;
                    v->w_last_i = motion->x;
                    v->w_last_j = motion->y;
                    View2DSetVertexCB(v, 1, TRUE, FALSE, FALSE);
                    status = TRUE;
                    break;

                  case VMA_VIEW2D_DRAG_SET_TEXCOORD:
                    /* Need to update last pointer position now for
                     * View2DSetVertexCB().
                     */
                    v->w_di = motion->x - v->w_last_i;
                    v->w_dj = motion->y - v->w_last_j;
                    v->w_last_i = motion->x;
                    v->w_last_j = motion->y;
                    View2DSetVertexCB(v, 2, TRUE, FALSE, FALSE);
                    status = TRUE;
                    break;
            }
          }
          /* Record last pointer position. */
          v->w_last_i = motion->x;
          v->w_last_j = motion->y;
          break;

        default:
          /* Unknown event type but for this widget, can't
           * handle it though so need to return FALSE since
           * we did not handle the event.
           */
          break;
      }


      return(status);
}

/*
 *      Event callback for 3d view. The given event pointer's
 *      type will be checked by the function, so an anonymous
 *      pointer to any valid gdk event structure is valid.
 *
 *      The given data pointer is assumed to be a 3d view.
 */
gint View3DViewEventCB(
        GtkWidget *widget, gpointer event, gpointer data  
)
{
      gbool status = FALSE;
      GdkCursor *cur;
        GtkWidget *w;
      gint etype, x, y;
        GdkModifierType mask;

        vma_view3d_struct *v = (vma_view3d_struct *)data;

      GdkEventConfigure *configure;
        GdkEventKey *key;
      guint keyval;
      gbool keystate;
      GdkEventCrossing *crossing;
        GdkEventButton *button;
        GdkEventMotion *motion;


      if((widget == NULL) ||
           (event == NULL) ||
           (v == NULL)
      )
          return(status);

        /* Get pointer to view widget, event widget must match it. */
        w = v->view;
        if(w != widget)
            return(status);
  
        /* Handle by event type. */
      etype = (*(gint *)event);
        switch(etype)
        {
          case GDK_EXPOSE:
            View3DDraw(v, &v->palette, 0, 0, TRUE);
            status = TRUE;
            break;

        case GDK_CONFIGURE:
          configure = (GdkEventConfigure *)event;
            if(!v->view_realized)
                v->view_realized = TRUE;
          if(!View3DGLEnableContext(v))
            glViewport(
                0, 0,
                configure->width, configure->height
            );
          status = TRUE;
          break;

          case GDK_KEY_PRESS: case GDK_KEY_RELEASE:
            key = event;
          keyval = key->keyval;
          keystate = ((etype == GDK_KEY_PRESS) ? TRUE : FALSE);
            if((keyval == GDK_Alt_L) || (keyval == GDK_Alt_R))
          {
                v->drag_state = (keystate ?
                    VMA_VIEW3D_DRAG_MOVE_STRAFE : VMA_VIEW3D_DRAG_NONE
                );
            status = TRUE;
          }
            else if((keyval == GDK_Control_L) || (keyval == GDK_Control_R))
          {
                v->drag_state = (keystate ?
                    VMA_VIEW3D_DRAG_MOVE_TURN : VMA_VIEW3D_DRAG_NONE
                );
                status = TRUE;
            }
            else if((keyval == GDK_Shift_L) || (keyval == GDK_Shift_R))
          {
                v->drag_state = (keystate ?
                    VMA_VIEW3D_DRAG_VERTICAL : VMA_VIEW3D_DRAG_NONE
                );
                status = TRUE;
            }
            /* Change cursor depending on drag state. */
            switch(v->drag_state)
            {
              case VMA_VIEW3D_DRAG_MOVE_STRAFE:
                cur = v->move_strafe_cur;
                break;
              case VMA_VIEW3D_DRAG_MOVE_TURN:
                cur = v->move_rotate_cur;
                break;
              case VMA_VIEW3D_DRAG_VERTICAL:
                cur = v->move_height_cur;
                break;
              case VMA_VIEW3D_DRAG_TURN:
                cur = v->rotate_cur;
                break;
              default:
                cur = NULL;
                break;
            }
            if(!GTK_WIDGET_NO_WINDOW(w) && status)
                gdk_window_set_cursor(w->window, cur);
            break;

          case GDK_ENTER_NOTIFY:
            crossing = (GdkEventCrossing *)event;
            if(!crossing->send_event)
            {
                /* Check if no keyboard modifiers are held when pointer
                 * re-enters the view widget.
                 */
                if(!(crossing->state & (GDK_SHIFT_MASK |
                    GDK_CONTROL_MASK | GDK_MOD1_MASK))
                )
                {
                    /* If there was a prior drag state then the drag state
                     * needs to be reset to VMA_VIEW3D_DRAG_NONE.
                     */
                    if(v->drag_state != VMA_VIEW3D_DRAG_NONE)
                    {
                        v->drag_state = VMA_VIEW3D_DRAG_NONE;
                        if(!GTK_WIDGET_NO_WINDOW(w))
                            gdk_window_set_cursor(w->window, NULL);
                    }
                }
                status = TRUE;
            }
            break;

          case GDK_BUTTON_PRESS:
            button = (GdkEventButton *)event;
            switch(button->button)
            {
              case GDK_BUTTON1:
                v->flags |= VMA_VIEW_FLAG_BUTTON1;
                status = TRUE;
                break;
                
              case GDK_BUTTON2:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON2
                     * is ignored.
                     */
                }
                else
                {
                    v->flags |= VMA_VIEW_FLAG_BUTTON2;
                    status = TRUE;
                }
                break;

              case GDK_BUTTON3:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON3
                     * is mapped to GDK_BUTTON2.
                     */
                    v->flags |= VMA_VIEW_FLAG_BUTTON2;
                    status = TRUE;
                }
            else
            {
                View3DMenuMapCB(widget, event, data);
                return(TRUE); /* Return now. */
            }
                break;

              case GDK_BUTTON4:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON4
                     * is ignored.
                     */
                }
                else
                {
                    v->flags |= VMA_VIEW_FLAG_BUTTON4;
                    status = TRUE;
                }
                break;

              case GDK_BUTTON5:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON5
                     * is ignored.
                     */
                }
                else
                {
                    v->flags |= VMA_VIEW_FLAG_BUTTON5;
                    status = TRUE;
                }
                break;
            }
            /* Grab pointer. */
            if(!GTK_WIDGET_NO_WINDOW(w))
            {
                gdk_pointer_grab(
                    w->window,
                    FALSE,
                    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                    GDK_POINTER_MOTION_MASK,
                    /* w->window, */ NULL,
                    GDK_NONE,   
                    button->time
                );
                gdk_flush();
            }
            /* If not dragging, then start drag set vertex. */
            if((v->drag_state == VMA_VIEW3D_DRAG_NONE) &&
               (v->flags & (VMA_VIEW_FLAG_BUTTON1 | VMA_VIEW_FLAG_BUTTON2))
            )
            {
                v->drag_state = VMA_VIEW3D_DRAG_TURN;

            /* Need to update cursor here. */
                cur = v->rotate_cur;
                if(!GTK_WIDGET_NO_WINDOW(w))
                    gdk_window_set_cursor(w->window, cur);
            }
            /* Record last button press position. */
            v->w_last_i = button->x;
            v->w_last_j = button->y;
            break;

          case GDK_BUTTON_RELEASE:
            button = (GdkEventButton *)event;
            switch(button->button)
            {
              case GDK_BUTTON1:
                v->flags &= ~VMA_VIEW_FLAG_BUTTON1;
                status = TRUE;
                break;

              case GDK_BUTTON2:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON2
                     * is ignored.
                     */
            }
            else
            {
                v->flags &= ~VMA_VIEW_FLAG_BUTTON2;
                status = TRUE;
            }
                break;

            case GDK_BUTTON3:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON3
                     * is mapped to GDK_BUTTON2.
                     */
                    v->flags &= ~VMA_VIEW_FLAG_BUTTON2;
                    status = TRUE;
                }
            else
            {
                return(FALSE);
            }
            break;

              case GDK_BUTTON4:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON4
                     * is ignored.
                     */
                }
                else
                {
                    v->flags &= ~VMA_VIEW_FLAG_BUTTON4;
                    status = TRUE;
                }
                break;

              case GDK_BUTTON5:
                if(v->flags & VMA_VIEW_FLAG_POINTER_EMULATE_2BUTTON)
                {
                    /* Emulating as 2 button pointer, so GDK_BUTTON5
                     * is ignored.
                     */
                }
                else
                {
                    v->flags &= ~VMA_VIEW_FLAG_BUTTON5;
                    status = TRUE;
                }
                break;
            }
            /* Stop drag turn (since no key modifiers control that)? */
            if(v->drag_state == VMA_VIEW3D_DRAG_TURN)
            {
                v->drag_state = VMA_VIEW3D_DRAG_NONE;
                /* Need to update cursor here. */
                cur = NULL;
                if(!GTK_WIDGET_NO_WINDOW(w))
                    gdk_window_set_cursor(w->window, cur);
            }
            /* Ungrab pointer. */
          gdk_flush();
            gdk_pointer_ungrab(button->time);
          gdk_flush();
            while(gdk_pointer_is_grabbed())
            { gdk_pointer_ungrab(GDK_CURRENT_TIME); gdk_flush(); }
            /* Record last button release position. */
            v->w_last_i = button->x;
            v->w_last_j = button->y;
            break;

          case GDK_MOTION_NOTIFY:
            motion = (GdkEventMotion *)event;
            if(motion->is_hint)
            {
                if((v->drag_state != VMA_VIEW3D_DRAG_NONE) &&
                   ((v->flags & VMA_VIEW_FLAG_BUTTON1) ||
                    (v->flags & VMA_VIEW_FLAG_BUTTON2)
                   )
                )
                    gdk_window_get_pointer(
                        motion->window, &x, &y, &mask
                    );
            }
            else
            {
                x = motion->x;
                y = motion->y;
                mask = motion->state;
            }
            /* Button1 or Button2 pressed? */
            if(v->flags & (VMA_VIEW_FLAG_BUTTON1 | VMA_VIEW_FLAG_BUTTON2))
            {    
                switch(v->drag_state)
                {
                  case VMA_VIEW3D_DRAG_MOVE_TURN:
                    View3DMoveTurnToW(v, x, y);
                    View3DMoveTurnCB(v);
                    status = TRUE;
                    break;

              case VMA_VIEW3D_DRAG_MOVE_STRAFE:
                    View3DMoveStrafeToW(v, x, y);
                    View3DMoveStrafeCB(v);
                    status = TRUE;
                break;

                  case VMA_VIEW3D_DRAG_TURN:
                    View3DTurnToW(v, x, y);
                    View3DTurnCB(v);
                    status = TRUE;
                    break;

                  case VMA_VIEW3D_DRAG_VERTICAL:
                    View3DMoveVerticalToW(v, x, y);
                    View3DMoveVerticalCB(v);
                    status = TRUE;
                    break;
            }
          }
            /* Record last pointer position. */
            v->w_last_i = motion->x;
            v->w_last_j = motion->y;
          break;

          default:
            /* Unknown event type but for this widget, can't
             * handle it though so need to return FALSE since
             * we did not handle the event.
             */
            break;
      }


        return(status);
}



/*
 *    View 2d text entry enter callback.
 *
 *    Special case if widget is NULL then all text entries on the
 *    2d view will be updated.
 */
void View2DTextEnterCB(
      GtkWidget *widget, gpointer data
)
{
        GtkWidget *w;
        gchar *text_ptr;
      vma_view2d_struct *v = (vma_view2d_struct *)data;
      if(v == NULL)
          return;

        /* Fetch values from text prompts. */
        w = v->text_i;
        if((w == widget) ||
           (widget == NULL)
      )
        {
            text_ptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(text_ptr != NULL)
                v->v_ti = ((v->flags & VMA_VIEW_FLAG_FLIP_I) ?
                -1 : 1) * atof(text_ptr);

          if(widget != NULL)
            View2DTranslateCB(v);
        }
        w = v->text_j;
        if((w == widget) ||
           (widget == NULL)
        )
        {
            text_ptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(text_ptr != NULL)
                v->v_tj = ((v->flags & VMA_VIEW_FLAG_FLIP_J) ?
                -1 : 1) * atof(text_ptr);

            if(widget != NULL)
            View2DTranslateCB(v);
        }

      /* If widget is NULL then we didn't know which text entry
       * it was and we would have updated all of them on the 2d view
       * and not redraw after each one we updated so redraw here.
       */
      if(widget == NULL)
      {
          View2DTranslateCB(v);
          View2DZoomCB(v);
      }

      return;
}

/*
 *      View 3d text entry enter callback.
 *
 *    Special case if widget is NULL then all text entries on the
 *    3d view will be updated.
 */
void View3DTextEnterCB(
      GtkWidget *widget, gpointer data
)
{
        GtkWidget *w;
        gchar *text_ptr;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
            return;

        /* Fetch values from text prompts. */
        w = v->text_x;
        if((w == widget) ||
           (widget == NULL)
        )
        {
            text_ptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(text_ptr != NULL)
                v->cam_x = atof(text_ptr);

            if(widget != NULL)
            View3DMoveStrafeCB(v);
        }

        w = v->text_y;
        if((w == widget) ||
           (widget == NULL)   
        )
        {
            text_ptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(text_ptr != NULL)
                v->cam_y = atof(text_ptr);

            if(widget != NULL)
                View3DMoveStrafeCB(v); 
        }

        w = v->text_z;
        if((w == widget) ||
           (widget == NULL)
        )
        {
            text_ptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(text_ptr != NULL)
                v->cam_z = atof(text_ptr);

            if(widget != NULL)
                View3DMoveStrafeCB(v); 
        }


        w = v->text_h;
        if((w == widget) ||
           (widget == NULL)
        )
        {
            text_ptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(text_ptr != NULL)
                v->cam_h = DEGTORAD(atof(text_ptr));

            if(widget != NULL)  
                View3DTurnCB(v);
        }

        w = v->text_p;
        if((w == widget) ||
           (widget == NULL)
        )
        {
            text_ptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(text_ptr != NULL)
                v->cam_p = DEGTORAD(atof(text_ptr));

            if(widget != NULL)
                View3DTurnCB(v);
        }

        w = v->text_b;
        if((w == widget) ||
           (widget == NULL)
        )
        {
            text_ptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(text_ptr != NULL)
                v->cam_b = DEGTORAD(atof(text_ptr));

            if(widget != NULL)  
                View3DTurnCB(v);
        }

        /* If widget is NULL then we didn't know which text entry
         * it was and we would have updated all of them on the 3d view
         * and not redraw after each one we updated so redraw here.
         */
        if(widget == NULL)
        {
            View3DMoveStrafeCB(v);
            View3DTurnCB(v);
        }

      return;
}


/*
 *    Callback whenever a spin widget changes its value on the
 *    2d view.
 */
void View2DSpinChangeCB(
      GtkWidget *widget, gpointer data
)
{
      GtkAdjustment *adj = (GtkAdjustment *)widget;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if((v == NULL) ||
           (adj == NULL)
        )
            return;

        if(adj == v->grid_spacing_adj)
        {
            v->grid_spacing = adj->value;
            View2DDraw(v, &v->palette, 0, 0, TRUE);
        }
        else if(adj == v->viewable_dim_adj)
        {
            v->viewable_dim = adj->value;
            View2DDraw(v, &v->palette, 0, 0, TRUE);
        }

      return;
}

/*
 *      Callback whenever a spin widget changes its value on the
 *      3d view.
 */
void View3DSpinChangeCB(
      GtkWidget *widget, gpointer data
)
{
      GtkAdjustment *adj = (GtkAdjustment *)widget;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((v == NULL) ||
           (adj == NULL)
      )
            return;

      if(adj == v->grid_spacing_adj)
      {
          v->grid_spacing = adj->value;
          View3DDraw(v, &v->palette, 0, 0, TRUE);
      }
        else if(adj == v->move_rate_adj)
        {
            v->move_rate = adj->value;
        }

      return;
}

/*
 *    Enabled/disabled drawing area event callback.
 */
gint View2DEnabledDACB(GtkWidget *widget, GdkEvent *event, gpointer data)
{
      gint i, etype, width, height;
      gint bb = 2;
      GdkGC *gc;
      GtkStyle *style_ptr;
      GdkPixmap *pixmap;
      GdkBitmap *mask;
      GdkWindow *window;
      GdkEventButton *button;
      GtkDrawingArea *da = (GtkDrawingArea *)widget;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
      if((da == NULL) || (v == NULL))
          return(FALSE);

      if(!v->initialized)
          return(FALSE);

      /* If event is NULL, then assume it is an expose event. */
      if(event == NULL)
          etype = GDK_EXPOSE;
      else
          etype = event->type;

      switch(etype)
      {
        case GDK_EXPOSE:
          width = widget->allocation.width;
          height = widget->allocation.height;
          window = widget->window;
          style_ptr = gtk_widget_get_style(widget);
          gc = ((style_ptr == NULL) ?
            NULL : style_ptr->bg_gc[GTK_STATE_NORMAL]
          );
          if(v->flags & VMA_VIEW_FLAG_ENABLED)
            i = VMA_PIXMAP_POWER_ON_20x20;
          else
            i = VMA_PIXMAP_POWER_OFF_20x20;
          VMAPixmapsListGetValues(
            &vma_pixmaps_list, i,
            &pixmap, &mask, NULL
          );
          if((window != NULL) && (pixmap != NULL) && (gc != NULL) &&
             (style_ptr != NULL)
          )
          {
            gdk_draw_rectangle(
                (GdkDrawable *)window,
                    gc,
                TRUE,
                0, 0, width, height
            );
            if(mask != NULL)
            {
                gdk_gc_set_clip_mask(gc, mask);
                gdk_gc_set_clip_origin(gc, -bb, -bb);
            }
                gdk_draw_pixmap(
                    (GdkDrawable *)window,
                    gc,
                    (GdkDrawable *)pixmap,
                    0, 0,           /* Src coord. */
                    -bb, -bb,       /* Tar coord. */
                    20, 20
                );
                if(mask != NULL)
                {
                    gdk_gc_set_clip_mask(gc, NULL);
                    gdk_gc_set_clip_origin(gc, 0, 0);
                }
          }
          break;

        case GDK_BUTTON_RELEASE:
          button = (GdkEventButton *)event;
          View2DEnabledCB(NULL, data);
          break;
      }

      return(TRUE);
}

/*
 *      Enabled/disabled drawing area event callback.
 */
gint View3DEnabledDACB(GtkWidget *widget, GdkEvent *event, gpointer data)
{
        gint i, etype, width, height;
        gint bb = 2;
        GdkGC *gc;
        GtkStyle *style_ptr;
        GdkPixmap *pixmap;
        GdkBitmap *mask;
        GdkWindow *window;
        GdkEventButton *button;
        GtkDrawingArea *da = (GtkDrawingArea *)widget;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((da == NULL) || (v == NULL))
            return(FALSE);

        /* If event is NULL, then assume it is an expose event. */
        if(event == NULL)
            etype = GDK_EXPOSE;
        else
            etype = event->type;

        switch(etype)
        {
          case GDK_EXPOSE:
            width = widget->allocation.width;
            height = widget->allocation.height;
            window = widget->window;
            style_ptr = gtk_widget_get_style(widget);
            gc = ((style_ptr == NULL) ?
                NULL : style_ptr->bg_gc[GTK_STATE_NORMAL]
            );
            if(v->flags & VMA_VIEW_FLAG_ENABLED)   
                i = VMA_PIXMAP_POWER_ON_20x20;
            else 
                i = VMA_PIXMAP_POWER_OFF_20x20;
            VMAPixmapsListGetValues(
                &vma_pixmaps_list, i,
                &pixmap, &mask, NULL
            );
            if((window != NULL) && (pixmap != NULL) && (gc != NULL) &&
               (style_ptr != NULL)
            )
            {
                gdk_draw_rectangle(
                    (GdkDrawable *)window,
                    gc,
                    TRUE,
                    0, 0, width, height
                );
                if(mask != NULL)
                {
                    gdk_gc_set_clip_mask(gc, mask);
                    gdk_gc_set_clip_origin(gc, -bb, -bb);
                }
                gdk_draw_pixmap(
                    (GdkDrawable *)window,
                    gc,
                    (GdkDrawable *)pixmap,
                    0, 0,               /* Src coord. */
                    -bb, -bb,           /* Tar coord. */
                    20, 20
                );
                if(mask != NULL)
                {
                    gdk_gc_set_clip_mask(gc, NULL);
                    gdk_gc_set_clip_origin(gc, 0, 0);
                }
            }
            break;

          case GDK_BUTTON_RELEASE:
            button = (GdkEventButton *)event;
            View3DEnabledCB(NULL, data);
            break;
        }

        return(TRUE);
}

/*
 *    Enable/disable callback, may be called by the enable_micheck
 *    or the enable/disable button.
 */
void View2DEnabledCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterant = FALSE;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)
            return;

        if(!v->initialized)
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

      if(v->flags & VMA_VIEW_FLAG_ENABLED)
          v->flags &= ~VMA_VIEW_FLAG_ENABLED;
      else
          v->flags |= VMA_VIEW_FLAG_ENABLED;

        View2DEnabledDACB(v->enabled_da, NULL, v);
      View2DDraw(v, &v->palette, 0, 0, TRUE);
      View2DUpdateMenus(v);

        reenterant = FALSE;
        return;
}

/*
 *      Enable/disable callback, may be called by the enable_micheck
 *      or the enable/disable button.
 */
void View3DEnabledCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterant = FALSE;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
            return;

      if(!v->initialized)
          return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

        if(v->flags & VMA_VIEW_FLAG_ENABLED)
            v->flags &= ~VMA_VIEW_FLAG_ENABLED;
        else
            v->flags |= VMA_VIEW_FLAG_ENABLED;

      View3DEnabledDACB(v->enabled_da, NULL, v);
      View3DDraw(v, &v->palette, 0, 0, TRUE);
        View3DUpdateMenus(v);

      reenterant = FALSE;
      return;
}


/*
 *    Button press menu mapping position callback, returns the new
 *    position to map the menu at.
 */
static void ViewMenuMapPositionCB(
      GtkMenu *menu, gint *x, gint *y, gpointer data
)
{
        gint rx, ry;
        GtkWidget *rel_widget = (GtkWidget *)data;
        if((menu == NULL) || (rel_widget == NULL))
            return;

        if(!GTK_WIDGET_NO_WINDOW(rel_widget))
        {
            GUIGetWindowRootPosition(
                (void *)rel_widget->window,
                &rx, &ry
            );

            if(x != NULL)
                (*x) = rx;   
            if(y != NULL)  
                (*y) = ry + rel_widget->allocation.height;
        }

        return;
}

/*
 *    Callback (from either the event box or button 3 click)
 *    to map the menu for the 2d view.
 */
gint View2DMenuMapCB(
      GtkWidget *widget, gpointer event, gpointer data
)
{
      GtkWidget *w;
      gint type;
      GdkEventButton *button = NULL;
      vma_view2d_struct *v = (vma_view2d_struct *)data;
      if((v == NULL) ||
           (event == NULL)
      )
          return(FALSE);

      type = (*(gint *)event);

        /* Skip if event is not a button press. */
        if(type != GDK_BUTTON_PRESS)
            return(FALSE);
        else
            button = event;

      w = v->menu;
      if(w != NULL)
      {
            if(widget == v->menu_map_btn)
                gtk_menu_popup(
                    GTK_MENU(w),
                    NULL, NULL,
                    ViewMenuMapPositionCB, (gpointer)widget,
                    button->button, button->time
                );
            else
                gtk_menu_popup(
                    GTK_MENU(w),
                    NULL, NULL,
                    NULL, NULL,
                    button->button, button->time
                );
          return(TRUE);
        }

      return(FALSE);
}

/*
 *      Callback (from either the event box or button 3 click)
 *      to map the menu for the 3d view.
 */
gint View3DMenuMapCB(
      GtkWidget *widget, gpointer event, gpointer data
)
{
      gint type;
      GtkWidget *w;
        GdkEventButton *button = NULL;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
            return(FALSE);

        type = (*(gint *)event);
  
        /* Skip if event is not a button press. */
        if(type != GDK_BUTTON_PRESS)
            return(FALSE);
        else
            button = event;

      w = v->menu;
        if(w != NULL)
        {
          if(widget == v->menu_map_btn)
            gtk_menu_popup(
                GTK_MENU(w),
                NULL, NULL,
                ViewMenuMapPositionCB, (gpointer)widget,
                button->button, button->time
            );
          else
                gtk_menu_popup(
                    GTK_MENU(w),
                    NULL, NULL,
                    NULL, NULL,
                    button->button, button->time
                );

            return(TRUE);
        }

        return(FALSE);
}


/*
 *    View 2d button menu map callback.
 */
void View2DButtonMenuMapCB(GtkButton *button, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkMenu *menu;
      vma_view2d_struct *v = (vma_view2d_struct *)data;
        if((button == NULL) || (v == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

#define DO_BUTTON_PRESSED     \
{ \
 if(button != NULL) \
 { \
  gtk_grab_remove(GTK_WIDGET(button)); \
  \
  while(gtk_events_pending() > 0) \
   gtk_main_iteration(); \
  \
  button->in_button = 1; \
  button->button_down = 1; \
  gtk_signal_emit_by_name(GTK_OBJECT(button), "pressed"); \
 } \
}
        /* Menu map button? */
        if((void *)button == (void *)v->menu_map_btn)
        {
            menu = (GtkMenu *)v->menu;
            if(menu != NULL)
            {
                /* Map menu. */
                gtk_menu_popup(
                    menu, NULL, NULL,
                    ViewMenuMapPositionCB, (gpointer)button,
                    1, GDK_CURRENT_TIME
                ); 
                DO_BUTTON_PRESSED
            }
        }

#undef DO_BUTTON_PRESSED

        reenterant = FALSE;
        return;
}

/*
 *      View 2d menu hide callback.
 */
void View2DMenuHideCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkWidget *w;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if((widget == NULL) || (v == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;   

#define DO_BUTTON_RELEASE     \
{ \
 if((w == NULL) ? 0 : GTK_IS_BUTTON(w)) \
 { \
  GTK_BUTTON(w)->in_button = 0; \
  gtk_signal_emit_by_name(GTK_OBJECT(w), "released"); \
 } \
}

        /* Scratch pad menu? */
        if(widget == v->menu)
        {       
            w = v->menu_map_btn;
            DO_BUTTON_RELEASE
        }

#undef DO_BUTTON_RELEASE

        reenterant = FALSE;
        return;
}

/*
 *      View 3d button menu map callback.
 */
void View3DButtonMenuMapCB(GtkButton *button, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkMenu *menu;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((button == NULL) || (v == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

#define DO_BUTTON_PRESSED     \
{ \
 if(button != NULL) \
 { \
  gtk_grab_remove(GTK_WIDGET(button)); \
  \
  while(gtk_events_pending() > 0) \
   gtk_main_iteration(); \
  \
  button->in_button = 1; \
  button->button_down = 1; \
  gtk_signal_emit_by_name(GTK_OBJECT(button), "pressed"); \
 } \
}

        /* Menu map button? */
        if((void *)button == (void *)v->menu_map_btn)
        {
            menu = (GtkMenu *)v->menu;
            if(menu != NULL)
            {
                /* Map menu. */
                gtk_menu_popup(
                    menu, NULL, NULL,
                    ViewMenuMapPositionCB, (gpointer)button,
                    1, GDK_CURRENT_TIME
                );
                DO_BUTTON_PRESSED
            }
        }

#undef DO_BUTTON_PRESSED

        reenterant = FALSE;  
        return;
}

/*
 *      View 3d menu hide callback.
 */
void View3DMenuHideCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkWidget *w;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((widget == NULL) || (v == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

#define DO_BUTTON_RELEASE     \
{ \
 if((w == NULL) ? 0 : GTK_IS_BUTTON(w)) \
 { \
  GTK_BUTTON(w)->in_button = 0; \
  gtk_signal_emit_by_name(GTK_OBJECT(w), "released"); \
 } \
}

        /* Scratch pad menu? */
        if(widget == v->menu)
        {
            w = v->menu_map_btn;
            DO_BUTTON_RELEASE
        }

#undef DO_BUTTON_RELEASE

        reenterant = FALSE;
        return;
}

/*
 *    2d view undo callback.
 */
void View2DUndoCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)
            return;

      editor = v->editor_ptr;
      if(editor == NULL)
          return;

      EditorUndoCB(NULL, editor);

      return;
}

/*
 *    2d view redo callback.
 */
void View2DRedoCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)
            return;

        editor = v->editor_ptr;
        if(editor == NULL)
            return;

        EditorRedoCB(NULL, editor);

        return;
}

/*
 *    Jump to editor's selected primitive's vertex (if any) callback.
 */
void View2DJumpToVertexCB(GtkWidget *widget, gpointer data)
{
      gint model_num, pn, vtx_num;
      v3d_model_struct *model_ptr;
      ma_editor_struct *editor;
      mp_vertex_struct *vertex;
      gpointer p;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)
            return;

      editor = v->editor_ptr;
      if(editor == NULL)
          return;

      /* Get currently selected model. */
      model_num = EditorSelectedModelIndex(editor);
      model_ptr = V3DModelListGetPtr(
          editor->model, editor->total_models, model_num
      );
      if(model_ptr == NULL)
          return;

      /* Is exactly one primitive selected? */
      if(editor->total_selected_primitives == 1)
          pn = editor->selected_primitive[0];
      else
          return;

      /* Get pointer to the only selected primitive. */
      p = V3DMPListGetPtr(
          model_ptr->primitive, model_ptr->total_primitives, pn
      );
      if(p == NULL)
          return;

      /* Get selected value item as vertex number. */
      vtx_num = EditorGetSelected(
          editor->selected_value, editor->total_selected_values,
          0
      );
      if(vtx_num < 0)
          return;

      /* Get pointer to vertex. */
      vertex = V3DMPGetVertex(p, vtx_num);
      if(vertex == NULL)
          return;

      /* Move view position to center at vertex. */
      switch(v->type)
      {
        case VMA_VIEW2D_TYPE_XY:
          v->v_ti = vertex->x * ((v->flags & VMA_VIEW_FLAG_FLIP_I) ? -1 : 1);
          v->v_tj = vertex->y * ((v->flags & VMA_VIEW_FLAG_FLIP_J) ? -1 : 1);
          break;

          case VMA_VIEW2D_TYPE_YZ:
            v->v_ti = vertex->y * ((v->flags & VMA_VIEW_FLAG_FLIP_I) ? -1 : 1);
            v->v_tj = vertex->z * ((v->flags & VMA_VIEW_FLAG_FLIP_J) ? -1 : 1);
            break;

          case VMA_VIEW2D_TYPE_XZ:
            v->v_ti = vertex->x * ((v->flags & VMA_VIEW_FLAG_FLIP_I) ? -1 : 1);
            v->v_tj = vertex->z * ((v->flags & VMA_VIEW_FLAG_FLIP_J) ? -1 : 1);
            break;
      }

      /* Call translate callback to redraw the view and update
       * values on the view.
       */
      View2DTranslateCB(v);

      return;
}

/*
 *    Set editor's selected primitive's vertex to the editor's
 *    cursor callback.
 */
void View2DSetToCursorCB(GtkWidget *widget, gpointer data)
{
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)  
            return;

      /* Report set to cursor event. */
      if(v->event_cb != NULL)
      {
          vma_view_event_settocursor2d_struct event;

          event.type = VMA_VIEW_EVENT_TYPE_SETTOCURSOR2D;
          event.view2d = v;
          event.vertex_type = 0;
          event.is_initial = TRUE;
          event.is_final = TRUE;

          v->event_cb(v->client_data, &event);
      }

      return;
}

/*
 *    Set background image browse button callback.
 */
static void View2DSetBGImageBrowseCB(void *widget, void *data)
{
      gbool status;
      gchar **path_rtn; gint path_total_rtns;
        const gchar *path_ptr;
        fb_type_struct *ftype_rtn;
      GtkWidget *w;
      ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)data;
      if(d == NULL)
          return;


        /* Map file browser and get user response. */
      FileBrowserSetTransientFor(d->toplevel);
      status = FileBrowserGetResponse(
            "Load Background Image", "Load", "Cancel",
            dname.fb_last_view_background_image,        /* Path. */
            ftype.view_background_image, ftype.view_background_image_total,
            &path_rtn, &path_total_rtns,
            &ftype_rtn
        );
      FileBrowserSetTransientFor(NULL);

      if(!status)
            return;

        /* Get pointer to first returned path. */
        path_ptr = (const gchar *)((path_total_rtns > 0) ?
            path_rtn[0] : NULL
        );
        if(path_ptr == NULL)
        {
            /* No path available, give up. */
            return;
        }

        /* Get file name entry widget. */
        w = EditorIDialogGetWidget(d, 0);
        if((w != NULL) ? GTK_IS_ENTRY(w) : 0)
        {
          gtk_entry_set_text(GTK_ENTRY(w), path_ptr);
        }
}

/*
 *    Set background image scale entry "changed" signal callback.
 */
static void View2DSetBGImageEntryChangedCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterent = FALSE;
      GtkWidget *w;
      gbool uniform_scale = FALSE;
      ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)data;
      if((widget == NULL) || (d == NULL))
          return;

      /* Get uniform scale toggle. */
        w = EditorIDialogGetWidget(d, 6);
        if((w != NULL) ? GTK_IS_TOGGLE_BUTTON(w) : FALSE)
          uniform_scale = GTK_TOGGLE_BUTTON(w)->active;

      /* Skip if not uniform scaling. */
      if(!uniform_scale)
          return;

      if(reenterent)
          return;
      else
          reenterent = TRUE;

      /* Check which entry changed. */
      w = EditorIDialogGetWidget(d, 4);
      if(w == widget)
      {
          GtkWidget *w2 = EditorIDialogGetWidget(d, 5);
          if(w2 != NULL)
          {
            const gchar *cstrptr = (const gchar *)gtk_entry_get_text(
                GTK_ENTRY(w)
            );
            if(cstrptr != NULL)
                gtk_entry_set_text(GTK_ENTRY(w2), cstrptr);
          }
      }
      w = EditorIDialogGetWidget(d, 5);
        if(w == widget)
        {
            GtkWidget *w2 = EditorIDialogGetWidget(d, 4);
            if(w2 != NULL)
            {
                const gchar *cstrptr = (const gchar *)gtk_entry_get_text(
                    GTK_ENTRY(w)
                );
                if(cstrptr != NULL)
                    gtk_entry_set_text(GTK_ENTRY(w2), cstrptr);
            }
        }

      reenterent = FALSE;
}

/*
 *    Set background image callback.
 */
void View2DSetBGImageCB(GtkWidget *widget, gpointer data)
{
        const gchar *msglist[] = VMA_MSGLIST_EDITOR_TOOLTIPS;
        GtkWidget *parent;
        ma_editor_struct *editor;
        ma_editor_idialog_struct *d;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)
            return;

        editor = (ma_editor_struct *)v->editor_ptr;
        if(editor == NULL)
            return;

        d = &editor->idialog;           /* Editor's input dialog. */

        /* Reset input dialog. */
        EditorIDialogReset(editor, d, FALSE);

        /* Get input dialog's client vbox. */
        parent = EditorIDialogClientParent(d);
        if(parent != NULL)
        {
            /* Begin creating our widgets on input dialog, order;
             *
             *    0     path entry
             *    1     path browse button
             *    2     offset i entry
             *    3     offset j entry
           *      4     length i entry
           *      5     length j entry
           *      6     uniform scale check
             */
          gint border_minor = 2, border_major = 5;
            GtkWidget *w, *parent2, *parent3;
          gpointer label, entry, browse_btn;
          gchar num_str[256];


          w = gtk_vbox_new(FALSE, border_major);
            gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
          gtk_widget_show(w);
          parent2 = w;


            /* Path prompt and browse button. */
          w = (GtkWidget *)GUIPromptBarWithBrowse(
            NULL, "File Name:",
            &label, &entry, &browse_btn,
            d,
            View2DSetBGImageBrowseCB
          );
          gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
          gtk_widget_show(w);
            /* Record entry widget as widget 0. */
            EditorIDialogRecordWidget(d, entry);
          /* Record browse button widget as widget 1. */
            EditorIDialogRecordWidget(d, browse_btn);

          w = (GtkWidget *)entry;
          if((w != NULL) && (v->bgimage_filename != NULL))
            gtk_entry_set_text(GTK_ENTRY(w), v->bgimage_filename);


          /* Offset I and J entry prompts. */
          w = gtk_hbox_new(FALSE, border_minor);
          gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
          parent3 = w;

          w = gtk_label_new("Offset I:");
          gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
          gtk_widget_show(w);

          w = gtk_entry_new_with_max_length(80);
          gtk_widget_set_usize(w, 100, -1);
          sprintf(num_str, "%f", v->bgimage_offset_i);
          gtk_entry_set_text(GTK_ENTRY(w), num_str);
            gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist, VMA_MSGNAME_EDITOR_VIEW_BGIMG_OFFSET
                )
            );
            gtk_widget_show(w);
            /* Record as widget 2. */
            EditorIDialogRecordWidget(d, w);

            w = gtk_label_new("J:");
            gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
            gtk_widget_show(w);

            w = gtk_entry_new_with_max_length(80);
            gtk_widget_set_usize(w, 100, -1);
            sprintf(num_str, "%f", v->bgimage_offset_j);
            gtk_entry_set_text(GTK_ENTRY(w), num_str);
            gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist, VMA_MSGNAME_EDITOR_VIEW_BGIMG_OFFSET
                )
            );
            gtk_widget_show(w);
            /* Record as widget 3. */
            EditorIDialogRecordWidget(d, w);


            /* Scale I and J entry prompts. */
            w = gtk_hbox_new(FALSE, border_minor);
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent3 = w;

            w = gtk_label_new("Scale I:");
            gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
            gtk_widget_show(w);

            w = gtk_entry_new_with_max_length(80);
            gtk_widget_set_usize(w, 100, -1);
            sprintf(num_str, "%f", v->bgimage_scale_i);
            gtk_entry_set_text(GTK_ENTRY(w), num_str);
            gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
            gtk_signal_connect(
                GTK_OBJECT(w), "changed",
                GTK_SIGNAL_FUNC(View2DSetBGImageEntryChangedCB),
                d
            );
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist, VMA_MSGNAME_EDITOR_VIEW_BGIMG_SCALE
                )
            );
            gtk_widget_show(w);
            /* Record as widget 4. */
            EditorIDialogRecordWidget(d, w);

            w = gtk_label_new("J:");
            gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
            gtk_widget_show(w);

            w = gtk_entry_new_with_max_length(80);
            gtk_widget_set_usize(w, 100, -1);
            sprintf(num_str, "%f", v->bgimage_scale_j);
            gtk_entry_set_text(GTK_ENTRY(w), num_str);
            gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
          gtk_signal_connect(
            GTK_OBJECT(w), "changed",
            GTK_SIGNAL_FUNC(View2DSetBGImageEntryChangedCB),
            d
          );
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist, VMA_MSGNAME_EDITOR_VIEW_BGIMG_SCALE
                )
            );
            gtk_widget_show(w);
            /* Record as widget 5. */
            EditorIDialogRecordWidget(d, w);

          /* Uniform scale check. */
          w = gtk_check_button_new_with_label("Uniform");
          GTK_TOGGLE_BUTTON(w)->active = (
            (v->bgimage_scale_i == v->bgimage_scale_j) ? TRUE : FALSE
          );
          gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
          gtk_widget_show(w);
            /* Record as widget 6. */
            EditorIDialogRecordWidget(d, w);
        }

        EditorIDialogMap(
            editor, d,
            "Set Background Image",
            "Set", NULL, "Cancel",
            (gpointer)v,
            View2DBGImageSetCB,
            View2DBGImageSetCB,
            ViewIDialogCancelCB
        );
}

/*
 *    Load new background image input dialog callback.
 */
static void View2DBGImageSetCB(void *idialog, void *data)
{
        GtkWidget *w;
      const gchar *path = NULL;
      gdouble offset_i = 0.0, offset_j = 0.0;
      gdouble scale_i = 1.0, scale_j = 1.0;
        ma_editor_struct *editor;
        ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)idialog;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if((d == NULL) || (v == NULL))
            return;

        editor = (ma_editor_struct *)d->editor_ptr;
        if(editor == NULL)
            return;

      EditorSetBusy(editor);

        /* Begin fetching background image values from input dialog 
       * widgets, store results on the view structure and on the global
         * configuration options list.
         */

        /* Image file name entry. */
        w = EditorIDialogGetWidget(d, 0);
        if((w != NULL) ? GTK_IS_ENTRY(w) : FALSE)
        {
          path = (const gchar *)gtk_entry_get_text(GTK_ENTRY(w));
      }

        /* Offset I and J entries. */
        w = EditorIDialogGetWidget(d, 2);
        if((w != NULL) ? GTK_IS_ENTRY(w) : FALSE)
        {
          const gchar *cstrptr = (const gchar *)gtk_entry_get_text(
            GTK_ENTRY(w)
          );
          if(cstrptr != NULL)
            offset_i = atof(cstrptr);
        }
        w = EditorIDialogGetWidget(d, 3);
        if((w != NULL) ? GTK_IS_ENTRY(w) : FALSE)
        {
            const gchar *cstrptr = (const gchar *)gtk_entry_get_text(
                GTK_ENTRY(w)
            );
            if(cstrptr != NULL)
                offset_j = atof(cstrptr);
        }

        /* Scale I and J entries. */
        w = EditorIDialogGetWidget(d, 4);
        if((w != NULL) ? GTK_IS_ENTRY(w) : FALSE)
        {
            const gchar *cstrptr = (const gchar *)gtk_entry_get_text(
                GTK_ENTRY(w)
            );
            if(cstrptr != NULL)
                scale_i = atof(cstrptr);
        }
        w = EditorIDialogGetWidget(d, 5);
        if((w != NULL) ? GTK_IS_ENTRY(w) : FALSE)
        {
            const gchar *cstrptr = (const gchar *)gtk_entry_get_text(
                GTK_ENTRY(w)
            );
            if(cstrptr != NULL)
                scale_j = atof(cstrptr);
        }

      /* Check if we got valid values. */
      if(path == NULL)
      {
          /* Reset input dialog and unmap it. */
          EditorIDialogReset(editor, d, TRUE);
          EditorSetReady(editor);
          return;
      }


        /* Record last view background image file path. */
      VMARecordFBPath(
          path,
          dname.fb_last_view_background_image,
          1
      );

      /* Record fetched background image values on view structure. */
      g_free(v->bgimage_filename);
      v->bgimage_filename = g_strdup(path);
      v->bgimage_offset_i = offset_i;
      v->bgimage_offset_j = offset_j;
      v->bgimage_scale_i = scale_i;
      v->bgimage_scale_j = scale_j;



        /* Check if an image is already loaded, if so then unload it 
       * first.
       */
        if(v->bg_image != NULL)
        {
            ViewBGImageUnload(v->bg_image);
            v->bg_image = NULL;
        }

      /* Load new background image. */
      v->bg_image = ViewBGImageLoad(
            VIEW_BGIMAGE_FLAG_VIEW_2D,
            (gpointer)v,            /* Flags determine this is 2d. */
            path,
            offset_i, offset_j,           /* Offsets in meters. */
            scale_i, scale_j        /* Pixel to meters coeff. */
        );
        if(v->bg_image == NULL)
        {
            gchar *buf;
            gint buf_len = strlen(path) + 256;

            buf = (gchar *)g_malloc((buf_len + 1) * sizeof(gchar));
            if(buf != NULL)
                sprintf(buf,
"Cannot load background image file:\n\
    %s",
                    path
                );
          CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Background Image Load Failed",
                buf,
"Make sure the specified image file exists and\n\
is of a format supported by this application.",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
          CDialogSetTransientFor(NULL);
            g_free(buf);
        }


        /* Update view menus. */
        View2DUpdateMenus(v);

        /* Redraw view. */
        View2DDraw(v, &v->palette, 0, 0, TRUE);


      /* Reset input dialog and unmap it. */
        EditorIDialogReset(editor, d, TRUE);

        EditorSetReady(editor);
}

/*
 *    Clear background.
 */
void View2DClearBGImageCB(GtkWidget *widget, gpointer data)
{
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)
             return;

      if(!v->initialized)
           return;

      /* Unload background image. */
      if(v->bg_image != NULL)
      {
          ViewBGImageUnload(v->bg_image);
          v->bg_image = NULL;
      }

        /* Update view menus. */
        View2DUpdateMenus(v);
        
        /* Redraw view. */
        View2DDraw(v, &v->palette, 0, 0, TRUE);
}


/*
 *    Callback for whenever the view has translated.
 *    This will update the coordinate text entry prompts.
 */
void View2DTranslateCB(vma_view2d_struct *v)
{
      GtkWidget *w;
      gchar text[80], fmt_str[80];

      if(v == NULL)
          return;

      /* Update values to text prompts. */
      w = v->text_i;
      if(w != NULL)
      {
          sprintf(fmt_str, "%%.%if", v->position_decimals);
          sprintf(text, fmt_str, v->v_ti *
            ((v->flags & VMA_VIEW_FLAG_FLIP_I) ? -1 : 1) 
          );
          gtk_entry_set_text(GTK_ENTRY(w), text);
      }
        w = v->text_j;
        if(w != NULL)
        {
            sprintf(fmt_str, "%%.%if", v->position_decimals);
            sprintf(text, fmt_str, v->v_tj *
            ((v->flags & VMA_VIEW_FLAG_FLIP_J) ? -1 : 1)
          );
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }

      /* Redraw view. */
      View2DDraw(v, &v->palette, 0, 0, TRUE);
}

/*
 *      Callback for whenever the view has zoomed (viewable dimension
 *    value has changed).
 *      This will update the viewable dimension spin.
 */
void View2DZoomCB(vma_view2d_struct *v)
{
      GtkAdjustment *adj;
      if(v == NULL)
          return;

      /* Update the view dimension text. */
      adj = v->viewable_dim_adj;
      if(adj != NULL)
      {
          gtk_adjustment_set_value(
            adj, v->viewable_dim
          );
/*
          gtk_signal_emit_by_name(
            GTK_OBJECT(adj), "changed"
          );
 */
      }

      /* Redraw view. */
      View2DDraw(v, &v->palette, 0, 0, TRUE);
}

/*      
 *      Callback for whenever the grid spacing changes.
 *
 *      The grid spacing adjustment will be updated.
 */
void View2DGridCB(vma_view2d_struct *v)
{
      GtkAdjustment *adj;
        if(v == NULL)
            return;

        /* Update the grid spacing adjustment. */
        adj = v->grid_spacing_adj;
        if(adj != NULL)
        {
            gtk_adjustment_set_value(
                adj, v->grid_spacing
            );
/*
            gtk_signal_emit_by_name(
                GTK_OBJECT(adj), "changed"
            );
 */
        }

        /* Redraw view. */
        View2DDraw(v, &v->palette, 0, 0, TRUE);
}

/*
 *    Callback for the rectangular select, must be called on all
 *    event types (including button press, drag, and button release).
 *
 *    A VMA_VIEW_EVENT_TYPE_SELECT2D event will be reported if
 *    report_event is TRUE.
 */
void View2DSelectRectangleCB(vma_view2d_struct *v, gbool report_event)
{
        if(v == NULL)
            return;

        /* Redraw view. */
        View2DDraw(v, &v->palette, 0, 0, TRUE);

      if(report_event)
      {
          GtkWidget *w = v->view;
          if(w != NULL)
          {
            int ww = w->allocation.width;
            int wh = w->allocation.height;

                /* Report select rectangular. */
                if((ww > 0) && (wh > 0) &&
                   (v->event_cb != NULL)
                )
                {
                    gint wi0 = v->w_sel_rect_i0 - (ww / 2);
                    gint wj0 = (wh - v->w_sel_rect_j0) - (wh / 2);
                    gint wi1 = v->w_sel_rect_i1 - (ww / 2);
                    gint wj1 = (wh - v->w_sel_rect_j1) - (wh / 2);
                    gdouble wtov_coeff = View2DGetWToVCoeff(v);
                    gdouble i_flip_coeff = ((v->flags & VMA_VIEW_FLAG_FLIP_I) ?
                        -1.0 : 1.0
                    );
                    gdouble j_flip_coeff = ((v->flags & VMA_VIEW_FLAG_FLIP_J) ?
                        -1.0 : 1.0
                    );
                    gdouble vi0, vj0, vi1, vj1;
                    vma_view_event_select2d_struct event;

                    vi0 = (((gdouble)wi0 * wtov_coeff) + v->v_ti) *
                        i_flip_coeff;
                    vj0 = (((gdouble)wj0 * wtov_coeff) + v->v_tj) *
                        j_flip_coeff;
                    vi1 = (((gdouble)wi1 * wtov_coeff) + v->v_ti) *
                        i_flip_coeff;
                    vj1 = (((gdouble)wj1 * wtov_coeff) + v->v_tj) *
                        j_flip_coeff;

                    event.type = VMA_VIEW_EVENT_TYPE_SELECT2D;
                    event.view2d = v;
                    event.is_area = TRUE;
                    event.wi0 = MIN(v->w_sel_rect_i0, v->w_sel_rect_i1);
                    event.wj0 = MIN(v->w_sel_rect_j0, v->w_sel_rect_j1);
                    event.vi0 = MIN(vi0, vi1);
                    event.vj0 = MIN(vj0, vj1);
                    event.wi1 = MAX(v->w_sel_rect_i0, v->w_sel_rect_i1);
                    event.wj1 = MAX(v->w_sel_rect_j0, v->w_sel_rect_j1);
                    event.vi1 = MAX(vi0, vi1);
                    event.vj1 = MAX(vj0, vj1);
                    v->event_cb(v->client_data, &event);
                }
          }
      }
}

/*
 *    Callback for set cursor, must be called on all events
 *    related to set cursor, including button press, drag, and
 *    button release.
 *
 *      A VMA_VIEW_EVENT_TYPE_CURSOR2D event will be reported if
 *      report_event is TRUE.
 */
void View2DSetCursorCB(
      vma_view2d_struct *v, gbool report_event,
      gbool is_initial, gbool is_final
)
{
      GtkWidget *w;
        gint ww, wh;


        if(v == NULL)
            return;

      w = v->view;
      if(w == NULL)
          return;

      ww = w->allocation.width;
      wh = w->allocation.height;

      /* Update cursor position. */
      if((ww > 0) && (wh > 0))
      {
            gint wi0 = v->w_last_i - (ww / 2);
            gint wj0 = (wh - v->w_last_j) - (wh / 2);
          gdouble vi0, vj0;
            gdouble wtov_coeff = View2DGetWToVCoeff(v);
            gdouble i_flip_coeff = ((v->flags & VMA_VIEW_FLAG_FLIP_I) ?
                -1.0 : 1.0  
            );
            gdouble j_flip_coeff = ((v->flags & VMA_VIEW_FLAG_FLIP_J) ?
                -1.0 : 1.0
            );

          vi0 = (((gdouble)wi0 * wtov_coeff) + v->v_ti) * i_flip_coeff;
          vj0 = (((gdouble)wj0 * wtov_coeff) + v->v_tj) * j_flip_coeff;

          v->w_cur_i = v->w_last_i;
          v->w_cur_j = v->w_last_j;
          v->v_cur_i = vi0;
          v->v_cur_j = vj0;
      }

        /* Redraw view. */
        View2DDraw(v, &v->palette, 0, 0, TRUE);

      /* Report event? */
        if(report_event)
        {
            /* Report set vertex event. */
            if((ww > 0) && (wh > 0) &&
               (v->event_cb != NULL)
            )
            {
                gint wi0 = v->w_last_i - (ww / 2);
                gint wj0 = (wh - v->w_last_j) - (wh / 2);
                gint wdi = v->w_di;
                gint wdj = v->w_dj;
                gdouble wtov_coeff = View2DGetWToVCoeff(v);
                gdouble i_flip_coeff = ((v->flags & VMA_VIEW_FLAG_FLIP_I) ?
                    -1.0 : 1.0
                );
                gdouble j_flip_coeff = ((v->flags & VMA_VIEW_FLAG_FLIP_J) ?
                    -1.0 : 1.0
                );
            gdouble vi0, vj0, vdi, vdj;
                vma_view_event_cursor2d_struct event;

                vi0 = (((gdouble)wi0 * wtov_coeff) + v->v_ti) * i_flip_coeff;
                vj0 = (((gdouble)wj0 * wtov_coeff) + v->v_tj) * j_flip_coeff;
                vdi = ((gdouble)wdi * wtov_coeff) * i_flip_coeff;
                vdj = -((gdouble)wdj * wtov_coeff) * j_flip_coeff;

                event.type = VMA_VIEW_EVENT_TYPE_CURSOR2D;
                event.view2d = v;
                event.wi = v->w_last_i;
                event.wj = v->w_last_j;
                event.vi = vi0;
                event.vj = vj0;
                event.vdi = vdi;
                event.vdj = vdj;
                event.is_initial = is_initial;
                event.is_final = is_final;
                v->event_cb(v->client_data, &event);
            }
        }

        return;
}

/*
 *      Callback for set vertex, must be called on all events related
 *    to set vertex, including button press, drag, and button
 *    release.
 *
 *      A VMA_VIEW_EVENT_TYPE_SET2D event will be reported if
 *      report_event is TRUE.
 */
void View2DSetVertexCB(
      vma_view2d_struct *v,
      gint vertex_type, /* 0 = Vertex, 1 = Normal, 2 = Texcoord. */
      gbool report_event,
      gbool is_initial, gbool is_final
)
{
        if(v == NULL)
            return;  

        /* Redraw view. */
        View2DDraw(v, &v->palette, 0, 0, TRUE);

        if(report_event)
        {
          GtkWidget *w = v->view;
            if(w != NULL)
            {
                gint ww = w->allocation.width;
                gint wh = w->allocation.height;

                /* Report set vertex event. */
                if((ww > 0) && (wh > 0) &&
                   (v->event_cb != NULL)
                )
                {
                gint vertex_type;
                    gint wi0 = v->w_last_i - (ww / 2);
                    gint wj0 = (wh - v->w_last_j) - (wh / 2);
                    gint wdi = v->w_di;
                    gint wdj = v->w_dj;
                    gdouble wtov_coeff = View2DGetWToVCoeff(v);
                    gdouble i_flip_coeff = ((v->flags & VMA_VIEW_FLAG_FLIP_I) ?
                        -1.0 : 1.0
                    );
                    gdouble j_flip_coeff = ((v->flags & VMA_VIEW_FLAG_FLIP_J) ?
                        -1.0 : 1.0  
                    );
                    gdouble vi0, vj0, vdi, vdj;
                    vma_view_event_set2d_struct event;

                    vi0 = (((gdouble)wi0 * wtov_coeff) + v->v_ti) *
                        i_flip_coeff;
                    vj0 = (((gdouble)wj0 * wtov_coeff) + v->v_tj) *
                        j_flip_coeff;
                vdi = ((gdouble)wdi * wtov_coeff) * i_flip_coeff;
                    vdj = -((gdouble)wdj * wtov_coeff) * j_flip_coeff; 

                /* Set vertex type by the view's drag state. */
                switch(v->drag_state)
                {
                  case VMA_VIEW2D_DRAG_SET_TEXCOORD:
                        vertex_type = 2;
                        break;

                  case VMA_VIEW2D_DRAG_SET_NORMAL:
                  vertex_type = 1;
                  break;

                  default:    /* VMA_VIEW2D_DRAG_SET_VERTEX */
                  vertex_type = 0;
                        break;
                }

                    event.type = VMA_VIEW_EVENT_TYPE_SET2D;
                    event.view2d = v;
                event.vertex_type = vertex_type;
                    event.wi = v->w_last_i;
                    event.wj = v->w_last_j;
                    event.vi = vi0;
                    event.vj = vj0;
                    event.vdi = vdi;
                    event.vdj = vdj;
                event.is_initial = is_initial;
                event.is_final = is_final;
                    v->event_cb(v->client_data, &event);
            }
          }
        }
        
        return;
}


/*
 *    Translates the 2d view given the new window coordinate
 *    positions. Deltas will be calculated from the last
 *    window positions on the 2d view structure.
 */
void View2DTranslateToW(
      vma_view2d_struct *v, gint wi, gint wj
)
{
      int wdi, wdj;
      GtkWidget *w;
      double wtov_coeff;


      if(v == NULL)
          return;

      w = v->view;
      if(w == NULL)
          return;

      /* Calculate deltas in window coordinates. */
      wdi = wi - v->w_last_i;
      wdj = wj - v->w_last_j;

      wtov_coeff = View2DGetWToVCoeff(v);

      v->v_ti -= (gdouble)(wtov_coeff * (gdouble)wdi);
        v->v_tj += (gdouble)(wtov_coeff * (gdouble)wdj);

      return;
}

/*
 *    Zooms the 2d view given the new window coordinate
 *    positions. Deltas will be calculated from the last
 *    window positions on the 2d view structure.
 */
void View2DZoomToW(
        vma_view2d_struct *v, gint wi, gint wj
)
{
        gint wdi, wdj;
        GtkWidget *w;
        gdouble wtov_coeff;
const gdouble viewable_dim_min = 0.01;

        if(v == NULL)
            return;

        w = v->view;
        if(w == NULL)
            return;

        /* Calculate deltas in window coordinates. */
        wdi = wi - v->w_last_i;
        wdj = wj - v->w_last_j;

        wtov_coeff = View2DGetWToVCoeff(v);

        v->viewable_dim -= (gdouble)(wtov_coeff * (gdouble)wdj);
      if(v->viewable_dim < viewable_dim_min)
          v->viewable_dim = viewable_dim_min;

      return;
}



/*
 *      Callback for whenever the 3d view has moved and turned.
 * 
 *      This will update the coordinate text entry prompts and redraw
 *      the view.
 */
void View3DMoveTurnCB(vma_view3d_struct *v)
{
        GtkWidget *w;
        gchar text[80], fmt_str[80];

        if(v == NULL)
            return;

        /* Update values to text prompts. */ 
        w = v->text_h;
        if(w != NULL)
        {
            sprintf(fmt_str, "%%.%if", v->angle_decimals);
            sprintf(text, fmt_str, RADTODEG(v->cam_h));
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }
        w = v->text_x;
        if(w != NULL)
        {
            sprintf(fmt_str, "%%.%if", v->position_decimals);
            sprintf(text, fmt_str, v->cam_x);
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }
        w = v->text_y;
        if(w != NULL)
        {
          sprintf(fmt_str, "%%.%if", v->position_decimals);
            sprintf(text, fmt_str, v->cam_y);
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }

        /* Redraw view. */
        View3DDraw(v, &v->palette, 0, 0, TRUE);

        return;
}

/*
 *      Callback for whenever the 3d view has moved strafe (only moved).
 *
 *      This will update the coordinate text entry prompts and redraw
 *      the view.
 */
void View3DMoveStrafeCB(vma_view3d_struct *v)
{
        GtkWidget *w;
        gchar text[80], fmt_str[80];

        if(v == NULL)
            return;

        /* Update values to text prompts. */
        sprintf(fmt_str, "%%.%if", v->position_decimals);

        w = v->text_x;
        if(w != NULL)
        {
            sprintf(text, fmt_str, v->cam_x); 
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }
        w = v->text_y;
        if(w != NULL)
        {
            sprintf(text, fmt_str, v->cam_y); 
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }
        w = v->text_z;
        if(w != NULL)
        {
            sprintf(text, fmt_str, v->cam_z);
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }

        /* Redraw view. */
        View3DDraw(v, &v->palette, 0, 0, TRUE);

        return;
}

/*
 *      Callback for whenever the 3d view has turned.
 *
 *      This will update the direction text entry prompts and redraw
 *      the view.
 */
void View3DTurnCB(vma_view3d_struct *v)
{
        GtkWidget *w;
        gchar text[80], fmt_str[80];

        if(v == NULL)
            return;

        /* Update values to text prompts. */
        sprintf(fmt_str, "%%.%if", v->angle_decimals);

        w = v->text_h;
        if(w != NULL)
        {
            sprintf(text, fmt_str, RADTODEG(v->cam_h));
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }
        w = v->text_p;
        if(w != NULL)
        {
            sprintf(text, fmt_str, RADTODEG(v->cam_p));
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }
        w = v->text_b;
        if(w != NULL)
        {
            sprintf(text, fmt_str, RADTODEG(v->cam_b));
            gtk_entry_set_text(GTK_ENTRY(w), text);
        }

        /* Redraw view. */
        View3DDraw(v, &v->palette, 0, 0, TRUE);

      return;
}

/*
 *      Callback for whenever the 3d view has moved vertically or
 *    left and right.
 *
 *      This will update the direction text entry prompts and redraw
 *      the view.
 */
void View3DMoveVerticalCB(vma_view3d_struct *v)
{
        if(v == NULL)
            return;

      /* Same as strafe. */
      View3DMoveStrafeCB(v);

        return;
}

/*
 *      Callback for whenever the grid spacing changes.
 *
 *      The grid spacing adjustment will be updated.
 */
void View3DGridCB(vma_view3d_struct *v)
{  
        GtkAdjustment *adj;
        if(v == NULL)
            return;
  
        /* Update the grid spacing adjustment. */
        adj = v->grid_spacing_adj;
        if(adj != NULL)
        {
            gtk_adjustment_set_value(
                adj, v->grid_spacing
            );
        }

        /* Redraw view. */
        View3DDraw(v, &v->palette, 0, 0, TRUE);

        return;
}

/*
 *      Callback for whenever the camera move rate changes.
 *
 *      The camera move rate adjustment will be updated.
 */
void View3DMoveRateCB(vma_view3d_struct *v)
{
        GtkAdjustment *adj;
        if(v == NULL)
            return;

        /* Update the camera move rate adjustment. */
        adj = v->move_rate_adj;
        if(adj != NULL)
        {
            gtk_adjustment_set_value(
                adj, v->move_rate
            );
        }

        /* Redraw view. */
        View3DDraw(v, &v->palette, 0, 0, TRUE);

        return;
}

/*
 *    3d view move and turn callback.
 *
 *    Turn left and right, move forwards and backwards.
 */
void View3DMoveTurnToW(vma_view3d_struct *v, gint wi, gint wj)
{
        gint wdi, wdj, ww, wh;
        GtkWidget *w;
        gdouble coeff, wtov_coeff;

        if(v == NULL)
            return;

      wtov_coeff = v->move_rate;    /* Get move rate. */

        w = v->view;
        if(w == NULL)
            return;

        /* Calculate deltas in window coordinates. */   
        wdi = wi - v->w_last_i;
        wdj = wj - v->w_last_j;

      /* Calculate window size. */         
        ww = w->allocation.width;
      wh = w->allocation.height;
      if((ww <= 0) ||
           (wh <= 0)
      )
          return;

      /* Turn heading along i. */
      coeff = (gdouble)wdi / (gdouble)ww;
      v->cam_h = ViewSanitizeRadians(
          v->cam_h + ((1.0 * PI) * -coeff)
      );

      /* Move along j. */
      v->cam_x += (wtov_coeff * wdj * sin(v->cam_h));
        v->cam_y += (wtov_coeff * wdj * cos(v->cam_h));

        return;
}

/*
 *      3d view move only callback.
 *
 *      Move forwards, backwards, left, and right.
 */
void View3DMoveStrafeToW(vma_view3d_struct *v, gint wi, gint wj)
{
        gint wdi, wdj;
        GtkWidget *w;
        gdouble wtov_coeff;

        if(v == NULL)
            return;

        wtov_coeff = v->move_rate;      /* Get move rate. */

        w = v->view;
        if(w == NULL)
            return;

        /* Calculate deltas in window coordinates. */
        wdi = wi - v->w_last_i;
        wdj = wj - v->w_last_j;

        /* Move along i. */
        v->cam_x += (-wtov_coeff * wdi * cos(v->cam_h));
        v->cam_y += (wtov_coeff * wdi * sin(v->cam_h));

      /* Move along j. */
        v->cam_x += (wtov_coeff * wdj * sin(v->cam_h));
        v->cam_y += (wtov_coeff * wdj * cos(v->cam_h));

        return;
}

/*
 *    3d view turn only callback.
 *
 *      Turn left, right, up, and down.
 */
void View3DTurnToW(vma_view3d_struct *v, gint wi, gint wj)
{
        gint wdi, wdj, ww, wh;
        GtkWidget *w;
        gdouble coeff, prev_angle;

        if(v == NULL)
            return;

        w = v->view;
        if(w == NULL)
            return;

        /* Calculate deltas in window coordinates. */
        wdi = wi - v->w_last_i;
        wdj = wj - v->w_last_j;

        /* Calculate window size. */
        ww = w->allocation.width;
        wh = w->allocation.height;
        if((ww <= 0) ||
           (wh <= 0)
        )
            return;

        /* Turn heading along i. */
        coeff = (gdouble)wdi / (gdouble)ww;
        v->cam_h = ViewSanitizeRadians(
            v->cam_h + ((1.0 * PI) * -coeff)
        );

      /* Turn pitch along j. */
        coeff = (gdouble)wdj / (gdouble)wh;
      prev_angle = v->cam_p;
      v->cam_p = ViewSanitizeRadians(
            v->cam_p + ((1.0 * PI) * -coeff)
        );
      /* Clip. */
      if((v->cam_p > (0.5 * PI)) &&
           (v->cam_p < (1.5 * PI))
      )
      {
          if(prev_angle <= (0.5 * PI))
            v->cam_p = 0.5 * PI;
          else
            v->cam_p = 1.5 * PI;
      }

        return;
}

/*
 *    3d view move only callback.
 *
 *      Move up, down, left, and right.
 */
void View3DMoveVerticalToW(vma_view3d_struct *v, gint wi, gint wj)
{
        gint wdi, wdj;
        GtkWidget *w;
        gdouble wtov_coeff;

        if(v == NULL)
            return;

        wtov_coeff = v->move_rate;      /* Get move rate. */

        w = v->view;
        if(w == NULL)
            return;

        /* Calculate deltas in window coordinates. */
        wdi = wi - v->w_last_i;
        wdj = wj - v->w_last_j;

        /* Move along i. */
        v->cam_x += (-wtov_coeff * wdi * cos(v->cam_h));
        v->cam_y += (wtov_coeff * wdi * sin(v->cam_h));

        /* Move along j. */
        v->cam_z += (wtov_coeff * wdj * 1.0);

        return;
}


/*
 *      2d view maximize callback.
 */
void View2DMaximizeCB(GtkWidget *widget, gpointer data)
{
      GtkWidget *w;
        ma_editor_struct *editor;
      vma_view2d_struct *v = (vma_view2d_struct *)data;
      if(v == NULL)
          return;

      if((v->toplevel == NULL) ||
         !v->view_realized
      )
          return;

        editor = v->editor_ptr;
        if(editor == NULL)
            return;

      /* Check if view is already maximized. */
      if(v->flags & VMA_VIEW_FLAG_MAXIMIZED)
          return;

      /* Check if a maximized parent exists. */
      if(v->maximized_parent == NULL)
          return;

      /* Set maximized flag. */
      v->flags |= VMA_VIEW_FLAG_MAXIMIZED;


      /* Map restore button and unmap maximized butotn. */
      w = v->restore_btn;
      if(w != NULL)
          gtk_widget_show(w);

        w = v->maximize_btn;
        if(w != NULL)
            gtk_widget_hide(w);

      /* Reparent view's toplevel vbox to the maximized vbox. */
      w = v->toplevel;
      if(w != NULL)
      {
          if(GTK_WIDGET_FLAGS(w) & GTK_NO_REPARENT)
                fprintf(stderr, "View reparenting not allowed.\n");
          else
          {
/*
            gtk_widget_reparent(w, v->maximized_parent);
 */
            gtk_object_ref(GTK_OBJECT(w));
            gtk_container_remove(
                GTK_CONTAINER(v->restored_parent), w
            );
            gtk_container_add(GTK_CONTAINER(v->maximized_parent), w);
            gtk_object_unref(GTK_OBJECT(w));
          }
      }

        /* Hide restored view panel. */
        w = editor->view_restored_panel;
        if(w != NULL)
            gtk_widget_hide(w);

      return;
}

/*
 *    2d view restore callback.
 */
void View2DRestoreCB(GtkWidget *widget, gpointer data)
{
        GtkWidget *w;
      ma_editor_struct *editor;
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)
            return;

        if((v->toplevel == NULL) ||
           !v->view_realized
        )
            return;

      editor = v->editor_ptr;
      if(editor == NULL)
          return;

        /* Check if view is already restored. */
        if(!(v->flags & VMA_VIEW_FLAG_MAXIMIZED))
            return;

        /* Check if a restored parent exists. */
        if(v->restored_parent == NULL)
            return;

        /* Remove maximized flag. */
        v->flags &= ~VMA_VIEW_FLAG_MAXIMIZED;


        /* Unmap restore button and map maximized butotn. */
        w = v->restore_btn;
        if(w != NULL)
            gtk_widget_hide(w);

        w = v->maximize_btn;
        if(w != NULL)
            gtk_widget_show(w);

        /* Reparent view to restored parent. */
        w = v->toplevel;
        if(w != NULL)
        {
            if(GTK_WIDGET_FLAGS(w) & GTK_NO_REPARENT)   
                fprintf(stderr, "View reparenting not allowed.\n");
            else
          {
/*
                gtk_widget_reparent(w, v->restored_parent);
 */
                gtk_object_ref(GTK_OBJECT(w));
                gtk_container_remove(
                    GTK_CONTAINER(v->maximized_parent), w
                );
                gtk_container_add(GTK_CONTAINER(v->restored_parent), w);
                gtk_object_unref(GTK_OBJECT(w));
          }
        }

      /* Show restored view panel. */
      w = editor->view_restored_panel;
      if(w != NULL)
          gtk_widget_show(w);

        return;
}

/*
 *    2d maximize or restore callback for the maximize/restore
 *    menu item.
 */
void View2DMaximizeRestoreMCB(GtkWidget *widget, gpointer data)
{
        vma_view2d_struct *v = (vma_view2d_struct *)data;
        if(v == NULL)
            return;

        if((v->toplevel == NULL) ||
           !v->view_realized
        )
            return;

      if(v->flags & VMA_VIEW_FLAG_MAXIMIZED)
          View2DRestoreCB(widget, data);
      else
          View2DMaximizeCB(widget, data);

      return;
}


/*
 *      3d view maximize callback. 
 */
void View3DMaximizeCB(GtkWidget *widget, gpointer data)
{
        GtkWidget *w;
        ma_editor_struct *editor;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
            return;
        
        if((v->toplevel == NULL) ||
           !v->view_realized
        )
            return;

        editor = v->editor_ptr; 
        if(editor == NULL)
            return;
        
        /* Check if view is already maximized. */
        if(v->flags & VMA_VIEW_FLAG_MAXIMIZED)
            return;  

        /* Check if a maximized parent exists. */
        if(v->maximized_parent == NULL)
            return;
  
        /* Set maximized flag. */
        v->flags |= VMA_VIEW_FLAG_MAXIMIZED;
            
            
        /* Map restore button and unmap maximized butotn. */
        w = v->restore_btn;
        if(w != NULL)
            gtk_widget_show(w);
        
        w = v->maximize_btn;
        if(w != NULL)
            gtk_widget_hide(w);


        /* Reparent view to maximized parent. */
        w = v->toplevel;
        if(w != NULL)
        {
            if(GTK_WIDGET_FLAGS(w) & GTK_NO_REPARENT)
                fprintf(stderr, "View reparenting not allowed.\n");
            else
            {
/*
                gtk_widget_reparent(w, v->maximized_parent);
 */
                gtk_object_ref(GTK_OBJECT(w));
                gtk_container_remove(
                    GTK_CONTAINER(v->restored_parent), w
                );
                gtk_container_add(GTK_CONTAINER(v->maximized_parent), w);
                gtk_object_unref(GTK_OBJECT(w));
            }
        }

        /* Hide restored view panel. */
        w = editor->view_restored_panel;
        if(w != NULL)
            gtk_widget_hide(w);

        return;
}

/*
 *      3d view restore callback.
 */     
void View3DRestoreCB(GtkWidget *widget, gpointer data)
{
        GtkWidget *w;
        ma_editor_struct *editor;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
            return;
        
        if((v->toplevel == NULL) ||
           !v->view_realized
        )   
            return;

        editor = v->editor_ptr; 
        if(editor == NULL)
            return;

        /* Check if view is already restored. */
        if(!(v->flags & VMA_VIEW_FLAG_MAXIMIZED))
            return;
        
        /* Check if a restored parent exists. */
        if(v->restored_parent == NULL)
            return;

        /* Remove maximized flag. */
        v->flags &= ~VMA_VIEW_FLAG_MAXIMIZED;


        /* Unmap restore button and map maximized butotn. */
        w = v->restore_btn;
        if(w != NULL)
            gtk_widget_hide(w);

        w = v->maximize_btn;
        if(w != NULL)
            gtk_widget_show(w);

        /* Reparent view to maximized parent. */
        w = v->toplevel;
        if(w != NULL)
        {
            if(GTK_WIDGET_FLAGS(w) & GTK_NO_REPARENT)
                fprintf(stderr, "View reparenting not allowed.\n");
            else
          {
/*
                gtk_widget_reparent(w, v->restored_parent);
 */
                gtk_object_ref(GTK_OBJECT(w));
                gtk_container_remove(
                    GTK_CONTAINER(v->maximized_parent), w
                );
                gtk_container_add(GTK_CONTAINER(v->restored_parent), w);
                gtk_object_unref(GTK_OBJECT(w));
          }
        }

        /* Show restored view panel. */
        w = editor->view_restored_panel;
        if(w != NULL)
            gtk_widget_show(w);

        return;
}

/*
 *      3d maximize or restore callback for the maximize/restore
 *      menu item.
 */
void View3DMaximizeRestoreMCB(GtkWidget *widget, gpointer data)
{
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
            return;

        if((v->toplevel == NULL) ||
           !v->view_realized
        )
            return;

        if(v->flags & VMA_VIEW_FLAG_MAXIMIZED) 
            View3DRestoreCB(widget, data);
        else
            View3DMaximizeCB(widget, data);

        return;
} 

/*
 *    3d view undo callback.
 */
void View3DUndoCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
            return;

        editor = v->editor_ptr;
        if(editor == NULL)
            return;

        EditorUndoCB(NULL, editor);

        return;
}

/*
 *    3d view redo callback.
 */
void View3DRedoCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
            return;

        editor = v->editor_ptr;
        if(editor == NULL)
            return;

        EditorRedoCB(NULL, editor);

        return;
}

/*
 *    Render actual toggle callback.
 */
void View3DRenderActualToggleCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterant = FALSE;
        GtkWidget *w;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((v == NULL) ||
           (widget == NULL)
      )
            return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

      /* Render toggle button? */
      if(widget == v->render_toggle)
      {
          w = v->render_toggle;
          v->render_state = GTK_TOGGLE_BUTTON(w)->active;
      }
      /* Render toggle check menu item? */
      else if(widget == v->render_micheck)
      {
          w = v->render_micheck;
          v->render_state = GTK_CHECK_MENU_ITEM(w)->active;
      }

      View3DUpdateMenus(v);
      View3DDraw(v, &v->palette, 0, 0, TRUE);

      reenterant = FALSE;

      return;
}

/*
 *    Cull toggle callback.
 */
void View3DCullToggleCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkWidget *w;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((v == NULL) ||
           (widget == NULL)
        )
            return;
 
        if(reenterant)
            return;
        else
            reenterant = TRUE;

        /* Cull toggle button? */
        if(widget == v->cull_toggle)
        {
            w = v->cull_toggle;
            v->cull_state = GTK_TOGGLE_BUTTON(w)->active;
        }
        /* Cull toggle check menu item? */
        else if(widget == v->cull_micheck)
        {
            w = v->cull_micheck;
            v->cull_state = GTK_CHECK_MENU_ITEM(w)->active;
        }

        View3DUpdateMenus(v);
        View3DDraw(v, &v->palette, 0, 0, TRUE);
        
        reenterant = FALSE;

      return;
}


/*
 *    Perform translation and rotation toggle callback
 */
void View3DTranslationsToggleCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkWidget *w;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((v == NULL) ||
           (widget == NULL)
        )
            return;
 
        if(reenterant)
            return;
        else
            reenterant = TRUE;

        /* Translations toggle button? */
        if(widget == v->translations_toggle)
        {
            w = v->translations_toggle;
            v->translations_state = GTK_TOGGLE_BUTTON(w)->active;
        }
        /* Translations toggle check menu item? */
        else if(widget == v->translations_micheck)
        {
            w = v->translations_micheck;
            v->translations_state = GTK_CHECK_MENU_ITEM(w)->active;
        }

        View3DUpdateMenus(v);
        View3DDraw(v, &v->palette, 0, 0, TRUE);

        reenterant = FALSE;

        return;
}


/*
 *    Enable alpha channel callback.
 */
void View3DEnableAlphaChannelToggleCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkWidget *w;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((v == NULL) ||
           (widget == NULL)
        )
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;
   
        /* Alpha channel toggle button? */
/*
Uncomment this when we actually have a toggle button for this function.
        if(widget == v->enable_alpha_channel_toggle)
        {
            w = v->enable_alpha_channel_toggle;
            v->enable_alpha_channel = GTK_TOGGLE_BUTTON(w)->active;
        }
 */
        /* Alpha channel check menu item? */
      if(widget == v->enable_alpha_channel_micheck)
        {
            w = v->enable_alpha_channel_micheck;
            v->enable_alpha_channel = GTK_CHECK_MENU_ITEM(w)->active;
        }

        View3DUpdateMenus(v);
        View3DDraw(v, &v->palette, 0, 0, TRUE);

        reenterant = FALSE;

      return;
}

/*
 *    Set background image callback.
 */
void View3DSetBGImageCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterent = FALSE;
      gbool status;
        GtkWidget *w;
        gint ww, wh;
        gchar **path_rtn; gint path_total_rtns;
        const gchar *path_ptr;
        fb_type_struct *ftype_rtn;
        view_bgimage_struct *bgimg;
      ma_editor_struct *editor;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
             return;

        if(!v->initialized || !v->view_realized)
             return;

        w = v->view;
        if(w == NULL)
            return;

        editor = (ma_editor_struct *)v->editor_ptr;
        if(editor == NULL)
            return;

      if(reenterent)
          return;
      else
          reenterent = TRUE;

        ww = w->allocation.width;
        wh = w->allocation.height;

        /* Prompt for response. */
/* Transient of editor toplevel for now. */
      FileBrowserSetTransientFor(editor->toplevel);
        status = FileBrowserGetResponse(   
            "Load Background Image", "Load", "Cancel",
            dname.fb_last_view_background_image,        /* Path. */
            ftype.view_background_image, ftype.view_background_image_total,
            &path_rtn, &path_total_rtns,
            &ftype_rtn
        );
      FileBrowserSetTransientFor(NULL);

      if(!status)
        {
            /* User canceled. */
          reenterent = FALSE;
            return;
        }

        /* Get pointer to first returned path. */
        path_ptr = (const char *)((path_total_rtns > 0) ?
            path_rtn[0] : NULL
        );
        if(path_ptr == NULL)
        {
            /* No path available, give up. */
          reenterent = FALSE;
            return;
        }

        /* Record last view background image file path. */
        VMARecordFBPath(
            path_ptr,
            dname.fb_last_view_background_image,
            1
        );

        EditorSetBusy(editor);

        /* Load new background image. */
        bgimg = ViewBGImageLoad(  
            VIEW_BGIMAGE_FLAG_VIEW_3D,
            (gpointer)v,                /* Flags determine this is 3d. */
            path_ptr,
            0.0, 0.0,
            0.0, 0.0
        );
        if(bgimg == NULL)
        {
            gchar *buf;
            gint buf_len = strlen(path_ptr) + 256;
         
            buf = (gchar *)g_malloc((buf_len + 1) * sizeof(gchar));
            if(buf != NULL)
                sprintf(buf,
"Cannot load background image file:\n\
    %s",
                    path_ptr
                );
            CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Background Image Load Failed",
                buf,
"Make sure the specified image file exists and\n\
is of a format supported by this application.",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
            CDialogSetTransientFor(NULL);

            g_free(buf);

          EditorSetReady(editor);
          reenterent = FALSE;
            return;
        }
            
        /* Unload current background image first. */
        if(v->bg_image != NULL)
        {
            ViewBGImageUnload(v->bg_image);
            v->bg_image = NULL;
        } 

        /* Record new background image. */
        v->bg_image = bgimg;

        /* Update view menus. */
        View3DUpdateMenus(v);

        /* Redraw view. */  
        View3DDraw(v, &v->palette, 0, 0, TRUE);

      EditorSetReady(editor);
      reenterent = FALSE;
}

/*
 *    Clear background image callback.
 */
void View3DClearBGImageCB(GtkWidget *widget, gpointer data)
{
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if(v == NULL)
             return;

        if(!v->initialized)
             return;

        /* Unload background image. */
        if(v->bg_image != NULL)
        {
            ViewBGImageUnload(v->bg_image);
            v->bg_image = NULL;
        }

        /* Update view menus. */
        View3DUpdateMenus(v);

        /* Redraw view. */
        View3DDraw(v, &v->palette, 0, 0, TRUE);

        return;
}


/*
 *    Set up and map camera properties using the editor's input
 *    dialog.
 */
void View3DCameraPropertiesMapCB(GtkWidget *widget, gpointer data)
{
      const char *msglist[] = VMA_MSGLIST_EDITOR_TOOLTIPS;
      GtkWidget *parent;
      ma_editor_struct *editor;
      ma_editor_idialog_struct *d;
      vma_view3d_struct *v = (vma_view3d_struct *)data;
      if(v == NULL)
          return;

      editor = v->editor_ptr;
      if(editor == NULL)
          return;

      d = &editor->idialog;         /* Editor's input dialog. */

      /* Reset input dialog. */
      EditorIDialogReset(editor, d, FALSE);

      /* Get input dialog's client vbox. */
      parent = EditorIDialogClientParent(d);
      if(parent != NULL)
      {
          /* Begin creating our widgets on input dialog, order;
           *
           *      0     near clip
           *      1     far clip
           *      2     field of view
           */
          GtkWidget *w, *parent2;
          GtkAdjustment *adj;

          /* Near clip. */
          w = gtk_hbox_new(FALSE, 0);
          gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
          gtk_widget_show(w);
          parent2 = w;
          /* Label. */
          w = gtk_label_new("Clip Near:");
          gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
            gtk_widget_show(w);
          /* Spin. */
          adj = (GtkAdjustment *)gtk_adjustment_new(
          0.0,                /* Initial. */
          0.0,                /* Minimum. */
            10000000.0,         /* Maximum. */
            0.001,              /* Step inc. */
            0.01,               /* Page inc. */   
            0.01                /* Page size. */
          );
          w = gtk_spin_button_new(
            adj,
            1.0,        /* Climb rate (0.0 to 1.0). */
            5           /* Digits. */
          );
          if(w != NULL)
          {
            gtk_widget_set_usize(w, 200, -1);
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
            gtk_widget_show(w);
            gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(w), v->cam_clip_near
            );
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                  msglist, VMA_MSGNAME_EDITOR_VIEW_CAM_CLIP_NEAR
                )
            );
          }
          /* Record as widget 0. */
            EditorIDialogRecordWidget(d, w);

          /* Far clip. */
            w = gtk_hbox_new(FALSE, 0);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;
            /* Label. */
            w = gtk_label_new("Clip Far:");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
            gtk_widget_show(w);
            /* Spin. */
            adj = (GtkAdjustment *)gtk_adjustment_new(
            0.0,                /* Initial. */
            0.0,                /* Minimum. */
            10000000.0,         /* Maximum. */
            100,                /* Step inc. */
            1000,               /* Page inc. */   
            1000                /* Page size. */
            );
            w = gtk_spin_button_new(
                adj,
                1.0,          /* Climb rate (0.0 to 1.0). */
                0       /* Digits. */
            );
            if(w != NULL)
            {
                gtk_widget_set_usize(w, 200, -1);
                gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
                gtk_widget_show(w);
                gtk_spin_button_set_value(
                    GTK_SPIN_BUTTON(w), v->cam_clip_far
                );
                GUISetWidgetTip(
                    w, 
                    MsgListMatchCaseMessage(
                        msglist, VMA_MSGNAME_EDITOR_VIEW_CAM_CLIP_FAR
                    )
                );
            }
            /* Record as widget 1. */
            EditorIDialogRecordWidget(d, w);

            /* Field of view. */
            w = gtk_hbox_new(FALSE, 0);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;
            /* Label. */
            w = gtk_label_new("Field Of View:");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
            gtk_widget_show(w);
            /* Spin. */
            adj = (GtkAdjustment *)gtk_adjustment_new(
            0.0,                /* Initial. */
            0.0,                /* Minimum. */
            360.0,              /* Maximum. */
            1.0,                /* Step inc. */
            10.0,               /* Page inc. */
            10.0                /* Page size. */
            );
            w = gtk_spin_button_new(
                adj,
                1.0,            /* Climb rate (0.0 to 1.0). */
                2               /* Digits. */
            );
            if(w != NULL)
            {
                gtk_widget_set_usize(w, 200, -1);
                gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
                gtk_widget_show(w);
                gtk_spin_button_set_value(
                    GTK_SPIN_BUTTON(w), RADTODEG(v->cam_fov)
                );
                GUISetWidgetTip(
                    w,
                    MsgListMatchCaseMessage(
                        msglist, VMA_MSGNAME_EDITOR_VIEW_CAM_FOV
                    )
                );
            }
            /* Record as widget 2. */
            EditorIDialogRecordWidget(d, w);
      }

      EditorIDialogMap(
          editor, d,
          "Camera Properties",
          "Set", "Apply", "Close",
          (void *)v,
          View3DCameraPropertiesOKCB,
          View3DCameraPropertiesApplyCB,
          View3DCameraPropertiesCancelCB
      );

      return;
}

/*
 *      Apply camera properties callback.
 */
void View3DCameraPropertiesOKCB(void *idialog, void *data)
{
        ma_editor_struct *editor;
        ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)idialog;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((d == NULL) ||
           (v == NULL)
        )
            return;

        editor = (ma_editor_struct *)d->editor_ptr;
        if(editor == NULL)
            return;

      View3DCameraPropertiesApplyCB(idialog, data);

      /* Reset input dialog. */
      EditorIDialogReset(editor, d, TRUE);
}

/*
 *    Apply camera properties callback.
 */
void View3DCameraPropertiesApplyCB(void *idialog, void *data)
{
      GtkWidget *w;
      double val_d;
      ma_editor_struct *editor;
      ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)idialog;
      vma_view3d_struct *v = (vma_view3d_struct *)data;
      if((d == NULL) ||
           (v == NULL)
      )
          return;

      editor = (ma_editor_struct *)d->editor_ptr;
        if(editor == NULL)
            return;

      /* Begin fetching new camera properties from input dialog widgets,
       * store results on the view structure and on the global
       * configuration options list.
       */

      /* Near clip text entry. */
      w = EditorIDialogGetWidget(d, 0);
      if(w != NULL)
      {
          val_d = (gdouble)gtk_spin_button_get_value_as_float(
            GTK_SPIN_BUTTON(w)
          );
          v->cam_clip_near = val_d;
          VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_VIEW_CAM_CLIP_NEAR,
            &val_d, FALSE
          );
      }

        /* Far clip text entry. */
        w = EditorIDialogGetWidget(d, 1);
        if(w != NULL)
        {
            val_d = (gdouble)gtk_spin_button_get_value_as_float( 
                GTK_SPIN_BUTTON(w)
            );
            v->cam_clip_far = val_d;
            VMACFGItemListMatchSetValue(
                option, VMA_CFG_PARM_VIEW_CAM_CLIP_FAR,
                &val_d, FALSE
            );
        }

        /* FOV text entry. */
        w = EditorIDialogGetWidget(d, 2);
        if(w != NULL)
        {
            val_d = (gdouble)DEGTORAD(gtk_spin_button_get_value_as_float(
                GTK_SPIN_BUTTON(w)
            ));
            v->cam_fov = val_d;
            VMACFGItemListMatchSetValue(
                option, VMA_CFG_PARM_VIEW_CAM_FOV,
                &val_d, FALSE
            );
        }

      /* Redraw view. */
      View3DDraw(v, &v->palette, 0, 0, TRUE);
}

/*
 *    Cancel set camera properties callback.
 */
void View3DCameraPropertiesCancelCB(void *idialog, void *data)
{
        ma_editor_struct *editor;
        ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)idialog;
        vma_view3d_struct *v = (vma_view3d_struct *)data;
        if((d == NULL) ||
           (v == NULL)
        )
          return;

        editor = (ma_editor_struct *)d->editor_ptr;
      if(editor == NULL)
          return;

      /* Reset input dialog and unmap it. */
        EditorIDialogReset(editor, d, TRUE);
}


Generated by  Doxygen 1.6.0   Back to index