Logo Search packages:      
Sourcecode: vertex version File versions

editorcb.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <math.h>

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

#include "../include/string.h"

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

#include "view.h"
#include "viewcb.h"
#include "viewdraw.h"
#include "texbrowser.h"
#include "texbrowsercb.h"
#include "editor.h"
#include "editorcb.h"
#include "editorviewcb.h"
#include "editorhf.h"
#include "editorfio.h"
#include "editorshipping.h"
#include "editorlist.h"
#include "editormodel.h"
#include "editorp.h"
#include "editorselect.h"
#include "editortdialog.h"
#include "editorundo.h"
#include "editordnd.h"

#include "backup.h"
#include "msglist.h"
#include "clrsel.h"
#include "scratchpad.h"
#include "prefwin.h"
#include "prefwinop.h"

#include "printwin.h"

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

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


void EditorSetWriteProtect(ma_editor_struct *editor, gbool state);
void EditorRecordPositionsCB(ma_editor_struct *editor);

gint EditorEventCB(GtkWidget *widget, void *event, gpointer data);

void EditorDestroyCB(GtkObject *object, gpointer data);
gint EditorCloseCB(GtkWidget *widget, GdkEvent *event, gpointer data);
void EditorCloseMCB(GtkWidget *widget, gpointer data);
void EditorCloseAllMCB(GtkWidget *widget, gpointer data);

gint EditorMenuItemEnterCB(
        GtkWidget *widget, GdkEvent *event, gpointer data
);
void EditorNewFileCB(GtkWidget *widget, gpointer data);
void EditorOpenFileCB(GtkWidget *widget, gpointer data);
void EditorSaveFileCB(GtkWidget *widget, gpointer data);
void EditorSaveAsFileCB(GtkWidget *widget, gpointer data);
void EditorBackupCB(GtkWidget *widget, gpointer data);
void EditorBackupPreferencesCB(GtkWidget *widget, gpointer data);
void EditorExportFileCB(GtkWidget *widget, gpointer data);
void EditorImportFileCB(GtkWidget *widget, gpointer data);
void EditorRevertFileCB(GtkWidget *widget, gpointer data);
void EditorSyncMemoryCB(GtkWidget *widget, gpointer data);

void EditorUndoCB(GtkWidget *widget, gpointer data);
void EditorRedoCB(GtkWidget *widget, gpointer data);
void EditorCutCB(GtkWidget *widget, gpointer data);
void EditorCopyCB(GtkWidget *widget, gpointer data);
void EditorPasteCB(GtkWidget *widget, gpointer data);
void EditorMapClipboardCB(GtkWidget *widget, gpointer data);
void EditorWriteProtectEnableCB(GtkWidget *widget, gpointer data);
void EditorWriteProtectDisableCB(GtkWidget *widget, gpointer data);
void EditorMapClrSelCB(GtkWidget *widget, gpointer data);
void EditorMapTextureBrowserCB(GtkWidget *widget, gpointer data);

void EditorPreferencesCB(GtkWidget *widget, gpointer data);

void EditorPrimitiveVertexAddCB(GtkWidget *widget, gpointer data);
void EditorPrimitiveVertexRemoveCB(GtkWidget *widget, gpointer data);

void EditorScratchPadVertexInsertCB(GtkWidget *widget, gpointer data);
void EditorScratchPadVertexAppendCB(GtkWidget *widget, gpointer data);
void EditorScratchPadVertexEditCB(GtkWidget *widget, gpointer data);

void EditorPluginRenderCB(GtkWidget *widget, gpointer data);

static void EditorMenuMapPositionCB(
        GtkMenu *menu, gint *x, gint *y, gpointer data
);
void EditorButtonMenuMapCB(GtkButton *button, gpointer data);
void EditorMenuHideCB(GtkWidget *widget, gpointer data);

void EditorValueBrowseCB(GtkWidget *widget, gpointer data);
void EditorValueEnterCB(GtkWidget *widget, gpointer data);
void EditorValueApplyCB(GtkWidget *widget, gpointer data);

void EditorModelsListSelectCB(
      GtkWidget *widget, gint row, gint column,
        GdkEventButton *event, gpointer data
);
void EditorModelsListUnselectCB(
        GtkWidget *widget, gint row, gint column,
        GdkEventButton *event, gpointer data
);
void EditorPrimitivesListSelectCB(
        GtkWidget *widget, gint row, gint column,
        GdkEventButton *event, gpointer data
);
void EditorPrimitivesListUnselectCB(
      GtkWidget *widget, gint row, gint column,
        GdkEventButton *event, gpointer data
);
static void EditorPrimitivesListDoubleClickCB(
        ma_editor_struct *editor, int pn
);
void EditorValuesListSelectCB(
        GtkWidget *widget, gint row, gint column,
      GdkEventButton *event, gpointer data
);
static void EditorValuesListDoubleClickCB(
        ma_editor_struct *editor, int value_item_num
);
void EditorListColumClickCB(GtkWidget *widget, gint column, gpointer data);

gint EditorListMenuMapCB(GtkWidget *widget, gpointer event, gpointer data);
void EditorToggleModelShowCB(GtkWidget *widget, gpointer 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)


/*
 *    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; \
}



/*
 *    Changes the write protect state on the editor and updates
 *    its widgets.
 */
void EditorSetWriteProtect(ma_editor_struct *editor, gbool state)
{
      int i;
      GtkWidget *w;


      if(editor == NULL)
          return;

#define DO_THAW_TEXT_ENTRY    \
{ \
if(w != NULL) \
 gtk_entry_set_editable(GTK_ENTRY(w), TRUE); \
}

#define DO_FREEZE_TEXT_ENTRY  \
{ \
if(w != NULL) \
 gtk_entry_set_editable(GTK_ENTRY(w), FALSE); \
}


      if(state)
      {
          /* Turn on write protect and freeze some widgets. */
          editor->write_protect = TRUE;

          for(i = 0; i < VMA_PRIMITIVES_MAX_VALUES; i++)
          {
            w = editor->values_text[i];
            DO_FREEZE_TEXT_ENTRY
          }
      }
      else
      {
          editor->write_protect = FALSE;

            for(i = 0; i < VMA_PRIMITIVES_MAX_VALUES; i++)
            {
                w = editor->values_text[i];
                DO_THAW_TEXT_ENTRY
            }
      }


#undef DO_THAW_TEXT_ENTRY
#undef DO_FREEZE_TEXT_ENTRY

      /* Update view menus. */
      EditorUpdateAllViewMenus(editor);

      return;
}

/*
 *    Updates position, size, and related values of the editor to
 *    the global configuration items list.
 *
 *    If cfg item VMA_CFG_PARM_RECORD_LAST_POS_AND_SIZE is false then
 *    no operation will be performed.
 *
 *    All related resources of the editor (ie views)
 *    will also have their values recorded.
 */
void EditorRecordPositionsCB(ma_editor_struct *editor)
{
      gint i, total;
      GtkWidget *w;
      vma_cfg_item_struct *opt = option;
      u_int32_t ui32;


      /* Check if we are suppose to record positions and sizes. */
      if(!VMACFGItemListGetValueI(
          option, VMA_CFG_PARM_RECORD_LAST_POS_AND_SIZE
      ))
          return;


      if(editor == NULL)
          return;

      if(!editor->initialized)
          return;

      /* Record size of toplevel window. */
      w = editor->toplevel;
      if(w != NULL)
      {
          ui32 = w->allocation.width;
          VMACFGItemListMatchSetValue(
            opt, VMA_CFG_PARM_WIDTH,
            &ui32, 0
          );
          ui32 = w->allocation.height;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_HEIGHT,
                &ui32, 0
            );
      }

      /* Lists and views paned toplevel. */
        w = editor->lists_and_views_toplevel;
        if(w != NULL)
        {
            ui32 = w->allocation.height;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_LIST_AND_VIEW_PANED_SIZE,
                &ui32, 0
            );
        }

        /* Text dialog paned toplevel. */
/*
        w = editor->text_dialog_toplevel;
        if(w != NULL)
        {
            ui32 = w->allocation.height;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_TDIALOG_PANED_SIZE,
                &ui32, 0
            );
        }
 */

      /* Panels. */
      w = editor->panel[0];
        if(w != NULL)
        {
            ui32 = w->allocation.width;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_PANEL0_SIZE,
                &ui32, 0
            );
      }
      w = editor->panel[1];
      if(w != NULL)
      {
            ui32 = w->allocation.width;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_PANEL1_SIZE,
                &ui32, 0
            );
        }
        w = editor->panel[2];
        if(w != NULL)
        {
            ui32 = w->allocation.width;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_PANEL2_SIZE,
                &ui32, 0
            );
        }


      /* Models list. */
      w = editor->models_list;
        if(w != NULL)
        {
            ui32 = w->allocation.height;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_PANEL_MODELS_LIST_SIZE,
                &ui32, 0
            );
        }

        /* View upper panel and view 0. */
      total = VMA_MAX_2D_VIEWS_PER_EDITOR + VMA_MAX_3D_VIEWS_PER_EDITOR;
      i = 0;
      if(i < total)
      {
            w = editor->view_restored_toplevel[i];
            if(w != NULL)
            {
                ui32 = w->allocation.width;
                VMACFGItemListMatchSetValue(
                    opt, VMA_CFG_PARM_PANEL_VIEW0_SIZE,
                    &ui32, 0
                );

                ui32 = w->allocation.height;
                VMACFGItemListMatchSetValue(
                    opt, VMA_CFG_PARM_PANEL_VIEW_UPPER_SIZE,      
                    &ui32, 0
                );
          }
        }

      /* View 2. */
        i = 2;
        if(i < total)
        {
            w = editor->view_restored_toplevel[i];
            if(w != NULL)
            {
                ui32 = w->allocation.width;
                VMACFGItemListMatchSetValue(
                    opt, VMA_CFG_PARM_PANEL_VIEW2_SIZE,
                    &ui32, 0
                );
            }
        }

      /* Values list (use value's list toplevel). */
      w = editor->values_toplevel;
        if(w != NULL)
        {
            ui32 = w->allocation.height;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_PANEL_VALUES_LIST_SIZE,
                &ui32, 0
            );
        }
      /* Column widths. */
        w = editor->values_list;
        if(w != NULL) 
        {
            GtkCList *clist = GTK_CLIST(w);
            GtkCListColumn *column_heading;

            for(i = 0; i < clist->columns; i++)
            {
                column_heading = &clist->column[i];
                switch(i)
                {
                  case 0:
                    ui32 = column_heading->width;
                    VMACFGItemListMatchSetValue(
                        opt, VMA_CFG_PARM_EDITOR_VALUES_CHWIDTH0,
                        &ui32, 0
                    );
                    break;

                  case 1:
                    ui32 = column_heading->width;
                    VMACFGItemListMatchSetValue(
                        opt, VMA_CFG_PARM_EDITOR_VALUES_CHWIDTH1,
                        &ui32, 0
                    );
                    break;

                  case 2:
                    ui32 = column_heading->width;
                    VMACFGItemListMatchSetValue(
                        opt, VMA_CFG_PARM_EDITOR_VALUES_CHWIDTH2,
                        &ui32, 0
                    );
                break;
            }
          }
      }

      /* Text dialog's `pulled out' window size. */
      w = editor->tdialog.toplevel;
      if(w != NULL)
      {
          int rx, ry, rwidth, rheight;

          if(GUIPullOutGetToplevelWindow(
            w, &rx, &ry, &rwidth, &rheight
          ) != NULL)
          {
            ui32 = rwidth;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_TDIALOG_WIDTH,
                &ui32, 0
            );

            ui32 = rheight;
            VMACFGItemListMatchSetValue(
                opt, VMA_CFG_PARM_TDIALOG_HEIGHT,
                &ui32, 0
            );
          }
      }

      /* Begin recording positions on editor's related windows. */

        /* 2d views. */
        for(i = 0; i < VMA_MAX_2D_VIEWS_PER_EDITOR; i++)
            View2DRecordPositionsCB(editor->view2d[i]);

        /* 3d views. */
        for(i = 0; i < VMA_MAX_3D_VIEWS_PER_EDITOR; i++)
            View3DRecordPositionsCB(editor->view3d[i]);

      /* Text dialog. */


      /* Texture browser. */
/*
Do not record position values of the texture browser, it will be
recorded whenever it is unmapped by the user.
      TexBrowserRecordPositionsCB(&editor->texture_browser);
 */

      return;
}


/*
 *    Editor toplevel event callback.
 */
gint EditorEventCB(GtkWidget *widget, void *event, gpointer data)
{
      gbool status = FALSE;
      int i;
      GtkWidget *w;
      ma_editor_struct *editor = (ma_editor_struct *)data;
      vma_view2d_struct *view2d;
      vma_view3d_struct *view3d;
      GdkEventConfigure *configure;
      GdkEventKey *key;


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

      if(!editor->initialized)
          return(status);

      switch(*(int *)event)
      {
        case GDK_CONFIGURE:
            w = editor->toplevel;
            configure = (GdkEventConfigure *)event;
            if(w == widget)
            {

            status = TRUE;
            }
            break;

        case GDK_KEY_PRESS: case GDK_KEY_RELEASE:
          key = (GdkEventKey *)event;
          switch(key->keyval)
          {
            case GDK_Escape:
            case GDK_Alt_L: case GDK_Alt_R:
            case GDK_Control_L: case GDK_Control_R:
            case GDK_Shift_L: case GDK_Shift_R:
              /* Pass these keys to view event handler. */
              for(i = 0; i < VMA_MAX_2D_VIEWS_PER_EDITOR; i++)
              {
                view2d = editor->view2d[i];
                if(view2d == NULL)
                    continue;

                if(View2DViewEventCB(view2d->view, event, view2d))
                  status = TRUE;
              }
                for(i = 0; i < VMA_MAX_3D_VIEWS_PER_EDITOR; i++)
                {
                    view3d = editor->view3d[i];
                    if(view3d == NULL)
                        continue;

                    if(View3DViewEventCB(view3d->view, event, view3d))
                  status = TRUE;
                }
            break;

          }
          break;
      }

      return(status);
}


/*
 *    Destroy callback.
 */
void EditorDestroyCB(GtkObject *object, gpointer data)
{
      return;
}

/*
 *    Close callback.
 */
gint EditorCloseCB(GtkWidget *widget, GdkEvent *event, gpointer data)
{
      gchar **path_rtn; gint path_total_rtns;
      fb_type_struct *type_rtn;
      gchar *strptr;
      gchar tmp_path[PATH_MAX + NAME_MAX];
      ma_editor_struct *editor = (ma_editor_struct *)data;
      if(editor == NULL)
          return(FALSE);

      if(!editor->initialized)
          return(TRUE);

      if(editor->processing)
          return(TRUE);

      if(editor->toplevel == NULL)
          return(TRUE);

      /* Prompt to save changes if any. */
      if(editor->has_changes)
      {
          gint status;

          CDialogSetTransientFor(editor->toplevel);
            status = CDialogGetResponse(
"Save Changes?",
"The current model has changes that have not been\n\
saved, do you want to save those changes?",
"There are changes made to the current model data\n\
that have not been saved. If you want to save those\n\
changes then click on `Yes'. If you want to discard\n\
those changes then click on `No'. If you do not\n\
want to proceed with this operation at all then\n\
click on `Cancel'.",
                CDIALOG_ICON_WARNING,
                CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO |
                CDIALOG_BTNFLAG_CANCEL | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_YES
            );
          CDialogSetTransientFor(NULL);
            switch(status)
            {
              case CDIALOG_RESPONSE_YES:
              case CDIALOG_RESPONSE_YES_TO_ALL:
              case CDIALOG_RESPONSE_OK:
                if(editor->loaded_filename == NULL)
            {
                FileBrowserSetTransientFor(editor->toplevel);
                status = FileBrowserGetResponse(
                  "Save Model As", "Save", "Cancel",
                  dname.fb_last_models,         /* Path. */
                  ftype.save_model, ftype.save_model_total,
                  &path_rtn, &path_total_rtns,
                  &type_rtn
                );
                FileBrowserSetTransientFor(NULL);

                if(!status)
                  return(TRUE);

                strptr = ((path_total_rtns > 0) ?
                  path_rtn[path_total_rtns - 1] : NULL
                );
                if(strptr != NULL)
                {
                  strncpy(tmp_path, strptr, PATH_MAX + NAME_MAX);
                  tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';

                        VMARecordFBPath(
                            tmp_path,
                            dname.fb_last_models,
                            1
                        );

                    EditorSaveModelFileAs(
                      editor, tmp_path
                    );
                }
            }
                else
            {
                    EditorSaveModelFile(
                  editor, editor->loaded_filename
                );
            }
            /* If there are still changes then it means the
             * save failed, return immediatly.
             */
            if(editor->has_changes)
                return(TRUE);
                break;

              case CDIALOG_RESPONSE_CANCEL:
            case CDIALOG_RESPONSE_NOT_AVAILABLE:
                return(TRUE);
                break;

            case CDIALOG_RESPONSE_NO:
            break;
            }
      }

      /* Record position, sizes, and related values of the editor
       * to the global configuration items list.
       */
      EditorRecordPositionsCB(editor);

      /* Reset and unmap the editor. */
      EditorReset(editor, TRUE);

      return(TRUE);
}

/*
 *    Editor menu close callback (called by menu).
 */
void EditorCloseMCB(GtkWidget *widget, gpointer data)
{
      EditorCloseCB(widget, NULL, data);
}

/*
 *    Close all windows.
 */
void EditorCloseAllMCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        /* Record position, sizes, and related values of the editor
         * to the global configuration items list.
         */
        EditorRecordPositionsCB(editor);

      need_close_all_windows = TRUE;
}

/*
 *    Menu item enter callback, updates the status bar to specify
 *    the tool tip for the given menu item if it is known.
 */
gint EditorMenuItemEnterCB(
        GtkWidget *widget, GdkEvent *event, gpointer data  
)
{
      static gbool reenterant = FALSE;
      gint i;
      GtkWidget *w;
      const gchar *msglist[] = VMA_MSGLIST_EDITOR_TOOLTIPS;
      const gchar *msglist_sb[] = VMA_MSGLIST_EDITOR_SB_TOOLTIPS;
      const gchar *tt_mesg_ptr = NULL;
      vma_core_struct *core_ptr;
      ma_editor_struct *editor = (ma_editor_struct *)data;
        if((widget == NULL) || (editor == NULL))
            return(TRUE);

      if(!editor->initialized || editor->processing)
          return(TRUE);

      core_ptr = (vma_core_struct *)editor->core_ptr;
      if(core_ptr == NULL)
          return(TRUE);

      if(reenterant)
          return(TRUE);
      else
          reenterant = TRUE;

      /* Mark editor as processing. */
      editor->processing = TRUE;

      /* Now check which input widget we got to determine the right
       * menu item to set tt_mesg_ptr to.
       */
      w = widget;

      if((w == editor->new_mi) || (w == editor->new_btn))
      {
          tt_mesg_ptr = MsgListMatchCaseMessage(
            msglist, VMA_MSGNAME_EDITOR_MODELFILE_NEW
          );
      }
        else if((w == editor->open_mi) || (w == editor->open_btn))
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_MODELFILE_OPEN
            );
        }
        else if((w == editor->save_mi) || (w == editor->save_btn))
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_MODELFILE_SAVE
            );
        }
        else if((w == editor->save_as_mi) || (w == editor->save_as_btn))
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_MODELFILE_SAVEAS
            );
        }
        else if(w == editor->backup_mi)
        {     
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_BACKUP
            );
        }
        else if(w == editor->backup_settings_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_BACKUP_SETTINGS
            );
        }
        else if(w == editor->revert_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_REVERT
            );
        }
        else if((w == editor->export_mi) || (w == editor->export_btn))
        {     
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_EXPORT
            );
        }
        else if((w == editor->import_mi) || (w == editor->import_btn))
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_IMPORT
            );
        }
        else if((w == editor->print_mi) || (w == editor->print_btn))
        {     
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRINT
            );
        }
        else if(w == editor->close_mi)
        {     
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_CLOSE
            );
        }
        else if(w == editor->exit_mi)
        {     
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_EXIT
            );
        }

        else if((w == editor->undo_mi) || (w == editor->undo_btn))
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_UNDO
            );
        }
        else if((w == editor->redo_mi) || (w == editor->redo_btn))
        {     
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_REDO
            );
        }
        else if(w == editor->cut_mi)
        {     
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_CUT
            );
        }
        else if(w == editor->copy_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_COPY
            );
        }
        else if(w == editor->paste_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PASTE
            );
        }
        else if(w == editor->clipboard_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_CLIPBOARD
            );
        }
        else if(w == editor->write_protect_enabled_mi)
        {     
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_WRITE_PROTECT_ENABLED
            );
        }
        else if(w == editor->write_protect_disabled_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_WRITE_PROTECT_DISABLED
            );
        }
        else if(w == editor->model_header_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_MODEL_HEADER
            );
        }
        else if(w == editor->sync_memory_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_SYNC_MEMORY
            );
        }
        else if(w == editor->preferences_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PREFERENCES
            );
        }

        else if(w == editor->model_create_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_MODEL_CREATE
            );
        }
        else if(w == editor->model_delete_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_MODEL_DELETE
            );
        }
        else if(w == editor->model_show_micheck)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_MODEL_SHOW
            );
        }
        else if(w == editor->model_properties_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_MODEL_PROPERTIES
            );
        }

        else if(w == editor->primitive_create_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_CREATE
            );
        }
        else if(w == editor->primitive_delete_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_DELETE
            );
        }
        else if(w == editor->primitive_flip_winding_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_FLIP_WINDING
            );
        }
        else if(w == editor->primitive_unitlize_normal_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_UNITLIZE_NORMAL
            );
        }
        else if(w == editor->primitive_translate_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_TRANSLATE
            );
        }
        else if(w == editor->primitive_rotate_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_ROTATE
            );
        }
        else if(w == editor->primitive_scale_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_SCALE
            );
        }
        else if(w == editor->primitive_mirror_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_MIRROR
            );
        }
        else if(w == editor->primitive_snap_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_SNAP
            );
        }
        else if(w == editor->primitive_add_vertex_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_ADD_VERTEX
            );
        }
        else if(w == editor->primitive_remove_vertex_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_PRIMITIVE_REMOVE_VERTEX
            );
        }
        else if(w == editor->primitive_scratchpad_insert_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_SCRATCHPAD_INSERT
            );
        }
        else if(w == editor->primitive_scratchpad_append_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_SCRATCHPAD_APPEND
            );
        }
        else if(w == editor->primitive_scratchpad_edit_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_SCRATCHPAD_EDIT
            );
        }
        else if(w == editor->surface_clrsel_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_CLRSEL
            );
        }
      else if(w == editor->surface_lighting_mi)
      {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_LIGHT_PROPERTIES
            );
        }
        else if(w == editor->surface_texture_browser_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_TEXTURE_BROWSER
            );
        }

        else if(w == editor->window_vertex_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist_sb, VMA_MSGNAME_EDITOR_SB_NEW_EDITOR
            );
        }
        else if(w == editor->window_clrsel_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist_sb, VMA_MSGNAME_EDITOR_SB_COLOR_SELECTOR
            );
        }
        else if(w == editor->window_lights_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist_sb, VMA_MSGNAME_EDITOR_SB_LIGHT_PROPERTIES
            );
        }
        else if(w == editor->window_texture_browser_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist_sb, VMA_MSGNAME_EDITOR_SB_TEXTURE_BROWSER
            );
        }
        else if(w == editor->window_scratchpad_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist_sb, VMA_MSGNAME_EDITOR_SB_SCRATCHPAD
            );
        }

        else if(w == editor->help_contents_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_HELP_CONTENTS
            );
        }
        else if(w == editor->help_tutorial_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_HELP_TUTORIAL
            );
        }
        else if(w == editor->help_viewing_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_HELP_VIEWING
            );
        }
        else if(w == editor->help_keyboard_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_HELP_KEYBOARD
            );
        }
        else if(w == editor->help_v3d_format_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_HELP_V3D_FORMAT
            );
        }
        else if(w == editor->help_plugins_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_HELP_PLUGINS
            );
        }
        else if(w == editor->help_tipofday_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_HELP_TIP_OF_DAY
            );
        }
        else if(w == editor->help_about_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_HELP_ABOUT
            );
        }

      /* Light button menu. */
        else if(w == editor->primitive_light_btn_enabled_micheck)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_LIGHT_ENABLE_CHECK
            );
        }
        else if(w == editor->primitive_light_btn_move_to_cursor_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_LIGHT_MOVE_TO_CURSOR
            );
        }
        else if(w == editor->primitive_light_btn_properties_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_LIGHT_PROPERTIES
            );
        }

        /* Scratchpad button menu. */
        else if(w == editor->primitive_scratchpad_btn_insert_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_SCRATCHPAD_INSERT
            );
        }
        else if(w == editor->primitive_scratchpad_btn_append_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_SCRATCHPAD_APPEND
            );
        }
        else if(w == editor->primitive_scratchpad_btn_edit_mi)
        {
            tt_mesg_ptr = MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_EDITOR_SCRATCHPAD_EDIT
            );
        }
      else
      {
          gint n;
          vma_editor_render_mi_struct *render_mi;
          vma_plugin_struct *plugin_ptr;


          /* Render menu plug-ins. */
          for(i = 0; i < editor->total_render_mis; i++)
          {
            render_mi = editor->render_mi[i];
            if(render_mi == NULL)
                continue;

            if(render_mi->w != w)
                continue;

            plugin_ptr = render_mi->plugin_ptr;
            if(plugin_ptr == NULL)
                continue;

            for(n = 0; n < core_ptr->total_plugins; n++)
            {
                if(core_ptr->plugin[n] == plugin_ptr)
                  break;
            }
            if(n >= core_ptr->total_plugins)
                continue;

            tt_mesg_ptr = (const char *)plugin_ptr->title;
            break;
          }

      }


      /* Set or clear status bar message. */
      VMAStatusBarMessage(editor->sb, tt_mesg_ptr);

      /* Mark editor as done processing. */
      editor->processing = FALSE;

      reenterant = FALSE;
      return(TRUE);
}

/*
 *    New file callback.
 */
void EditorNewFileCB(GtkWidget *widget, gpointer data)
{
      gchar **path_rtn; gint path_total_rtns;
      gchar *strptr;
      gchar tmp_path[PATH_MAX + NAME_MAX];
      fb_type_struct *type_rtn;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

      if(editor->processing)
          return;

        if(editor->has_changes)
        {
          gint status;

          CDialogSetTransientFor(editor->toplevel);
            status = CDialogGetResponse(
"Save Changes?",
"The current model has changes that have not been\n\
saved, do you want to save those changes?",
"There are changes made to the current model data\n\
that have not been saved. If you want to save those\n\
changes then click on `Yes'. If you want to discard\n\
those changes then click on `No'. If you do not\n\
want to proceed with this operation at all then\n\
click on `Cancel'.",
                CDIALOG_ICON_WARNING,
                CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO |
                CDIALOG_BTNFLAG_CANCEL | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_YES
            );
          CDialogSetTransientFor(NULL);
            switch(status)
            {
              case CDIALOG_RESPONSE_YES:
              case CDIALOG_RESPONSE_YES_TO_ALL:
              case CDIALOG_RESPONSE_OK:
                if(editor->loaded_filename == NULL)
                {
                FileBrowserSetTransientFor(editor->toplevel);
                    status = FileBrowserGetResponse(
                        "Save Model As", "Save", "Cancel",
                        dname.fb_last_models,   /* Path. */
                        ftype.save_model, ftype.save_model_total,
                        &path_rtn, &path_total_rtns,
                        &type_rtn
                    );
                FileBrowserSetTransientFor(NULL);

                if(!status)
                        return;

                    strptr = ((path_total_rtns > 0) ?
                        path_rtn[path_total_rtns - 1] : NULL
                    );
                    if(strptr != NULL)
                    {
                        strncpy(tmp_path, strptr, PATH_MAX + NAME_MAX);
                        tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';

                        VMARecordFBPath(
                            tmp_path,
                            dname.fb_last_models,  
                            1
                        );

                        EditorSaveModelFileAs(
                      editor, tmp_path
                  );
                }
                }
                else
                {   
                    EditorSaveModelFile(
                  editor, editor->loaded_filename
                );
                }
                /* If there are still changes then it means the
                 * save failed, return immediatly.
                 */
                if(editor->has_changes)
                    return;
                break;
        
              case CDIALOG_RESPONSE_CANCEL:
            case CDIALOG_RESPONSE_NOT_AVAILABLE:
                return;
                break;
            }
        }

      /* Reset values. */
      EditorReset(editor, FALSE);


      EDITOR_DO_UPDATE_HAS_CHANGES

      EditorUpdateMenus(editor);
      EditorUpdateAllViewMenus(editor);
      EditorRedrawAllViews(editor);
}

/*
 *    Open model file callback.
 */
void EditorOpenFileCB(GtkWidget *widget, gpointer data)
{
      gint status;
        gchar **path_rtn; gint path_total_rtns;
        const gchar *path_ptr;
        gchar tmp_path[PATH_MAX + NAME_MAX];
      fb_type_struct *ftype_rtn;
      ma_editor_struct *editor = (ma_editor_struct *)data;
      if(editor == NULL)
          return;

        if(editor->processing)
            return;

      if(editor->has_changes)
      {
          CDialogSetTransientFor(editor->toplevel);
          status = CDialogGetResponse(
"Save Changes?",
"The current model has changes that have not been\n\
saved, do you want to save those changes?",
"There are changes made to the current model data\n\
that have not been saved. If you want to save those\n\
changes then click on `Yes'. If you want to discard\n\
those changes then click on `No'. If you do not\n\
want to proceed with this operation at all then\n\
click on `Cancel'.",
            CDIALOG_ICON_WARNING,
            CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO |
            CDIALOG_BTNFLAG_CANCEL | CDIALOG_BTNFLAG_HELP,
            CDIALOG_BTNFLAG_YES
          );
          CDialogSetTransientFor(NULL);
          switch(status)
          {
            case CDIALOG_RESPONSE_YES:
            case CDIALOG_RESPONSE_YES_TO_ALL:
            case CDIALOG_RESPONSE_OK:
                if(editor->loaded_filename == NULL)
                {
                FileBrowserSetTransientFor(editor->toplevel);
                    status = FileBrowserGetResponse(
                        "Save Model As", "Save", "Cancel",
                        dname.fb_last_models,   /* Path. */
                        ftype.save_model, ftype.save_model_total,
                        &path_rtn, &path_total_rtns,
                        &ftype_rtn
                    );
                FileBrowserSetTransientFor(NULL);

                if(!status)
                        return;  

                    path_ptr = (const gchar *)((path_total_rtns > 0) ?
                        path_rtn[path_total_rtns - 1] : NULL
                    );
                    if(path_ptr != NULL)
                    {
                        strncpy(tmp_path, path_ptr, PATH_MAX + NAME_MAX);
                        tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';

                        VMARecordFBPath(
                            tmp_path,
                            dname.fb_last_models,
                      1
                  );

                        EditorSaveModelFileAs(
                      editor, tmp_path
                    );
                }
                }
                else
                {
                    EditorSaveModelFile(
                  editor, editor->loaded_filename
                );
                }
                /* If there are still changes then it means the
                 * save failed, return immediatly.
                 */
                if(editor->has_changes)
                    return;
            break;

            case CDIALOG_RESPONSE_CANCEL:
            case CDIALOG_RESPONSE_NOT_AVAILABLE:
            return;
            break;
          }
      }

      /* Open file. */
      FileBrowserSetTransientFor(editor->toplevel);
      status = FileBrowserGetResponse(
            "Open Model", "Open", "Cancel",
          dname.fb_last_models,     /* Path. */
            ftype.load_model, ftype.load_model_total,
          &path_rtn, &path_total_rtns,
          &ftype_rtn
      );
      FileBrowserSetTransientFor(NULL);

      if(!status)
          return;

        path_ptr = (const gchar *)((path_total_rtns > 0) ?
            path_rtn[path_total_rtns - 1] : NULL
        );
        if(path_ptr != NULL)
        {
          const gchar *ftype_ext_ptr;

            strncpy(tmp_path, path_ptr, PATH_MAX + NAME_MAX);
            tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';

          VMARecordFBPath(
            tmp_path,
            dname.fb_last_models,
            1
          );

          ftype_ext_ptr = (const gchar *)((ftype_rtn == NULL) ?
            NULL : ftype_rtn->ext
          );
          if(ftype_ext_ptr == NULL)
            ftype_ext_ptr = "";

          if(strcasestr(ftype_ext_ptr, ".pov"))
          {
/* Need to work on this. */

          }
          else
          {
            EditorLoadModelFile(
                editor, tmp_path
            );
          }
      }

        EditorUpdateMenus(editor);
      EditorUpdateAllViewMenus(editor);
}

/*
 *    Save model file callback.
 */
void EditorSaveFileCB(GtkWidget *widget, gpointer data)
{
      gint status;
      gchar **path_rtn; gint path_total_rtns;
        fb_type_struct *ftype_rtn;
        gchar tmp_path[PATH_MAX + NAME_MAX];
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

      /* Check if loaded model has a filename. */
      if(editor->loaded_filename == NULL)
        {
          /* No file name, this is either a new file or a save as
           * operation.
           */
          const gchar *path_ptr, *ftype_ext_ptr;

          /* Map file browser and get user response, if false then
           * that implies user has canceled.
           */
          FileBrowserSetTransientFor(editor->toplevel);
          status = FileBrowserGetResponse(
                "Save Model As", "Save", "Cancel",
                dname.fb_last_models,     /* Path. */
                ftype.save_model, ftype.save_model_total,
                &path_rtn, &path_total_rtns,
                &ftype_rtn
            );
          FileBrowserSetTransientFor(NULL);

          if(!status)
            return;

          /* Get pointer to first path from user response in
           * the path_rtn array.
           */
          path_ptr = (const gchar *)((path_total_rtns > 0) ?
            path_rtn[path_total_rtns - 1] : NULL
          );
          ftype_ext_ptr = (const gchar *)((ftype_rtn == NULL) ?
            NULL : ftype_rtn->ext
          );

          /* Got path and extension? */
            if((path_ptr != NULL) && (ftype_ext_ptr != NULL))
            {
            /* Save as by extension type. */
            /* V3D model file. */
            if(strcasestr(ftype_ext_ptr, ".v3d") ||
                   strcasestr(ftype_ext_ptr, ".3d")
            )
            {
                strncpy(tmp_path, path_ptr, PATH_MAX + NAME_MAX);
                tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';

                VMARecordFBPath(
                  tmp_path,
                  dname.fb_last_models,
                  1
                );
                EditorSaveModelFileAs(
                  editor, tmp_path
                );
            }
            /* Unsupported format. */
            else
            {
                CDialogSetTransientFor(editor->toplevel);
                CDialogGetResponse(
"Unsupported format",
"Unsupported file format.",
"The specified file format is not supported by\n\
this program. Please try a different file format.",
                  CDIALOG_ICON_ERROR,
                  CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                  CDIALOG_BTNFLAG_OK
                );
                CDialogSetTransientFor(NULL);

            }
          }
        }
        else
        {
            /* Got file name, so just save using that file name. */
            EditorSaveModelFile(
            editor, editor->loaded_filename
          );
        }

      /* The save callback will update menus and update member
       * has_changes as needed.
       */
}

/*
 *      Save as model file callback.
 */
void EditorSaveAsFileCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

      /* Reset the loaded filename. */
      g_free(editor->loaded_filename);
      editor->loaded_filename = NULL;

      /* Call the save model file callback, it will see that member
       * loaded_filename is NULL and then prompt for a save as.
       */
      EditorSaveFileCB(widget, data);
}

/*
 *    Backup model file now callback.
 */
void EditorBackupCB(GtkWidget *widget, gpointer data)
{
      gint min, max, new_index_highest;
      ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

      if(!editor->initialized || editor->processing)
          return;

      /* No model file currently loaded? */
      if(editor->loaded_filename == NULL)
          return;

      /* Mark as processing. */
      EditorSetBusy(editor);
      editor->processing = TRUE;

      VMAStatusBarMessage(editor->sb, "Backing up current model file...");
        VMAStatusBarProgress(editor->sb, 0.0);

      /* Make backup. */
      min = 1;
      max = VMACFGItemListGetValueI(
          option, VMA_CFG_PARM_BACKUP_MAX
        );
        new_index_highest = VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_BACKUP_NEW_INDEX_HIGHEST
        );
        if(VMABackupFile(
            editor->loaded_filename,
          min, max,
          (new_index_highest) ?
            VMA_BACKUP_ORDER_HL : VMA_BACKUP_ORDER_LH,
          1       /* Can remove overflow. */
      ) != VMA_BACKUP_SUCCESS)
      {
          gint len = 256 + strlen(editor->loaded_filename);
          gchar *buf;

          buf = (gchar *)g_malloc((len + 1) * sizeof(gchar));
          if(buf != NULL)
            sprintf(buf,
"Unable to make backup of file:\n\n    %s",
                editor->loaded_filename
            );

            CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Backup Failed",
            buf,
"The attempt to make a backup of the specified file\n\
has failed. This may be caused by issues related to\n\
permissions, ownership, insufficient disk space, or\n\
an invalid backup setting (see Edit->Preferances->Backup).",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
            CDialogSetTransientFor(NULL);

          g_free(buf);
          buf = NULL;
      }


        VMAStatusBarMessage(editor->sb, "Backup done");
      VMAStatusBarProgress(editor->sb, 0.0);

      /* Mark as done processing. */
        EditorSetReady(editor);
        editor->processing = FALSE;

        /* Need to update menus on editor. */
        EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor);
}

/*
 *    Backup options callback.
 */
void EditorBackupPreferencesCB(GtkWidget *widget, gpointer data)
{
        vma_pref_struct *pref_win;
        vma_core_struct *core_ptr;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        core_ptr = (vma_core_struct *)editor->core_ptr;
        if(core_ptr == NULL)
            return;

        pref_win = core_ptr->pref_win;
        if(pref_win == NULL)
      {
          core_ptr->pref_win = pref_win = PrefNew(core_ptr);
      }
      if(pref_win == NULL)
            return;

        /* Preferences window initialized and unmapped? */   
        if(pref_win->initialized && !pref_win->map_state)
        {
            GtkCTreeNode *branch;
            GtkCTreeRow *branch_row;
            GtkCTree *ctree;
            gchar *strptr;

            ctree = (GtkCTree *)((pref_win == NULL) ?  
                NULL : pref_win->catagory_ctree
            );
            if(ctree != NULL)
            {
                /* Get toplevel branch. */
                branch = gtk_ctree_node_nth(ctree, 0);
                if(branch != NULL)
                    gtk_ctree_expand(ctree, branch);

                /* Get first child branch of toplevel. */   
                if(branch != NULL)  
                {
                    branch_row = GTK_CTREE_ROW(branch);
                    branch = ((branch_row == NULL) ?
                        NULL : branch_row->children
                    );
                }

                /* Select the branch named "Backup". */
                while(branch != NULL)
                {
                    strptr = PrefPanelGetBranchText(pref_win, branch);
                    if(strptr != NULL)
                    {
                        if(!strcasecmp(strptr, "Backup"))
                        {
                            gtk_ctree_expand(ctree, branch);
                            gtk_ctree_select(ctree, branch);
                            break;
                        }
                    }

                    branch_row = GTK_CTREE_ROW(branch);
                    branch = ((branch_row == NULL) ?
                        NULL : branch_row->sibling
                    );
                }  

                PrefDoFetch(pref_win);
                PrefMap(pref_win);
            }
        }
}


/*
 *    Export model data to alternate file format callback.
 */
void EditorExportFileCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterant = FALSE;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

      EditorDoExport(editor);

      reenterant = FALSE;
}

/*
 *      Import model data from alternate file format callback.
 */
void EditorImportFileCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterant = FALSE;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

      EditorDoImport(editor);

        reenterant = FALSE;
}

/*
 *    Revert to last saved version of current model set file.
 */
void EditorRevertFileCB(GtkWidget *widget, gpointer data)
{
      gchar tmp_path[PATH_MAX + NAME_MAX];
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

      /* No saved file to revert to? */
      if(editor->loaded_filename == NULL)
      {
          CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Revert Failed",
"There is no file associated with the current model\n\
data to revert to.",
"Revert means to discard any current changes made to\n\
the model data and reload the model data from file,\n\
this means that you must have saved your model to file\n\
atleast once. There was no previously saved file found\n\
for the current model data.",
                CDIALOG_ICON_INFO,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
          CDialogSetTransientFor(NULL);
          return;
      }
      else
      {
          strncpy(tmp_path, editor->loaded_filename, PATH_MAX + NAME_MAX);
          tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';
      }


      /* If has changes, make comfermation. */
        if(editor->has_changes)
        {
          gint status;

          CDialogSetTransientFor(editor->toplevel);
          status = CDialogGetResponse(
"Revert Confirmation",
"Discard changes and revert model data to the data\n\
from file?",
"Current changes have not been saved, if you say\n\
yes then those changes will be discarded and the\n\
data will revert back to the data that was last\n\
saved.",
                CDIALOG_ICON_WARNING,
                CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO |
            CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_NO
            );
          CDialogSetTransientFor(NULL);
          switch(status)
          {
            case CDIALOG_RESPONSE_YES:
              case CDIALOG_RESPONSE_YES_TO_ALL:
              case CDIALOG_RESPONSE_OK:
            break;

            default:
            return;
            break;
          }
      }

      /* Reset some values and load file. */
      g_free(editor->loaded_filename);
      editor->loaded_filename = NULL;

      editor->has_changes = FALSE;

      /* load model from file. */
      EditorLoadModelFile(editor, tmp_path);

        EditorUpdateMenus(editor);
      EditorUpdateAllViewMenus(editor);
}

/*
 *    Sync memory callback.
 */
void EditorSyncMemoryCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterent = FALSE;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

      if(reenterent)
          return;
      else
          reenterent = TRUE;

      EditorSetBusy(editor);

      /* Sync all data in memory. */
      EditorSyncData(editor);

      /* Update menu and redraw. */
      EditorRenderMenuRegenerate(editor); /* Update render menu as well. */
      EditorUpdateMenus(editor);
        EditorUpdateAllViewMenus(editor);
      EditorRedrawAllViews(editor);

      EditorSetReady(editor);

      reenterent = FALSE;
}

/*
 *    Undo callback.
 */
void EditorUndoCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterent = FALSE;
      gint i, repeats = 1;
      vma_undo_common_struct *uc;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

      if(reenterent)
          return;
      else
          reenterent = TRUE;

      /* Get highest (newest) undo. */
      uc = (vma_undo_common_struct *)VMAUndoListGetPtr(
          editor->undo, editor->total_undos, editor->total_undos - 1
      );
      if(uc != NULL)
      {
          repeats = MAX(uc->repeats, 1);
          /* Can't repeat for more than the number of undos we have. */
          if(repeats > editor->total_undos)
          {
            repeats = editor->total_undos;
            /* Fix remaining undo structures' repeat values. */
            for(i = 0; i < editor->total_undos; i++)
                VMAUndoSetRepeats(editor->undo[i], editor->total_undos);
          }
      }

      /* Process undos. */
      for(i = 0; i < repeats; i++)
      {
          if(editor->total_undos <= 0)
            break;
          EditorUndoProcess(editor);
      }

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

      reenterent = FALSE;
}

/*
 *    Redo callback.
 */
void EditorRedoCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterent = FALSE;
        gint i, repeats = 1;
        vma_undo_common_struct *uc;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

        if(reenterent)
            return;
        else
            reenterent = TRUE;

        /* Get highest (newest) redo. */
        uc = (vma_undo_common_struct *)VMAUndoListGetPtr(
            editor->redo, editor->total_redos, editor->total_redos - 1
        );
        if(uc != NULL)
        {
            repeats = MAX(uc->repeats, 1);
            /* Can't repeat for more than the number of redos we have. */
            if(repeats > editor->total_redos)
            {
                repeats = editor->total_redos;    
                /* Fix remaining redo structures' repeat values. */
                for(i = 0; i < editor->total_redos; i++)
                    VMAUndoSetRepeats(editor->redo[i], editor->total_redos);
            }
        }

        /* Process redos. */
        for(i = 0; i < repeats; i++)
      {
          if(editor->total_redos <= 0)
            break;
          EditorRedoProcess(editor);
      }

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

        reenterent = FALSE;
}

/*
 *    Cut (selected primitive) callback.
 */
void EditorCutCB(GtkWidget *widget, gpointer data)
{
      gint model_num;
        v3d_model_struct *model_ptr;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(!editor->initialized || editor->processing)
            return;

      if(VMAWriteProtectCheck(editor))
          return;

      /* Call copy procedure then delete selected primitives. */
      EditorCopyCB(widget, data);

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

      /* Delete primitives. */
      EditorPrimitiveDoDelete(
          editor, model_num,
          editor->selected_primitive,
          editor->total_selected_primitives,
          FALSE         /* No record undo. */
      );

        if(!editor->has_changes)
            editor->has_changes = TRUE;
            
        EditorUpdateMenus(editor);
        EditorRedrawAllViews(editor);
        EditorUpdateAllViewMenus(editor);
}

/*
 *    Copy (selected primitive) callback.
 */
void EditorCopyCB(GtkWidget *widget, gpointer data)
{
      gint i, n, model_num;
      v3d_model_struct *model_ptr;
        u_int8_t *buf_out;
        gint buf_out_len;
      gint pn, p_out_total = 0;
      gpointer *p_out = NULL;

        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(!editor->initialized || editor->processing)
            return;

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

      /* Itterate through selected primitives on the selected model,
       * copy to tempory array p_out.
       */
      for(i = 0; i < editor->total_selected_primitives; i++)
      {
          pn = editor->selected_primitive[i];

          /* Selected primitive index pn not in bounds? */
          if((pn < 0) || (pn >= model_ptr->total_primitives))
            continue;

          /* Allocate one more pointer on p_out list. */
          n = p_out_total;
          p_out_total++;
          p_out = (gpointer *)g_realloc(
            p_out,
            p_out_total * sizeof(gpointer)
          );
          if(p_out == NULL)
          {
            p_out_total = 0;
            break;
          }

          /* Referance to model's primitive pn on the p_out list. */
          p_out[n] = model_ptr->primitive[pn];
      }

      /* No primitives to copy? */
      if(p_out_total <= 0)
          return;

      /* Convert list of primitives to a V3D format string. */
      buf_out = (u_int8_t *)VMADDEPrimitivesToText(p_out, p_out_total);
      if(buf_out != NULL)
      {
          /* Get length of outgoing buffer, include null terminating
           * byte.
           */
          buf_out_len = strlen((const char *)buf_out) + 1;

          if(buf_out_len > 0)
            ClipboardPutBinary(buf_out, buf_out_len);
      }

      /* Deallocate outgoing buffer. */
      g_free(buf_out);
      buf_out = NULL;
      buf_out_len = 0;


      /* Free p_out pointer array only, not each structure since they
       * still exist on the selected model.
       */
      g_free(p_out);
      p_out = NULL;
      p_out_total = 0;
}

/*
 *    Paste (primitive to selected model) callback.
 */
void EditorPasteCB(GtkWidget *widget, gpointer data)
{
      gint i, n, model_num;
      v3d_model_struct *model_ptr;
      gint p_in_total, p_insert_pos;
      gint total_pasted = 0;
      gpointer p, *p_in;
      u_int8_t *buf_in;
      gint buf_in_len;
      GtkCList *primitives_list;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(!editor->initialized || editor->processing)
            return;

        if(VMAWriteProtectCheck(editor))
            return;

      primitives_list = (GtkCList *)editor->primitives_list;
      if(primitives_list == NULL)
          return;

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

      /* Get primitives list insert position. */
      if(editor->total_selected_primitives > 0)
          p_insert_pos = editor->selected_primitive[0];
      else
          p_insert_pos = model_ptr->total_primitives;

      /* Sanitize insert position (we can go one over total). */
      if(p_insert_pos > model_ptr->total_primitives)
          p_insert_pos = model_ptr->total_primitives;
        if(p_insert_pos < 0)
            p_insert_pos = 0;


      /* Read from DDE. */
      buf_in = ClipboardFetchBinary(&buf_in_len);
      if(buf_in == NULL)
      {
            CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"No data available!",
"No data available from clipboard.",
"There is no data on the clipboard buffer to paste\n\
from.",
            CDIALOG_ICON_INFO,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
            CDialogSetTransientFor(NULL);

          return;
      }

      /* Recieved buffer is assumed to be in V3D format, it needs
       * to be parsed and converted into a list of V3D primitives.
       */
      if(VMADDETextToPrimitives(
          buf_in, &p_in, &p_in_total
      ))
      {
          g_free(buf_in);
          buf_in = NULL;
          buf_in_len = 0;

            CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Unable to parse data!",
"Unable to parse data from clipboard into V3D\n\
primitives.",
"The data recieved from the clipboard buffer could not\n\
be parsed into V3D primitives. It is possible that the\n\
clipboard buffer is not that of a previously coppied\n\
V3D primitive data.",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
            CDialogSetTransientFor(NULL);

          return;
      }

      /* Recieved clipboard buffer successfully parsed into V3D
       * primitives.
       */

      /* Deallocate recieved clipboard buffer, we do not need it
       * anymore.
       */
      free(buf_in);
      buf_in = NULL;
      buf_in_len = 0;


      /* Unselect all primitives on editor's primitives list. */
      gtk_clist_unselect_all(primitives_list);

      /* Insert incoming primitives at p_insert_pos. */

      /* Itterate through incoming primitives, last to first. */
      for(i = p_in_total - 1; i >= 0; i--)
      {
          p = p_in[i];
          if(p == NULL)
            continue;

          /* Allocate one more pointer on the model's primitives
           * list.
           */
          model_ptr->total_primitives++;
          model_ptr->primitive = (gpointer *)g_realloc(
            model_ptr->primitive,
            model_ptr->total_primitives * sizeof(gpointer)
          );
          if(model_ptr->primitive == NULL)
          {
            model_ptr->total_primitives = 0;
            break;
          }

          /* Shift pointers. */
          for(n = model_ptr->total_primitives - 1;
              n > p_insert_pos;
            n--
          )
            model_ptr->primitive[n] = model_ptr->primitive[n - 1];

          /* Get insert primitive position n. */
          n = p_insert_pos;

          /* Insert primitive at position n on model's primitives list. */
          model_ptr->primitive[n] = p;

          /* Mark primitive on incoming list as NULL since it has been
           * transfered.
           */
          p_in[i] = NULL;


          /* Insert a new row on the primitives list. */
          if(primitives_list != NULL)
          {
            gchar *val[1];

            val[0] = ""; 
            gtk_clist_insert(primitives_list, n, val);
            EditorListPrimitivesSet(
                (GtkWidget *)primitives_list, n,
                p, FALSE
            );
          }

          /* Realize incoming primitive. */
          EditorPrimitiveRealize(editor, p, TRUE);

          total_pasted++;
      }

      /* Deallocate pointer array only for incoming primitives,
       * the actual primitives have been transfered to the model's
       * primitives list.
       */
      free(p_in);
      p_in = NULL;
      p_in_total = 0;


      if(!editor->has_changes)
          editor->has_changes = TRUE;

      /* Select first inserted primitive. */
      gtk_clist_select_row(primitives_list, p_insert_pos, 0);

      EditorUpdateMenus(editor);
      EditorRedrawAllViews(editor);
      EditorUpdateAllViewMenus(editor);

      return;
}


/*
 *    Map clipboard callback.
 */
void EditorMapClipboardCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

      ClipboardBrowserMap();

      return;
}

/*
 *    Enables write protect and updates the status bar.
 */
void EditorWriteProtectEnableCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(!editor->initialized || editor->processing)
            return;

        editor->write_protect = TRUE;

      EditorUpdateMenus(editor);

      return;
}

/*
 *    Disables write protect and updates the status bar.
 */
void EditorWriteProtectDisableCB(GtkWidget *widget, gpointer data)
{
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

      if(!editor->initialized || editor->processing)
          return;

      editor->write_protect = FALSE;

      EditorUpdateMenus(editor);

      return;
}


/*
 *    Map color selection dialog (not the default csd dialog).
 */
void EditorMapClrSelCB(GtkWidget *widget, gpointer data)
{
      vma_clrsel_struct *cs;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

      if(editor->processing)
            return;

      cs = &editor->clrsel;
      if(!cs->initialized)
          return;

      ClrSelFetchFromEditor(cs);
      ClrSelUpdateMenus(cs);
      ClrSelMap(cs);

      return;
}

/*
 *    Map texture browser callback.
 */
void EditorMapTextureBrowserCB(GtkWidget *widget, gpointer data)
{
      int model_num, pn;
      v3d_model_struct *model_ptr;
      void *p;
      ma_texture_browser_struct *tb;
      ma_editor_struct *editor = (ma_editor_struct *)data;
      if(editor == NULL)
          return;

        if(editor->processing)
            return;

      tb = &editor->texture_browser;
      if(!tb->initialized)
          return;

        /* Map texture browser. */
        TexBrowserMap(tb);  

        /* Get selected model. */
        model_num = EditorSelectedModelIndex(editor);
        model_ptr = V3DModelListGetPtr(
            editor->model, editor->total_models, model_num
        );
        if(model_ptr != NULL)
      {
            /* Get selected primitive. */
            pn = ((editor->total_selected_primitives == 1) ?
                editor->selected_primitive[0] : -1
            );
            p = V3DMPListGetPtr(
                model_ptr->primitive, model_ptr->total_primitives, pn
            );
          /* Is primitive valid and of type V3DMP_TYPE_TEXTURE_SELECT? */
            if((p == NULL) ? 0 : ((*(int *)p) == V3DMP_TYPE_TEXTURE_SELECT))
          {
            GtkCList *clist;
            const char *tex_name;
            mp_texture_select_struct *texture_select = (mp_texture_select_struct *)p;

            /* Get textures list widget from texture browser
             * structure.
             */
            clist = (GtkCList *)tb->textures_list;

            /* Get texture name of currently selected primitive on
             * editor.
             */
            tex_name = (const char *)texture_select->name;

            /* Do we have enough information to begin selecting the
             * item on the texture browser's list for the
             * corresponding item with the same texture name?
             */
            if((tex_name != NULL) && (clist != NULL))
            {
                int i;
                gchar *cell_text;

                for(i = 0; i < clist->rows; i++)
                {
                  if(!gtk_clist_get_text(clist, i, 1, &cell_text))
                      cell_text = NULL;
                  if(cell_text == NULL)
                      continue;
                  if(!strcmp(cell_text, tex_name))
                      break;
                }
                if(i < clist->rows)
                {
                  /* Unselect all previously selected rows and select
                   * new matched row.
                   */
                  gtk_clist_unselect_all(clist);
                  gtk_clist_select_row(
                      clist, i, 0
                  );
                }
            }
          } /* Is primitive valid and of type V3DMP_TYPE_TEXTURE_SELECT? */
      }

        /* Update texture browser menus. */
        TexBrowserUpdateMenus(tb);

      return;
}

/*
 *    Maps the preferences window.
 */
void EditorPreferencesCB(GtkWidget *widget, gpointer data)
{
      vma_pref_struct *pref_win;
      vma_core_struct *core_ptr;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

      core_ptr = (vma_core_struct *)editor->core_ptr;
      if(core_ptr == NULL)
          return;

      pref_win = core_ptr->pref_win;
        if(pref_win == NULL)
        {   
            core_ptr->pref_win = pref_win = PrefNew(core_ptr);
        }
        if(pref_win == NULL)
            return;

      /* Preferences window initialized and unmapped? */
      if(pref_win->initialized && !pref_win->map_state)
      {
            GtkCTreeNode *branch;
            GtkCTreeRow *branch_row;
            GtkCTree *ctree;
            char *strptr;

            ctree = (GtkCTree *)((pref_win == NULL) ?
                NULL : pref_win->catagory_ctree
            );
            if(ctree != NULL)
            {
            /* Get toplevel branch. */
                branch = gtk_ctree_node_nth(ctree, 0);
            if(branch != NULL)
                gtk_ctree_expand(ctree, branch);

            /* Get first child branch of toplevel. */
            if(branch != NULL)
            {
                    branch_row = GTK_CTREE_ROW(branch);
                    branch = ((branch_row == NULL) ?
                        NULL : branch_row->children
                    );
            }

            /* Select the branch named "Appearance". */
                while(branch != NULL)
                {
                    strptr = PrefPanelGetBranchText(pref_win, branch);
                    if(strptr != NULL)
                    {
                        if(!strcasecmp(strptr, "Appearance"))
                        {
                            gtk_ctree_expand(ctree, branch);
                            gtk_ctree_select(ctree, branch);
                            break;
                        }
                    }

                    branch_row = GTK_CTREE_ROW(branch);
                    branch = ((branch_row == NULL) ?
                        NULL : branch_row->sibling
                    );
                }

                PrefDoFetch(pref_win);
                PrefMap(pref_win);
            }
        }

      return;
}

/*
 *    Add vertex to selected primitive callback.
 */
void EditorPrimitiveVertexAddCB(GtkWidget *widget, gpointer data)
{
      int pn;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(!editor->initialized || editor->processing)
            return;

        if(VMAWriteProtectCheck(editor))
            return;

        /* Get selected primitive. */
        if(editor->total_selected_primitives == 1)
            pn = editor->selected_primitive[0];
        else
            pn = -1;
        if(pn < 0)
            return;

        /* Call add vertex procedure, set vertex insert position to
       * be current selected position plus 1.
       */
        EditorPrimitiveVertexAdd(
            editor,
            EditorSelectedModelIndex(editor),
            pn,
          EditorGetSelected(
            editor->selected_value, editor->total_selected_values,
            0
          ),
          NULL, NULL, NULL,
            TRUE        /* Report errors. */
        );

      return;
}

/*
 *    Remove vertex from selected primitive callback.
 */
void EditorPrimitiveVertexRemoveCB(GtkWidget *widget, gpointer data)
{
      int pn;
      ma_editor_struct *editor = (ma_editor_struct *)data;
      if(editor == NULL)
          return;

      if(!editor->initialized || editor->processing)
          return;

        if(VMAWriteProtectCheck(editor))
            return;

        /* Get selected primitive. */
      if(editor->total_selected_primitives == 1)
          pn = editor->selected_primitive[0];
      else
          pn = -1;
      if(pn < 0)
            return;

      /* Call remove vertex procedure. */
      EditorPrimitiveVertexRemove(
          editor,
          EditorSelectedModelIndex(editor),
          pn,
            EditorGetSelected(
                editor->selected_value, editor->total_selected_values,
                0
            ),
          TRUE,         /* Record undo. */
          TRUE          /* Report errors. */
      );

      return;
}

/*
 *    Inserts selected primitive's selected vertex (if any) to the
 *    scratch pad.
 */
void EditorScratchPadVertexInsertCB(GtkWidget *widget, gpointer data)
{
      gint value_num, pn, model_num;
        mp_vertex_struct *v, *n, *tc;
      v3d_model_struct *model_ptr;
      void *p;
        vma_core_struct *core_ptr;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(!editor->initialized || editor->processing)
            return;

        core_ptr = (vma_core_struct *)editor->core_ptr;
        if(core_ptr == NULL)
            return;

        model_num = EditorSelectedModelIndex(editor);
        model_ptr = V3DModelListGetPtr(
            editor->model, editor->total_models, model_num
        );
        if(model_ptr == NULL)
            return;

        if(editor->total_selected_primitives < 1)
            return;
        else
            pn = editor->selected_primitive[
                editor->total_selected_primitives - 1
            ];
        p = V3DMPListGetPtr(
            model_ptr->primitive, model_ptr->total_primitives, pn
        );
        if(p == NULL)
            return;

      /* Get selected item on the editor's values list. */
      value_num = EditorGetSelected(
            editor->selected_value, editor->total_selected_values,
            0
        );
        if(value_num < 0)
            return;

      /* Get pointers to vertex, normal and texcoords if any. */
        v = V3DMPGetVertex(p, value_num);
      if(v == NULL)
          return;

        n = V3DMPGetNormal(p, value_num);
        if(n == NULL)
            return;

        tc = V3DMPGetTexCoord(p, value_num);
        if(tc == NULL)
            return;

      /* Got atleast one valid vertex pointer? */
      if((v != NULL) || (n != NULL) || (tc != NULL))
        {
            gint new_row;
            vma_scratch_pad_struct *sp = core_ptr->scratch_pad;
            if(sp != NULL)
            {
                GtkCList *clist = (GtkCList *)sp->clist;
 
                new_row = ScratchPadRowInsert(sp, v, n, tc, NULL);
                if(sp->map_state && (clist != NULL))
                {
                    if(new_row > -1)
                        gtk_clist_select_row(clist, new_row, 0);
                }
            }
        }   

      return;
}

/*
 *      Appends selected primitive's selected vertex (if any) to the
 *      scratch pad.
 */
void EditorScratchPadVertexAppendCB(GtkWidget *widget, gpointer data)
{
        gint value_num, pn, model_num;
        mp_vertex_struct *v, *n, *tc;
        v3d_model_struct *model_ptr;
        gpointer p;
        vma_core_struct *core_ptr;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

      if(!editor->initialized || editor->processing)
          return;

      core_ptr = (vma_core_struct *)editor->core_ptr;
      if(core_ptr == NULL)
          return;

        model_num = EditorSelectedModelIndex(editor);
        model_ptr = V3DModelListGetPtr(
            editor->model, editor->total_models, model_num
        );
        if(model_ptr == NULL)
            return;

        if(editor->total_selected_primitives < 1)
            return;  
        else
            pn = editor->selected_primitive[
                editor->total_selected_primitives - 1
            ];
        p = V3DMPListGetPtr(
            model_ptr->primitive, model_ptr->total_primitives, pn
        );
        if(p == NULL) 
            return;

        /* Get selected item on the editor's values list. */
        value_num = EditorGetSelected(
          editor->selected_value, editor->total_selected_values,
          0
      );
        if(value_num < 0)
            return;

        /* Get pointers to vertex, normal and texcoords if any. */
        v = V3DMPGetVertex(p, value_num);
        if(v == NULL)
            return;
 
        n = V3DMPGetNormal(p, value_num);
        if(n == NULL)
            return;

        tc = V3DMPGetTexCoord(p, value_num);
        if(tc == NULL)
            return;

        /* Got atleast one valid vertex pointer? */
        if((v != NULL) || (n != NULL) || (tc != NULL))
      {
          gint new_row;
          vma_scratch_pad_struct *sp = core_ptr->scratch_pad;
          if(sp != NULL)
          {
            GtkCList *clist = (GtkCList *)sp->clist;

            new_row = ScratchPadRowAppend(sp, v, n, tc, NULL);
            if(sp->map_state && (clist != NULL))
            {
                if(new_row > -1)
                  gtk_clist_select_row(clist, new_row, 0);

            }
          }
      }

      return;
}

/*
 *    This just maps the scratch pad.
 */
void EditorScratchPadVertexEditCB(GtkWidget *widget, gpointer data)
{
      vma_core_struct *core_ptr;
      vma_scratch_pad_struct *sp;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

      if(!editor->initialized || editor->processing)
          return;

      core_ptr = (vma_core_struct *)editor->core_ptr;
      if(core_ptr == NULL)
          return;

      sp = core_ptr->scratch_pad;
      if(sp == NULL)
          return;

      /* Select our editor on the scratch pad. */
      ScratchPadSelectEditorPtr(sp, editor);

      /* Map the scratch pad. */
      VMAScratchPadMapCB(widget, editor->core_ptr);

      return;
}


/*
 *    Editor's render menu plug-in render menu item callback.
 */
void EditorPluginRenderCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterant = FALSE;
      gint i, n, status;
      vma_editor_render_mi_struct *render_mi;
      vpi_op_render_struct v;
      vma_core_struct *core_ptr;
      vma_light_struct *light_ptr;
      vpi_light_struct *plight_ptr;
      vma_plugin_struct *plugin_ptr;
      vma_view3d_struct *view3d;
      ma_editor_struct *editor = (ma_editor_struct *)data;
        if((widget == NULL) || (editor == NULL))
            return;

      core_ptr = (vma_core_struct *)editor->core_ptr;
      if(core_ptr == NULL)
          return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;


      /* Get pointer to first 3d view. */
      if(VMA_MAX_3D_VIEWS_PER_EDITOR > 0)
          view3d = editor->view3d[0];
      else
          view3d = NULL;

      /* Set up render operation values structure. */
      v.flags = (VPI_OP_RENDER_EDITOR_ID |
          VPI_OP_RENDER_HEADER | VPI_OP_RENDER_MODELS |
          VPI_OP_RENDER_LIGHT
      );
      for(i = 0; i < core_ptr->total_editors; i++)
      {
          if(core_ptr->editor[i] == editor)
            break;
      }
      if(i >= core_ptr->total_editors)
          i = -1;
      v.editor_id = i;
      v.mh_item = editor->mh_item;
      v.total_mh_items = editor->total_mh_items;
        v.model = editor->model;
        v.total_models = editor->total_models;
      /* Set lights. */
      v.light = NULL;
      v.total_lights = 0;
      for(i = 0; i < VMA_LIGHTS_MAX; i++)
      {
          light_ptr = &(editor->light[i]);
          if(!(light_ptr->flags & VMA_LIGHT_FLAG_ENABLED))
            continue;

          n = v.total_lights;
          v.total_lights = n + 1;
          v.light = (vpi_light_struct *)g_realloc(
            v.light, v.total_lights * sizeof(vpi_light_struct)
          );
          if(v.light == NULL)
          {
            v.total_lights = 0;
            break;
          }

          plight_ptr = &(v.light[n]);
          plight_ptr->flags = (VPI_LIGHT_POS |
            VPI_LIGHT_AMBIENT | VPI_LIGHT_DIFFUSE | VPI_LIGHT_SPECULAR
          );
          plight_ptr->pos_x = light_ptr->x;
          plight_ptr->pos_y = light_ptr->y;
          plight_ptr->pos_z = light_ptr->z;
          /* Check if light is directional? */
          if(light_ptr->spot_cutoff < (0.5 * PI))
          {
            plight_ptr->flags |= (VPI_LIGHT_DIR | VPI_LIGHT_SPOT_CUTOFF);
            plight_ptr->dir_x = sin(light_ptr->heading);
            plight_ptr->dir_y = cos(light_ptr->heading);
            plight_ptr->dir_z = -sin(light_ptr->pitch);
            plight_ptr->spot_cutoff = light_ptr->spot_cutoff;
          }
          else
          {
            plight_ptr->dir_x = 0.0;
                plight_ptr->dir_y = 0.0;
                plight_ptr->dir_z = 0.0;
                plight_ptr->spot_cutoff = (1.0 * PI);
          }
          plight_ptr->ambient.r = light_ptr->ambient.r;
            plight_ptr->ambient.g = light_ptr->ambient.g;
            plight_ptr->ambient.b = light_ptr->ambient.b;
            plight_ptr->ambient.a = light_ptr->ambient.a;

          plight_ptr->diffuse.r = light_ptr->diffuse.r;
            plight_ptr->diffuse.g = light_ptr->diffuse.g;
            plight_ptr->diffuse.b = light_ptr->diffuse.b;
            plight_ptr->diffuse.a = light_ptr->diffuse.a;

            plight_ptr->specular.r = light_ptr->specular.r;
            plight_ptr->specular.g = light_ptr->specular.g;
            plight_ptr->specular.b = light_ptr->specular.b;
          plight_ptr->specular.a = light_ptr->specular.a;
      }
      /* Check if the 3d view on the editor was valid, if so then set
       * specific values obtained from that 3d view.
       */
      if(view3d != NULL)
      {
          gdouble mag;
          vpi_camera_struct *cam = &v.camera;


          /* Update render op values structure to include the camera
           * flag.
           */
          v.flags |= VPI_OP_RENDER_CAMERA;

          /* Begin setting up camera structure values. */
          cam->flags = (VPI_CAMERA_POS | VPI_CAMERA_DIR |
            VPI_CAMERA_UP | VPI_CAMERA_FOV | VPI_CAMERA_ASPECT |
            VPI_CAMERA_CLIP_NEAR | VPI_CAMERA_CLIP_FAR
          );
          cam->pos_x = view3d->cam_x;
          cam->pos_y = view3d->cam_y;
          cam->pos_z = view3d->cam_z;
          cam->dir_x = sin(view3d->cam_h);
          cam->dir_y = cos(view3d->cam_h);
          cam->dir_z = -sin(view3d->cam_p);
          mag = sqrt(
            (cam->dir_x * cam->dir_x) +
            (cam->dir_y * cam->dir_y) +
            (cam->dir_z * cam->dir_z)
          );
          if(mag > 0.0)
          {
            cam->dir_x = cam->dir_x / mag;
                cam->dir_y = cam->dir_y / mag;
                cam->dir_z = cam->dir_z / mag;
          }
          cam->up_x = 0.0;
          cam->up_y = 0.0;
          cam->up_z = (((view3d->cam_p > (0.5 * PI)) &&
                (view3d->cam_p < (1.5 * PI))) ? -1.0 : 1.0
          );
          cam->fov = view3d->cam_fov;     /* In radians. */
          cam->aspect = 1.33333;
          cam->clip_near = view3d->cam_clip_near;
          cam->clip_far = view3d->cam_clip_far;

          /* Also set cull face and cull direction values on the
           * render op values structure.
           */
          v.flags |= (VPI_OP_RENDER_CULL_FACE |
            VPI_OP_RENDER_CULL_DIR
          );
          v.cull_face = ((view3d->cull_state) ? 1 : 0);
          v.cull_dir = ((view3d->cull_direction) ?
            VPI_CULL_DIRECTION_CCW : VPI_CULL_DIRECTION_CW
          );
      }

      /* Itterate through editor's render menu items. */
      for(i = 0; i < editor->total_render_mis; i++)
      {
          render_mi = editor->render_mi[i];
          if(render_mi == NULL)
            continue;

          /* Skip recorded menu items with no plug-in reference,
           * these suggest that they have other functions (other
           * functions than calling the plug-in's render function).
           */
          if(render_mi->plugin_ptr == NULL)
            continue;

          /* This the menu item that was triggered? */
          if(render_mi->fw == widget)
            plugin_ptr = render_mi->plugin_ptr;
          else
            plugin_ptr = NULL;
          if(plugin_ptr == NULL)
            continue;

          /* Verify that plug-in exists. */
          for(n = 0; n < core_ptr->total_plugins; n++)
          {
            if(core_ptr->plugin[n] == plugin_ptr)
                break;
          }
          if(n >= core_ptr->total_plugins)
            continue;

          /* Plug-in exists, now check if its not enabled or busy. */
          if((plugin_ptr->handle == NULL) ?
            1 : plugin_ptr->processing
          )
            continue;

          if(plugin_ptr->render == NULL)
            continue;

          plugin_ptr->processing = 1;
          status = plugin_ptr->render(
            (vpi_id *)plugin_ptr,
            &v,
            plugin_ptr->render_data
          );
          plugin_ptr->processing = 0;
      }


      /* Deallocate any allocated members of the render operation
       * values structure.
       */
      if(v.light != NULL)
      {
          g_free(v.light);
          v.light = NULL;
      }
      v.total_lights = 0;


      reenterant = FALSE;
}



/*
 *      Updates the position relative to be just under the given
 *      widget as data.  
 */
static void EditorMenuMapPositionCB(
        GtkMenu *menu, gint *x, gint *y, gpointer data
)
{
        gint rx, ry;
        GtkWidget *rel_widget = (GtkWidget *)data;
        if((menu == NULL) || (rel_widget == NULL))
            return;

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

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

        return;
}

/*
 *      Menu mapping from a button "pressed" signal callback.
 */
void EditorButtonMenuMapCB(GtkButton *button, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkMenu *menu;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if((button == NULL) || (editor == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

#define DO_BUTTON_PRESSED     \
{ \
 if(button != NULL) \
 { \
  gtk_grab_remove(GTK_WIDGET(button)); \
  \
  while(gtk_events_pending() > 0) \
   gtk_main_iteration(); \
  \
  button->in_button = 1; \
  button->button_down = 1; \
  gtk_signal_emit_by_name(GTK_OBJECT(button), "pressed"); \
 } \
}
        /* Scratch pad button. */
        if((void *)button == (void *)editor->primitive_scratchpad_btn)
        {
            menu = (GtkMenu *)editor->primitive_scratchpad_btn_menu;
            if(menu != NULL)
            {
                /* Map menu. */
                gtk_menu_popup(
                    menu, NULL, NULL,
                    EditorMenuMapPositionCB, (gpointer)button,
                    1, GDK_CURRENT_TIME
                );
                DO_BUTTON_PRESSED
            }
        }
      /* Light menu button. */
        else if((void *)button == (void *)editor->primitive_light_btn)
        {
            menu = (GtkMenu *)editor->primitive_light_btn_menu;
            if(menu != NULL)
            {
            gint light_num;
            GtkWidget *w2;

            /* Need to update lighting enabled check menu item
             * state.
             */
            light_num = EditorSelectedLightNumber(editor);
            w2 = editor->primitive_light_btn_enabled_micheck;
            if((light_num >= 0) && (light_num < VMA_LIGHTS_MAX) &&
               (w2 != NULL)
            )
            {
                vma_light_struct *light_ptr = &editor->light[light_num];

                GTK_CHECK_MENU_ITEM(w2)->active = (
                  (light_ptr->flags & VMA_LIGHT_FLAG_ENABLED) ?
                  TRUE : FALSE
                    );
            }

                /* Map menu. */
                gtk_menu_popup(
                    menu, NULL, NULL,
                    EditorMenuMapPositionCB, (gpointer)button,
                    1, GDK_CURRENT_TIME
                );
                DO_BUTTON_PRESSED
            }
        }

#undef DO_BUTTON_PRESSED

        reenterant = FALSE;
        return;
}

/*
 *      Menu hide callback.
 *
 *      Used for buttons which map menus, when the menu for a button
 *      unmaps (hides) then function handles the "hide" signal and sets
 *      the associated button released by emitting the "released"
 *      signal.
 */
void EditorMenuHideCB(GtkWidget *widget, gpointer data)
{
        static gbool reenterant = FALSE;
        GtkWidget *w;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if((widget == NULL) || (editor == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE; 

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

        /* Scratch pad menu? */
        if(widget == editor->primitive_scratchpad_btn_menu)
        {    
            w = editor->primitive_scratchpad_btn;
            DO_BUTTON_RELEASE
        }
        /* Light menu? */
        if(widget == editor->primitive_light_btn_menu)
        {
            w = editor->primitive_light_btn;
            DO_BUTTON_RELEASE
        }

#undef DO_BUTTON_RELEASE

        reenterant = FALSE;
        return;
}


/*
 *    Values browse button callback.
 *
 *    Note that this function may also be called on double click.
 */
void EditorValueBrowseCB(GtkWidget *widget, gpointer data)
{
      gint model_num, pn, ptype, value_num;
      v3d_model_struct *model_ptr;
      mp_color_struct *mp_color;
      mp_texture_select_struct *mp_texture_select;
      mp_heightfield_load_struct *mp_heightfield_load;
      gpointer p;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

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

      /* Get selected primitive. */
      pn = ((editor->total_selected_primitives == 1) ?
          editor->selected_primitive[0] : -1
      );
      p = V3DMPListGetPtr(
          model_ptr->primitive, model_ptr->total_primitives, pn
      );
      if(p == NULL)
          return;
      else
          ptype = (*(int *)p);

      /* Get selected value item (item on values list). */
      value_num = EditorGetSelected(
          editor->selected_value, editor->total_selected_values,
            0
        );

      /* Handle by primitive type. */
      switch(ptype)
      {
        case V3DMP_TYPE_COLOR:
          mp_color = (mp_color_struct *)p;
          EditorMapClrSelCB(widget, editor);
          break;

        case V3DMP_TYPE_TEXTURE_SELECT:
          mp_texture_select = (mp_texture_select_struct *)p;
          /* Ignore selected_value_item. */
          EditorMapTextureBrowserCB(widget, editor);
          break;

        case V3DMP_TYPE_HEIGHTFIELD_LOAD:
          mp_heightfield_load = (mp_heightfield_load_struct *)p;
          switch(value_num)
          {
            case -1: case 0: case 1:
            EditorHFDialogMap(editor, p);
            break;

            default:
            break;
          }
          break;

/* Additional mapping of primitives that have browseable dialogs
 * need to be added here.
 */
      }


      return;
}

/*
 *    Values input text prompt enter callback.
 */
void EditorValueEnterCB(GtkWidget *widget, gpointer data)
{
      ma_editor_struct *editor = (ma_editor_struct *)data;
      if(editor == NULL)
          return;

        if(editor->processing)
            return;

        if(VMAWriteProtectCheck(editor))
            return;

      EDITOR_DO_UPDATE_HAS_CHANGES

      /* Apply values on the values prompts. */
      EditorListValuePromptApply(editor, widget, TRUE);

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

      return;
}


/*
 *      Values prompts apply button callback.
 */
void EditorValueApplyCB(GtkWidget *widget, gpointer data)
{
      int i;
      GtkWidget *w;

        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

        if(VMAWriteProtectCheck(editor))
            return;

      /* Apply values for all mapped value prompt widgets. */
      for(i = 0; i < VMA_PRIMITIVES_MAX_VALUES; i++)
      {
          w = editor->values_text[i];
          if(w == NULL)
            continue;
          if(!GTK_WIDGET_MAPPED(w))
            continue;

          /* Entry widget is mapped, so apply values on it. */
          EditorValueEnterCB(w, data);
      }

      return;
}

/*
 *    Models list select callback.
 */
void EditorModelsListSelectCB(
      GtkWidget *widget, gint row, gint column,
      GdkEventButton *event, gpointer data
)
{
        static gbool reenterant = FALSE;
        ma_editor_struct *editor = data;
        if((editor == NULL) ||
         (widget == NULL)
      )
            return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

        /* Change in selection? */
/* Should always accept change in selection, for instance if a row
   was deleted this function would be called again but maybe with
   the same row number.
        if(EditorSelectedModelIndex(editor) != row)
 */
      if(1)
        {
          v3d_model_struct *model = V3DModelListGetPtr(
            editor->model, editor->total_models, row
          );

          /* Clear text on text dialog. */
          EditorTDialogClear(&editor->tdialog);

          /* Delete all values on values and primitives gui lists. */
          EditorListDeleteValuesG(editor);  
          EditorListDeletePrimitivesG(editor);

          /* Scroll to row if not visable. */
            if(gtk_clist_row_is_visible(GTK_CLIST(widget), row) !=
            GTK_VISIBILITY_FULL
          )
            gtk_clist_moveto(
                GTK_CLIST(widget),
                    row, 0,   /* Row, column. */
                0.5, 0.0      /* Row, column. */
            );

          /* Update selected model item. */
          editor->selected_model_item = row;

          /* Update primitives gui list. */
          EditorListAddPrimitivesRG(editor, model);

          /* Update text dialog values. */
            EditorTDialogSetValues(&editor->tdialog, row, -1);
          EditorTDialogFetch(editor, &editor->tdialog);

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

          /* Update menus on text dialog. */
          EditorTDialogUpdateMenus(&editor->tdialog);
      }

        reenterant = FALSE;
      return;
}

/*
 *    Models list unselect row callback.
 */
void EditorModelsListUnselectCB(
        GtkWidget *widget, gint row, gint column,
        GdkEventButton *event, gpointer data
)
{
        static gbool reenterant = FALSE;
        ma_editor_struct *editor = data;
        if((editor == NULL) ||
           (widget == NULL)
        )
            return;
             
        if(reenterant)
            return;
        else
            reenterant = TRUE;
 
      /* Sync data on editor. */
      EditorSyncData(editor);

      /* Update selected model item. */
      editor->selected_model_item = -1;


        /* Clear text dialog. */
        EditorTDialogClear(&editor->tdialog); 

        /* Delete all values on values and primitives gui lists. */
        EditorListDeleteValuesG(editor);
        EditorListDeletePrimitivesG(editor); 

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

      /* Update menus on text dialog. */
      EditorTDialogUpdateMenus(&editor->tdialog);

        reenterant = FALSE;
        return;
}


/*
 *    Primitives list select callback.
 */
void EditorPrimitivesListSelectCB(
      GtkWidget *widget, gint row, gint column,
      GdkEventButton *event, gpointer data
)
{
        static gbool reenterant = FALSE;
      gint model_num;
        v3d_model_struct *model_ptr;
      ma_editor_struct *editor = (ma_editor_struct *)data;
        if((editor == NULL) || (widget == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

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

      /* Double click? */
      if((event != NULL) ?
          (event->type == GDK_2BUTTON_PRESS) : 0
      )
      {
          /* Double click handling. */
          EditorPrimitivesListDoubleClickCB(editor, row);

          /* Return, do not process selecting. */
          reenterant = FALSE;
          return;
      }


      /* Add selected primitive to list. */
      EditorSelect(
          &editor->selected_primitive,
          &editor->total_selected_primitives,
          row
      );

        /* Scroll to row if not visable. */
        if(gtk_clist_row_is_visible(GTK_CLIST(widget), row) !=
            GTK_VISIBILITY_FULL
        )
            gtk_clist_moveto(
                GTK_CLIST(widget),
                row, 0,       /* Row, column. */
                0.5, 0.0      /* Row, column. */
            );                                    


      /* Clear and unselect all values in the values gui list. */
        EditorListDeleteValuesG(editor);

      /* Clear text dialog. */
      EditorTDialogClear(&editor->tdialog);


        /* Selected exactly one item? */
        if(editor->total_selected_primitives == 1)
      {
          /* Selected exactly one item, so update values
           * list for the selected primitive.
           */
          gint pn = row;
          gpointer p = NULL;

          /* Was a model selected? */
          if(model_ptr != NULL)
          {
            /* Is the selected primitive allocated on the real
             * primitives list?
             */
            if((pn >= 0) && (pn < model_ptr->total_primitives))
            {
                p = model_ptr->primitive[pn];

                EditorListAddValuesRG(editor, p);

                /* Select first row on values list. */
/* Don't do this, what if we want to move the whole primitive? We need
 * ensure no selected values to do that.
                w = editor->values_list;
                if(w != NULL)
                    gtk_clist_select_row(
                      GTK_CLIST(w), 0, 0
                  );
 */
            }

                /* Update text dialog values. */
            EditorTDialogSetValues(&editor->tdialog, model_num, pn);
                EditorTDialogFetch(editor, &editor->tdialog);
          }
      }

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

        /* Update menus on text dialog. */
        EditorTDialogUpdateMenus(&editor->tdialog);

        reenterant = FALSE;
      return;
}

/*
 *      Primitives list unselect callback.
 */
void EditorPrimitivesListUnselectCB(
        GtkWidget *widget, gint row, gint column, GdkEventButton *event,
        gpointer data
)
{
        static gbool reenterant = FALSE;
      GtkWidget *w;
      gint model_num;
      v3d_model_struct *model_ptr;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if((editor == NULL) || (widget == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

        /* Sync data on editor. */
        EditorSyncData(editor);

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

      /* Unselect row from primitives selection list. */
      EditorUnselect(
          &editor->selected_primitive,
          &editor->total_selected_primitives,
          row
      );

        /* Clear and unselect all values in the values gui list. */
        EditorListDeleteValuesG(editor);

        /* Clear text dialog. */
        EditorTDialogClear(&editor->tdialog);

        /* Only one item selected now? */
        if(editor->total_selected_primitives == 1)
        {
          gint pn = editor->selected_primitive[0];
          gpointer p = NULL;

            /* Was a model selected? */
            if(model_ptr != NULL)
            {
                /* Is the selected primitive allocated on the real
                 * primitives list?
                 */
                if((pn >= 0) && (pn < model_ptr->total_primitives))
                {
                p = model_ptr->primitive[pn];

                    EditorListAddValuesRG(editor, p);

                    /* Select first row on values list. */
                    w = editor->values_list;
                    if(w != NULL)
                        gtk_clist_select_row(
                            GTK_CLIST(w), 0, 0
                        );
                }
            }
      }

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

        reenterant = FALSE;

      return;
}

/*
 *    Handles a double click on the editor's primitives list.
 */
static void EditorPrimitivesListDoubleClickCB(
      ma_editor_struct *editor, int pn
)
{
      EditorValueBrowseCB(NULL, editor);
      return;
}

/*
 *    Values list select callback.
 */
void EditorValuesListSelectCB(
        GtkWidget *widget, gint row, gint column,
        GdkEventButton *event, gpointer data
)
{
      static gbool reenterant = FALSE;
      gint i, model_num, value_num;
      v3d_model_struct *model_ptr;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if((editor == NULL) || (widget == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

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

        /* Double click? */
        if((event != NULL) ?
            (event->type == GDK_2BUTTON_PRESS) : 0
        )
        {
            /* Double click handling. */
            EditorValuesListDoubleClickCB(editor, row);

            /* Return, do not process selecting. */
            reenterant = FALSE;
            return;
        }

      /* Change in selection? */
      value_num = EditorGetSelected(
          editor->selected_value, editor->total_selected_values,
            0
        );
      if(value_num != row)
      {
            /* Scroll to row if not visable. */
            if(gtk_clist_row_is_visible(GTK_CLIST(widget), row) !=
                GTK_VISIBILITY_FULL
            )
                gtk_clist_moveto(
                    GTK_CLIST(widget),
                    row, 0,   /* Row, column. */
                    0.5, 0.0  /* Row, column. */
                );

            /* Update selected value. */
          value_num = row;
          EditorSelectSingle(
            &editor->selected_value, &editor->total_selected_values,
            value_num
          );

          /* Got valid model? */
          if(model_ptr != NULL)
          {
            if(editor->total_selected_primitives == 1)
                i = editor->selected_primitive[0];
            else
                i = -1;
            if((i >= 0) && (i < model_ptr->total_primitives))
                EditorListValuePromptFetch(
                    editor, model_ptr->primitive[i]
                );
          }

            EditorUpdateMenus(editor);
          EditorUpdateAllViewMenus(editor);
          EditorRedrawAllViews(editor);
      }

      reenterant = FALSE;
        return;
}

/*
 *    Values list unselect callback.
 */
void EditorValuesListUnselectCB(
        GtkWidget *widget, gint row, gint column,
        GdkEventButton *event, gpointer data
)
{
        static gbool reenterant = FALSE;
      gint model_num, pn, value_num;
      v3d_model_struct *model_ptr;
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if((editor == NULL) || (widget == NULL))
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

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

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

        /* Change in selection? */
        if(value_num != -1)
        {
          /* Unselect all values. */
          EditorUnselectAll(
            &editor->selected_value, &editor->total_selected_values
          );
          value_num = -1;

          /* Got valid model? */
          if(model_ptr != NULL)
          {
            /* Get first selected primitive. */
            pn = EditorGetSelected(
                editor->selected_primitive, editor->total_selected_primitives,
                0
            );
            if((pn >= 0) && (pn < model_ptr->total_primitives))
                EditorListValuePromptFetch(
                  editor, model_ptr->primitive[pn]
                );
          }

            EditorUpdateMenus(editor);
            EditorUpdateAllViewMenus(editor);
            EditorRedrawAllViews(editor);
        }

        reenterant = FALSE;
      return;
}

/*
 *    Values list double click callback.
 */
static void EditorValuesListDoubleClickCB(
        ma_editor_struct *editor, int value_item_num
)
{
        EditorValueBrowseCB(NULL, editor);
      return;
}

/*
 *    Clicked on colums list callback.
 */
void EditorListColumClickCB(
      GtkWidget *widget,
      gint column,
      gpointer data
)
{
      static gbool reenterant = FALSE;
      ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return;

        if(editor->processing)
            return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

      if(editor->models_list == widget)
      {

      }
        else if(editor->primitives_list == widget)
        {

        }
      else if(editor->values_list == widget)
      {


      }

      reenterant = FALSE;

      return;
}


/*
 *    Button press handling for clist widgets on editor.
 */
gint EditorListMenuMapCB(GtkWidget *widget, gpointer event, gpointer data)
{
      GtkWidget *w;
      GdkEventButton *button = NULL;
      ma_editor_struct *editor = (ma_editor_struct *)data;
      if((editor == NULL) ||
         (event == NULL) ||
           (widget == NULL)
      )
          return(FALSE);

      if(*(gint *)event == GDK_BUTTON_PRESS)
          button = (GdkEventButton *)event;


      /* Models clist widget? */
      if(widget == editor->models_list)
      {
          /* If a button press, then update DND icon. */
          if(button != NULL)
            EditorDNDModelsListSetIcon(editor, button->x, button->y);

          /* Map menu? */
          w = editor->models_list_menu;
          if((button != NULL) && (w != NULL))
          {
            if(button->button == 3)
            {
                /* Map menu. */
                    gtk_menu_popup(
                        GTK_MENU(w),
                        NULL, NULL, NULL, NULL,
                        button->button, button->time
                    );
                    return(TRUE);
            }
          }
      }
      /* Primitives list? */
        else if(widget == editor->primitives_list)
        {
            /* If a button press, then update DND icon. */
            if(button != NULL)
                EditorDNDPrimitivesListSetIcon(editor, button->x, button->y);

          /* Map menu? */
          w = editor->primitives_list_menu;
            if((button != NULL) && (w != NULL))
            {
                if(button->button == 3)
                {
                    /* Map menu. */
                    gtk_menu_popup(
                        GTK_MENU(w),
                        NULL, NULL, NULL, NULL,
                        button->button, button->time
                    );
                    return(TRUE);
                }
            }
        }
      /* Values list. */
        else if(widget == editor->values_list)
        {
            /* If a button press, then update DND icon. */
            if(button != NULL)
                EditorDNDValuesListSetIcon(editor, button->x, button->y);

          /* Map menu? */
            w = editor->values_list_menu;
            if((button != NULL) && (w != NULL))
            {
                if(button->button == 3)
                {
                    /* Map menu. */
                    gtk_menu_popup(
                        GTK_MENU(w),
                        NULL, NULL, NULL, NULL,
                        button->button, button->time
                    );
                    return(TRUE);
                }
            }
        }


      return(FALSE);
}


/*
 *    Handles a show/hide currently selected model on editor.
 */
void EditorToggleModelShowCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterant = FALSE;
      gbool show_model = TRUE;
      GtkWidget *w;
      int model_num;
      v3d_model_struct *model = NULL;
      ma_editor_struct *editor = (ma_editor_struct *)data;
      if((editor == NULL) ||
           (widget == NULL)
      )
          return;

        if(editor->processing)
            return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

        /* Model show menu check item? */
        if(widget == editor->model_show_micheck)
        {
            w = editor->model_show_micheck;
          show_model = GTK_CHECK_MENU_ITEM(w)->active;
        }
      /* Models list popup menu check item? */
      else if(widget == editor->models_list_show_micheck)
        {
            w = editor->models_list_show_micheck;
            show_model = GTK_CHECK_MENU_ITEM(w)->active;
        }


        /* Get pointer to currently selected model if any. */
        model_num = EditorSelectedModelIndex(editor);
      model = V3DModelListGetPtr(
          editor->model, editor->total_models, model_num
      );
      if(model != NULL)
      {
          /* Update show/hide flag on model. */
          if(show_model)
            model->flags &= ~V3D_MODEL_FLAG_HIDE;
          else
            model->flags |= V3D_MODEL_FLAG_HIDE;

          EditorListModelsSet(
            editor->models_list, model_num,
            model, TRUE
          );
      }


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

      reenterant = FALSE;

      return;
}

Generated by  Doxygen 1.6.0   Back to index