Logo Search packages:      
Sourcecode: vertex version File versions

editorpcb.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <math.h>
#include <GL/gl.h>
#include <gdk/gdkkeysyms.h>                
#include <gtk/gtk.h>

#include "matrixmath.h"
#include "v3dmp.h"
#include "v3dmodel.h"
#include "msglist.h"
#include "guiutils.h"
#include "cdialog.h"

#include "editor.h"
#include "editorcb.h"
#include "editorselect.h"
#include "editoridialog.h"
#include "editorviewcb.h"
#include "editorlist.h"
#include "editorundo.h"
#include "editortexture.h"
#include "editorhf.h"
#include "editorp.h"
#include "editorpcb.h"

#include "vmacfg.h"
#include "vmacfglist.h"
#include "vma.h"
#include "vmautils.h"
#include "messages.h"

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


/* All purpose editor input dialog cancel callback. */
static void EditorPrimitiveIDialogCancelCB(void *idialog, void *data);

/* Flip winding. */
void EditorPrimitiveFlipWindingCB(GtkWidget *widget, gpointer data);

/* Unitlize normal. */
void EditorPrimitiveUnitlizeNormalCB(GtkWidget *widget, gpointer data);

/* Rotate. */
void EditorPrimitiveRotateCB(GtkWidget *widget, gpointer data);
static void EditorPrimitiveRotateOKCB(void *idialog, void *data);
static void EditorPrimitiveRotateApplyCB(void *idialog, void *data);

/* Translate. */
void EditorPrimitiveTranslateCB(GtkWidget *widget, gpointer data);
static void EditorPrimitiveTranslateOKCB(void *idialog, void *data);
static void EditorPrimitiveTranslateApplyCB(void *idialog, void *data);

/* Scale. */
static void EditorPrimitiveScaleCompoentCheckCB(GtkWidget *widget, gpointer data);
void EditorPrimitiveScaleCB(GtkWidget *widget, gpointer data);
static void EditorPrimitiveScaleOKCB(void *idialog, void *data);
static void EditorPrimitiveScaleApplyCB(void *idialog, void *data);

/* Mirror. */
void EditorPrimitiveMirrorCB(GtkWidget *widget, gpointer data);
static void EditorPrimitiveMirrorOKCB(void *idialog, void *data);
static void EditorPrimitiveMirrorApplyCB(void *idialog, void *data);

/* Snap. */
static double EditorPrimitiveSnapDigits(double x, int dig);
static double EditorPrimitiveSnapDecimals(double x, int dec);
static void EditorPrimitiveSnapTestCB(GtkWidget *widget, gpointer data);
static void EditorPrimitiveSnapResetCB(GtkWidget *widget, gpointer data);
static void EditorPrimitiveSnapRadioCB(GtkWidget *widget, gpointer data);
void EditorPrimitiveSnapCB(GtkWidget *widget, gpointer data);
static void EditorPrimitiveSnapOKCB(void *idialog, void *data);
static void EditorPrimitiveSnapApplyCB(void *idialog, void *data);


#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 RADTODEG(r)     ((r) * 180 / PI)
#define DEGTORAD(d)     ((d) * PI / 180.0)


/*
 *      Checks if editor has changes, if it does not then it will
 *      mark it as having changes.
 */
#define EDITOR_DO_UPDATE_HAS_CHANGES    \
{ \
 if(!editor->has_changes) \
  editor->has_changes = TRUE; \
}


/*
 *    All purpose editor input dialog cancel callback.
 *
 *    Just resets and unmaps the input dialog when called.
 */
static void EditorPrimitiveIDialogCancelCB(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);
}


/*
 *    Callback to flip winding of selected primitive.
 */
void EditorPrimitiveFlipWindingCB(GtkWidget *widget, gpointer data)
{
      gint i, model_num, pn, first_sel_pn;
      gint undos_recorded = 0;
      v3d_model_struct *model_ptr;
      gpointer p, u;
      vma_undo_flip_winding_struct *uf;
        ma_editor_struct *editor = (ma_editor_struct *)data;  
        if(editor == NULL)
            return;


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

      EditorSetBusy(editor);

      /* Get first selected primitive (can be -1). */
      first_sel_pn = ((editor->total_selected_primitives == 1) ?
          editor->selected_primitive[0] : -1
      );

        /* Apply to each selected primitive. */
      for(i = 0; i < editor->total_selected_primitives; i++)
      {
          pn = editor->selected_primitive[i];
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, pn
            );
            if(p == NULL)
                continue;

          /* Clear values list. */
          if(first_sel_pn > -1)
            EditorListDeleteValuesG(editor);

          /* Flip winding. */
          V3DMPFlipWinding(p, TRUE, TRUE);

          /* Update has changes on editor. */
          EDITOR_DO_UPDATE_HAS_CHANGES

          /* Update values list. */
          if(first_sel_pn > -1)
            EditorListAddValuesRG(editor, p);

          /* Record undo. */
          uf = (vma_undo_flip_winding_struct *)VMAUndoNew(
            VMA_UNDO_TYPE_FLIP_WINDING,
            "Flip Winding"
          );
          if(uf != NULL)
          {
            uf->editor = editor;
            uf->model_num = model_num;
            uf->primitive_num = pn;
            EditorUndoRecord(editor, uf);

            undos_recorded++;
          }
      }

      /* Update repeat values for each new undo. */
      for(i = 0; i < undos_recorded; i++)
      {
          u = VMAUndoListGetPtr(
            editor->undo, editor->total_undos,
            editor->total_undos - i - 1
          );
          VMAUndoSetRepeats(u, undos_recorded);
      }


        /* Update menus and redraw views. */
        EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor);
        EditorRedrawAllViews(editor);

      EditorSetReady(editor);
}


/*
 *    Unitlize selected vertex's normal on the selected primitive
 *    callback.
 */
void EditorPrimitiveUnitlizeNormalCB(GtkWidget *widget, gpointer data)
{
      gint i, model_num, pn;
      gint undos_recorded = 0;
        v3d_model_struct *model_ptr;
        gpointer p, u;
        vma_undo_set_normal_struct *un;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

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

        EditorSetBusy(editor);   

        /* Single selected primitive? */
      if(editor->total_selected_primitives == 1)
      {
          /* Single selected primitive. */
          pn = editor->selected_primitive[0];
          p = V3DMPListGetPtr(
            model_ptr->primitive, model_ptr->total_primitives, pn
          );
          if(p != NULL)
          {
            /* Unitlize only the selected vertex since only one
             * primitive was selected.
             */
            GtkCList *values_list = (GtkCList *)editor->values_list;
            gint vtx_num = EditorGetSelected(
                editor->selected_value, editor->total_selected_values,
                0
            );
            mp_vertex_struct *vtx_ptr;
            gdouble len;


            vtx_ptr = V3DMPGetNormal(p, vtx_num);
            if(vtx_ptr != NULL)
            {
                    /* Update has changes on editor. */
                    EDITOR_DO_UPDATE_HAS_CHANGES

                /* Record undo. */
                un = (vma_undo_set_normal_struct *)VMAUndoNew(
                  VMA_UNDO_TYPE_SET_NORMAL, "Set Normal"
                );
                if(un != NULL)
                {
                  un->editor = editor;
                  un->model_num = model_num;
                  un->primitive_num = pn;
                  un->vertex_num = vtx_num;
                  un->n.x = vtx_ptr->x;
                  un->n.y = vtx_ptr->y;
                  un->n.z = vtx_ptr->z;
                  EditorUndoRecord(editor, un);

                  undos_recorded++;
                }

                /* Calculate magnitude length and then divide each
                 * compoent by it to form unit length of each
                 * compoent.
                 */
                len = sqrt(
                  (vtx_ptr->x * vtx_ptr->x) +
                  (vtx_ptr->y * vtx_ptr->y) +
                  (vtx_ptr->z * vtx_ptr->z)
                );
                if(len > 0.0)
                {
                  vtx_ptr->x = vtx_ptr->x / len;
                  vtx_ptr->y = vtx_ptr->y / len;
                        vtx_ptr->z = vtx_ptr->z / len;
                }

                /* Refetch values list. */
                EditorListDeleteValuesG(editor);
                EditorListAddValuesRG(editor, p);

                /* Reselect last value item. */
                if(values_list != NULL)
                  gtk_clist_select_row(values_list, vtx_num, 0);
            }
          }
      }
      /* More than 1 primitive selected? */
      else if(editor->total_selected_primitives > 1)
      {
          int vtx_num;
          mp_vertex_struct *vtx_ptr;
          gdouble len;

          /* Apply to each selected primitive. */
          for(i = 0; i < editor->total_selected_primitives; i++)
          {
            pn = editor->selected_primitive[i];
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, pn
            );
            if(p == NULL)
                continue;

            /* Unitlize all vertex's normals since multiple primitives
             * selected.
             */
                for(vtx_num = 0; 1; vtx_num++)
                {
                    /* Get next normal on vertex, break if there are
                     * no more.
                     */
                    vtx_ptr = V3DMPGetNormal(p, vtx_num);
                    if(vtx_ptr == NULL)
                        break;

                    /* Update has changes on editor. */
                    EDITOR_DO_UPDATE_HAS_CHANGES

                    /* Record undo. */
                    un = (vma_undo_set_normal_struct *)VMAUndoNew(
                        VMA_UNDO_TYPE_SET_NORMAL, "Set Normal"
                    );
                    if(un != NULL)
                    {
                        un->editor = editor;
                        un->model_num = model_num;
                        un->primitive_num = pn;
                        un->vertex_num = vtx_num;
                        un->n.x = vtx_ptr->x;
                        un->n.y = vtx_ptr->y;
                        un->n.z = vtx_ptr->z;
                        EditorUndoRecord(editor, un);

                  undos_recorded++;
                    }

                    /* Calculate magnitude length and then divide each
                     * compoent by it to form unit length of each
                     * compoent.
                     */
                    len = sqrt(
                        (vtx_ptr->x * vtx_ptr->x) +
                        (vtx_ptr->y * vtx_ptr->y) +
                        (vtx_ptr->z * vtx_ptr->z)
                    );
                    if(len > 0.0)
                    {
                        vtx_ptr->x = vtx_ptr->x / len;                          
                        vtx_ptr->y = vtx_ptr->y / len;
                        vtx_ptr->z = vtx_ptr->z / len;
                    }
                }
          }
      }

        /* Update repeat values for each new undo. */
        for(i = 0; i < undos_recorded; i++)
        {
            u = VMAUndoListGetPtr(
                editor->undo, editor->total_undos,
                editor->total_undos - i - 1
            );
            VMAUndoSetRepeats(u, undos_recorded);
        }


        /* Update menus and redraw views. */
        EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor);   
        EditorRedrawAllViews(editor);   

        EditorSetReady(editor);
}

/*
 *    Rotate selected primitives callback.
 *
 *    Sets up the input dialog and maps it.
 */
void EditorPrimitiveRotateCB(GtkWidget *widget, gpointer data)
{
      const gchar *msglist[] = VMA_MSGLIST_EDITOR_TOOLTIPS;
        GtkWidget *parent;
        GSList *gslist;
        ma_editor_idialog_struct *d;
      ma_editor_struct *editor = (ma_editor_struct *)data;
        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       Angle of rotation (in degrees) spin
             *    1     X axis radio
             *    2     Y axis radio
           *      3     Z axis radio
           *      4     Rotate normals check
           *      5     Orbital center X entry
           *      6     Orbital center Y entry
           *      7     Orbital center Z entry
             */
          gpointer entry;
            GtkWidget *w, *parent2;
            GtkAdjustment *adj;
          gdouble rotation_angle;
          gint orbital_axis;
          gbool rotate_normals;
          gchar num_str[256];
          gchar fmt_str[80];

            /* Angle of rotation spin. */
            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("Angle Of Rotation:");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
            gtk_widget_show(w);
            /* Spin. */
          rotation_angle = RADTODEG(VMACFGItemListGetValueD(
            option, VMA_CFG_PARM_ROTATE_PRIMITIVE_ANGLE
          ));
            adj = (GtkAdjustment *)gtk_adjustment_new(
            rotation_angle,   /* Initial. */
            -360.0,           /* Minimum. */
            360.0,            /* Maximum. */
            10.0,       /* Step inc. */
            1.0,        /* Page inc. */
            1.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, 100, -1);
                gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
                gtk_widget_show(w);
                GUISetWidgetTip(
                    w,
                    MsgListMatchCaseMessage(
                        msglist,
                  VMA_MSGNAME_EDITOR_ROTATE_PRIMITIVE_ROTATE_ANGLE
                    )
                );
            }
            /* Record as widget 0. */
            EditorIDialogRecordWidget(d, w);

          /* Orbital axis table. */
          w = gtk_table_new(1, 4, FALSE);
          gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;
          /* Label. */
          w = gtk_label_new("Orbital Axis:");
          gtk_table_attach(GTK_TABLE(parent2), w,
            0, 1, 0, 1,
            0, 0,
            2, 2
          );
          gtk_widget_show(w);

          orbital_axis = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_ROTATE_PRIMITIVE_ORBITAL_AXIS
            );

          /* X axis radio. */
          gslist = NULL;
          w = gtk_radio_button_new_with_label(gslist, "X");
          gtk_table_attach(GTK_TABLE(parent2), w,
                1, 2, 0, 1,
                0, 0,
                2, 2
            );
          gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));
            if(orbital_axis == 1)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_ROTATE_PRIMITIVE_ORBITAL_AXIS
                )
            );
            gtk_widget_show(w);
            /* Record as widget 1. */
            EditorIDialogRecordWidget(d, w);

            /* Y axis radio. */
            w = gtk_radio_button_new_with_label(gslist, "Y");
            gtk_table_attach(GTK_TABLE(parent2), w,
                2, 3, 0, 1,
                0, 0,
                2, 2
            );
            gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));
            if(orbital_axis == 2)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_ROTATE_PRIMITIVE_ORBITAL_AXIS
                )
            );
            gtk_widget_show(w);
            /* Record as widget 2. */
            EditorIDialogRecordWidget(d, w);

            /* Z axis radio. */
            w = gtk_radio_button_new_with_label(gslist, "Z");
            gtk_table_attach(GTK_TABLE(parent2), w,
                3, 4, 0, 1,
                0, 0,
                2, 2 
            );
          if(orbital_axis == 3)
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_ROTATE_PRIMITIVE_ORBITAL_AXIS
                )
            );
            gtk_widget_show(w);
            /* Record as widget 3. */
            EditorIDialogRecordWidget(d, w);

          /* Rotate normals check. */
          rotate_normals = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_ROTATE_PRIMITIVE_ROTATE_NORMALS
            );
          w = gtk_check_button_new_with_label("Rotate Normals");
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
          if(rotate_normals)
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 4. */
            EditorIDialogRecordWidget(d, w);

            /* Orbital center table. */
            w = gtk_table_new(1, 4, FALSE);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;
            /* Label. */
            w = gtk_label_new("Orbital Center");
            gtk_table_attach(GTK_TABLE(parent2), w,
                0, 1, 0, 1,
                0, 0,
                2, 2
            );
            gtk_widget_show(w);

          /* Orbital center X entry. */
          w = GUIPromptBar(
            NULL, "X:",
            NULL, &entry
          );
            gtk_table_attach(GTK_TABLE(parent2), w,
                1, 2, 0, 1,
                0, 0,
                2, 2
            );
          sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
          sprintf(num_str, fmt_str, editor->vcursor_x);
          gtk_entry_set_text((GtkEntry *)entry, num_str);
          gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,    
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_ROTATE_PRIMITIVE_ORBITAL_CENTER
                )
            );
          gtk_widget_show(w);
          /* Record as widget 5. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* Orbital center Y entry. */
            w = GUIPromptBar(
                NULL, "Y:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                2, 3, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, editor->vcursor_y);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_ROTATE_PRIMITIVE_ORBITAL_CENTER
                )
            );
            gtk_widget_show(w);
            /* Record as widget 6. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* Orbital center Z entry. */
            w = GUIPromptBar(
                NULL, "Z:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                3, 4, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, editor->vcursor_z);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_ROTATE_PRIMITIVE_ORBITAL_CENTER
                )
            );
            gtk_widget_show(w);
            /* Record as widget 7. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);
        }

        EditorIDialogMap(
            editor, d,
            "Rotate Primitives",
            "Rotate", "Apply", "Close",
            (void *)editor,
            EditorPrimitiveRotateOKCB,
            EditorPrimitiveRotateApplyCB,
            EditorPrimitiveIDialogCancelCB
        );
}

/*
 *    Rotate input dialog ok callback.
 */
static void EditorPrimitiveRotateOKCB(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;

      EditorPrimitiveRotateApplyCB(idialog, data);

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

/*
 *    Rotate input dialog apply callback.
 */
static void EditorPrimitiveRotateApplyCB(void *idialog, void *data) 
{
      gint i, model_num, pn, first_sel_pn, vtx_num;
      gint undos_recorded = 0;
      gbool rotate_normals = TRUE;
      v3d_model_struct *model_ptr;
      mp_vertex_struct *vtx_ptr;
      gpointer p;
      gdouble theta = 0.0 * PI;
      gint axis_code = 0;
      gdouble center_x = 0.0, center_y = 0.0, center_z = 0.0;
      u_int32_t val_ui32;
      u_int8_t val_ui8;
      gdouble val_d;
      GtkWidget *w;
      gpointer u;
        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;

      /* Begin fetching values from widgets. */
      /* Angle of rotation (in radians). */
      w = EditorIDialogGetWidget(d, 0);
        if(w != NULL)
        {
            theta = ((double)gtk_spin_button_get_value_as_float(
                GTK_SPIN_BUTTON(w)
            ) * PI / 180.0);
        }

      /* Axis code: 0 = x, 1 = y, 2 = z */
      w = EditorIDialogGetWidget(d, 1);
        if(w != NULL)
        {
          if(GTK_IS_TOGGLE_BUTTON(w))
          {
            if(GTK_TOGGLE_BUTTON(w)->active)
                axis_code = 0;
          }
      }
        w = EditorIDialogGetWidget(d, 2);
        if(w != NULL)
        {
            if(GTK_IS_TOGGLE_BUTTON(w))
            {
                if(GTK_TOGGLE_BUTTON(w)->active)
                    axis_code = 1;
            }
        }
        w = EditorIDialogGetWidget(d, 3);
        if(w != NULL)
        {
            if(GTK_IS_TOGGLE_BUTTON(w))
            {
                if(GTK_TOGGLE_BUTTON(w)->active)
                    axis_code = 2;
            }
        }

      /* Rotate normals check. */
        w = EditorIDialogGetWidget(d, 4);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
          rotate_normals = GTK_TOGGLE_BUTTON(w)->active;

      /* Orbital center. */
      w = EditorIDialogGetWidget(d, 5);
        if(w != NULL)
        {
            if(GTK_IS_ENTRY(w))
            {
                const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(cstrptr != NULL)
                center_x = atof(cstrptr);
          }
        }
        w = EditorIDialogGetWidget(d, 6);
        if(w != NULL)
        {
            if(GTK_IS_ENTRY(w))
            {
                const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                if(cstrptr != NULL)
                    center_y = atof(cstrptr);
            }
        }
        w = EditorIDialogGetWidget(d, 7);
        if(w != NULL)
        {
            if(GTK_IS_ENTRY(w))
            {
                const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                if(cstrptr != NULL)
                    center_z = atof(cstrptr);
            }
        }

      /* Record fetched values. */
      val_d = theta;
      VMACFGItemListMatchSetValue(
          option, VMA_CFG_PARM_ROTATE_PRIMITIVE_ANGLE,
          &val_d, FALSE
      );
        val_ui32 = axis_code + 1;   /* Cfg val starts from 1. */
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_ROTATE_PRIMITIVE_ORBITAL_AXIS,
            &val_ui32, FALSE
        );
        val_ui8 = rotate_normals;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_ROTATE_PRIMITIVE_ROTATE_NORMALS,
            &val_ui8, FALSE
        );


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

      /* Itterate through selected primitives. */
      first_sel_pn = ((editor->total_selected_primitives == 1) ?
          editor->selected_primitive[0] : -1
      );
      for(i = 0; i < editor->total_selected_primitives; i++)
      {
          pn = editor->selected_primitive[i];
          p = V3DMPListGetPtr(
            model_ptr->primitive, model_ptr->total_primitives, pn
          );
          if(p == NULL)
            continue;

          /* Rotate all vertex on primitive. */
          for(vtx_num = 0; 1; vtx_num++)
          {
            vma_undo_set_vertex_struct *uv;
            gdouble a[3 * 1], r[3 * 1];

            /* Get next vertex on primitive. */
            vtx_ptr = V3DMPGetVertex(p, vtx_num);
            if(vtx_ptr == NULL)
                break;

            /* Update has changes on editor. */
            EDITOR_DO_UPDATE_HAS_CHANGES

            /* Record undo. */
            uv = (vma_undo_set_vertex_struct *)VMAUndoNew(
                VMA_UNDO_TYPE_SET_VERTEX, "Set Vertex"
            );
            if(uv != NULL)
            {
                    uv->editor = editor;
                    uv->model_num = model_num;
                    uv->primitive_num = pn;
                    uv->vertex_num = vtx_num;
                    uv->v.x = vtx_ptr->x;
                    uv->v.y = vtx_ptr->y;
                    uv->v.z = vtx_ptr->z;
                    EditorUndoRecord(editor, uv);

                undos_recorded++;
                }

            /* Offset from center of orbit and set matrix a. */
            a[0] = vtx_ptr->x - center_x;
            a[1] = vtx_ptr->y - center_y;
            a[2] = vtx_ptr->z - center_z;
            switch(axis_code)
            {
              case 0:   /* Rotate about X axis. */
                MatrixRotatePitch3(a, theta, r);
                    vtx_ptr->x = r[0] + center_x;
                    vtx_ptr->y = r[1] + center_y;
                    vtx_ptr->z = r[2] + center_z;
                break;

                  case 1:       /* Rotate about Y axis. */
                    MatrixRotateBank3(a, theta, r);
                    vtx_ptr->x = r[0] + center_x;
                    vtx_ptr->y = r[1] + center_y;
                    vtx_ptr->z = r[2] + center_z;
                    break;

                  case 2:       /* Rotate about Z axis. */
                    MatrixRotateHeading3(a, theta, r);
                    vtx_ptr->x = r[0] + center_x;
                    vtx_ptr->y = r[1] + center_y;
                    vtx_ptr->z = r[2] + center_z;
                    break;
            }

            /* Rotate normals? */
                vtx_ptr = V3DMPGetNormal(p, vtx_num);
            if(rotate_normals && (vtx_ptr != NULL))
            {
                vma_undo_set_normal_struct *un;

                    /* Record undo. */
                    un = (vma_undo_set_normal_struct *)VMAUndoNew(
                        VMA_UNDO_TYPE_SET_NORMAL, "Set Normal"
                    );
                    if(un != NULL)
                    {
                        un->editor = editor;
                        un->model_num = model_num;
                        un->primitive_num = pn;
                        un->vertex_num = vtx_num;
                        un->n.x = vtx_ptr->x;
                        un->n.y = vtx_ptr->y;
                        un->n.z = vtx_ptr->z;
                        EditorUndoRecord(editor, un);

                  undos_recorded++;
                    }

                    /* Set matrix a (no offset). */
                    a[0] = vtx_ptr->x;
                    a[1] = vtx_ptr->y;
                    a[2] = vtx_ptr->z;

                    switch(axis_code)
                    {
                      case 0:       /* Rotate about X axis. */
                        MatrixRotatePitch3(a, theta, r);
                        vtx_ptr->x = r[0];
                        vtx_ptr->y = r[1];
                        vtx_ptr->z = r[2];
                        break;

                      case 1:       /* Rotate about Y axis. */
                        MatrixRotateBank3(a, theta, r);
                        vtx_ptr->x = r[0];
                        vtx_ptr->y = r[1];
                        vtx_ptr->z = r[2];
                        break;

                      case 2:       /* Rotate about Z axis. */
                        MatrixRotateHeading3(a, theta, r);
                        vtx_ptr->x = r[0];
                        vtx_ptr->y = r[1];
                        vtx_ptr->z = r[2];
                        break;
                    }
            }
          }
      }

      /* Was there a single selected primitive? */
      if(first_sel_pn > -1)
      {
          GtkCList *values_list = (GtkCList *)editor->values_list;

          /* Get previously selected value. */
          vtx_num = EditorGetSelected(
                editor->selected_value, editor->total_selected_values,
                0
            );

          /* Get first selected primitive. */
          p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, first_sel_pn
            );
          if(p != NULL)
          {
            /* Refetch values list. */
                EditorListDeleteValuesG(editor);
                EditorListAddValuesRG(editor, p);

                /* Reselect last value item. */
                if((values_list != NULL) && (vtx_num > -1))
                    gtk_clist_select_row(values_list, vtx_num, 0);
          }
      }

        /* Update repeat values for each new undo. */
        for(i = 0; i < undos_recorded; i++)
        {
            u = VMAUndoListGetPtr(
                editor->undo, editor->total_undos,
                editor->total_undos - i - 1
            );
            VMAUndoSetRepeats(u, undos_recorded);
        }

        /* Update menus and redraw views. */
        EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor);
        EditorRedrawAllViews(editor);

      EditorSetReady(editor);
}


/*
 *    Translate selected primitives callback.
 */
void EditorPrimitiveTranslateCB(GtkWidget *widget, gpointer data)
{
        const char *msglist[] = VMA_MSGLIST_EDITOR_TOOLTIPS;
        GtkWidget *parent;
        ma_editor_idialog_struct *d;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        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     X translate entry
           *      1     Y translate entry
           *      2     Z translate entry
             */
            void *entry;
            GtkWidget *w, *parent2;
          char num_str[256];
          char fmt_str[80];

          /* Translate table. */
            w = gtk_table_new(1, 4, FALSE);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;
            /* Label. */
            w = gtk_label_new("Translate");
            gtk_table_attach(GTK_TABLE(parent2), w,
                0, 1, 0, 1,
                0, 0,
                2, 2
            );
            gtk_widget_show(w);

            /* X translate entry. */
            w = GUIPromptBar(
                NULL, "X:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                1, 2, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
          sprintf(num_str, fmt_str,
            VMACFGItemListGetValueD(
                    option, VMA_CFG_PARM_TRANSLATE_PRIMITIVE_X
                )
          );
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_TRANSLATE_PRIMITIVE_X
                )
            );
            gtk_widget_show(w);
            /* Record as widget 0. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* Y translate entry. */
            w = GUIPromptBar(
                NULL, "Y:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                2, 3, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str,
                VMACFGItemListGetValueD(
                    option, VMA_CFG_PARM_TRANSLATE_PRIMITIVE_Y
                )
          );
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_TRANSLATE_PRIMITIVE_Y
                )
            );
            gtk_widget_show(w);
            /* Record as widget 1. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* Z translate entry. */
            w = GUIPromptBar(
                NULL, "Z:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                3, 4, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str,
                VMACFGItemListGetValueD(
                    option, VMA_CFG_PARM_TRANSLATE_PRIMITIVE_Z
                )
          );
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_TRANSLATE_PRIMITIVE_Z
                )
            );
            gtk_widget_show(w);
            /* Record as widget 2. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);
        }
 
        EditorIDialogMap(
            editor, d,
            "Translate Primitives",
            "Translate", "Apply", "Close",
            (void *)editor,
            EditorPrimitiveTranslateOKCB,
            EditorPrimitiveTranslateApplyCB,
            EditorPrimitiveIDialogCancelCB
        );
}

/*
 *    Translate selected primitives input dialog ok callback.
 */
static void EditorPrimitiveTranslateOKCB(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;

        EditorPrimitiveTranslateApplyCB(idialog, data);

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

/*
 *    Translate selected primitives input dialog apply callback.
 */
static void EditorPrimitiveTranslateApplyCB(void *idialog, void *data)
{
      gint i, model_num, pn, first_sel_pn, vtx_num;
      gint undos_recorded = 0;
      v3d_model_struct *model_ptr;
      mp_vertex_struct *vtx_ptr;
      gpointer p, u;
      gdouble trans_x = 0.0, trans_y = 0.0, trans_z = 0.0;
      gdouble val_d;
      GtkWidget *w;
        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;

        /* Begin fetching values from widgets. */
        /* Translate entries. */
        w = EditorIDialogGetWidget(d, 0);
        if(w != NULL)
        {
            if(GTK_IS_ENTRY(w))
            {
                const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                if(cstrptr != NULL)
                    trans_x = atof(cstrptr);
            }
        }
        w = EditorIDialogGetWidget(d, 1);
        if(w != NULL)
        {
            if(GTK_IS_ENTRY(w))
            {
                const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                if(cstrptr != NULL)
                    trans_y = atof(cstrptr);
            }
        }
        w = EditorIDialogGetWidget(d, 2);
        if(w != NULL)
        {
            if(GTK_IS_ENTRY(w))
            {
                const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                if(cstrptr != NULL)
                    trans_z = atof(cstrptr);
            }
        }

        /* Record fetched values. */
        val_d = trans_x;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_TRANSLATE_PRIMITIVE_X,
            &val_d, FALSE
        );
        val_d = trans_y;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_TRANSLATE_PRIMITIVE_Y,
            &val_d, FALSE
        );
        val_d = trans_z;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_TRANSLATE_PRIMITIVE_Z,
            &val_d, FALSE
        );


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

        EditorSetBusy(editor);

        /* Itterate through selected primitives. */
        first_sel_pn = ((editor->total_selected_primitives == 1) ?
            editor->selected_primitive[0] : -1
        );
        for(i = 0; i < editor->total_selected_primitives; i++)
        {
            pn = editor->selected_primitive[i];
            p = V3DMPListGetPtr(   
                model_ptr->primitive, model_ptr->total_primitives, pn
            );
            if(p == NULL)
                continue;

            /* Translate all vertices on primitive. */
            for(vtx_num = 0; 1; vtx_num++)
            {
            vma_undo_set_vertex_struct *uv;

                /* Get next vertex on primitive. */
                vtx_ptr = V3DMPGetVertex(p, vtx_num);
                if(vtx_ptr == NULL)
                    break;

                /* Update has changes on editor. */
                EDITOR_DO_UPDATE_HAS_CHANGES

                /* Record undo. */
                uv = (vma_undo_set_vertex_struct *)VMAUndoNew(
                    VMA_UNDO_TYPE_SET_VERTEX, "Set Vertex"
                );
                if(uv != NULL)
                {
                    uv->editor = editor;
                    uv->model_num = model_num;
                    uv->primitive_num = pn;
                    uv->vertex_num = vtx_num;  
                    uv->v.x = vtx_ptr->x;
                    uv->v.y = vtx_ptr->y;
                    uv->v.z = vtx_ptr->z;
                    EditorUndoRecord(editor, uv);

                undos_recorded++;
                }

            /* Translate vertex. */
            vtx_ptr->x += trans_x;
                vtx_ptr->y += trans_y;
                vtx_ptr->z += trans_z;
          }
      }

        /* Was there a single selected primitive? */
        if(first_sel_pn > -1)
        {
            GtkCList *values_list = (GtkCList *)editor->values_list;

            /* Get previously selected value. */
            vtx_num = EditorGetSelected(
                editor->selected_value, editor->total_selected_values,
                0
            );

            /* Get first selected primitive. */
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, first_sel_pn
            );
            if(p != NULL)
            {   
                /* Refetch values list. */
                EditorListDeleteValuesG(editor);
                EditorListAddValuesRG(editor, p);
        
                /* Reselect last value item. */
                if((values_list != NULL) && (vtx_num > -1))
                    gtk_clist_select_row(values_list, vtx_num, 0);
            }
        }

        /* Update repeat values for each new undo. */
        for(i = 0; i < undos_recorded; i++)
        {
            u = VMAUndoListGetPtr(
                editor->undo, editor->total_undos,
                editor->total_undos - i - 1
            );
            VMAUndoSetRepeats(u, undos_recorded);
        }


        /* Update menus and redraw views. */
        EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor);
        EditorRedrawAllViews(editor);

        EditorSetReady(editor);
}


/*
 *    Scale by compoents check callback.
 */
static void EditorPrimitiveScaleCompoentCheckCB(GtkWidget *widget, gpointer data)
{
      GtkWidget *w;
      ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)data;
      if(d == NULL)
          return;

      /* Uniform scale radio active? */
      w = EditorIDialogGetWidget(d, 0);
      if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
      {
          if(GTK_TOGGLE_BUTTON(w)->active)
          {
                w = EditorIDialogGetWidget(d, 2);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, TRUE);
                w = EditorIDialogGetWidget(d, 3);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, FALSE);
            w = EditorIDialogGetWidget(d, 4);
            if(w != NULL)
                gtk_widget_set_sensitive(w, FALSE);
                w = EditorIDialogGetWidget(d, 5);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, FALSE);
          }
      }
      /* Compoent scale radio active? */
        w = EditorIDialogGetWidget(d, 1);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
        {    
            if(GTK_TOGGLE_BUTTON(w)->active)
            {
                w = EditorIDialogGetWidget(d, 2);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, FALSE); 
                w = EditorIDialogGetWidget(d, 3);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, TRUE);
                w = EditorIDialogGetWidget(d, 4);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, TRUE);
                w = EditorIDialogGetWidget(d, 5);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, TRUE);
            }
        }    
}

/*
 *    Scale primitives callback.
 */
void EditorPrimitiveScaleCB(GtkWidget *widget, gpointer data)
{
        const gchar *msglist[] = VMA_MSGLIST_EDITOR_TOOLTIPS;
        GtkWidget *parent;
        ma_editor_idialog_struct *d;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        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     Uniform scale radio
           *      1     Compoent scale radio
           *      2     Uniform scale entry
           *      3     Compoent scale X entry
           *      4     Compoent scale Y entry
           *      5     Compoent scale Z entry
           *      6     Scale origin X entry
             *  7       Scale origin Y entry
             *  8       Scale origin Z entry
             */
          gpointer entry;
          GtkWidget *w, *parent2;
          GSList *gslist;
          gint scale_type;
          gdouble scale_coeff;
          gchar num_str[256];
          gchar fmt_str[80];

            /* Table. */
            w = gtk_table_new(2, 4, FALSE);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;

          /* Get scale type (uniform or compoent scaling). */
          scale_type = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_TYPE
            );

          /* Uniform scale radio. */
          gslist = NULL;
            w = gtk_radio_button_new_with_label(gslist, "Uniform Scale");
            gtk_table_attach(GTK_TABLE(parent2), w,
                0, 1, 0, 1,
                0, 0,      
                2, 2
            );
            gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));
          gtk_widget_set_usize(w, 150, -1);  
            gtk_signal_connect(
                GTK_OBJECT(w), "toggled",
                GTK_SIGNAL_FUNC(EditorPrimitiveScaleCompoentCheckCB),
                (gpointer)d
            );
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_UNIFORM
                )
            );
            gtk_widget_show(w);
            /* Record as widget 0. */
            EditorIDialogRecordWidget(d, w);

            /* Compoent scale radio. */
            w = gtk_radio_button_new_with_label(gslist, "Compoent Scale");
            gtk_table_attach(GTK_TABLE(parent2), w,
                0, 1, 1, 2,
                0, 0,
                2, 2
            );
            gtk_widget_set_usize(w, 150, -1);
          gtk_signal_connect(
            GTK_OBJECT(w), "toggled",
                GTK_SIGNAL_FUNC(EditorPrimitiveScaleCompoentCheckCB),
                (gpointer)d
          );
            GUISetWidgetTip(
                w,                 
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_COMPOENT
                )
            );
            gtk_widget_show(w);
            /* Record as widget 1. */
            EditorIDialogRecordWidget(d, w);

          /* Uniform scale entry. */
            scale_coeff = VMACFGItemListGetValueD(
                option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_COEFF
            );
            w = GUIPromptBar(
                NULL, "S:",
                NULL, &entry
            );
          gtk_table_attach(GTK_TABLE(parent2), w,
                1, 2, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, scale_coeff);
          gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_COEFF
                )
            );
            gtk_widget_show(w);
            /* Record as widget 2. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* X compoent scale entry. */
          scale_coeff = VMACFGItemListGetValueD(
                option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_X
            );
            w = GUIPromptBar(
                NULL, "X:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                1, 2, 1, 2,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, scale_coeff);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
          gtk_widget_set_sensitive((GtkWidget *)entry, FALSE);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_COEFF
                )
            );
            gtk_widget_show(w);
            /* Record as widget 3. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* Y compoent scale entry. */
            scale_coeff = VMACFGItemListGetValueD(
                option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_Y
            );
            w = GUIPromptBar(
                NULL, "Y:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                2, 3, 1, 2,
                0, 0,
                2, 2 
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, scale_coeff);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
          gtk_widget_set_sensitive((GtkWidget *)entry, FALSE);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_COEFF
                )
            );
            gtk_widget_show(w);
            /* Record as widget 4. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* Z compoent scale entry. */
            scale_coeff = VMACFGItemListGetValueD(
                option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_Z
            );
            w = GUIPromptBar(
                NULL, "Z:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                3, 4, 1, 2,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, scale_coeff);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
          gtk_widget_set_sensitive((GtkWidget *)entry, FALSE);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_COEFF
                )
            );
            gtk_widget_show(w);
            /* Record as widget 5. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);


          w = gtk_hseparator_new();
          gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 5);
            gtk_widget_show(w);


          /* Scale origin table. */
            w = gtk_table_new(1, 4, FALSE);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;

          w = gtk_label_new("Scale Origin:");
            gtk_table_attach(GTK_TABLE(parent2), w,
                0, 1, 0, 1,
                0, 0,
                2, 2
            );
            gtk_widget_show(w);

            /* X scale origin entry. */
            w = GUIPromptBar(
                NULL, "X:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                1, 2, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, editor->vcursor_x);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_ORIGIN
                )
            );
            gtk_widget_show(w);
            /* Record as widget 6. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* Y scale origin entry. */
            w = GUIPromptBar(
                NULL, "Y:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                2, 3, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, editor->vcursor_y);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_ORIGIN
                )
            );
            gtk_widget_show(w);
            /* Record as widget 7. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);

            /* Z scale origin entry. */
            w = GUIPromptBar(
                NULL, "Z:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                3, 4, 0, 1,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, editor->vcursor_z);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_SCALE_PRIMITIVE_ORIGIN
                )
            );
            gtk_widget_show(w);
            /* Record as widget 8. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);


          /* Set scale type radio toggled state to TRUE depending on
           * uniform or compoent scale.
           */
          if(scale_type == 0)
            w = EditorIDialogGetWidget(d, 0);
          else
            w = EditorIDialogGetWidget(d, 1);
          if(w != NULL)
            gtk_toggle_button_set_active(
                GTK_TOGGLE_BUTTON(w), TRUE
            );
        }

        EditorIDialogMap(
            editor, d,
            "Scale Primitives",
            "Scale", "Apply", "Close",
            (void *)editor,
            EditorPrimitiveScaleOKCB,
            EditorPrimitiveScaleApplyCB,
            EditorPrimitiveIDialogCancelCB
        );
}

/*
 *    Scale primitives input dialog ok callback.
 */
static void EditorPrimitiveScaleOKCB(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;

        EditorPrimitiveScaleApplyCB(idialog, data);

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

/*
 *    Scale primitives input dialog apply callback.
 */
static void EditorPrimitiveScaleApplyCB(void *idialog, void *data)
{
      gint i, model_num, pn, first_sel_pn, vtx_num;
      gint undos_recorded = 0;
        v3d_model_struct *model_ptr;
        mp_vertex_struct *vtx_ptr;
      gpointer p;
      gint scale_type = 0;
      gdouble scale_x = 1.0, scale_y = 1.0, scale_z = 1.0, scale_u = 1.0;
      gdouble origin_x = 0.0, origin_y = 0.0, origin_z = 0.0;
      u_int32_t val_ui32;
      gdouble val_d;
        GtkWidget *w;
      gpointer u;
        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;

        /* Begin fetching values from widgets. */

      /* Get uniform scaling coeff (for cfg purposes). */
      w = EditorIDialogGetWidget(d, 2);
        if(w != NULL)
        {
            const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(cstrptr != NULL)
                scale_u = atof(cstrptr);
        }

        /* Check which radio is toggled. */
      /* Uniform scaling radio. */
        w = EditorIDialogGetWidget(d, 0);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
        {
          if(GTK_TOGGLE_BUTTON(w)->active)
          {
            scale_type = 0;

            /* Get scaling compoents from uniform scale entry. */
            w = EditorIDialogGetWidget(d, 2);
            if(w != NULL)
            {
                  const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                  if(cstrptr != NULL)
                    scale_x = atof(cstrptr);
              scale_y = scale_x;
              scale_z = scale_y;
            }
            }
        }
      /* Compoent scaling radio. */
        w = EditorIDialogGetWidget(d, 1);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
        {
            if(GTK_TOGGLE_BUTTON(w)->active)
            {
            scale_type = 1;

                /* Get scaling compoents from compoent scale entries. */
                w = EditorIDialogGetWidget(d, 3);
                if(w != NULL)
                {
                  const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                  if(cstrptr != NULL)
                    scale_x = atof(cstrptr);
                }
                w = EditorIDialogGetWidget(d, 4);
                if(w != NULL)
                {
                  const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                  if(cstrptr != NULL)
                    scale_y = atof(cstrptr);
                }
                w = EditorIDialogGetWidget(d, 5);
                if(w != NULL)
                {
                  const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
                  if(cstrptr != NULL)
                    scale_z = atof(cstrptr);
                }
            }
        }

      /* Get scale origin. */
        w = EditorIDialogGetWidget(d, 6);
        if((w == NULL) ? 0 : GTK_IS_ENTRY(w))
        {
          const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
          if(cstrptr != NULL)
            origin_x = atof(cstrptr);
        }
        w = EditorIDialogGetWidget(d, 7);
        if((w == NULL) ? 0 : GTK_IS_ENTRY(w))
        {
            const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(cstrptr != NULL)
                origin_y = atof(cstrptr);
        }
        w = EditorIDialogGetWidget(d, 8);
        if((w == NULL) ? 0 : GTK_IS_ENTRY(w))
        {
            const gchar *cstrptr = gtk_entry_get_text(GTK_ENTRY(w));
            if(cstrptr != NULL)
                origin_z = atof(cstrptr);
        }

        /* Record fetched values. */
        val_ui32 = scale_type;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_TYPE,
            &val_ui32, FALSE
        );
        val_d = scale_u;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_COEFF,
            &val_d, FALSE
        );
        val_d = scale_x;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_X,
            &val_d, FALSE
        );
        val_d = scale_y;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_Y,
            &val_d, FALSE
        );
        val_d = scale_z;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SCALE_PRIMITIVE_SCALE_Z,
            &val_d, FALSE
        );


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

        EditorSetBusy(editor);

        /* Itterate through selected primitives. */
        first_sel_pn = ((editor->total_selected_primitives == 1) ?
            editor->selected_primitive[0] : -1
        );
        for(i = 0; i < editor->total_selected_primitives; i++)
        {
            pn = editor->selected_primitive[i];
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, pn
            );
            if(p == NULL)
                continue;

            /* Rotate all vertex on primitive. */
            for(vtx_num = 0; 1; vtx_num++)
            {
            mp_vertex_struct tmp_vtx;
            vma_undo_set_vertex_struct *uv;

                /* Get next vertex on primitive. */
                vtx_ptr = V3DMPGetVertex(p, vtx_num);
                if(vtx_ptr == NULL)
                    break;

                /* Update has changes on editor. */
                EDITOR_DO_UPDATE_HAS_CHANGES

                /* Record undo. */
                uv = (vma_undo_set_vertex_struct *)VMAUndoNew(
                    VMA_UNDO_TYPE_SET_VERTEX, "Set Vertex"
                );
                if(uv != NULL)
                {
                    uv->editor = editor;
                    uv->model_num = model_num;
                    uv->primitive_num = pn;
                    uv->vertex_num = vtx_num;
                    uv->v.x = vtx_ptr->x;
                    uv->v.y = vtx_ptr->y;
                    uv->v.z = vtx_ptr->z;
                    EditorUndoRecord(editor, uv);

                undos_recorded++;
                }

            /* Calculate tempory vertex oriented at origin center. */
            tmp_vtx.x = vtx_ptr->x - origin_x;
                tmp_vtx.y = vtx_ptr->y - origin_y;
                tmp_vtx.z = vtx_ptr->z - origin_z;

            /* Scale and remove origin. */
            vtx_ptr->x = (tmp_vtx.x * scale_x) + origin_x;
                vtx_ptr->y = (tmp_vtx.y * scale_y) + origin_y;
                vtx_ptr->z = (tmp_vtx.z * scale_z) + origin_z;
          }
      }


        /* Was there a single selected primitive? */
        if(first_sel_pn > -1)
        {
            GtkCList *values_list = (GtkCList *)editor->values_list;

            /* Get previously selected value item. */
            vtx_num = EditorGetSelected(
                editor->selected_value, editor->total_selected_values,
                0    
            );

            /* Get first selected primitive. */
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, first_sel_pn
            );
            if(p != NULL)
            {
                /* Refetch values list. */
                EditorListDeleteValuesG(editor);
                EditorListAddValuesRG(editor, p);

                /* Reselect last value item. */
                if((values_list != NULL) && (vtx_num > -1))
                    gtk_clist_select_row(values_list, vtx_num, 0);
            }
      }

        /* Update repeat values for each new undo. */
        for(i = 0; i < undos_recorded; i++)
        {
            u = VMAUndoListGetPtr(
                editor->undo, editor->total_undos,
                editor->total_undos - i - 1
            );
            VMAUndoSetRepeats(u, undos_recorded);
        }


        /* Update menus and redraw views. */
        EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor);
        EditorRedrawAllViews(editor);

        EditorSetReady(editor);
}


/*
 *    Mirror primitives flip winding check callback.
 */
static void EditorPrimitiveMirrorFlipWindingCheckCB(
      GtkWidget *widget, gpointer data
)
{
        GtkWidget *w;
        ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)data;
        if(d == NULL)
            return;

        /* Flip winding check active? */
        w = EditorIDialogGetWidget(d, 6);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
        {
            if(GTK_TOGGLE_BUTTON(w)->active)
            {
                w = EditorIDialogGetWidget(d, 7);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, TRUE);
                w = EditorIDialogGetWidget(d, 8);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, TRUE);
            }
          else
          {
                w = EditorIDialogGetWidget(d, 7);
                if(w != NULL)
                    gtk_widget_set_sensitive(w, FALSE);
                w = EditorIDialogGetWidget(d, 8);   
                if(w != NULL)
                    gtk_widget_set_sensitive(w, FALSE);
          }
        }
}


/*
 *    Mirror primitives callback.
 */
void EditorPrimitiveMirrorCB(GtkWidget *widget, gpointer data)
{
        const gchar *msglist[] = VMA_MSGLIST_EDITOR_TOOLTIPS;
        GtkWidget *parent;
        ma_editor_idialog_struct *d;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        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     X axis mirror radio
           *      1     Y axis mirror radio
           *      2     Z axis mirror radio
           *      3     Offset from axis entry
           *      4     Mirror normals check
           *      5     Mirror texcoords check
           *      6     Flip winding check
           *      7     Wind normals check
           *      8     Wind texcoods check
             */
          gpointer entry;
            GtkWidget *w, *parent2;
          gint mirror_axis;
          gdouble axis_offset;
          gbool mirror_normals, mirror_texcoords, flip_winding;
          gbool wind_normals, wind_texcoords;
            GSList *gslist;
            gchar num_str[256];
            gchar fmt_str[80];


            /* Orbital axis table. */
            w = gtk_table_new(2, 4, FALSE);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;
            /* Label. */
            w = gtk_label_new("Mirror Axis:");
            gtk_table_attach(GTK_TABLE(parent2), w,
                0, 1, 0, 1,
                0, 0,
                2, 2
            );
            gtk_widget_show(w);

          /* Get axis to mirror about. */
            mirror_axis = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_MIRROR_PRIMITIVE_MIRROR_AXIS
            );

            /* X axis mirror radio. */
            gslist = NULL;
            w = gtk_radio_button_new_with_label(gslist, "X");
            gtk_table_attach(GTK_TABLE(parent2), w,
                1, 2, 0, 1,
                0, 0,
                2, 2
            );
            gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));
            if(mirror_axis == 1)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            GUISetWidgetTip(
                w,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_MIRROR_PRIMITIVE_AXIS
                )
            );
            gtk_widget_show(w);
            /* Record as widget 0. */
            EditorIDialogRecordWidget(d, w);

            /* Y axis mirror radio. */
            w = gtk_radio_button_new_with_label(gslist, "Y");
            gtk_table_attach(GTK_TABLE(parent2), w,
                2, 3, 0, 1,
                0, 0,
                2, 2 
            );
            gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));
            if(mirror_axis == 2)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            GUISetWidgetTip(   
                w,                 
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_MIRROR_PRIMITIVE_AXIS 
                )
            );
            gtk_widget_show(w);
            /* Record as widget 1. */
            EditorIDialogRecordWidget(d, w);

            /* Z axis mirror radio. */
            w = gtk_radio_button_new_with_label(gslist, "Z");
            gtk_table_attach(GTK_TABLE(parent2), w,
                3, 4, 0, 1,
                0, 0,
                2, 2 
            );
            gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));
          if(mirror_axis == 3)
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            GUISetWidgetTip(   
                w,                 
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_MIRROR_PRIMITIVE_AXIS 
                )
            );
            gtk_widget_show(w);
            /* Record as widget 2. */
            EditorIDialogRecordWidget(d, w);

            /* Offset from axis entry. */
          axis_offset = VMACFGItemListGetValueD(
                option, VMA_CFG_PARM_MIRROR_PRIMITIVE_AXIS_OFFSET
            );
            w = GUIPromptBar(
                NULL, "Offset From Axis:",
                NULL, &entry
            );
            gtk_table_attach(GTK_TABLE(parent2), w,
                0, 4, 1, 2,
                0, 0,
                2, 2
            );
            sprintf(fmt_str, "%%.%if", editor->vertex_decimals);
            sprintf(num_str, fmt_str, axis_offset);
            gtk_entry_set_text((GtkEntry *)entry, num_str);
            gtk_widget_set_usize((GtkWidget *)entry, 100, -1);
            GUISetWidgetTip(   
                (GtkWidget *)entry,
                MsgListMatchCaseMessage(
                    msglist,
                    VMA_MSGNAME_EDITOR_MIRROR_PRIMITIVE_OFFSET
                )
            );
            gtk_widget_show(w);
            /* Record as widget 3. */
            EditorIDialogRecordWidget(d, (GtkWidget *)entry);


            w = gtk_hseparator_new();
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 5);
            gtk_widget_show(w);


            /* Mirror normals check. */
            mirror_normals = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_MIRROR_PRIMITIVE_MIRROR_NORMALS
            );
            w = gtk_check_button_new_with_label("Mirror Normals");
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
          if(mirror_normals)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 4. */
            EditorIDialogRecordWidget(d, w);

            /* Mirror texcoords check. */
            mirror_texcoords = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_MIRROR_PRIMITIVE_MIRROR_TEXCOORDS
            );
            w = gtk_check_button_new_with_label("Mirror Texcoords");
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
          if(mirror_texcoords)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 5. */
            EditorIDialogRecordWidget(d, w);


            /* Flip winding check. */
            flip_winding = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_MIRROR_PRIMITIVE_FLIP_WINDING
            );
            w = gtk_check_button_new_with_label("Flip Winding");
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
          if(flip_winding)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            gtk_signal_connect(
                GTK_OBJECT(w), "toggled",
                GTK_SIGNAL_FUNC(EditorPrimitiveMirrorFlipWindingCheckCB),
                (gpointer)d
            );
            /* Record as widget 6. */
            EditorIDialogRecordWidget(d, w);

            /* Wind normals check. */
            wind_normals = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_MIRROR_PRIMITIVE_WIND_NORMALS
            );
            w = gtk_check_button_new_with_label("Wind Normals");
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
          if(wind_normals)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 7. */
            EditorIDialogRecordWidget(d, w);

            /* Wind texcoords check. */
            wind_texcoords = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_MIRROR_PRIMITIVE_WIND_TEXCOORDS
            );
            w = gtk_check_button_new_with_label("Wind Texcoords");
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
          if(wind_texcoords)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 8. */
            EditorIDialogRecordWidget(d, w);
      }

        EditorIDialogMap(
            editor, d,
            "Mirror Primitives",
            "Mirror", "Apply", "Close",
            (void *)editor,
            EditorPrimitiveMirrorOKCB,
            EditorPrimitiveMirrorApplyCB,
            EditorPrimitiveIDialogCancelCB
        );
}

/*
 *      Mirror selected primitives input dialog ok callback.
 */
static void EditorPrimitiveMirrorOKCB(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;

        EditorPrimitiveMirrorApplyCB(idialog, data);

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

/*
 *    Mirror selected primitives input dialog apply callback.
 */
static void EditorPrimitiveMirrorApplyCB(void *idialog, void *data)
{
      gint i, model_num, pn, first_sel_pn, vtx_num;
      gint undos_recorded = 0;
        v3d_model_struct *model_ptr;
      mp_vertex_struct *vtx_ptr;
        gpointer p;
      gint axis_code = 0;
        gdouble axis_offset = 1.0;
      gbool mirror_normals = TRUE,
            mirror_texcoords = TRUE,
            flip_winding = TRUE,
            wind_normals = TRUE,
            wind_texcoords = TRUE;
      u_int8_t val_ui8;
      u_int32_t val_ui32;
      gdouble val_d;
        GtkWidget *w;
      gpointer u;
        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;

        /* Begin fetching values from widgets. */
        /* Check which radio is toggled. */
        /* X axis mirror radio. */ 
        w = EditorIDialogGetWidget(d, 0);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
        {
            if(GTK_TOGGLE_BUTTON(w)->active)
            axis_code = 0;
        }
        /* Y axis mirror radio. */    
        w = EditorIDialogGetWidget(d, 1);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
        {
            if(GTK_TOGGLE_BUTTON(w)->active)
                axis_code = 1;
        }
        /* Z axis mirror radio. */
        w = EditorIDialogGetWidget(d, 2);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
        {
            if(GTK_TOGGLE_BUTTON(w)->active)
                axis_code = 2;
        }

        /* Axis offset entry. */
        w = EditorIDialogGetWidget(d, 3);
        if((w == NULL) ? 0 : GTK_IS_ENTRY(w))
        {
          const gchar *cstrptr = (const gchar *)gtk_entry_get_text(
            GTK_ENTRY(w)
          );
          if(cstrptr != NULL)
            axis_offset = atof(cstrptr);
        }

        /* Mirror normals check. */
        w = EditorIDialogGetWidget(d, 4);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
          mirror_normals = GTK_TOGGLE_BUTTON(w)->active;

        /* Mirror texcoords check. */
        w = EditorIDialogGetWidget(d, 5);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            mirror_texcoords = GTK_TOGGLE_BUTTON(w)->active;

        /* Flip winding check. */
        w = EditorIDialogGetWidget(d, 6);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            flip_winding = GTK_TOGGLE_BUTTON(w)->active;

        /* Wind normals check. */
        w = EditorIDialogGetWidget(d, 7);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            wind_normals = GTK_TOGGLE_BUTTON(w)->active;

        /* Wind texcoords check. */
        w = EditorIDialogGetWidget(d, 8);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            wind_texcoords = GTK_TOGGLE_BUTTON(w)->active;


        /* Record fetched values. */
      val_ui32 = axis_code + 1;     /* Cfg vals for this start from 1. */
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_MIRROR_PRIMITIVE_MIRROR_AXIS,
            &val_ui32, FALSE
        );
        val_d = axis_offset;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_MIRROR_PRIMITIVE_AXIS_OFFSET,
            &val_d, FALSE
        );
        val_ui8 = mirror_normals;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_MIRROR_PRIMITIVE_MIRROR_NORMALS,
            &val_ui8, FALSE
        );
        val_ui8 = mirror_texcoords;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_MIRROR_PRIMITIVE_MIRROR_TEXCOORDS,
            &val_ui8, FALSE
        );
        val_ui8 = flip_winding;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_MIRROR_PRIMITIVE_FLIP_WINDING,
            &val_ui8, FALSE
        );
        val_ui8 = wind_normals;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_MIRROR_PRIMITIVE_WIND_NORMALS,
            &val_ui8, FALSE
        );
        val_ui8 = wind_texcoords;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_MIRROR_PRIMITIVE_WIND_TEXCOORDS,
            &val_ui8, FALSE
        );


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

        EditorSetBusy(editor);

        /* Itterate through selected primitives. */
        first_sel_pn = ((editor->total_selected_primitives == 1) ?
            editor->selected_primitive[0] : -1
        );
        for(i = 0; i < editor->total_selected_primitives; i++)
        {
            pn = editor->selected_primitive[i];
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, pn
            );
            if(p == NULL)
                continue;

            /* Mirror all vertex on primitive. */
            for(vtx_num = 0; 1; vtx_num++)
            {
                mp_vertex_struct tmp_vtx;
            vma_undo_set_vertex_struct *uv;

                /* Get next vertex on primitive. */
                vtx_ptr = V3DMPGetVertex(p, vtx_num);
                if(vtx_ptr == NULL)
                    break;

                /* Update has changes on editor. */
                EDITOR_DO_UPDATE_HAS_CHANGES

                /* Record undo. */
                uv = (vma_undo_set_vertex_struct *)VMAUndoNew(
                    VMA_UNDO_TYPE_SET_VERTEX, "Set Vertex"
                );
                if(uv != NULL)
                {
                    uv->editor = editor;
                    uv->model_num = model_num;
                    uv->primitive_num = pn;
                    uv->vertex_num = vtx_num;
                    uv->v.x = vtx_ptr->x;
                    uv->v.y = vtx_ptr->y;
                    uv->v.z = vtx_ptr->z;
                    EditorUndoRecord(editor, uv);

                undos_recorded++;
                }

            /* Offset, mirror, reoffset vertex. */
            switch(axis_code)
            {
                  case 0:       /* Mirror along X axis. */
                    tmp_vtx.x = (vtx_ptr->x - axis_offset) * -1;
                    vtx_ptr->x = tmp_vtx.x + axis_offset;
                    break;

                  case 1:       /* Mirror along Y axis. */
                    tmp_vtx.y = (vtx_ptr->y - axis_offset) * -1;
                    vtx_ptr->y = tmp_vtx.y + axis_offset;
                    break;

                  case 2:       /* Mirror along Z axis. */
                    tmp_vtx.z = (vtx_ptr->z - axis_offset) * -1;
                    vtx_ptr->z = tmp_vtx.z + axis_offset;
                    break;
            }
          }
            /* Mirror all normals on primitive. */
            for(vtx_num = 0; mirror_normals; vtx_num++)
            {
            vma_undo_set_normal_struct *un;

                /* Get next normal on primitive. */
                vtx_ptr = V3DMPGetNormal(p, vtx_num);
                if(vtx_ptr == NULL)
                    break;

                /* Update has changes on editor. */
                EDITOR_DO_UPDATE_HAS_CHANGES

                /* Record undo. */
                un = (vma_undo_set_normal_struct *)VMAUndoNew(
                    VMA_UNDO_TYPE_SET_NORMAL, "Set Normal"
                );
                if(un != NULL)
                {
                    un->editor = editor;
                    un->model_num = model_num;
                    un->primitive_num = pn;
                    un->vertex_num = vtx_num;
                    un->n.x = vtx_ptr->x;
                    un->n.y = vtx_ptr->y;
                    un->n.z = vtx_ptr->z;
                    EditorUndoRecord(editor, un);

                undos_recorded++;
                }

                /* Mirror normal. */
                switch(axis_code)
                {
                  case 0:       /* Mirror along X axis. */
                    vtx_ptr->x *= -1.0;
                    break;

                  case 1:       /* Mirror along Y axis. */
                    vtx_ptr->y *= -1.0;
                    break;

                  case 2:       /* Mirror along Z axis. */
                    vtx_ptr->z *= -1.0;
                    break;
                }
          }
            /* Mirror all texcoords on primitive. */
            for(vtx_num = 0; mirror_texcoords; vtx_num++)
            {
                mp_vertex_struct tmp_vtx;
                vma_undo_set_texcoord_struct *ut;

                /* Get next texcoord on primitive. */
                vtx_ptr = V3DMPGetTexCoord(p, vtx_num);
                if(vtx_ptr == NULL)
                    break;

                /* Update has changes on editor. */
                EDITOR_DO_UPDATE_HAS_CHANGES

                /* Record undo. */
                ut = (vma_undo_set_texcoord_struct *)VMAUndoNew(
                    VMA_UNDO_TYPE_SET_TEXCOORD, "Set TexCoord"
                );
                if(ut != NULL)
                {
                    ut->editor = editor;
                    ut->model_num = model_num;
                    ut->primitive_num = pn;
                    ut->vertex_num = vtx_num;
                    ut->tc.x = vtx_ptr->x;
                    ut->tc.y = vtx_ptr->y;
                    ut->tc.z = vtx_ptr->z;
                    EditorUndoRecord(editor, ut);

                undos_recorded++;
                }

                /* Offset, mirror, reoffset texcoord. */
                switch(axis_code)
                {
                  case 0:       /* Mirror along X axis. */
                    tmp_vtx.x = (vtx_ptr->x - axis_offset) * -1;
                    vtx_ptr->x = tmp_vtx.x + axis_offset;
                    break;

                  case 1:       /* Mirror along Y axis. */
                    tmp_vtx.y = (vtx_ptr->y - axis_offset) * -1;
                    vtx_ptr->y = tmp_vtx.y + axis_offset;
                    break;

                  case 2:       /* Mirror along Z axis. */
                    tmp_vtx.z = (vtx_ptr->z - axis_offset) * -1;
                    vtx_ptr->z = tmp_vtx.z + axis_offset;
                    break;
                }
            }

          /* Flip winding? */
          if(flip_winding)
          {
            vma_undo_flip_winding_struct *uf;


            /* Flip winding. */
            V3DMPFlipWinding(p, wind_normals, wind_texcoords);

            /* Need to record undo for flip winding. */
                uf = (vma_undo_flip_winding_struct *)VMAUndoNew(
                    VMA_UNDO_TYPE_FLIP_WINDING,
                    "Flip Winding"
                );
                if(uf != NULL)
                {
                    uf->editor = editor;
                    uf->model_num = model_num;
                    uf->primitive_num = pn;
                    EditorUndoRecord(editor, uf);

                    undos_recorded++;
                }
          }
      }

      /* Was there a single selected primitive? */
        if(first_sel_pn > -1)
        {
            GtkCList *values_list = (GtkCList *)editor->values_list;

            /* Get previously selected value. */
            vtx_num = EditorGetSelected(
                editor->selected_value, editor->total_selected_values,
                0    
            );

            /* Get first selected primitive. */
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, first_sel_pn
            );
            if(p != NULL)
            {
                /* Refetch values list. */
                EditorListDeleteValuesG(editor);
                EditorListAddValuesRG(editor, p);

                /* Reselect last value item. */
                if((values_list != NULL) && (vtx_num > -1))
                    gtk_clist_select_row(values_list, vtx_num, 0);
            }
        }

        /* Update repeat values for each new undo. */
        for(i = 0; i < undos_recorded; i++)
        {
            u = VMAUndoListGetPtr(
                editor->undo, editor->total_undos,
                editor->total_undos - i - 1
            );
            VMAUndoSetRepeats(u, undos_recorded);
        }


        /* Update menus and redraw views. */
        EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor);
        EditorRedrawAllViews(editor);

        EditorSetReady(editor);
}


/*
 *    Snaps value x to the number of digits given by dig.
 */
static gdouble EditorPrimitiveSnapDigits(gdouble x, gint dig)
{
      gdouble lbase = pow(10, dig);
      return(lbase * floor((x + 0.5) / lbase));
}

/*
 *    Snaps value x to the number of decimaps given by dec.
 */
static gdouble EditorPrimitiveSnapDecimals(gdouble x, gint dec)
{
        gdouble scale = pow(10, dec);
        return(floor((x * scale) + 0.5) / scale);
}

/*
 *    Snap test button callback.
 */
static void EditorPrimitiveSnapTestCB(GtkWidget *widget, gpointer data)
{
      gdouble x = 0.0;
      gbool snap_to_digits = FALSE;
      gint resolution = 0;
      GtkWidget *w;
      ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)data;
        if(d == NULL)
            return;

        /* Sample entry. */
        w = EditorIDialogGetWidget(d, 9);
        if((w == NULL) ? 0 : GTK_IS_ENTRY(w))
        {
            const gchar *cstrptr = (const gchar *)gtk_entry_get_text(
            GTK_ENTRY(w)
          );
          if(cstrptr != NULL)
            x = atof(cstrptr);
        }

        /* Get digits (TRUE) or decimals (FALSE). */
        /* Digits radio. */  
        w = EditorIDialogGetWidget(d, 6);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            snap_to_digits = GTK_TOGGLE_BUTTON(w)->active;

        /* Get resolution. */
        w = EditorIDialogGetWidget(d, 8);
        if((w == NULL) ? 0 : GTK_IS_SPIN_BUTTON(w))
      {
            resolution = gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(w)
            );
          if(resolution < 0)
            resolution = 0;
      }

      /* Snap to digits or snap to decimals? */
      if(snap_to_digits)
          x = EditorPrimitiveSnapDigits(x, resolution);
      else
          x = EditorPrimitiveSnapDecimals(x, resolution);

        /* Update sample entry. */
        w = EditorIDialogGetWidget(d, 9);
        if((w == NULL) ? 0 : GTK_IS_ENTRY(w))
        {
          gchar num_str[256];

          if(snap_to_digits)
            sprintf(num_str, "%.0f", x);
          else
            sprintf(num_str, "%f", x);

            gtk_entry_set_text(GTK_ENTRY(w), num_str);
        }
}

/*
 *    Snap reest button callback.
 */
static void EditorPrimitiveSnapResetCB(GtkWidget *widget, gpointer data)
{
        GtkWidget *w;
        ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)data;
        if(d == NULL)
            return;

        /* Sample entry. */
        w = EditorIDialogGetWidget(d, 9);
        if((w == NULL) ? 0 : GTK_IS_ENTRY(w))
      {
          gtk_entry_set_text(GTK_ENTRY(w), "12345.12345");
      }
}

/*
 *    Snap digits/decimals radio callback.
 */
static void EditorPrimitiveSnapRadioCB(GtkWidget *widget, gpointer data)
{
        GtkToggleButton *tb = (GtkToggleButton *)widget;
        ma_editor_idialog_struct *d = (ma_editor_idialog_struct *)data;
        if((tb == NULL) || (d == NULL))
            return;


}

/*
 *    Snap primitives callback.
 */
void EditorPrimitiveSnapCB(GtkWidget *widget, gpointer data)
{
/*    const gchar *msglist[] = VMA_MSGLIST_EDITOR_TOOLTIPS; */
      GtkWidget *parent;
      ma_editor_idialog_struct *d;
      ma_editor_struct *editor = (ma_editor_struct *)data;
      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     Snap vertices check
             *    1     Snap normals check
             *    2     Snap texcoords check
           *      3     Snap X axis values check
           *      4     Snap Y axis values check
           *      5     Snap Z axis values check
             *    6     Snap digits radio
           *      7     Snap decimals radio
           *      8     Resolution digits/decimals spin
           *  9   Sample entry
           *      10    Sample test button
           *      11    Sample reset button
           */
          gint border_major = 5, border_minor = 2;
          gint bw = 100 + (2 * 3), bh = 30 + (2 * 3);
          GtkAdjustment *adj;
            GtkWidget *w, *parent2;
            gbool snap_vertices, snap_normals, snap_texcoords, snap_to_digits;
          gint resolution;
            GSList *gslist;


          /* Main vbox. */
          w = gtk_vbox_new(FALSE, border_major);
          gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
          parent = w;

            /* Hbox for snap checks. */
            w = gtk_hbox_new(FALSE, border_minor);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;

          w = gtk_label_new("Snap:");
          gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_widget_show(w);

          /* Snap vertices. */
            snap_vertices = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_SNAP_PRIMITIVE_VERTICES
            );
            w = gtk_check_button_new_with_label("Vertices");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            if(snap_vertices)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 0. */
            EditorIDialogRecordWidget(d, w);

            /* Snap normals. */
            snap_normals = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_SNAP_PRIMITIVE_NORMALS
            );
            w = gtk_check_button_new_with_label("Normals");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            if(snap_normals)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 1. */
            EditorIDialogRecordWidget(d, w);

            /* Snap texcoords. */
            snap_texcoords = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_SNAP_PRIMITIVE_TEXCOORDS
            );
            w = gtk_check_button_new_with_label("TexCoords");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            if(snap_texcoords)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 2. */
            EditorIDialogRecordWidget(d, w);


            /* Hbox for axis checks. */
            w = gtk_hbox_new(FALSE, border_minor);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;

            w = gtk_label_new("On Dimension(s):");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_widget_show(w);

            /* Snap X axis values. */
            w = gtk_check_button_new_with_label("X");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            if(VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_AXIS_X
          ))
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 3. */
            EditorIDialogRecordWidget(d, w);

            /* Snap Y axis values. */
            w = gtk_check_button_new_with_label("Y");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            if(VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_SNAP_PRIMITIVE_AXIS_Y
            ))
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 4. */
            EditorIDialogRecordWidget(d, w);

            /* Snap Z axis values. */
            w = gtk_check_button_new_with_label("Z");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            if(VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_SNAP_PRIMITIVE_AXIS_Z
            ))
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_widget_show(w);
            /* Record as widget 5. */
            EditorIDialogRecordWidget(d, w);


          /* Hbox for digits/decimals radio. */
          w = gtk_hbox_new(FALSE, border_minor);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
            parent2 = w;

            w = gtk_label_new("To:");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_widget_show(w);

          /* Get setting to snap to digits or decimals. */
          snap_to_digits = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_SNAP_PRIMITIVE_DIGITS
            );

            /* Digits. */
            gslist = NULL;
            w = gtk_radio_button_new_with_label(gslist, "Digits");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));
            if(snap_to_digits)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_signal_connect(
                GTK_OBJECT(w), "toggled",
            GTK_SIGNAL_FUNC(EditorPrimitiveSnapRadioCB),
                (gpointer)d
            );
            gtk_widget_show(w);
            /* Record as widget 6. */
            EditorIDialogRecordWidget(d, w);

            /* Decimals radio. */
            w = gtk_radio_button_new_with_label(gslist, "Decimals");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));
            if(!snap_to_digits)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
            gtk_signal_connect(
                GTK_OBJECT(w), "toggled",
                GTK_SIGNAL_FUNC(EditorPrimitiveSnapRadioCB),
                (gpointer)d
            );
            gtk_widget_show(w);
            /* Record as widget 7. */
            EditorIDialogRecordWidget(d, w);


            w = gtk_label_new("Of Resolution:");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_widget_show(w);

          /* Resolution spin. */
            resolution = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_SNAP_PRIMITIVE_RESOLUTION
            );
            adj = (GtkAdjustment *)gtk_adjustment_new(
                (gdouble)resolution,      /* Initial. */
                0.0,                /* Minimum. */
                1024.0,             /* Maximum. */
                1.0,                /* Step inc. */
                1.0,                /* Page inc. */
                1.0                 /* Page size. */
            );
            w = gtk_spin_button_new(
                adj,
                1.0,          /* Climb rate (0.0 to 1.0). */
                0       /* Digits. */
            );
          gtk_widget_set_usize(w, 50, -1);
          gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
          gtk_widget_show(w);
            /* Record as widget 8. */
            EditorIDialogRecordWidget(d, w);


          /* Separator before samples section. */
          w = gtk_hseparator_new();
          gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
          gtk_widget_show(w);



          /* Hbox for sample. */
          w = gtk_hbox_new(FALSE, border_minor);
          gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
            gtk_widget_show(w);
          parent2 = w;

            w = gtk_label_new("Sample:");
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_widget_show(w);

            /* Sample entry. */
            w = gtk_entry_new();
          gtk_widget_set_usize(w, 100, -1);
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
          gtk_entry_set_text(GTK_ENTRY(w), "12345.12345");
            gtk_widget_show(w);
            /* Record as widget 9. */
            EditorIDialogRecordWidget(d, w);

            /* Sample button. */
            w = gtk_button_new_with_label("Test");
            gtk_widget_set_usize(w, bw, bh);
          GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_signal_connect(
                GTK_OBJECT(w), "clicked",
                GTK_SIGNAL_FUNC(EditorPrimitiveSnapTestCB),
                (gpointer)d
            );
            gtk_widget_show(w);
            /* Record as widget 10. */
            EditorIDialogRecordWidget(d, w);

            w = gtk_button_new_with_label("Reset");
            gtk_widget_set_usize(w, bw, bh);
            GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
            gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
            gtk_signal_connect(
                GTK_OBJECT(w), "clicked",
                GTK_SIGNAL_FUNC(EditorPrimitiveSnapResetCB),
                (gpointer)d
            );
            gtk_widget_show(w);
            /* Record as widget 11. */
            EditorIDialogRecordWidget(d, w);
        }

        EditorIDialogMap(
            editor, d,
            "Snap Primitives",
            "Snap", "Apply", "Close",
            (void *)editor,
            EditorPrimitiveSnapOKCB,
            EditorPrimitiveSnapApplyCB,
            EditorPrimitiveIDialogCancelCB
        );
}

/*
 *    Snap selected primitives input dialog ok callback.
 */
static void EditorPrimitiveSnapOKCB(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;

        EditorPrimitiveSnapApplyCB(idialog, data);

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

/*
 *    Snap selected primitives input dialog apply callback.
 */
static void EditorPrimitiveSnapApplyCB(void *idialog, void *data)
{
        gint i, model_num, pn, first_sel_pn, vtx_num;
        gint undos_recorded = 0;
        v3d_model_struct *model_ptr;
        gpointer p;
      gbool snap_vertices = FALSE,
            snap_normals = FALSE,
            snap_texcoords = FALSE,
            snap_axis_x = TRUE,
            snap_axis_y = TRUE,
            snap_axis_z = TRUE,
            snap_to_digits = FALSE;
      gint resolution = 0;
        u_int8_t val_ui8;
        u_int32_t val_ui32;
        GtkWidget *w;
        gpointer u;
        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;

        /* Begin fetching values from widgets. */
        /* Snap vertices check. */
        w = EditorIDialogGetWidget(d, 0);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            snap_vertices = GTK_TOGGLE_BUTTON(w)->active;

        /* Snap normals check. */
        w = EditorIDialogGetWidget(d, 1);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            snap_normals = GTK_TOGGLE_BUTTON(w)->active;

        /* Snap texcoords check. */
        w = EditorIDialogGetWidget(d, 2);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            snap_texcoords = GTK_TOGGLE_BUTTON(w)->active;

        /* Snap X axis values check. */
        w = EditorIDialogGetWidget(d, 3);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
          snap_axis_x = GTK_TOGGLE_BUTTON(w)->active;

        /* Snap Y axis values check. */
        w = EditorIDialogGetWidget(d, 4);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
          snap_axis_y = GTK_TOGGLE_BUTTON(w)->active;

        /* Snap Z axis values check. */
        w = EditorIDialogGetWidget(d, 5);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            snap_axis_z = GTK_TOGGLE_BUTTON(w)->active;


        /* Get digits (TRUE) or decimals (FALSE). */
      /* Digits radio. */
        w = EditorIDialogGetWidget(d, 6);
        if((w == NULL) ? 0 : GTK_IS_TOGGLE_BUTTON(w))
            snap_to_digits = GTK_TOGGLE_BUTTON(w)->active;

        /* Get resolution. */
        w = EditorIDialogGetWidget(d, 8);
        if((w == NULL) ? 0 : GTK_IS_SPIN_BUTTON(w))
      {
            resolution = gtk_spin_button_get_value_as_int(
                GTK_SPIN_BUTTON(w)
            );
          if(resolution < 0)
            resolution = 0;
      }


      /* Update configuration values. */
      /* Snap vertices, normals, and texcoords. */
      val_ui8 = snap_vertices;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_VERTICES,
            &val_ui8, FALSE
        );
        val_ui8 = snap_normals;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_NORMALS,
            &val_ui8, FALSE
        );
        val_ui8 = snap_texcoords;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_TEXCOORDS,
            &val_ui8, FALSE
        );

      /* Snap along axises. */
        val_ui8 = snap_axis_x;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_AXIS_X,
            &val_ui8, FALSE
        );
        val_ui8 = snap_axis_y;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_AXIS_Y,
            &val_ui8, FALSE
        );
        val_ui8 = snap_axis_z;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_AXIS_Z,
            &val_ui8, FALSE
        );

      /* Snap to digits. */
      val_ui8 = snap_to_digits;
      VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_DIGITS,
            &val_ui8, FALSE
        );
      /* Resolution. */
        val_ui32 = (u_int32_t)resolution;
        VMACFGItemListMatchSetValue(
            option, VMA_CFG_PARM_SNAP_PRIMITIVE_RESOLUTION,
            &val_ui32, FALSE
        );


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

        EditorSetBusy(editor);

        /* Itterate through selected primitives. */
        first_sel_pn = ((editor->total_selected_primitives == 1) ?
            editor->selected_primitive[0] : -1
        );
        for(i = 0; i < editor->total_selected_primitives; i++)
        {
            pn = editor->selected_primitive[i];
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, pn
            );
            if(p == NULL)
                continue;

          /* Get selected vertex number matching this primitive. */
          vtx_num = EditorGetSelected(
            editor->selected_value, editor->total_selected_values, i
          );


          /* Snap vertices? */
          if(snap_vertices)
          {
            mp_vertex_struct *v = V3DMPGetVertex(p, vtx_num);
#define DO_SET_VERTEX         \
{ \
 vma_undo_set_vertex_struct *uv; \
 \
 /* Record undo. */ \
 uv = (vma_undo_set_vertex_struct *)VMAUndoNew( \
  VMA_UNDO_TYPE_SET_VERTEX, "Set Vertex" \
 ); \
 if(uv != NULL) \
 { \
  uv->editor = editor; \
  uv->model_num = model_num; \
  uv->primitive_num = pn; \
  uv->vertex_num = vtx_num; \
  uv->v.x = v->x; \
  uv->v.y = v->y; \
  uv->v.z = v->z; \
  EditorUndoRecord(editor, uv); \
 \
  undos_recorded++; \
 } \
 \
 if(snap_to_digits) \
 { \
  if(snap_axis_x) \
   v->x = EditorPrimitiveSnapDigits(v->x, resolution); \
  if(snap_axis_y) \
   v->y = EditorPrimitiveSnapDigits(v->y, resolution); \
  if(snap_axis_z) \
   v->z = EditorPrimitiveSnapDigits(v->z, resolution); \
 } \
 else \
 { \
  if(snap_axis_x) \
   v->x = EditorPrimitiveSnapDecimals(v->x, resolution); \
  if(snap_axis_y) \
   v->y = EditorPrimitiveSnapDecimals(v->y, resolution); \
  if(snap_axis_z) \
   v->z = EditorPrimitiveSnapDecimals(v->z, resolution); \
 } \
 \
 /* Update has changes on editor. */ \
 EDITOR_DO_UPDATE_HAS_CHANGES \
}
            /* If pointer to vertex is not valid, that implies we
             * must set for all vertices on this primitive.
             */
            if(v == NULL)
            {
                    for(vtx_num = 0; 1; vtx_num++)
                    {
                        v = V3DMPGetVertex(p, vtx_num);
                        if(v == NULL)
                            break;
                        DO_SET_VERTEX
                    }
                }
                else
                {
                    DO_SET_VERTEX
                }
#undef DO_SET_VERTEX
          }
          /* Snap normals? */
          if(snap_normals)
          {
            mp_vertex_struct *v = V3DMPGetNormal(p, vtx_num);
#define DO_SET_NORMAL         \
{ \
 vma_undo_set_normal_struct *un; \
 \
 /* Record undo. */ \
 un = (vma_undo_set_normal_struct *)VMAUndoNew( \
  VMA_UNDO_TYPE_SET_NORMAL, "Set Normal" \
 ); \
 if(un != NULL) \
 { \
  un->editor = editor; \
  un->model_num = model_num; \
  un->primitive_num = pn; \
  un->vertex_num = vtx_num; \
  un->n.x = v->x; \
  un->n.y = v->y; \
  un->n.z = v->z; \
  EditorUndoRecord(editor, un); \
 \
  undos_recorded++; \
 } \
 \
 if(snap_to_digits) \
 { \
  if(snap_axis_x) \
   v->x = EditorPrimitiveSnapDigits(v->x, resolution); \
  if(snap_axis_y) \
   v->y = EditorPrimitiveSnapDigits(v->y, resolution); \
  if(snap_axis_z) \
   v->z = EditorPrimitiveSnapDigits(v->z, resolution); \
 } \
 else \
 { \
  if(snap_axis_x) \
   v->x = EditorPrimitiveSnapDecimals(v->x, resolution); \
  if(snap_axis_y) \
   v->y = EditorPrimitiveSnapDecimals(v->y, resolution); \
  if(snap_axis_z) \
   v->z = EditorPrimitiveSnapDecimals(v->z, resolution); \
 } \
 \
 /* Update has changes on editor. */ \
 EDITOR_DO_UPDATE_HAS_CHANGES \
}
                /* If pointer to normal is not valid, that implies we
                 * must set for all normals on this primitive.
                 */
                if(v == NULL)
                {
                    for(vtx_num = 0; 1; vtx_num++)
                    {
                        v = V3DMPGetNormal(p, vtx_num);
                        if(v == NULL)
                            break;
                        DO_SET_NORMAL
                    }
            }
                else
                {
                    DO_SET_NORMAL
                }
#undef DO_SET_NORMAL
          }
          /* Snap texcoords? */
          if(snap_texcoords)
          {
            mp_vertex_struct *v = V3DMPGetTexCoord(p, vtx_num);
#define DO_SET_TEXCOORD       \
{ \
 vma_undo_set_texcoord_struct *ut; \
 \
 /* Record undo. */ \
 ut = (vma_undo_set_texcoord_struct *)VMAUndoNew( \
  VMA_UNDO_TYPE_SET_TEXCOORD, "Set TexCoord" \
 ); \
 if(ut != NULL) \
 { \
  ut->editor = editor; \
  ut->model_num = model_num; \
  ut->primitive_num = pn; \
  ut->vertex_num = vtx_num; \
  ut->tc.x = v->x; \
  ut->tc.y = v->y; \
  ut->tc.z = v->z; \
  EditorUndoRecord(editor, ut); \
 \
  undos_recorded++; \
 } \
 \
 if(snap_to_digits) \
 { \
  if(snap_axis_x) \
   v->x = EditorPrimitiveSnapDigits(v->x, resolution); \
  if(snap_axis_y) \
   v->y = EditorPrimitiveSnapDigits(v->y, resolution); \
  if(snap_axis_z) \
   v->z = EditorPrimitiveSnapDigits(v->z, resolution); \
 } \
 else \
 { \
  if(snap_axis_x) \
   v->x = EditorPrimitiveSnapDecimals(v->x, resolution); \
  if(snap_axis_y) \
   v->y = EditorPrimitiveSnapDecimals(v->y, resolution); \
  if(snap_axis_z) \
   v->z = EditorPrimitiveSnapDecimals(v->z, resolution); \
 } \
 \
 /* Update has changes on editor. */ \
 EDITOR_DO_UPDATE_HAS_CHANGES \
}
                /* If pointer to texcoord is not valid, that implies we
                 * must set for all texcoords on this primitive.
                 */
                if(v == NULL)
                {
                for(vtx_num = 0; 1; vtx_num++)
                {
                  v = V3DMPGetTexCoord(p, vtx_num);
                  if(v == NULL)
                      break;

                  DO_SET_TEXCOORD
                }
            }
            else
            {
                DO_SET_TEXCOORD
            }
#undef DO_SET_TEXCOORD
          }

          /* At this point vtx_num may be invalid. */
          vtx_num = -1;
      }








        /* Was there a single selected primitive? */
        if(first_sel_pn > -1)
        {
            GtkCList *values_list = (GtkCList *)editor->values_list;

            /* Get previously selected value. */
            vtx_num = EditorGetSelected(
                editor->selected_value, editor->total_selected_values,
                0
            );

            /* Get first selected primitive. */
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, first_sel_pn
            );
            if(p != NULL)
            {
                /* Refetch values list. */
                EditorListDeleteValuesG(editor);
                EditorListAddValuesRG(editor, p);

                /* Reselect last value item. */
                if((values_list != NULL) && (vtx_num > -1))
                    gtk_clist_select_row(values_list, vtx_num, 0);
            }
        }

        /* Update repeat values for each new undo. */
        for(i = 0; i < undos_recorded; i++)
        {
            u = VMAUndoListGetPtr(
                editor->undo, editor->total_undos,
                editor->total_undos - i - 1
            );
            VMAUndoSetRepeats(u, undos_recorded);
        }


        /* Update menus and redraw views. */
        EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor); 
        EditorRedrawAllViews(editor);

        EditorSetReady(editor);
}

Generated by  Doxygen 1.6.0   Back to index