/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/*
* Copyright (C) 1991-93 Silicon Graphics, Inc.
*
_______________________________________________________________________
______________ S I L I C O N G R A P H I C S I N C . ____________
|
| $Revision: 1.1 $
|
| Classes:
| MyTextureEditor
|
| Author(s): Alain Dumesny
|
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#ifndef __sgi
#define ffloor floor
#endif
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/CascadeBG.h>
#include <Xm/PushBG.h>
#include <Xm/LabelG.h>
#include <Xm/Text.h>
#include <Xm/Scale.h>
#include <Xm/SelectioB.h>
#include <Xm/MessageB.h>
#include <Xm/FileSB.h>
#include <GL/GLwMDrawA.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoText2.h>
#include <Inventor/nodes/SoTexture2.h>
#include <Inventor/nodes/SoTexture2Transform.h>
#include <Inventor/nodes/SoTextureCoordinateDefault.h>
#include <Inventor/nodes/SoTextureCoordinateEnvironment.h>
#include <Inventor/nodes/SoTextureCoordinatePlane.h>
#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/errors/SoDebugError.h>
#include <GL/gl.h>
#include "MyColorWheel.h"
#include "MyColorSlider.h"
#include "MyThumbWheel.h"
#include "MyTextureEditor.h"
// stuff to read images (similar to gl/image.h)
// Although the IMAGE stuff is in <gl/image.h> the prototypes are
// incomplete and wrong... So, we include this stuff here.
// Also, the types of some fields change from 5.3 to 6.2, so we use
// something to guarantee the size is 32 bits.
extern "C" {
#define CM_NORMAL 0 /* file contains rows of values which
* are either RGB values (zsize == 3)
* or greyramp values (zsize == 1) */
typedef struct {
unsigned short imagic; /* stuff saved on disk . . */
unsigned short type;
unsigned short dim;
unsigned short xsize;
unsigned short ysize;
unsigned short zsize;
uint32_t min;
uint32_t max;
uint32_t wastebytes;
char name[80];
uint32_t colormap;
} IMAGE;
extern IMAGE *iopen(const char *, const char *);
extern void getrow(IMAGE *, short *, int, int);
extern void iclose(IMAGE *);
extern void i_seterror(void (*func)(char *));
};
/*
* Defines
*/
enum WidgetsID {
PALETTE_BUTTON = 0, // must start at 0 since we use array
PALETTE_MENU,
MENU_BAR,
// file menu
FILE_NEW,
FILE_RESET,
FILE_DELETE,
// text field widgets
SCALE_X_FIELD,
SCALE_Y_FIELD,
TRANS_X_FIELD,
TRANS_Y_FIELD,
ROT_FIELD,
// sliders widgets
TRANS_X_SLD,
TRANS_Y_SLD,
ROT_SLD,
SCALE_X_LABEL,
SCALE_Y_LABEL,
ACCEPT,
TEXTURE_GLX,
TEXTURE_NAME,
// mapping option menu types
MAPP_PULLDOWN,
MAP_DEFAULT,
MAP_ENV,
MAP_PLANE_XY,
MAP_PLANE_XZ,
MAP_PLANE_YZ,
MAP_UNKNOWN,
// other option menu types
OPT_PULLDOWN,
OPT_REPEAT,
OPT_CLAMP,
OPT_UNKNOWN,
// image dialog widgets
DIALOG_WINDOW,
DIALOG_IMAGE,
DIALOG_NAME,
DIALOG_INFO,
DIALOG_BUTTON_0,
DIALOG_FILE_BROWSER,
NUM, // this must be the last entry
};
// time in msec between consecutive clicks to consider
// the second click being a double click.
#define DOUBLE_CLICK_TIME 300
enum TileDrawStyle {
DRAW_NORM,
DRAW_CURRENT,
DRAW_SELECTED,
};
#define IMAGE_SIZE 38
#define IMAGE_NUM 5
#define IMAGE_TOTAL (IMAGE_NUM*IMAGE_NUM)
#define IMAGE_SPACE 2
#define GLX_SIZE (IMAGE_NUM * IMAGE_SIZE + (IMAGE_NUM + 1) * IMAGE_SPACE)
// ??? doing a GL_LINE_LOOP seems to be missing the top right
// ??? pixel due to subpixel == TRUE in openGL.
#define RECT(x1, y1, x2, y2) \
glBegin(GL_LINE_STRIP); \
glVertex2s(x2, y2); glVertex2s(x1, y2); \
glVertex2s(x1, y1); glVertex2s(x2, y1); \
glVertex2s(x2, y2+1); \
glEnd();
struct TextureNameStruct {
char *name;
char *fullName;
int zsize;
char *iconImage;
};
struct PaletteStruct {
char *name;
SbBool user;
SbBool system;
};
/*
* static vars
*/
static char *customTextureDir = ".textures";
static char *defaultDir = "/usr/share/data/textures";
static char *editorTitle = "Texture Editor";
static char *noFileNameStr = "<empty>";
#define hourglass_width 32
#define hourglass_height 32
#define hourglass_x_hot 16
#define hourglass_y_hot 16
static char hourglass_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfe, 0xff, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x04, 0x40, 0x00,
0x00, 0x04, 0x40, 0x00, 0x00, 0xe8, 0x2e, 0x00, 0x00, 0xd0, 0x17, 0x00,
0x00, 0xa0, 0x0b, 0x00, 0x00, 0x40, 0x05, 0x00, 0x00, 0x40, 0x05, 0x00,
0x00, 0x40, 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x40, 0x04, 0x00,
0x00, 0x20, 0x09, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x88, 0x23, 0x00,
0x00, 0xc4, 0x47, 0x00, 0x00, 0xe4, 0x4f, 0x00, 0x00, 0xf4, 0x5f, 0x00,
0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
//
// returns true if the passed file is a subdirectory in the current directory.
// (call chdir() before calling this).
//
static SbBool
isDirectory(char *file)
{
struct stat buf;
if (stat(file, &buf) == 0)
if ((buf.st_mode & S_IFMT) == S_IFDIR)
return TRUE;
return FALSE;
}
//
// return PaletteStruct pointer which contains the given name string
// in the given PbPlist of structs.
//
static PaletteStruct *
findPalette(char *str, SbPList *list)
{
PaletteStruct *pal = NULL;
for (int i=0; i<list->getLength(); i++) {
pal = (PaletteStruct *) (*list)[i];
if (strcmp(str, pal->name) == 0)
return pal;
else
pal = NULL;
}
return pal;
}
static void imageErrorHandler(char *)
{ }
////////////////////////////////////////////////////////////////////////
//
// constructor
//
MyTextureEditor::MyTextureEditor(
Widget parent,
const char *name,
SbBool buildInsideParent,
const char *dir)
//
////////////////////////////////////////////////////////////////////////
: SoXtComponent(parent, name, buildInsideParent)
{
int i;
setClassName("MyTextureEditor");
paletteDir = (dir != NULL) ? strdup(dir) : strdup(defaultDir);
ignoreCallback = FALSE;
selectedItem = currentItem = -1;
curPalette = -1;
loadedPalette = FALSE;
// widget vars
widgetList = new Widget[NUM];
for (i=0; i<NUM; i++)
widgetList[i] = NULL;
oldXThumbVal = oldYThumbVal = 0;
fieldChanged = FALSE;
imageDialogCtx = 0;
// image dialog vars
dialogImage = NULL;
dialogImageName = NULL;
dialogImageInfo = NULL;
// scene graph vars
sceneRoot = new SoSeparator;
texXfNode = new SoTexture2Transform;
texFuncNode = new SoTextureCoordinateDefault;
texNode = new SoTexture2;
sceneRoot->ref();
sceneRoot->addChild(texFuncNode);
sceneRoot->addChild(texXfNode);
sceneRoot->addChild(texNode);
texXfNode->center.setValue(SbVec2f(.5, .5));
//???alain
// if (getgdesc(GD_TEXTURE))
if (1)
userGeometry = new SoCube;
else {
SoText2 *text = new SoText2;
text->string.set1Value(0, SbString("Texture not visible"));
text->string.set1Value(1, SbString("on this machine"));
text->spacing = 2;
text->justification = SoText2::CENTER;
userGeometry = text;
}
sceneRoot->addChild(userGeometry);
// allocate the texture name list
textureNames = new TextureNameStruct[IMAGE_TOTAL];
for (i=0; i<IMAGE_TOTAL; i++) {
textureNames[i].name = textureNames[i].fullName = NULL;
textureNames[i].iconImage = new char[IMAGE_SIZE*IMAGE_SIZE*3]; // for RGB
}
// set the image lib error handler to prevent iopen() to exit()
// when given a non image fileName.
i_seterror(imageErrorHandler);
// Build the widget tree, and let SoXtComponent know about our base widget.
getPaletteNames();
setBaseWidget(buildWidget(getParentWidget()));
}
////////////////////////////////////////////////////////////////////////
//
// Destructor.
//
MyTextureEditor::~MyTextureEditor()
//
////////////////////////////////////////////////////////////////////////
{
int i;
// delete components
delete colWheel;
delete colSlider;
delete viewer;
delete scaleXThumb;
delete scaleYThumb;
// delete stuff
delete [] widgetList;
free(paletteDir);
// delete palette names
PaletteStruct *pal;
for (i=0; i<paletteList.getLength(); i++) {
pal = (PaletteStruct *) paletteList[i];
free(pal->name);
delete pal;
}
paletteList.truncate(0);
// delete texture names
for (i=0; i<IMAGE_TOTAL; i++) {
deleteTextureEntry(i);
delete [] textureNames[i].iconImage;
}
delete [] textureNames;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets the geometry to use
//
// Use: public
void
MyTextureEditor::setObjectGeometry(SoNode *newGeom)
//
////////////////////////////////////////////////////////////////////////
{
//???alain
// if (! getgdesc(GD_TEXTURE))
// return;
// remove the old geometry, and replace it with supplied one
sceneRoot->replaceChild(userGeometry, newGeom);
userGeometry = newGeom;
viewer->viewAll();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets the SoTexture2 node to use.
//
// Use: public
void
MyTextureEditor::setTextureNode(const SoTexture2 *node)
//
////////////////////////////////////////////////////////////////////////
{
// copies values over
texNode->filename = node->filename.getValue();
texNode->wrapS = node->wrapS.getValue();
texNode->wrapT = node->wrapT.getValue();
texNode->model = node->model.getValue();
texNode->blendColor = node->blendColor.getValue();
// update the UI and unselect current texture
updateTexture2UI();
deselectCurrentItem();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets the SoTexture2 Transform node to use.
//
// Use: public
void
MyTextureEditor::setTransformNode(const SoTexture2Transform *node)
//
////////////////////////////////////////////////////////////////////////
{
// copies values over
texXfNode->translation = node->translation.getValue();
texXfNode->rotation = node->rotation.getValue();
texXfNode->scaleFactor = node->scaleFactor.getValue();
texXfNode->center = node->center.getValue();
//
// constrain the transform node to our paradigm
//
// change the center of rotation/scale to make it look like
// we are rotation around the center of the image
texXfNode->center.setValue(
SbVec2f(.5, .5) - texXfNode->translation.getValue());
// normalize rotation to [0,2PI]
float val = texXfNode->rotation.getValue();
while (val < 0) val += 2*M_PI;
while (val > 2*M_PI) val -= 2*M_PI;
texXfNode->rotation = val;
// update the UI
updateTextureXfUI();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets the SoTextureCoordinateFunction node to use.
//
// Use: public
void
MyTextureEditor::setFunctionNode(const SoTextureCoordinateFunction *node)
//
////////////////////////////////////////////////////////////////////////
{
// make our local func node reflect the given node
SoNode *newFunc = (node != NULL) ? node->copy() : new SoTextureCoordinateDefault;
sceneRoot->replaceChild(texFuncNode, newFunc);
texFuncNode = (SoTextureCoordinateFunction *) newFunc;
// finally update the UI
updateTextureFuncUI();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Builds the editor layout
//
// Use: protected
Widget
MyTextureEditor::buildWidget(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
int n;
Arg args[12];
// create a top level form to hold everything together
Widget form = XmCreateForm(parent, "textureForm", NULL, 0);
//
// create all the parts
//
// create all the comps
Widget menu = buildMenu(form);
colWheel = new MyColorWheel(form);
colWheel->setSize(SbVec2s(110, 110));
colWheel->setWYSIWYG(TRUE);
colWheel->addValueChangedCallback(
MyTextureEditor::colWheelCB, this);
Widget wheelW = colWheel->getWidget();
colSlider = new MyColorSlider(form, NULL, TRUE, MyColorSlider::VALUE_SLIDER);
colSlider->setNumericFieldVisible(FALSE);
colSlider->setSize(SbVec2s(1, 23));
colSlider->addValueChangedCallback(
MyTextureEditor::colSliderCB, this);
colSlider->setWYSIWYG(TRUE);
Widget sliderW = colSlider->getWidget();
viewer = new SoXtExaminerViewer(form, NULL, TRUE, SoXtFullViewer::BUILD_NONE);
viewer->setSize(SbVec2s(150, 150));
viewer->setSceneGraph(sceneRoot);
viewer->setDrawStyle(SoXtViewer::STILL,SoXtViewer::VIEW_AS_IS);
viewer->setDrawStyle(SoXtViewer::INTERACTIVE,SoXtViewer::VIEW_AS_IS);
sceneRoot->unref();
Widget viewerW = viewer->getWidget();
Widget glx = buildTexturePaletteWidget(form);
Widget sldForm = buildSliders(form);
Widget butForm = buildButtons(form);
n = 0;
XtSetArg(args[n], XmNalignment, XmALIGNMENT_CENTER); n++;
Widget textName = XmCreateLabelGadget(form, "name", args, n);
widgetList[TEXTURE_NAME] = textName;
//
// layout !
//
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetValues(menu, args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, menu); n++;
XtSetArg(args[n], XmNtopOffset, 5); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 5); n++;
XtSetValues(glx, args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, menu); n++;
XtSetArg(args[n], XmNtopOffset, 5); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, glx); n++;
XtSetArg(args[n], XmNleftOffset, 5); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightOffset, 5); n++;
XtSetValues(viewerW, args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, glx); n++;
XtSetArg(args[n], XmNtopOffset, 5); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNrightWidget, glx); n++;
XtSetValues(textName, args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, butForm); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 5); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomOffset, 5); n++;
XtSetValues(sldForm, args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, viewerW); n++;
XtSetArg(args[n], XmNtopOffset, 2); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, viewerW); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightOffset, 5); n++;
XtSetValues(butForm, args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, butForm); n++;
XtSetArg(args[n], XmNtopOffset, 10); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, sldForm); n++;
XtSetArg(args[n], XmNleftOffset, 20); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightOffset, 15); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomOffset, 7+23); n++;
XtSetValues(wheelW, args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, wheelW); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, wheelW); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNrightWidget, wheelW); n++;
XtSetValues(sliderW, args, n);
updateTexture2UI();
updateTextureXfUI();
updateTextureFuncUI();
updateTextureName();
updateWindowTitle();
updateFileMenu();
// manage children
XtManageChild(menu);
XtManageChild(glx);
XtManageChild(textName);
XtManageChild(viewerW);
XtManageChild(butForm);
XtManageChild(sldForm);
return form;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// creates the menu bar
//
// Use: private
Widget
MyTextureEditor::buildMenu(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
Arg args[8];
Widget buttons[10], subButtons[10], subMenu;
XmString xmstr;
int n, butNum = 0, subButNum;
Widget menu = XmCreateMenuBar(parent, "menuBar", NULL, 0);
widgetList[MENU_BAR] = menu;
//
// create the "File" menu
//
subMenu = XmCreatePulldownMenu(menu, NULL, NULL, 0);
XtSetArg(args[0], XmNsubMenuId, subMenu);
buttons[butNum] = XmCreateCascadeButtonGadget(menu, "File", args, 1);
// create the menu entries
n = 0;
XtSetArg(args[n], XmNuserData, this); n++;
#define ADD_ENTRY(NAME, ID, ACC, ACCTEXT) \
xmstr = XmStringCreateSimple(ACCTEXT); \
XtSetArg(args[n], XmNaccelerator, ACC); \
XtSetArg(args[n+1], XmNacceleratorText, xmstr); \
subButtons[subButNum] = XmCreatePushButtonGadget(subMenu, NAME, args, n+2); \
XtAddCallback(subButtons[subButNum], XmNactivateCallback, \
(XtCallbackProc) MyTextureEditor::fileMenuCB, (XtPointer) ID); \
XmStringFree(xmstr); \
widgetList[ID] = subButtons[subButNum++];
subButNum = 0;
ADD_ENTRY("New...", FILE_NEW, "Alt <Key> n", "Alt+n")
ADD_ENTRY("Reset", FILE_RESET, "Alt <Key> r", "Alt+r")
ADD_ENTRY("Delete", FILE_DELETE, "Alt <Key> d", "Alt+d")
#undef ADD_ENTRY
XtManageChildren(subButtons, subButNum);
butNum++;
//
// create the "Palette" menu
//
widgetList[PALETTE_BUTTON] = buttons[butNum] = XmCreateCascadeButtonGadget(
menu, "Palettes", NULL, 0);
buildPaletteSubMenu();
butNum++;
XtManageChildren(buttons, butNum);
return menu;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// adds an entry to the palette menu.
//
// Use: private
Widget
MyTextureEditor::buildPaletteMenuEntry(int id)
//
////////////////////////////////////////////////////////////////////////
{
PaletteStruct *pal = (PaletteStruct *) paletteList[id];
char accel[20];
char accelText[20];
XmString xmstr = NULL;
Arg args[4];
int n = 0;
XtSetArg(args[n], XmNuserData, this); n++;
if (id < 10) {
sprintf(accel, "Alt <Key> %d", id);
sprintf(accelText, "Alt+%d", id);
xmstr = XmStringCreateSimple(accelText);
XtSetArg(args[n], XmNaccelerator, accel); n++;
XtSetArg(args[n], XmNacceleratorText, xmstr); n++;
}
Widget w = XmCreatePushButtonGadget(widgetList[PALETTE_MENU], pal->name, args, n);
XtAddCallback(w, XmNactivateCallback,
(XtCallbackProc) MyTextureEditor::paletteMenuCB,
(XtPointer) id);
if (xmstr != NULL)
XmStringFree(xmstr);
return w;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Buils the palette popup menu.
//
// Use: private
void
MyTextureEditor::buildPaletteSubMenu()
//
////////////////////////////////////////////////////////////////////////
{
// rebuild the palette popup
// ??? we cannot delete the old popup menu or things will brake.
// ??? Is it automatically deleted for us ?
widgetList[PALETTE_MENU] = XmCreatePulldownMenu(widgetList[MENU_BAR],
NULL, NULL, 0);
Arg args[1];
XtSetArg(args[0], XmNsubMenuId, widgetList[PALETTE_MENU]);
XtSetValues(widgetList[PALETTE_BUTTON], args, 1);
// build all of the entries
Widget *entries = new Widget[paletteList.getLength()];
for (int i=0; i<paletteList.getLength(); i++)
entries[i] = buildPaletteMenuEntry(i);
XtManageChildren(entries, paletteList.getLength());
delete [] entries;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Builds the GLX region where the textures will be displayed
//
// Use: private
Widget
MyTextureEditor::buildTexturePaletteWidget(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
Arg args[12];
int n = 0;
XtSetArg(args[n], XtNwidth, GLX_SIZE); n++;
XtSetArg(args[n], XtNheight, GLX_SIZE); n++;
XtSetArg(args[n], GLwNrgba, TRUE); n++;
// This makes sure we get the maximum buffer configuration
// (by showing interest the number of bits)
XtSetArg(args[n], GLwNredSize, 1); n++;
XtSetArg(args[n], GLwNgreenSize, 1); n++;
XtSetArg(args[n], GLwNblueSize, 1); n++;
Widget glx = XtCreateWidget("paletteGLX", glwMDrawingAreaWidgetClass,
parent, args, n);
widgetList[TEXTURE_GLX] = glx;
XtUninstallTranslations(glx);
XtAddCallback(glx, GLwNginitCallback,
(XtCallbackProc) MyTextureEditor::glxInitCB, (XtPointer) this);
XtAddCallback(glx, GLwNexposeCallback,
(XtCallbackProc) MyTextureEditor::glxExposeCB, (XtPointer) this);
XtAddEventHandler(glx,
(PointerMotionMask | ButtonPressMask | ButtonReleaseMask | LeaveWindowMask),
FALSE, (XtEventHandler) MyTextureEditor::glxEventCB, (XtPointer) this);
return glx;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Builds the sliders into a form.
//
// Use: private
Widget
MyTextureEditor::buildSliders(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
int i, n, num;
Arg args[12];
Widget labels[5], fields[5], sliders[5];
// create a form to hold everything together
Widget form = XmCreateForm(parent, "slidersForm", NULL, 0);
//
// create all the parts
//
repeatState = TRUE; // build the UI with repeat turned on (default)
// create all of the labels
n = 0;
XtSetArg(args[n], XmNalignment, XmALIGNMENT_END); n++;
labels[0] = XmCreateLabelGadget(form, "Translate X:", args, n);
labels[1] = XmCreateLabelGadget(form, "Translate Y:", args, n);
labels[2] = XmCreateLabelGadget(form, "Rotate:", args, n);
widgetList[SCALE_X_LABEL] = labels[3] = XmCreateLabelGadget(form, "Repeat X:", args, n);
widgetList[SCALE_Y_LABEL] = labels[4] = XmCreateLabelGadget(form, "Repeat Y:", args, n);
// create all of the text fields
n = 0;
XtSetArg(args[n], XmNuserData, this); n++;
XtSetArg(args[n], XmNhighlightThickness, 1); n++;
XtSetArg(args[n], XmNcolumns, 5); n++;
#define CREATE_FIELD(ID) \
widgetList[ID] = fields[num] = XmCreateText(form, "", args, n); \
XtAddCallback(fields[num], XmNvalueChangedCallback, \
(XtCallbackProc) MyTextureEditor::fieldChangedCB, (XtPointer) this); \
XtAddCallback(fields[num], XmNactivateCallback, \
(XtCallbackProc) MyTextureEditor::fieldsCB, (XtPointer) ID); \
XtAddCallback(fields[num], XmNlosingFocusCallback, \
(XtCallbackProc) MyTextureEditor::fieldsCB, (XtPointer) ID); \
num++;
num = 0;
CREATE_FIELD(TRANS_X_FIELD)
CREATE_FIELD(TRANS_Y_FIELD)
CREATE_FIELD(ROT_FIELD)
CREATE_FIELD(SCALE_X_FIELD)
CREATE_FIELD(SCALE_Y_FIELD)
#undef CREATE_FIELD
// create all of the sliders
n = 0;
XtSetArg(args[n], XmNuserData, this); n++;
XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
XtSetArg(args[n], XmNwidth, 100); n++;
XtSetArg(args[n], XmNheight, 20); n++;
#define CREATE_SCALE(ID) \
widgetList[ID] = sliders[num] = XmCreateScale(form, "", args, n); \
XtAddCallback(sliders[num], XmNvalueChangedCallback, \
(XtCallbackProc) MyTextureEditor::slidersCB, (XtPointer) ID); \
XtAddCallback(sliders[num++], XmNdragCallback, \
(XtCallbackProc) MyTextureEditor::slidersCB, (XtPointer) ID);
num = 0;
CREATE_SCALE(TRANS_X_SLD)
CREATE_SCALE(TRANS_Y_SLD)
CREATE_SCALE(ROT_SLD)
scaleXThumb = new MyThumbWheel(form);
scaleXThumb->setSize(SbVec2s(100, 21));
scaleXThumb->addValueChangedCallback(
MyTextureEditor::scaleXThumbCB, this);
scaleYThumb = new MyThumbWheel(form);
scaleYThumb->setSize(SbVec2s(100, 21));
scaleYThumb->addValueChangedCallback(
MyTextureEditor::scaleYThumbCB, this);
sliders[num++] = scaleXThumb->getWidget();
sliders[num++] = scaleYThumb->getWidget();
#undef CREATE_SCALE
//
// layout !
//
// text field first
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetValues(fields[0], args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopOffset, 1); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
for (i=1; i<5; i++) {
XtSetArg(args[n], XmNtopWidget, fields[i-1]);
XtSetValues(fields[i], args, n+1);
}
// sliders
n = 0;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNrightOffset, 1); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
for (i=0; i<5; i++) {
XtSetArg(args[n], XmNrightWidget, fields[i]);
XtSetArg(args[n+1], XmNbottomWidget, fields[i]);
XtSetArg(args[n+2], XmNbottomOffset, (i < 3) ? 7 : 4);
XtSetValues(sliders[i], args, n+3);
}
// labels (centered around text fields)
n = 0;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNrightOffset, 3); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 5); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
for (i=0; i<5; i++) {
XtSetArg(args[n], XmNrightWidget, sliders[i]);
XtSetArg(args[n+1], XmNtopWidget, fields[i]);
XtSetArg(args[n+2], XmNbottomWidget, fields[i]);
XtSetValues(labels[i], args, n+3);
}
// manage children
XtManageChildren(fields, 5);
XtManageChildren(sliders, 5);
XtManageChildren(labels, 5);
return form;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Builds the accept + option buttons
//
// Use: private
Widget
MyTextureEditor::buildButtons(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
Widget acceptBut, labels[2], buttons[2];
Arg args[12];
int i, n;
// create a form to hold everything together
Widget form = XmCreateForm(parent, "buttonForm", NULL, 0);
//
// create all the parts
//
// create the accept button
n = 0;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
acceptBut = XmCreatePushButtonGadget(form, "Accept", args, n);
XtAddCallback(acceptBut, XmNactivateCallback,
(XtCallbackProc) MyTextureEditor::acceptCB, (XtPointer) this);
// create the labels
n = 0;
XtSetArg(args[n], XmNalignment, XmALIGNMENT_END); n++;
labels[0] = XmCreateLabelGadget(form, "Mapping:", args, n);
labels[1] = XmCreateLabelGadget(form, "Options:", args, n);
// make all the labels the same width (for layout)
short w; int width = 0;
for (i=0; i<2; i++) {
XtVaGetValues(labels[i], XtNwidth, &w, NULL);
if (w > width) width = w;
}
for (i=0; i<2; i++)
XtVaSetValues(labels[i], XtNwidth, width, NULL);
// create the mapping button option
Widget list[15];
int num;
widgetList[MAPP_PULLDOWN] = XmCreatePulldownMenu(form, NULL, NULL, 0);
n = 0;
XtSetArg(args[n], XmNuserData, this); n++;
#define ADD_ENTRY(NAME, ID) \
widgetList[ID] = list[num] = XmCreatePushButtonGadget(widgetList[MAPP_PULLDOWN], \
NAME, args, n); \
XtAddCallback(list[num++], XmNactivateCallback, \
(XtCallbackProc) MyTextureEditor::mappingMenuCB, (XtPointer) ID);
num = 0;
ADD_ENTRY("default", MAP_DEFAULT)
ADD_ENTRY("reflection", MAP_ENV)
ADD_ENTRY("xy plane", MAP_PLANE_XY)
ADD_ENTRY("yz plane", MAP_PLANE_YZ)
ADD_ENTRY("xz plane", MAP_PLANE_XZ)
ADD_ENTRY("unknown", MAP_UNKNOWN)
#undef ADD_ENTRY
XtVaSetValues(widgetList[MAP_UNKNOWN], XmNsensitive, FALSE, NULL);
XtManageChildren(list, num);
n = 0;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
XtSetArg(args[n], XmNmarginWidth, 0); n++;
XtSetArg(args[n], XmNmarginHeight, 0); n++;
XtSetArg(args[n], XmNsubMenuId, widgetList[MAPP_PULLDOWN]); n++;
buttons[0] = XmCreateOptionMenu(form, "optionMenu", args, n);
// create the options button
widgetList[OPT_PULLDOWN] = XmCreatePulldownMenu(form, NULL, NULL, 0);
n = 0;
XtSetArg(args[n], XmNuserData, this); n++;
#define ADD_ENTRY(NAME, ID) \
widgetList[ID] = list[num] = XmCreatePushButtonGadget(widgetList[OPT_PULLDOWN], \
NAME, args, n); \
XtAddCallback(list[num++], XmNactivateCallback, \
(XtCallbackProc) MyTextureEditor::optionMenuCB, (XtPointer) ID);
num = 0;
ADD_ENTRY("repeat", OPT_REPEAT)
ADD_ENTRY("clamp", OPT_CLAMP)
ADD_ENTRY("unknown", OPT_UNKNOWN)
#undef ADD_ENTRY
XtVaSetValues(widgetList[OPT_UNKNOWN], XmNsensitive, FALSE, NULL);
XtManageChildren(list, num);
n = 0;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
XtSetArg(args[n], XmNmarginWidth, 0); n++;
XtSetArg(args[n], XmNmarginHeight, 0); n++;
XtSetArg(args[n], XmNsubMenuId, widgetList[OPT_PULLDOWN]); n++;
buttons[1] = XmCreateOptionMenu(form, "optionMenu", args, n);
//
// layout !
//
// center the accept button
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg(args[n], XmNleftPosition, 50); n++;
XtSetArg(args[n], XmNleftOffset, -30); n++;
XtSetValues(acceptBut, args, n);
// attach option menu buttons to the right
n = 0;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopOffset, 5);
XtSetArg(args[n+1], XmNtopWidget, acceptBut);
XtSetValues(buttons[0], args, n+2);
XtSetArg(args[n], XmNtopWidget, buttons[0]);
XtSetValues(buttons[1], args, n+1);
// center the labels around the option menu
n = 0;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNrightWidget, buttons[0]);
XtSetArg(args[n+1], XmNtopWidget, buttons[0]);
XtSetArg(args[n+2], XmNbottomWidget, buttons[0]);
XtSetValues(labels[0], args, n+3);
XtSetArg(args[n], XmNrightWidget, buttons[1]);
XtSetArg(args[n+1], XmNtopWidget, buttons[1]);
XtSetArg(args[n+2], XmNbottomWidget, buttons[1]);
XtSetValues(labels[1], args, n+3);
// manage children
XtManageChild(acceptBut);
XtManageChildren(buttons, 2);
XtManageChildren(labels, 2);
return form;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// redraws the texture palette
//
// Use: private
void
MyTextureEditor::redrawPalette()
//
////////////////////////////////////////////////////////////////////////
{
if (! isVisible())
return;
glXMakeCurrent(getDisplay(), XtWindow(widgetList[TEXTURE_GLX]), paletteCtx);
for (int i=0; i<IMAGE_TOTAL; i++) {
if (i != currentItem && i != selectedItem)
drawTextureTile(i, DRAW_NORM);
}
if (currentItem != -1 && currentItem != selectedItem)
drawTextureTile(currentItem, DRAW_CURRENT);
if (selectedItem != -1)
drawTextureTile(selectedItem, DRAW_SELECTED);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// draws the given texture tile
//
// Use: private
void
MyTextureEditor::drawTextureTile(int id, int style)
//
////////////////////////////////////////////////////////////////////////
{
if (id < 0)
return;
int row = int(id / IMAGE_NUM);
int col = id - IMAGE_NUM * row;
int s = IMAGE_SPACE + IMAGE_SIZE;
int x1 = col * s + IMAGE_SPACE;
int x2 = x1 + IMAGE_SIZE - 1;
int y1 = (IMAGE_NUM - row - 1) * s + IMAGE_SPACE;
int y2 = y1 + IMAGE_SIZE - 1;
// paste the texture image
if (textureNames[id].name != NULL) {
glRasterPos2i(x1, y1);
glDrawPixels(IMAGE_SIZE, IMAGE_SIZE, GL_RGB, GL_UNSIGNED_BYTE,
textureNames[id].iconImage);
}
else {
glColor3ub(150, 150, 150);
glBegin(GL_POLYGON);
glVertex2s(x1, y1); glVertex2s(x2+1, y1);
glVertex2s(x2+1, y2+1); glVertex2s(x1, y2+1);
glEnd();
}
// show the boder highlighting
switch(style) {
case DRAW_NORM:
glColor3ub(150, 150, 150);
RECT(x1-2, y1-2, x2+2, y2+2);
RECT(x1-1, y1-1, x2+1, y2+1);
break;
case DRAW_CURRENT:
glColor3ub(230, 50, 50);
RECT(x1-2, y1-2, x2+2, y2+2);
RECT(x1-1, y1-1, x2+1, y2+1);
break;
case DRAW_SELECTED:
glColor3ub(230, 50, 50);
RECT(x1-2, y1-2, x2+2, y2+2);
RECT(x1-1, y1-1, x2+1, y2+1);
RECT(x1, y1, x2, y2);
RECT(x1+1, y1+1, x2-1, y2-1);
break;
}
glFlush();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// update the texture name based of what is selected
//
// Use: private
void
MyTextureEditor::updateTextureName()
//
////////////////////////////////////////////////////////////////////////
{
char *str = (currentItem < 0) ? ((selectedItem < 0) ? " " :
textureNames[selectedItem].name) : textureNames[currentItem].name;
if (str == NULL)
str = noFileNameStr;
XmString xmstr = XmStringCreateSimple(str);
XtVaSetValues(widgetList[TEXTURE_NAME], XmNlabelString, xmstr, NULL);
XmStringFree(xmstr);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// update the texture node based on currently selected texture
//
// Use: private
void
MyTextureEditor::updateTextureNode()
//
////////////////////////////////////////////////////////////////////////
{
if (selectedItem < 0)
return;
TextureNameStruct *txt = &textureNames[selectedItem];
if (txt->fullName != NULL) {
texNode->filename.setValue(txt->fullName);
// check MODULATE vs BLEND
if (txt->zsize == 3 || txt->zsize == 4)
texNode->model = SoTexture2::MODULATE;
else
texNode->model = SoTexture2::BLEND;
}
else // no texture
texNode->filename.setValue("");
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// updates the UI to reflect the current texture2 node
//
// Use: private
void
MyTextureEditor::updateTexture2UI()
//
////////////////////////////////////////////////////////////////////////
{
// update color wheel + color slider
float hsv[3];
SbColor rgb = texNode->blendColor.getValue();
rgb.getHSVValue(hsv);
ignoreCallback = TRUE;
colWheel->setBaseColor(hsv);
colSlider->setBaseColor(hsv);
ignoreCallback = FALSE;
// update the option menu
int ID = OPT_UNKNOWN;
if (texNode->wrapS.getValue() == texNode->wrapT.getValue()) {
if (texNode->wrapS.getValue() == SoTexture2::REPEAT)
ID = OPT_REPEAT;
else if (texNode->wrapS.getValue() == SoTexture2::CLAMP)
ID = OPT_CLAMP;
}
XtVaSetValues(widgetList[OPT_PULLDOWN], XmNmenuHistory, widgetList[ID], NULL);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// updates the UI to reflect the current texture2Transform node
//
// Use: private
void
MyTextureEditor::updateTextureXfUI()
//
////////////////////////////////////////////////////////////////////////
{
updateTextureFieldAndSlider(SCALE_X_FIELD);
updateTextureFieldAndSlider(SCALE_Y_FIELD);
updateTextureFieldAndSlider(TRANS_X_FIELD);
updateTextureFieldAndSlider(TRANS_Y_FIELD);
updateTextureFieldAndSlider(ROT_FIELD);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// updates the UI to reflect the current texture function node
//
// Use: private
void
MyTextureEditor::updateTextureFuncUI()
//
////////////////////////////////////////////////////////////////////////
{
// update the option menu
int ID = MAP_UNKNOWN;
if (texFuncNode->isOfType(SoTextureCoordinateDefault::getClassTypeId()))
ID = MAP_DEFAULT;
else if (texFuncNode->isOfType(SoTextureCoordinateEnvironment::getClassTypeId()))
ID = MAP_ENV;
else if (texFuncNode->isOfType(SoTextureCoordinatePlane::getClassTypeId())) {
SbVec3f directionS = ((SoTextureCoordinatePlane *)texFuncNode)->directionS.getValue();
SbVec3f directionT = ((SoTextureCoordinatePlane *)texFuncNode)->directionT.getValue();
if (directionS == SbVec3f(1, 0, 0) && directionT == SbVec3f(0, 1, 0))
ID = MAP_PLANE_XY;
else if (directionS == SbVec3f(1, 0, 0) && directionT == SbVec3f(0, 0, 1))
ID = MAP_PLANE_XZ;
else if (directionS == SbVec3f(0, 1, 0) && directionT == SbVec3f(0, 0, 1))
ID = MAP_PLANE_YZ;
}
XtVaSetValues(widgetList[MAPP_PULLDOWN], XmNmenuHistory, widgetList[ID], NULL);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets the repeate state and updates the UI. It returns TRUE if
// set state has changed.
//
// Use: private
SbBool
MyTextureEditor::setRepeatState(SbBool flag)
//
////////////////////////////////////////////////////////////////////////
{
if (repeatState == flag)
return FALSE;
repeatState = flag;
// XmToggleButtonSetState(widgetList[REPEAT_TOGGLE], repeatState, FALSE);
// update the scale labels
XmString xmstr1, xmstr2;
if (repeatState) {
xmstr1 = XmStringCreateSimple("Repeat X:");
xmstr2 = XmStringCreateSimple("Repeat Y:");
}
else {
xmstr1 = XmStringCreateSimple("Scale X:");
xmstr2 = XmStringCreateSimple("Scale Y:");
}
XtVaSetValues(widgetList[SCALE_X_LABEL], XmNlabelString, xmstr1, NULL);
XtVaSetValues(widgetList[SCALE_Y_LABEL], XmNlabelString, xmstr2, NULL);
XmStringFree(xmstr1);
XmStringFree(xmstr2);
// update the scale text fields to new format
updateTextureFieldAndSlider(SCALE_X_FIELD);
updateTextureFieldAndSlider(SCALE_Y_FIELD);
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// updates the given text field and slider to relfect current settings
//
// Use: private
void
MyTextureEditor::updateTextureFieldAndSlider(int fieldID)
//
////////////////////////////////////////////////////////////////////////
{
float val;
int intVal;
char str[10];
switch(fieldID) {
case TRANS_X_FIELD:
val = texXfNode->translation.getValue()[0];
sprintf(str, "%.2f", val);
intVal = (val > 1.0) ? 100 : ((val < -1) ? 0 : int((val + 1) * 50));
XmScaleSetValue(widgetList[TRANS_X_SLD], intVal);
break;
case TRANS_Y_FIELD:
val = texXfNode->translation.getValue()[1];
sprintf(str, "%.2f", val);
intVal = (val > 1.0) ? 100 : ((val < -1) ? 0 : int((val + 1) * 50));
XmScaleSetValue(widgetList[TRANS_Y_SLD], intVal);
break;
case ROT_FIELD:
val = texXfNode->rotation.getValue() * 180.0 / M_PI; // ??? prevent roundoff
intVal = int(val);
sprintf(str, "%d", intVal);
intVal = int(100 * intVal / 360.0);
XmScaleSetValue(widgetList[ROT_SLD], intVal);
break;
case SCALE_X_FIELD:
val = texXfNode->scaleFactor.getValue()[0];
// make it look like we are scaling the image, not the texture coord
if (repeatState)
sprintf(str, "%.1f", val);
else
sprintf(str, "%.2f", 1 / val);
break;
case SCALE_Y_FIELD:
val = texXfNode->scaleFactor.getValue()[1];
// make it look like we are scaling the image, not the texture coord
if (repeatState)
sprintf(str, "%.1f", val);
else
sprintf(str, "%.2f", 1 / val);
break;
}
XmTextSetString(widgetList[fieldID], str);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Get the list of directories (palettes) from the env var, or the
// paletteDir variable (constructor). This also searches for palettes
// saved under the home directory, which would overide the system
// installed palettes.
//
// Use: private
void
MyTextureEditor::getPaletteNames()
//
////////////////////////////////////////////////////////////////////////
{
struct dirent *direntry;
DIR *dirp;
char *f;
char currentDir[MAXPATHLEN];
// this is only called once at startup, so it doesn't
// need to free the old paletteList.
//
// look for palettes under the default installed place
//
// see if SO_TEXTURE_DIR is set, if so use it...
char *envDir = getenv("SO_TEXTURE_DIR");
if (envDir != NULL) {
free(paletteDir);
paletteDir = strdup(envDir);
}
if (dirp = opendir(paletteDir)) {
getwd(currentDir);
chdir(paletteDir);
while (direntry = readdir(dirp)) {
f = direntry->d_name;
// hide '.' files
if (f[0] != '.' && isDirectory(f)) {
PaletteStruct *pal = new PaletteStruct;
pal->name = strdup(f);
pal->system = TRUE;
pal->user = FALSE;
paletteList.append(pal);
}
}
closedir(dirp);
chdir(currentDir); // back to our working directory
}
//
// Now look for palettes under the user's home directory, which
// would overide the installed palettes.
//
char customDir[MAXPATHLEN];
sprintf(customDir, "%s/%s", getenv("HOME"), customTextureDir);
if (dirp = opendir(customDir)) {
getwd(currentDir);
chdir(customDir);
while (direntry = readdir(dirp)) {
f = direntry->d_name;
// hide '.' files
if (f[0] != '.' && !isDirectory(f)) {
// check if palette name is already in the list
// (user overide case), else create a new entry
//
PaletteStruct *pal = findPalette(f, &paletteList);
if (pal != NULL)
pal->user = TRUE;
else {
pal = new PaletteStruct;
pal->name = strdup(f);
pal->user = TRUE;
pal->system = FALSE;
paletteList.append(pal);
}
}
}
closedir(dirp);
chdir(currentDir); // back to our working directory
}
//
// make sure we have at least one palette, else create an empty default
// palette for things to work.
//
curPalette = 0;
if (paletteList.getLength() == 0) {
#ifdef DEBUG
SoDebugError::post("MyTextureEditor::getPaletteNames",
"cannot find palettes in directory %s or home directory. Try setting the environment variable SO_TEXTURE_DIR to a directory which has texture files in it.", paletteDir );
#endif
// create an empty default palette, withough loading anything
// since we started with a blank palette.
PaletteStruct *pal = new PaletteStruct;
pal->name = strdup("default");
pal->user = TRUE;
pal->system = FALSE;
paletteList.append(pal);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// saves the current palette in the user's HOME directory.
//
// Use: private
void
MyTextureEditor::savePalette()
//
////////////////////////////////////////////////////////////////////////
{
char dirName[MAXPATHLEN];
char fileName[MAXPATHLEN];
struct stat buf;
FILE *file;
PaletteStruct *pal = (PaletteStruct *) paletteList[curPalette];
// open the file for writting
sprintf(dirName, "%s/%s/", getenv("HOME"), customTextureDir);
if (stat(dirName, &buf) != 0)
mkdir(dirName, 0x1ff);
strcpy(fileName, dirName);
strcat(fileName, pal->name);
if ((file = fopen(fileName, "w")) == NULL) {
#ifdef DEBUG
SoDebugError::post("MyTextureEditor::savePalette",
"couldn't create file: %s", fileName);
#endif
return;
}
// write the names of the textures, on line at a time
for (int i=0; i<IMAGE_TOTAL; i++) {
if (textureNames[i].fullName != NULL)
fprintf(file, "%s\n", textureNames[i].fullName);
}
fclose(file);
// finally update things
if (! pal->user) {
pal->user = TRUE;
updateFileMenu();
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Given the current palette, loads all of the textures in the
// current palette directory.
//
// Use: private
void
MyTextureEditor::loadPaletteItems()
//
////////////////////////////////////////////////////////////////////////
{
if (curPalette == -1)
return;
//
// set the hour glass cursor on....
//
Display *display = getDisplay();
Widget shell = SoXt::getShellWidget(getWidget());
Window window = XtWindow(shell);
static Cursor cursor = 0;
if (cursor == 0) {
Drawable d = DefaultRootWindow(display);
XColor foreground;
foreground.red = 65535;
foreground.green = foreground.blue = 0;
Pixmap source = XCreateBitmapFromData(display, d,
hourglass_bits, hourglass_width, hourglass_height);
cursor = XCreatePixmapCursor(display, source, source,
&foreground, &foreground, hourglass_x_hot, hourglass_y_hot);
XFreePixmap(display, source);
}
XDefineCursor(display, window, cursor);
XSync(display, FALSE); // better than XFlush() (we get the glClear() to show up)
// we are going to redraw each tile at a time once it is loaded
// instead of waiting for all the textures to be read in
// (feels a lot faster).
glXMakeCurrent(getDisplay(), XtWindow(widgetList[TEXTURE_GLX]), paletteCtx);
glClearColor(.6, .6, .6, 0);
glClear(GL_COLOR_BUFFER_BIT);
char palDir[MAXPATHLEN];
char currentDir[MAXPATHLEN];
DIR *dirp;
int i, num = 0;
// go to the palette directory (user saved palettes override system
// installed palettes).
PaletteStruct *pal = (PaletteStruct *) paletteList[curPalette];
if (pal->user)
sprintf(palDir, "%s/%s", getenv("HOME"), customTextureDir);
else
sprintf(palDir, "%s/%s", paletteDir, pal->name);
if ((dirp = opendir(palDir)) != NULL) {
// cd to palette directory
getwd(currentDir);
chdir(palDir);
//
// get the names of the texture files
//
char fullName[MAXPATHLEN];
if (pal->user) {
// open the custom file and read the content of it (list of textures)
FILE *file;
if ((file = fopen(pal->name, "r")) != NULL) {
// read each line entry (full texture path name) and add it to
// the list of textures
while(fscanf(file, " %s ", fullName) != EOF && num < (IMAGE_TOTAL-1)) {
if (addTextureEntry(num, fullName)) {
drawTextureTile(num, DRAW_NORM);
num++;
}
}
fclose(file);
}
}
// system installed. Look for any image files under the current directory
else {
struct dirent *direntry;
while ((direntry = readdir(dirp)) && num < (IMAGE_TOTAL-1)) {
char *file = direntry->d_name;
// ignore '.' files and directories
if (file[0] == '.' || isDirectory(file))
continue;
// add the file to the list of textures
sprintf(fullName, "%s/%s", palDir, file);
if (addTextureEntry(num, fullName)) {
drawTextureTile(num, DRAW_NORM);
num++;
}
}
}
closedir(dirp);
chdir(currentDir); // back to our working directory
}
// clear the remaining texture names
for (i=num; i<IMAGE_TOTAL; i++)
deleteTextureEntry(i);
// restore the cursor
XUndefineCursor(display, window);
loadedPalette = TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// reads the given image file (1-4 channels) in the given buffer of the
// given size (the image file will be scaled to fit the given size). and
// returns if succesfull.
//
// Note: this ignores the alpha channel of the image, and scales the image
// by skipping/duplicating pixels (VERY fast).
//
// Use: private
SbBool
MyTextureEditor::readScaledImage(
char *file,
int xsize, int ysize,
char *buf,
int &zsize)
//
////////////////////////////////////////////////////////////////////////
{
// check to make sure file is a valid image file
IMAGE *image;
if (file == NULL || file[0] == '\0')
return FALSE;
if ((image = iopen(file, "r")) == NULL)
return FALSE;
if (image->colormap != CM_NORMAL) {
iclose(image);
return FALSE;
}
zsize = image->zsize;
// allocate needed memory
short *rbuf, *gbuf, *bbuf;
rbuf = new short[image->xsize];
if (image->zsize > 2) {
gbuf = new short[image->xsize];
bbuf = new short[image->xsize];
}
// read image in, one row at a time
char *p = buf;
for (int row = 0; row < ysize; row++) {
// The row we'll read
int rrow = (row*image->ysize)/ysize;
if (zsize > 2) {
getrow(image, rbuf, rrow, 0);
getrow(image, gbuf, rrow, 1);
getrow(image, bbuf, rrow, 2);
}
else
getrow(image, rbuf, rrow, 0);
// store these into an unsigned byte RGB format
for (int i=0; i < xsize; i++) {
int ri = (i*image->xsize)/xsize;
if (zsize > 2) {
*p++ = rbuf[ri];
*p++ = gbuf[ri];
*p++ = bbuf[ri];
}
else {
*p++ = rbuf[ri];
*p++ = rbuf[ri];
*p++ = rbuf[ri];
}
}
}
// delete image buffers
delete [] rbuf;
if (zsize > 2) {
delete [] gbuf;
delete [] bbuf;
}
iclose(image);
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// reads the given image file in (1-4 channels) and return a char *
// RGB format of the image (withought the alpha channel)
//
// Use: private
char *
MyTextureEditor::readImage(char *file, int &xsize, int &ysize, int &zsize)
//
////////////////////////////////////////////////////////////////////////
{
// check to make sure file is a valid image file
IMAGE *image;
if (file == NULL || file[0] == '\0')
return NULL;
if ((image = iopen(file, "r")) == NULL)
return NULL;
if (image->colormap != CM_NORMAL) {
iclose(image);
return NULL;
}
xsize = image->xsize;
ysize = image->ysize;
zsize = image->zsize;
// allocate needed memory
short *rbuf, *gbuf, *bbuf;
char *buf = new char[image->xsize * image->ysize * 3];
rbuf = new short[image->xsize];
if (image->zsize > 2) {
gbuf = new short[image->xsize];
bbuf = new short[image->xsize];
}
// read image in, one row at a time
char *p = buf;
for (int row = 0; row < image->ysize; row++) {
if (image->zsize > 2) {
getrow(image, rbuf, row, 0);
getrow(image, gbuf, row, 1);
getrow(image, bbuf, row, 2);
}
else
getrow(image, rbuf, row, 0);
// store these into an unsigned byte RGB format
for (int i=0; i < image->xsize; i++) {
if (image->zsize > 2) {
*p++ = rbuf[i];
*p++ = gbuf[i];
*p++ = bbuf[i];
}
else {
*p++ = rbuf[i];
*p++ = rbuf[i];
*p++ = rbuf[i];
}
}
}
// delete image buffers
delete [] rbuf;
if (zsize > 2) {
delete [] gbuf;
delete [] bbuf;
}
iclose(image);
return buf;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// switches to the given palette number
//
// Use: private
void
MyTextureEditor::switchPalette(int id)
//
////////////////////////////////////////////////////////////////////////
{
curPalette = id;
// get new textures, update material and selection
loadPaletteItems();
deselectCurrentItem(FALSE); // don't redraw palette changes there
redrawPalette();
updateWindowTitle();
updateFileMenu();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// deselect the currently selected item in the palette.
//
// Use: private
void
MyTextureEditor::deselectCurrentItem(SbBool drawHighlight)
//
////////////////////////////////////////////////////////////////////////
{
if (selectedItem == -1)
return;
// deselect the current item and update things that depend on it
if (drawHighlight) {
glXMakeCurrent(getDisplay(), XtWindow(widgetList[TEXTURE_GLX]), paletteCtx);
if (selectedItem == currentItem)
drawTextureTile(selectedItem, DRAW_CURRENT);
else {
drawTextureTile(selectedItem, DRAW_NORM);
if (currentItem != -1)
drawTextureTile(currentItem, DRAW_CURRENT);
}
}
selectedItem = -1;
updateTextureName();
// hide color wheel
colSlider->hide();
colWheel->hide();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// deletes the names + images of the given texture.
//
// NOTE: we don't ever delete the iconImage buffer since those
// will always be IMAGE_SIZE X IMAGE_SIZE
//
// Use: private
void
MyTextureEditor::deleteTextureEntry(int id)
//
////////////////////////////////////////////////////////////////////////
{
if (textureNames[id].name != NULL)
free(textureNames[id].name);
if (textureNames[id].fullName != NULL)
free(textureNames[id].fullName);
textureNames[id].name = textureNames[id].fullName = NULL;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// adds a texture entry (name + fullName + load small size image)
// at the given index. This returns TRUE if this routine was successfull
// (valid image file).
//
// Use: private
SbBool
MyTextureEditor::addTextureEntry(int id, char *fullName)
//
////////////////////////////////////////////////////////////////////////
{
// read the image in iconized form
if (! readScaledImage(fullName, IMAGE_SIZE, IMAGE_SIZE,
textureNames[id].iconImage, textureNames[id].zsize))
return FALSE;
// extract the file name (text after the last '/' char)
deleteTextureEntry(id);
char *p = strrchr(fullName, '/');
p = (p != NULL) ? p+1 : fullName;
textureNames[id].name = strdup(p);
textureNames[id].fullName = strdup(fullName);
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets the window title based on the current palette and save status
//
// Use: private
void
MyTextureEditor::updateWindowTitle()
//
////////////////////////////////////////////////////////////////////////
{
char str[150];
sprintf(str, "%s: %s", editorTitle, ((PaletteStruct *)paletteList[curPalette])->name);
setTitle(str);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Called whenever an event occurs in the texture palette window.
//
// Use: private
void
MyTextureEditor::handleEvent(XAnyEvent *xe)
//
////////////////////////////////////////////////////////////////////////
{
switch(xe->type) {
case ButtonPress:
{
XButtonEvent *be = (XButtonEvent *)xe;
if (be->button != Button1)
break;
//
// check for double click. If the time difference between
// successive mouse down is less than 3/10th sec, then
// consider it a double click.
// ??? do we need to worry about time warping ?
//
if ((be->time - prevTime) < DOUBLE_CLICK_TIME) {
// open the image dialog
openImageDialog();
// if we don't have any images there, automatically open
// the file browser
// ??? make it look like the "Open..." button was pressed
// if (textureNames[selectedItem].fullName == NULL)
// imageDialogOpenCB(NULL, this, NULL);
}
else { // single click (or first click of the double click)
// select the current texture (if it is not currently selected)
if (currentItem != selectedItem) {
// update the feedback
glXMakeCurrent(getDisplay(), XtWindow(widgetList[TEXTURE_GLX]), paletteCtx);
drawTextureTile(selectedItem, DRAW_NORM);
drawTextureTile(currentItem, DRAW_SELECTED);
selectedItem = currentItem;
updateTextureNode();
// ??? update the image dialog (if visible)
if (widgetList[DIALOG_WINDOW] != NULL)
setNewDialogImage(textureNames[selectedItem].fullName);
// show/hide the color wheel
if (textureNames[selectedItem].zsize < 3 &&
textureNames[selectedItem].fullName != NULL) {
colWheel->show();
colSlider->show();
}
else {
colSlider->hide();
colWheel->hide();
}
}
}
prevTime = be->time;
}
break;
case MotionNotify:
{
// do a region picking to figure which tile we are above
XMotionEvent *me = (XMotionEvent *)xe;
int xpos = int (ffloor( IMAGE_NUM * me->x / float(GLX_SIZE) ));
int ypos = int (ffloor( IMAGE_NUM * me->y / float(GLX_SIZE) ));
int whichTexture = xpos + IMAGE_NUM * ypos;
if (whichTexture != currentItem) {
glXMakeCurrent(getDisplay(), XtWindow(widgetList[TEXTURE_GLX]), paletteCtx);
if (currentItem != selectedItem &&
currentItem != -1)
drawTextureTile(currentItem, DRAW_NORM);
if (whichTexture != selectedItem)
drawTextureTile(whichTexture, DRAW_CURRENT);
drawTextureTile(selectedItem, DRAW_SELECTED);
currentItem = whichTexture;
updateTextureName();
}
}
break;
case LeaveNotify:
if (currentItem != selectedItem) {
glXMakeCurrent(getDisplay(), XtWindow(widgetList[TEXTURE_GLX]), paletteCtx);
drawTextureTile(currentItem, DRAW_NORM);
drawTextureTile(selectedItem, DRAW_SELECTED);
currentItem = -1;
updateTextureName();
}
break;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// creates a dialog which lets the user type a name.
//
// Use: private
void
MyTextureEditor::createNewDialog()
//
////////////////////////////////////////////////////////////////////////
{
Arg args[5];
XmString xmstr = XmStringCreateSimple("New Palette Name:");
int n = 0;
XtSetArg(args[n], XmNautoUnmanage, FALSE); n++;
XtSetArg(args[n], XtNtitle, "New Palette Dialog"); n++;
XtSetArg(args[n], XmNselectionLabelString, xmstr); n++;
Widget dialog = XmCreatePromptDialog(SoXt::getShellWidget(getWidget()),
"saveDialog", args, n);
XmStringFree(xmstr);
XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SEPARATOR));
// register callback to destroy (and not just unmap) the dialog
// and retreive the text (ok push button case).
XtAddCallback(dialog, XmNokCallback,
(XtCallbackProc) MyTextureEditor::newDialogCB, (XtPointer) this);
XtAddCallback(dialog, XmNcancelCallback,
(XtCallbackProc) MyTextureEditor::newDialogCB, (XtPointer) this);
XtManageChild(dialog);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// create a dialog which lets the user change it's mind about deleting
// the current palette.
//
// Use: private
void
MyTextureEditor::createDeleteDialog(char *title, char *str1, char *str2)
//
////////////////////////////////////////////////////////////////////////
{
Arg args[5];
XmString xmstr = XmStringCreateSimple(str1);
xmstr = XmStringConcat(xmstr, XmStringSeparatorCreate());
xmstr = XmStringConcat(xmstr, XmStringCreateSimple(str2));
int n = 0;
XtSetArg(args[n], XmNautoUnmanage, FALSE); n++;
XtSetArg(args[n], XtNtitle, title); n++;
XtSetArg(args[n], XmNmessageString, xmstr); n++;
Widget dialog = XmCreateWarningDialog(SoXt::getShellWidget(getWidget()),
"DeleteDialog", args, n);
XmStringFree(xmstr);
XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_SEPARATOR));
// register callback to destroy (and not just unmap) the dialog
XtAddCallback(dialog, XmNokCallback,
(XtCallbackProc) MyTextureEditor::deleteDialogCB, (XtPointer) this);
XtAddCallback(dialog, XmNcancelCallback,
(XtCallbackProc) MyTextureEditor::deleteDialogCB, (XtPointer) this);
XtManageChild(dialog);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// updates the "File" menu (grey things out) to reflect current
// palette.
//
// Use: private
void
MyTextureEditor::updateFileMenu()
//
////////////////////////////////////////////////////////////////////////
{
PaletteStruct *pal = (PaletteStruct *) paletteList[curPalette];
XtVaSetValues(widgetList[FILE_RESET], XmNsensitive,
pal->user && pal->system, NULL);
XtVaSetValues(widgetList[FILE_DELETE], XmNsensitive,
pal->user && !pal->system, NULL);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This creates a new palette with the given name (called when the
// user wants a new palette using the 'new' button).
//
// Use: private
void
MyTextureEditor::createNewPalette(char *palName)
//
////////////////////////////////////////////////////////////////////////
{
//
// create that empty palette file (under the user's home
// directory).
//
char dirName[MAXPATHLEN];
char fileName[MAXPATHLEN];
struct stat buf;
FILE *file;
sprintf(dirName, "%s/%s/", getenv("HOME"), customTextureDir);
if (stat(dirName, &buf) != 0)
mkdir(dirName, 0x1ff);
strcpy(fileName, dirName);
strcat(fileName, palName);
if ((file = fopen(fileName, "w")) == NULL) {
#ifdef DEBUG
SoDebugError::post("MyTextureEditor::createNewPalette",
"couldn't create file: %s", fileName);
#endif
return;
}
fclose(file);
//
// add the palette to the popup menu
//
PaletteStruct *pal = new PaletteStruct;
pal->name = strdup(palName);
pal->user = TRUE;
pal->system = FALSE;
paletteList.append(pal);
int id = paletteList.getLength() - 1;
XtManageChild(buildPaletteMenuEntry(id));
// now switch to new palette
switchPalette(id);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// updates the image dialog based on the currently selected texture.
//
// Use: private
void
MyTextureEditor::redrawImageDialog()
//
////////////////////////////////////////////////////////////////////////
{
// make sure window is on the screen
Widget glx = widgetList[DIALOG_IMAGE];
if (glx == NULL)
return;
Window window = XtWindow(glx);
if (window == 0)
return;
glXMakeCurrent(XtDisplay(glx), window, imageDialogCtx);
// reset projection
short w, h;
XtVaGetValues(glx, XmNwidth, &w, XmNheight, &h, NULL);
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, -1, 1);
// draw image
if (dialogImage != NULL) {
glRasterPos2i(0, 0);
glDrawPixels(dialogImageSize[0], dialogImageSize[1], GL_RGB,
GL_UNSIGNED_BYTE, dialogImage);
}
else {
glClearColor(.6, .6, .6, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
glFlush();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets which image should be displayed (NULL for clear) in the
// image dialog.
//
// Use: private
void
MyTextureEditor::setNewDialogImage(char *fileName)
//
////////////////////////////////////////////////////////////////////////
{
// make sure widget is built
if (widgetList[DIALOG_WINDOW] == NULL)
return;
// delete the existing dialog image and names
delete dialogImage;
if (dialogImageName != NULL)
free(dialogImageName);
if (dialogImageInfo != NULL)
free(dialogImageInfo);
dialogImage = NULL;
dialogImageName = NULL;
dialogImageInfo = NULL;
if (fileName != NULL) {
// read the full image in and update the picture
dialogImage = readImage(fileName,
dialogImageSize[0], dialogImageSize[1], dialogImageSize[2]);
if (dialogImage != NULL) {
// resize the glx window
XtVaSetValues(widgetList[DIALOG_IMAGE], XmNwidth, dialogImageSize[0],
XmNheight, dialogImageSize[1], NULL);
// save the texture name
dialogImageName = strdup(fileName);
// format the image info
char str[100];
sprintf(str, "%d x %d %d component", dialogImageSize[0],
dialogImageSize[1], dialogImageSize[2]);
if (dialogImageSize[2] > 1)
strcat(str, "s");
dialogImageInfo = strdup(str);
}
else {
// bogus file name was given. print an error dialog
char str[MAXPATHLEN+100];
sprintf(str, "Error opening image file: %s", fileName);
SoXt::createSimpleErrorDialog(widgetList[DIALOG_WINDOW], "File Error", str);
}
}
if (dialogImageName == NULL)
// no images so make the glx tiny size (non zero)
XtVaSetValues(widgetList[DIALOG_IMAGE], XmNwidth, 1, XmNheight, 1, NULL);
// update the name label
XmString xmstr = (dialogImageName != NULL) ?
XmStringCreateSimple(dialogImageName) : XmStringCreateSimple(noFileNameStr);
XtVaSetValues(widgetList[DIALOG_NAME], XmNlabelString, xmstr, NULL);
XmStringFree(xmstr);
// update the info label
xmstr = (dialogImageInfo != NULL) ? XmStringCreateSimple(dialogImageInfo) :
XmStringCreateSimple("");
XtVaSetValues(widgetList[DIALOG_INFO], XmNlabelString, xmstr, NULL);
XmStringFree(xmstr);
//
// center everything in the window
//
// get the max size of all the pieces
short width[4];
int maxWidth;
width[0] = buttonsTotalWidth; // constant size
XtVaGetValues(widgetList[DIALOG_INFO], XmNwidth, &width[1], NULL);
XtVaGetValues(widgetList[DIALOG_NAME], XmNwidth, &width[2], NULL);
XtVaGetValues(widgetList[DIALOG_IMAGE], XmNwidth, &width[3], NULL);
maxWidth = width[0];
for (int i=1; i<4; i++)
if (width[i] > maxWidth)
maxWidth = width[i];
// now center things
XtVaSetValues(widgetList[DIALOG_BUTTON_0], XmNleftOffset,
(maxWidth - width[0]) / 2, NULL);
XtVaSetValues(widgetList[DIALOG_INFO], XmNleftOffset,
(maxWidth - width[1]) / 2, NULL);
XtVaSetValues(widgetList[DIALOG_NAME], XmNleftOffset,
(maxWidth - width[2]) / 2, NULL);
XtVaSetValues(widgetList[DIALOG_IMAGE], XmNleftOffset,
(maxWidth - width[3]) / 2, NULL);
// ??? redraw the image now (instead of waiting for an expose event)
// to minimize the weird display when resizing a window.
redrawImageDialog();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Builds the image dialog window
//
// Use: private
void
MyTextureEditor::openImageDialog()
//
////////////////////////////////////////////////////////////////////////
{
// check to see if dialog has already been built
Widget shell = widgetList[DIALOG_WINDOW];
if (shell != NULL) {
SoXt::show(shell);
return;
}
int i, n;
Arg args[12];
Widget buttons[4], labels[2], glx;
//
// create the topLevel Shell window
//
n = 0;
XtSetArg(args[n], XtNtitle, "Texture Image Dialog"); n++;
XtSetArg(args[n], XmNiconName, "Image Dialog"); n++;
XtSetArg(args[n], XmNallowShellResize, TRUE); n++;
shell = XtCreatePopupShell("SoXtImageDialog", topLevelShellWidgetClass,
SoXt::getShellWidget(getWidget()), args, n);
widgetList[DIALOG_WINDOW] = shell;
XtAddCallback(shell, XmNdestroyCallback,
(XtCallbackProc) MyTextureEditor::imageDialogDestroyCB,
(XtPointer) this);
// create a top level form to hold everything together
n = 0;
XtSetArg(args[n], XmNmarginHeight, 10); n++;
XtSetArg(args[n], XmNmarginWidth, 10); n++;
Widget dummyForm = XmCreateForm(shell, "dummyForm", args, n);
Widget form = XmCreateForm(dummyForm, "imageDialogForm", NULL, 0);
//
// create all the parts
//
// create the push buttons
n = 0;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
buttons[0] = XmCreatePushButtonGadget(form, "Open...", args, n);
buttons[1] = XmCreatePushButtonGadget(form, "Clear", args, n);
buttons[2] = XmCreatePushButtonGadget(form, "Apply", args, n);
buttons[3] = XmCreatePushButtonGadget(form, "CloseWin", args, n);
widgetList[DIALOG_BUTTON_0] = buttons[0];
// make them all the same size
short w; int width = 0;
for (i=0; i<4; i++) {
XtVaGetValues(buttons[i], XtNwidth, &w, NULL);
if (w > width) width = w;
}
for (i=0; i<4; i++)
XtVaSetValues(buttons[i], XtNwidth, width, NULL);
buttonsTotalWidth = 4 * width + 3 * 5; // see layout spacing
XtAddCallback(buttons[0], XmNactivateCallback,
(XtCallbackProc) MyTextureEditor::imageDialogOpenCB, (XtPointer) this);
XtAddCallback(buttons[1], XmNactivateCallback,
(XtCallbackProc) MyTextureEditor::imageDialogClearCB, (XtPointer) this);
XtAddCallback(buttons[2], XmNactivateCallback,
(XtCallbackProc) MyTextureEditor::imageDialogApplyCB, (XtPointer) this);
XtAddCallback(buttons[3], XmNactivateCallback,
(XtCallbackProc) MyTextureEditor::imageDialogCloseCB, (XtPointer) this);
// create the image name label
widgetList[DIALOG_INFO] = labels[0] = XmCreateLabelGadget(form, "imageInfo", NULL, 0);
widgetList[DIALOG_NAME] = labels[1] = XmCreateLabelGadget(form, "imageName", NULL, 0);
// create the image glx window
n = 0;
XtSetArg(args[n], GLwNrgba, TRUE); n++;
XtSetArg(args[n], GLwNredSize, 1); n++;
XtSetArg(args[n], GLwNgreenSize, 1); n++;
XtSetArg(args[n], GLwNblueSize, 1); n++;
glx = XtCreateWidget("imageDialogGLX", glwMDrawingAreaWidgetClass, form, args, n);
widgetList[DIALOG_IMAGE] = glx;
XtUninstallTranslations(glx);
XtAddCallback(glx, GLwNginitCallback,
(XtCallbackProc) MyTextureEditor::imageDialogInitCB, (XtPointer) this);
XtAddCallback(glx, GLwNexposeCallback,
(XtCallbackProc) MyTextureEditor::imageDialogExposeCB, (XtPointer) this);
//
// layout !
//
n = 0;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);
XtSetValues(buttons[0], args, n+1);
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET);
XtSetArg(args[n+1], XmNleftOffset, 5);
for (i=1; i<4; i++) {
XtSetArg(args[n+2], XmNleftWidget, buttons[i-1]);
XtSetValues(buttons[i], args, n+3);
}
n = 0;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomWidget, buttons[0]);
XtSetArg(args[n+1], XmNbottomOffset, 10);
XtSetValues(labels[0], args, n+2);
XtSetArg(args[n], XmNbottomWidget, labels[0]);
XtSetArg(args[n+1], XmNbottomOffset, 7);
XtSetValues(labels[1], args, n+2);
n = 0;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, labels[1]); n++;
XtSetArg(args[n], XmNbottomOffset, 10); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetValues(glx, args, n);
n = 0;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetValues(form, args, n);
setNewDialogImage(textureNames[selectedItem].fullName);
// manage children
XtManageChildren(buttons, 4);
XtManageChildren(labels, 2);
XtManageChild(glx);
XtManageChild(form);
XtManageChild(dummyForm);
SoXt::show(shell);
}
//
// redefine those generic virtual functions
//
const char *
MyTextureEditor::getDefaultWidgetName() const
{ return "MyTextureEditor"; }
const char *
MyTextureEditor::getDefaultTitle() const
{ return "Texture Editor"; }
const char *
MyTextureEditor::getDefaultIconTitle() const
{ return "Texture Editor"; }
//
////////////////////////////////////////////////////////////////////////
// static callbacks stubs
////////////////////////////////////////////////////////////////////////
//
void
MyTextureEditor::fieldChangedCB(Widget, MyTextureEditor *p, void *)
{ p->fieldChanged = TRUE; }
void
MyTextureEditor::glxExposeCB(Widget, MyTextureEditor *p, void *)
{
if (p->loadedPalette)
p->redrawPalette();
else
p->loadPaletteItems(); // this will also redraw the tiles
}
void
MyTextureEditor::glxInitCB(Widget glx, MyTextureEditor *p, void *)
{
// create a GLX context
XVisualInfo *vis;
XtVaGetValues(glx, GLwNvisualInfo, &vis, NULL);
p->paletteCtx = glXCreateContext(XtDisplay(glx), vis, NULL, GL_TRUE);
glXMakeCurrent(XtDisplay(glx), XtWindow(glx), p->paletteCtx);
// set the projection
short w, h;
XtVaGetValues(glx, XmNwidth, &w, XmNheight, &h, NULL);
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, -1, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void
MyTextureEditor::glxEventCB(Widget, MyTextureEditor *p, XAnyEvent *xe, Boolean *)
{ p->handleEvent(xe); }
void
MyTextureEditor::imageDialogExposeCB(Widget, MyTextureEditor *p, void *)
{ p->redrawImageDialog(); }
void
MyTextureEditor::imageDialogInitCB(Widget glx, MyTextureEditor *p, void *)
{
// create a GLX context
XVisualInfo *vis;
XtVaGetValues(glx, GLwNvisualInfo, &vis, NULL);
p->imageDialogCtx = glXCreateContext(XtDisplay(glx), vis, NULL, GL_TRUE);
glXMakeCurrent(XtDisplay(glx), XtWindow(glx), p->imageDialogCtx);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void
MyTextureEditor::imageDialogClearCB(Widget, MyTextureEditor *p, void *)
{ p->setNewDialogImage(NULL); }
void
MyTextureEditor::imageDialogCloseCB(Widget, MyTextureEditor *p, void *)
{ SoXt::hide(p->widgetList[DIALOG_WINDOW]); }
void
MyTextureEditor::acceptCB(Widget, MyTextureEditor *p, void *)
{ p->callbackList.invokeCallbacks(p); }
//
// called by the color wheel when the color changes
//
void
MyTextureEditor::colWheelCB(void *pt, const float hsv[3])
{
MyTextureEditor *p = (MyTextureEditor *)pt;
if (p->ignoreCallback)
return;
p->ignoreCallback = TRUE;
p->colSlider->setBaseColor(hsv);
p->ignoreCallback = FALSE;
// update the texture
SbColor rgb;
rgb.setHSVValue(hsv);
p->texNode->blendColor = rgb;
}
//
// called by color slider when the color changes
//
void
MyTextureEditor::colSliderCB(void *pt, float)
{
MyTextureEditor *p = (MyTextureEditor *)pt;
if (p->ignoreCallback)
return;
const float *hsv = p->colSlider->getBaseColor();
p->ignoreCallback = TRUE;
p->colWheel->setBaseColor(hsv);
p->ignoreCallback = FALSE;
// update the texture
SbColor rgb;
rgb.setHSVValue(hsv);
p->texNode->blendColor = rgb;
}
//
// called whenever the scale X thumbwheel changes
//
void
MyTextureEditor::scaleXThumbCB(void *pt, float val)
{
MyTextureEditor *p = (MyTextureEditor *)pt;
SbVec2f scale = p->texXfNode->scaleFactor.getValue();
// shorter/grow the scale value
if (p->repeatState)
scale[0] *= pow(1.5, val - p->oldXThumbVal);
else
scale[0] /= pow(1.5, val - p->oldXThumbVal);
p->texXfNode->scaleFactor.setValue(scale);
p->oldXThumbVal = val;
p->updateTextureFieldAndSlider(SCALE_X_FIELD);
}
//
// called whenever the scale Y thumbwheel changes
//
void
MyTextureEditor::scaleYThumbCB(void *pt, float val)
{
MyTextureEditor *p = (MyTextureEditor *)pt;
SbVec2f scale = p->texXfNode->scaleFactor.getValue();
// shorter/grow the scale value
if (p->repeatState)
scale[1] *= pow(1.5, val - p->oldYThumbVal);
else
scale[1] /= pow(1.5, val - p->oldYThumbVal);
p->texXfNode->scaleFactor.setValue(scale);
p->oldYThumbVal = val;
p->updateTextureFieldAndSlider(SCALE_Y_FIELD);
}
//
// called whenever the slider's text fields changes
//
void
MyTextureEditor::fieldsCB(Widget w, int id, void *)
{
// get the class pointer
MyTextureEditor *p;
XtVaGetValues(w, XmNuserData, &p, NULL);
if (! p->fieldChanged)
return;
p->fieldChanged = FALSE;
// get the text field value and update the texture node
char *str = XmTextGetString(w);
float val;
SbVec2f trans, scale, cent;
if (sscanf(str, "%f", &val)) {
switch(id) {
case SCALE_X_FIELD:
// make it look like we are scaling the image, not the texture coord
scale = p->texXfNode->scaleFactor.getValue();
if (p->repeatState)
scale[0] = val;
else
scale[0] = 1 / val;
p->texXfNode->scaleFactor.setValue(scale);
break;
case SCALE_Y_FIELD:
// make it look like we are scaling the image, not the texture coord
scale = p->texXfNode->scaleFactor.getValue();
if (p->repeatState)
scale[1] = val;
else
scale[1] = 1 / val;
p->texXfNode->scaleFactor.setValue(scale);
break;
case TRANS_X_FIELD:
trans = p->texXfNode->translation.getValue();
trans[0] = val;
p->texXfNode->translation.setValue(trans);
// update to rotate around center of image
cent = p->texXfNode->center.getValue();
cent[0] = 0.5 - trans[0];
p->texXfNode->center.setValue(cent);
break;
case TRANS_Y_FIELD:
trans = p->texXfNode->translation.getValue();
trans[1] = val;
p->texXfNode->translation.setValue(trans);
// update to rotate around center of image
cent = p->texXfNode->center.getValue();
cent[1] = 0.5 - trans[1];
p->texXfNode->center.setValue(cent);
break;
case ROT_FIELD:
while (val < 0) val += 360;
while (val > 360) val -= 360;
p->texXfNode->rotation = val * M_PI / 180.0;
break;
}
}
XtFree(str);
// reformat the text field and update the slider
p->updateTextureFieldAndSlider(id);
// make the text field loose the focus
XmProcessTraversal(SoXt::getShellWidget(p->getWidget()), XmTRAVERSE_CURRENT);
}
//
// called whenever the motif sliders changes
//
void
MyTextureEditor::slidersCB(Widget w, int id, void *)
{
// get the class pointer
MyTextureEditor *p;
XtVaGetValues(w, XmNuserData, &p, NULL);
// get the slider value
int intVal;
XmScaleGetValue(w, &intVal);
float val = intVal / 100.0;
char str[10];
SbVec2f trans, cent;
switch(id) {
case TRANS_X_SLD:
val = 1 - 2 * val; // make inverted [-1,1]
trans = p->texXfNode->translation.getValue();
trans[0] = val;
p->texXfNode->translation.setValue(trans);
// update to rotate around center of image
cent = p->texXfNode->center.getValue();
cent[0] = 0.5 - trans[0];
p->texXfNode->center.setValue(cent);
sprintf(str, "%.2f", val);
XmTextSetString(p->widgetList[TRANS_X_FIELD], str);
break;
case TRANS_Y_SLD:
val = 1 - 2 * val; // make inverted [-1,1]
trans = p->texXfNode->translation.getValue();
trans[1] = val;
p->texXfNode->translation.setValue(trans);
// update to rotate around center of image
cent = p->texXfNode->center.getValue();
cent[1] = 0.5 - trans[1];
p->texXfNode->center.setValue(cent);
sprintf(str, "%.2f", val);
XmTextSetString(p->widgetList[TRANS_Y_FIELD], str);
break;
case ROT_SLD:
p->texXfNode->rotation = val * 2 * M_PI;
intVal = int(val * 360);
sprintf(str, "%d", intVal);
XmTextSetString(p->widgetList[ROT_FIELD], str);
break;
}
}
//
// called whenever an entry within the mapping menu gets selected
//
void
MyTextureEditor::mappingMenuCB(Widget w, int id, void *)
{
// get the class pointer
MyTextureEditor *p;
XtVaGetValues(w, XmNuserData, &p, NULL);
SoTextureCoordinateFunction *newFunc;
switch(id) {
case MAP_DEFAULT:
newFunc = new SoTextureCoordinateDefault;
break;
case MAP_ENV:
newFunc = new SoTextureCoordinateEnvironment;
break;
case MAP_PLANE_XY:
newFunc = new SoTextureCoordinatePlane;
((SoTextureCoordinatePlane *)newFunc)->directionS = SbVec3f(1, 0, 0);
((SoTextureCoordinatePlane *)newFunc)->directionT = SbVec3f(0, 1, 0);
break;
case MAP_PLANE_XZ:
newFunc = new SoTextureCoordinatePlane;
((SoTextureCoordinatePlane *)newFunc)->directionS = SbVec3f(1, 0, 0);
((SoTextureCoordinatePlane *)newFunc)->directionT = SbVec3f(0, 0, 1);
break;
case MAP_PLANE_YZ:
newFunc = new SoTextureCoordinatePlane;
((SoTextureCoordinatePlane *)newFunc)->directionS = SbVec3f(0, 1, 0);
((SoTextureCoordinatePlane *)newFunc)->directionT = SbVec3f(0, 0, 1);
break;
case MAP_UNKNOWN:
// this should never happen !
#ifdef DEBUG
SoDebugError::post("MyTextureEditor::mappingMenuCB",
"MAP_UNKNOWN selected!");
#endif
return;
}
// replace the old func node
p->sceneRoot->replaceChild(p->texFuncNode, newFunc);
p->texFuncNode = newFunc;
}
//
// called whenever an entry within the option menu gets selected
//
void
MyTextureEditor::optionMenuCB(Widget w, int id, void *)
{
// get the class pointer
MyTextureEditor *p;
XtVaGetValues(w, XmNuserData, &p, NULL);
switch(id) {
case OPT_REPEAT:
p->texNode->wrapS = SoTexture2::REPEAT;
p->texNode->wrapT = SoTexture2::REPEAT;
p->setRepeatState(TRUE);
break;
case OPT_CLAMP:
p->texNode->wrapS = SoTexture2::CLAMP;
p->texNode->wrapT = SoTexture2::CLAMP;
p->setRepeatState(FALSE);
break;
case OPT_UNKNOWN:
// this should never happen !
#ifdef DEBUG
SoDebugError::post("MyTextureEditor::optionMenuCB",
"OPT_UNKNOWN selected!");
#endif
break;
}
}
//
// called when an entry within the "File" menu is selected
//
void
MyTextureEditor::fileMenuCB(Widget w, int id, void *)
{
// get the class pointer
MyTextureEditor *p;
XtVaGetValues(w, XmNuserData, &p, NULL);
switch(id) {
case FILE_NEW:
p->createNewDialog();
break;
case FILE_RESET:
p->createDeleteDialog("Reset Palette Dialog",
"Reset to default palette ?", "(all changes will be lost)");
break;
case FILE_DELETE:
p->createDeleteDialog("Delete Palette Dialog",
"Delete current palette ?", "(list of texture files will be lost)");
break;
}
}
//
// Called whenever a new item menu is selected from the palette
// popup menu.
//
void
MyTextureEditor::paletteMenuCB(Widget w, int num, void *)
{
// get the class pointer
MyTextureEditor *p;
XtVaGetValues(w, XmNuserData, &p, NULL);
// return if the same palette is choosen
if (p->curPalette == num)
return;
p->switchPalette(num);
}
//
// called when the delete dialog "ok"/"Cancel" buttons gets pressed. This
// will delete the palette in the user's home directory.
//
void
MyTextureEditor::deleteDialogCB(Widget dialog, MyTextureEditor *p,
XmAnyCallbackStruct *cb)
{
// remove the user's palette
if (cb->reason == XmCR_OK) {
PaletteStruct *pal = (PaletteStruct *) p->paletteList[p->curPalette];
// remove the palette in user's home
char palDir[MAXPATHLEN];
sprintf(palDir, "%s/%s/%s", getenv("HOME"), customTextureDir, pal->name);
unlink(palDir);
// check if palette was also in the installed place (reset vs delete)
if (pal->system)
pal->user = FALSE;
else {
// remove the palette from the list, making sure we have
// at least one palette entry
free(pal->name);
if (p->paletteList.getLength() == 1) {
// create an empty default palette
pal->name = strdup("default");
}
else {
delete pal;
p->paletteList.remove( p->curPalette );
// check what palette will be next
if (p->curPalette == p->paletteList.getLength())
p->curPalette--;
}
// rebuild the new menu ( ??? have to rebuild, since we can't
// remove entries)
p->buildPaletteSubMenu();
}
// finaly load the new palette
p->switchPalette(p->curPalette);
}
XtDestroyWidget(dialog);
}
//
// called whenever the "ok"/"Cancel" buttons within the "New Palette" dialog
// gets pressed.
//
void
MyTextureEditor::newDialogCB(Widget dialog, MyTextureEditor *p,
XmAnyCallbackStruct *cb)
{
if (cb->reason == XmCR_OK) {
// retreive text and create palette
Widget field = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
char *str = XmTextGetString(field);
if (str[0] != '\0')
p->createNewPalette(str);
XtFree(str);
}
XtDestroyWidget(dialog);
}
//
// called when image dialog gets destroyed (reset widget pointers).
//
void
MyTextureEditor::imageDialogDestroyCB(Widget, MyTextureEditor *p, void *)
{
//printf("shell destroyed\n");
// reset widget pointers
p->widgetList[DIALOG_WINDOW] = NULL;
p->widgetList[DIALOG_IMAGE] = NULL;
p->widgetList[DIALOG_NAME] = NULL;
p->widgetList[DIALOG_FILE_BROWSER] = NULL;
// free image data and name
delete p->dialogImage;
if (p->dialogImageName != NULL)
free(p->dialogImageName);
if (p->dialogImageInfo != NULL)
free(p->dialogImageInfo);
p->dialogImage = NULL;
p->dialogImageName = NULL;
p->dialogImageInfo = NULL;
// free the glx contex
if (p->imageDialogCtx)
glXDestroyContext(p->getDisplay(), p->imageDialogCtx);
p->imageDialogCtx = 0;
}
//
// called when image dialog "Open..." button gets pressed
//
void
MyTextureEditor::imageDialogOpenCB(Widget, MyTextureEditor *p, void *)
{
if (p->widgetList[DIALOG_FILE_BROWSER] == NULL) {
Arg args[5];
int n = 0;
// ??? need to find a way to save the motif file browser current directory
// ??? for next time around (we really shouldn't delete this guy, but its
// ??? parent gets destroyed when closed).
// unmanage when ok/cancel are pressed
XtSetArg(args[n], XmNautoUnmanage, TRUE); n++;
XtSetArg(args[n], XmNtitle, "Image File Browser"); n++;
Widget fileDialog = XmCreateFileSelectionDialog(
p->widgetList[DIALOG_WINDOW], "fileBrowser", args, n);
p->widgetList[DIALOG_FILE_BROWSER] = fileDialog;
XtAddCallback(fileDialog, XmNokCallback,
(XtCallbackProc) MyTextureEditor::fileDialogOkCB,
(XtPointer) p);
}
XtManageChild(p->widgetList[DIALOG_FILE_BROWSER]);
}
//
// called when file dialog "Ok" button gets pressed
//
void
MyTextureEditor::fileDialogOkCB(Widget, MyTextureEditor *p,
XmFileSelectionBoxCallbackStruct *data)
{
// Get the file name
char *fileName;
if (!XmStringGetLtoR(data->value, XmSTRING_DEFAULT_CHARSET, &fileName))
return;
p->setNewDialogImage(fileName);
XtFree(fileName);
}
//
// called when image dialog "Apply" button gets pressed
//
void
MyTextureEditor::imageDialogApplyCB(Widget, MyTextureEditor *p, void *)
{
if (p->selectedItem == -1)
return;
TextureNameStruct *txt = &p->textureNames[ p->selectedItem ];
SbBool textureChanged = FALSE;
// assign new texture
if (p->dialogImageName != NULL) {
if (txt->fullName == NULL || strcmp(p->dialogImageName, txt->fullName) != 0) {
// add the new texture
p->addTextureEntry(p->selectedItem, p->dialogImageName);
textureChanged = TRUE;
}
}
// else clear existing texture
else {
if (txt->fullName != NULL) {
p->deleteTextureEntry(p->selectedItem);
textureChanged = TRUE;
}
}
if (textureChanged) {
// update the feedback and texture node
glXMakeCurrent(p->getDisplay(), XtWindow(p->widgetList[TEXTURE_GLX]),
p->paletteCtx);
p->drawTextureTile(p->selectedItem, DRAW_SELECTED);
p->updateTextureNode();
p->savePalette();
}
}