/* * * 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/ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Error.h" #include "FieldEditor.h" #include "MotifHelp.h" char *FieldEditor::fieldBuf; // Buffer for writing field values int FieldEditor::fieldBufSize = 0; // Size of buffer in bytes //////////////////////////////////////////////////////////////////////// // // This class is used to hold info about each individual field widget. // It contains the widget and other information accessible through // callbacks in one neat package. // //////////////////////////////////////////////////////////////////////// struct FieldInfo { // These are set in initInfo(): FieldEditor *editor; // Pointer to FieldEditor this is part of SoField *field; // Field being edited int index; // Index of field in node SbName fieldName; // Name of field SbBool setToDefault; // TRUE if user set value to default SbBool isMultiple; // TRUE if field is multiple value // These are set in buildFieldWidget(): Widget widget; // Top-level field editing widget Widget textWidget; // Text editing widget Widget ignoreButton; // Ignore button widget Widget defaultButton; // Set-default button widget // These are used only for multiple-value fields: Widget numberWidget; // Text widget showing current scale value Widget scrollWidget; // Text widget scrollbar widget }; //////////////////////////////////////////////////////////////////////// // // Description: // Constructor. Takes pointer to node whose fields we are to edit. // FieldEditor::FieldEditor(SoNode *node, Widget parent, const char *name) : SoXtComponent(parent, name) // //////////////////////////////////////////////////////////////////////// { int i; nodeToEdit = node; nodeToEdit->ref(); defNode = (SoNode *) node->getTypeId().createInstance(); defNode->ref(); finishCB = NULL; finishData = NULL; errorString = NULL; // Set up sensor. Make it priority 0 so we can be called // immediately when a field changes. This way we can update only // the fields that change in the sensor callback. nodeSensor = new SoNodeSensor(&FieldEditor::nodeSensorCB, (void *) this); nodeSensor->setPriority(0); nodeSensor->attach(node); // Allocate buffer to write field values into. We have to use // malloc, since we may use realloc on it later if (fieldBufSize == 0) fieldBuf = (char *) malloc((unsigned) (fieldBufSize = 1028)); // Allocate and initialize field info structures numFields = getNumFields(nodeToEdit); fieldInfos = new FieldInfo[numFields]; for (i = 0; i < numFields; i++) initInfo(&fieldInfos[i], i); // Set up the class name setClassName("FieldEditor"); // Build and set the widget setBaseWidget(buildWidget(getParentWidget())); } //////////////////////////////////////////////////////////////////////// // // Description: // Destructor. // FieldEditor::~FieldEditor() // //////////////////////////////////////////////////////////////////////// { // Widget stuff is handled in SoXtComponent... nodeToEdit->unref(); defNode->unref(); delete nodeSensor; delete [] fieldInfos; } //////////////////////////////////////////////////////////////////////// // // Description: // Returns number of fields in node being edited. This is static to // allow the check to be made before an editor is constructed. // int FieldEditor::getNumFields(SoNode *node) // //////////////////////////////////////////////////////////////////////// { const SoFieldData *fdata = node->getFieldData(); if (fdata == NULL) return 0; return fdata->getNumFields(); } //////////////////////////////////////////////////////////////////////// // // Description: // Sets a callback to call when editing is finished. // void FieldEditor::setFinishCallback(FieldEditorCB *cb, const void *userData) // //////////////////////////////////////////////////////////////////////// { finishCB = cb; finishData = (void *) userData; } //////////////////////////////////////////////////////////////////////// // // Description: // Builds and returns top-level editor widget. // Widget FieldEditor::buildWidget(Widget parent) // //////////////////////////////////////////////////////////////////////// { Widget topWidget, fieldForm, sep, buttonForm; ARG_VARS(20); // If no fields to edit, go away if (numFields == 0) return NULL; // The title is the class of the node whose fields we are editing setTitle(nodeToEdit->getTypeId().getName().getString()); // Create the top level form widget and register it with a class name RESET_ARGS(); topWidget = XmCreateForm(parent, (char *) getWidgetName(), ARGS); registerWidget(topWidget); // Create buttons at bottom buttonForm = buildButtonWidget(topWidget); RESET_ARGS(); ADD_LEFT_FORM(4); ADD_RIGHT_FORM(4); ADD_BOTTOM_FORM(4); XtSetValues(buttonForm, ARGS); // Create separator between buttons and field editors RESET_ARGS(); ADD_ARG(XmNorientation, XmHORIZONTAL); ADD_LEFT_FORM(0); ADD_RIGHT_FORM(0); ADD_BOTTOM_WIDGET(buttonForm, 4); sep = XmCreateSeparatorGadget(topWidget, "Separator", ARGS); // Create widget containing all field widgets fieldForm = buildFieldForm(topWidget); RESET_ARGS(); ADD_TOP_FORM(4); ADD_LEFT_FORM(4); ADD_RIGHT_FORM(4); ADD_BOTTOM_WIDGET(sep, 4); XtSetValues(fieldForm, ARGS); // Manage the children XtManageChild(fieldForm); XtManageChild(sep); XtManageChild(buttonForm); return topWidget; } //////////////////////////////////////////////////////////////////////// // // Description: // Creates and returns a widget containing all field-editing widgets. // Widget FieldEditor::buildFieldForm(Widget parent) // //////////////////////////////////////////////////////////////////////// { Widget form, sep; FieldInfo *info; int i; ARG_VARS(20); // Create form to hold all field widgets RESET_ARGS(); form = XmCreateForm(parent, "Form", ARGS); // Create widget for each field and add them to form for (i = 0, info = fieldInfos; i < numFields; i++, info++) { // Build separator if necessary if (i > 0) { RESET_ARGS(); ADD_ARG(XmNorientation, XmHORIZONTAL); ADD_LEFT_FORM(0); ADD_RIGHT_FORM(0); ADD_TOP_WIDGET((info - 1)->widget, 1); sep = XmCreateSeparatorGadget(form, "Separator", ARGS); XtManageChild(sep); } // Build field editor widget; sets widget field in info buildFieldWidget(form, info); // Set up widget contents from node updateInfo(info); RESET_ARGS(); ADD_LEFT_FORM(0); ADD_RIGHT_FORM(0); if (i == 0) { ADD_TOP_FORM(0); } else { ADD_TOP_WIDGET(sep, 0); } if (i == numFields - 1) { ADD_BOTTOM_FORM(0); } XtSetValues(info->widget, ARGS); XtManageChild(info->widget); } return form; } //////////////////////////////////////////////////////////////////////// // // Description: // Creates a widget for editing the i'th field // void FieldEditor::buildFieldWidget(Widget parent, FieldInfo *info) // //////////////////////////////////////////////////////////////////////// { Widget form, label, sep1, valueWidget, sep2, butForm, ign, def; ARG_VARS(20); // Create a form widget RESET_ARGS(); form = XmCreateForm(parent, "Field", ARGS); // Create a label widget with the name of the field RESET_ARGS(); ADD_ARG(XmNlabelString, STRING((char *) info->fieldName.getString())); ADD_ARG(XmNwidth, 140); ADD_LEFT_FORM(4); ADD_TOP_FORM(4); ADD_BOTTOM_FORM(4); label = XmCreateLabelGadget(form, "Label", ARGS); // Create a separator widget RESET_ARGS(); ADD_ARG(XmNorientation, XmVERTICAL); ADD_LEFT_WIDGET(label, 4); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); sep1 = XmCreateSeparatorGadget(form, "Separator", ARGS); // Create a form widget to hold buttons RESET_ARGS(); ADD_BOTTOM_FORM(4); ADD_RIGHT_FORM(4); butForm = XmCreateForm(form, "FieldButtons", ARGS); // Create a push button to set the field to default value. // (Disable it if the field is already the default value) RESET_ARGS(); ADD_ARG(XmNlabelString, STRING("Set To Default")); ADD_ARG(XmNheight, 28); ADD_ARG(XmNhighlightThickness, 0); ADD_RIGHT_FORM(0); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); def = XmCreatePushButton(butForm, "defaultButton", ARGS); ADD_CB(def, XmNactivateCallback, &FieldEditor::defaultButtonCB, info); // Create a toggle button to set the ignore flag RESET_ARGS(); ADD_ARG(XmNlabelString, STRING("Ignore")); ADD_ARG(XmNhighlightThickness, 0); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); ADD_LEFT_FORM(0); ADD_RIGHT_WIDGET(def, 4); ign = XmCreateToggleButtonGadget(butForm, "ignoreButton", ARGS); // Create a separator widget RESET_ARGS(); ADD_ARG(XmNorientation, XmVERTICAL); ADD_RIGHT_WIDGET(butForm, 4); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); sep2 = XmCreateSeparatorGadget(form, "Separator", ARGS); // Field value editor widget if (info->isMultiple) valueWidget = buildMultipleValueWidget(form, info); else valueWidget = buildSingleValueWidget(form, info); RESET_ARGS(); ADD_LEFT_WIDGET(sep1, 4); ADD_RIGHT_WIDGET(sep2, 4); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); XtSetValues(valueWidget, ARGS); // Manage the children XtManageChild(label); XtManageChild(sep1); XtManageChild(valueWidget); XtManageChild(sep2); XtManageChild(ign); XtManageChild(def); XtManageChild(butForm); info->widget = form; info->ignoreButton = ign; info->defaultButton = def; } //////////////////////////////////////////////////////////////////////// // // Description: // Creates and returns a widget that does the main work of editing // a multiple-value field. // Widget FieldEditor::buildMultipleValueWidget(Widget parent, FieldInfo *info) // //////////////////////////////////////////////////////////////////////// { Widget form, text, number, scroll; char buf[20]; ARG_VARS(20); ////////////////////////////////////////////////////////////////// // // Set up top level form widget // RESET_ARGS(); form = XmCreateForm(parent, "Form", ARGS); ////////////////////////////////////////////////////////////////// // // Set up the main scrolled text widget // RESET_ARGS(); ADD_ARG(XmNeditMode, XmMULTI_LINE_EDIT); ADD_ARG(XmNcolumns, 30); ADD_ARG(XmNrows, 5); ADD_RIGHT_FORM(4); ADD_TOP_FORM(4); ADD_BOTTOM_FORM(4); text = XmCreateScrolledText(form, "text", ARGS); // Set up a callback to check changes to text before allowing them ADD_CB(text, XmNmodifyVerifyCallback, &FieldEditor::textChangedCB, info); // Get the scroll bar resource from the scrolled window (the // parent widget of the scrolled text) XtVaGetValues(XtParent(text), XmNverticalScrollBar, &scroll, NULL); // Set up callbacks on the scroll bar so that the line number // widget scrolls with the other text. Note that we have to add // this to all of the scroll bar callbacks since they are already // set up for the scrolled window. ADD_CB(scroll, XmNvalueChangedCallback, &FieldEditor::scrollCB, info); ADD_CB(scroll, XmNincrementCallback, &FieldEditor::scrollCB, info); ADD_CB(scroll, XmNdecrementCallback, &FieldEditor::scrollCB, info); ADD_CB(scroll, XmNpageIncrementCallback, &FieldEditor::scrollCB, info); ADD_CB(scroll, XmNpageDecrementCallback, &FieldEditor::scrollCB, info); ADD_CB(scroll, XmNtoTopCallback, &FieldEditor::scrollCB, info); ADD_CB(scroll, XmNtoBottomCallback, &FieldEditor::scrollCB, info); ADD_CB(scroll, XmNdragCallback, &FieldEditor::scrollCB, info); ////////////////////////////////////////////////////////////////// // // Set up the line number text widget // RESET_ARGS(); ADD_ARG(XmNeditable, False); ADD_ARG(XmNeditMode, XmMULTI_LINE_EDIT); ADD_ARG(XmNvalue, buf); ADD_ARG(XmNcolumns, 5); ADD_ARG(XmNrows, 5); ADD_ARG(XmNhighlightThickness, 0); ADD_RIGHT_WIDGET(text, 0); ADD_TOP_FORM(6); ADD_LEFT_FORM(4); ADD_BOTTOM_FORM(4); number = XmCreateText(form, "numbers", ARGS); XmTextSetString(number, "?"); // Bogus value // Set up a callback on the line number widget so that users // cannot scroll the text in there by moving the cursor ADD_CB(number, XmNmotionVerifyCallback, &FieldEditor::lineCB, info); XtManageChild(text); XtManageChild(number); info->textWidget = text; info->numberWidget = number; info->scrollWidget = scroll; return form; } //////////////////////////////////////////////////////////////////////// // // Description: // Creates and returns a widget that does the main work of editing // a single-value field. // Widget FieldEditor::buildSingleValueWidget(Widget parent, FieldInfo *info) // //////////////////////////////////////////////////////////////////////// { Widget form, text; ARG_VARS(20); RESET_ARGS(); form = XmCreateForm(parent, "Form", ARGS); RESET_ARGS(); ADD_ARG(XmNcolumns, 30); ADD_ARG(XmNeditMode, XmSINGLE_LINE_EDIT); ADD_LEFT_FORM(4); ADD_RIGHT_FORM(4); ADD_TOP_FORM(4); ADD_BOTTOM_FORM(4); text = XmCreateText(form, "Text", ARGS); ADD_CB(text, XmNmodifyVerifyCallback, &FieldEditor::textChangedCB, info); XtManageChild(text); info->textWidget = text; return form; } //////////////////////////////////////////////////////////////////////// // // Description: // Creates and returns a widget containing all buttons. // Widget FieldEditor::buildButtonWidget(Widget parent) // //////////////////////////////////////////////////////////////////////// { Widget form, acceptButton, revertButton, cancelButton; ARG_VARS(20); // Create a form widget to hold it all RESET_ARGS(); form = XmCreateForm(parent, "Field", ARGS); // Create "Apply", "Accept", "Revert", and "Cancel" buttons RESET_ARGS(); ADD_ARG(XmNlabelString, STRING("Accept")); ADD_ARG(XmNhighlightThickness, 0); ADD_LEFT_FORM(0); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); acceptButton = XmCreatePushButton(form, "acceptButton", ARGS); ADD_CB(acceptButton, XmNactivateCallback, &FieldEditor::acceptButtonCB, this); RESET_ARGS(); ADD_ARG(XmNlabelString, STRING("Apply")); ADD_ARG(XmNhighlightThickness, 0); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); ADD_LEFT_WIDGET(acceptButton, 4); applyButton = XmCreatePushButton(form, "applyButton", ARGS); ADD_CB(applyButton, XmNactivateCallback, &FieldEditor::applyButtonCB, this); RESET_ARGS(); ADD_ARG(XmNlabelString, STRING("Revert")); ADD_ARG(XmNhighlightThickness, 0); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); ADD_LEFT_WIDGET(applyButton, 4); revertButton = XmCreatePushButton(form, "revertButton", ARGS); ADD_CB(revertButton, XmNactivateCallback, &FieldEditor::revertButtonCB, this); RESET_ARGS(); ADD_ARG(XmNlabelString, STRING("Cancel")); ADD_ARG(XmNhighlightThickness, 0); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); ADD_LEFT_WIDGET(revertButton, 4); cancelButton = XmCreatePushButton(form, "cancelButton", ARGS); ADD_CB(cancelButton, XmNactivateCallback, &FieldEditor::cancelButtonCB, this); // Create an "Override" toggle button to allow user to change // override flag for node RESET_ARGS(); ADD_ARG(XmNset, nodeToEdit->isOverride()); ADD_ARG(XmNlabelString, STRING("Override")); ADD_ARG(XmNhighlightThickness, 0); ADD_TOP_FORM(0); ADD_BOTTOM_FORM(0); ADD_RIGHT_FORM(0); overrideButton = XmCreateToggleButtonGadget(form, "overrideButton", ARGS); XtManageChild(acceptButton); XtManageChild(applyButton); XtManageChild(revertButton); XtManageChild(cancelButton); XtManageChild(overrideButton); // Hitting a carriage-return activates the "Apply" button RESET_ARGS(); ADD_ARG(XmNdefaultButton, applyButton); XtSetValues(parent, ARGS); return form; } //////////////////////////////////////////////////////////////////////// // // Description: // Applies changes made in editor to node it is editing. // void FieldEditor::apply() // //////////////////////////////////////////////////////////////////////// { FieldInfo *info; int i; SbBool override; // Turn off sensor for a minute... nodeSensor->detach(); // Get all field values and flags and store them back into the node for (i = 0, info = fieldInfos; i < numFields; i++, info++) updateNode(info); // Set the override flag if it has changed override = XmToggleButtonGadgetGetState(overrideButton); if (nodeToEdit->isOverride() != override) nodeToEdit->setOverride(override); // Make sure the text window reflects the exact state revert(); // Reattach sensor nodeSensor->attach(nodeToEdit); } //////////////////////////////////////////////////////////////////////// // // Description: // Sets field editor based on the values in the node being edited. // If the passed field index is not -1 (the default), only the // value for the specified field is reverted. // void FieldEditor::revert(int fieldIndex) // //////////////////////////////////////////////////////////////////////// { FieldInfo *info; int i; if (fieldIndex >= 0) updateInfo(&fieldInfos[fieldIndex]); // Update all field info from node else for (i = 0, info = fieldInfos; i < numFields; i++, info++) updateInfo(info); // Set the override button XmToggleButtonGadgetSetState(overrideButton, nodeToEdit->isOverride(), FALSE); } //////////////////////////////////////////////////////////////////////// // // Description: // Initializes given FieldInfo structure based on indexed field. // void FieldEditor::initInfo(FieldInfo *info, int i) // //////////////////////////////////////////////////////////////////////// { info->editor = this; info->index = i; info->setToDefault = FALSE; // Set the field name and pointer and determine whether it is a // single or multiple value field. const SoFieldData *fdata = nodeToEdit->getFieldData(); info->field = fdata->getField(nodeToEdit, i); info->fieldName = fdata->getFieldName(i); info->isMultiple = info->field->isOfType(SoMField::getClassTypeId()); } //////////////////////////////////////////////////////////////////////// // // Description: // Sets up field editor from correct field in node being edited. // void FieldEditor::updateInfo(FieldInfo *info) // //////////////////////////////////////////////////////////////////////// { SbString fieldString; ARG_VARS(10); // Set text field from current field value(s) showFieldString(info); // Set ignore button state RESET_ARGS(); ADD_ARG(XmNset, info->field->isIgnored()); XtSetValues(info->ignoreButton, ARGS); // Turn set-default button on or off info->setToDefault = info->field->isDefault(); RESET_ARGS(); ADD_ARG(XmNsensitive, ! info->setToDefault); XtSetValues(info->defaultButton, ARGS); } //////////////////////////////////////////////////////////////////////// // // Description: // Makes field editor show default value for given field. Does NOT // change the node at all! // void FieldEditor::showDefaultValue(FieldInfo *info) // //////////////////////////////////////////////////////////////////////// { SoField *defField, *saveField; ARG_VARS(10); // Make sure we know that this was done info->setToDefault = TRUE; // Get the appropriate field from a node instance with all default values defField = defNode->getFieldData()->getField(defNode, info->index); // Temporarily change the field to the default one saveField = info->field; info->field = defField; // Set the string in the text widget showFieldString(info); // Restore the real field value info->field = saveField; // Disable the default button RESET_ARGS(); ADD_ARG(XmNsensitive, FALSE); XtSetValues(info->defaultButton, ARGS); } //////////////////////////////////////////////////////////////////////// // // Description: // Sets string(s) in text widget to display field value(s). // void FieldEditor::showFieldString(FieldInfo *info) const // //////////////////////////////////////////////////////////////////////// { SbString valueString; REM_CB(info->textWidget, XmNmodifyVerifyCallback, &FieldEditor::textChangedCB, info); // Get the field value(s) as a string getFieldString(info, valueString); // Set the text widget to show the string XmTextSetString(info->textWidget, (char *) valueString.getString()); // Set up the line number widget for multiple-value fields if (info->isMultiple) updateLineNumbers(info, TRUE); // Reconnect the callback ADD_CB(info->textWidget, XmNmodifyVerifyCallback, &FieldEditor::textChangedCB, info); } //////////////////////////////////////////////////////////////////////// // // Description: // Updates field value and flags in node from current editor settings. // void FieldEditor::updateNode(FieldInfo *info) // //////////////////////////////////////////////////////////////////////// { SbString string; SbBool ignore; getEditorString(info, string); // Set the read-error callback so that read errors caused by the // following call to SoField::set() can be trapped and displayed // in an error dialog box SoReadError::setHandlerCallback(&FieldEditor::readErrorCB, info); if (! nodeToEdit->set(string.getString())) displayReadError(); // If value was set to default, set flag in the field if (info->setToDefault) info->field->setDefault(TRUE); // Make sure ignore flag is correct ignore = XmToggleButtonGadgetGetState(info->ignoreButton); if (info->field->isIgnored() != ignore) info->field->setIgnored(ignore); } //////////////////////////////////////////////////////////////////////// // // Description: // Fills in SbString with string representing field value that can // be displayed in editor's text widget. // void FieldEditor::getFieldString(FieldInfo *info, SbString &string) const // //////////////////////////////////////////////////////////////////////// { // Get the field value(s) as a string info->field->get(string); // If it's a multiple-value string, we have to some extra work if (info->isMultiple) { const char *s = string.getString(); // If first character is an open bracket, we have work to do if (*s == '[') { // Skip over the open brace s++; // Allocate working space for the string char *buf = new char [strlen(s) + 1]; char *b = buf; SbBool atNewline = TRUE; while (*s != '\0') { // Skip over quoted strings - this will have a problem // with quotes nested inside quotes ??? if (*s == '\"') { *b++ = *s++; while (*s != '\"') *b++ = *s++; *b++ = *s++; // Closing quote } // Change commas into newlines if (*s == ',') { *b++ = '\n'; atNewline = TRUE; s++; } // Skip over spaces (including extra newlines) after newlines else if (atNewline && isspace(*s)) s++; else { *b++ = *s++; atNewline = FALSE; } } // Remove closing brace and space before it while (*b != ']') b--; while (isspace(*(b - 1))) b--; // Terminate string *b = '\0'; string = buf; delete [] buf; } } } //////////////////////////////////////////////////////////////////////// // // Description: // Fills in SbString with string containing current field value in // editor's text widget, to store back into node being edited. // void FieldEditor::getEditorString(FieldInfo *info, SbString &string) const // //////////////////////////////////////////////////////////////////////// { char *str = XmTextGetString(info->textWidget); // Store name of string and a space string = info->fieldName.getString(); string += " "; // If a single-value field, just append text string from widget if (! info->isMultiple) string += str; // If this is a multiple-value field, add extra characters else { char *c; string += "["; for (c = str; *c != '\0'; c++) { // Change all newlines to commas in string first if (*c == '\n') { *c = ','; // Remove all whitespace after commas. This also gets // rid of blank lines. c++; while (isspace(*c)) c++; c--; } } string += str; string += "]"; } XtFree(str); } //////////////////////////////////////////////////////////////////////// // // Description: // Redefines this method to clean up when the window is destroyed. // void FieldEditor::windowCloseAction() // //////////////////////////////////////////////////////////////////////// { // Alert caller if necessary if (finishCB != NULL) finishCB(finishData, this); delete this; } //////////////////////////////////////////////////////////////////////// // // Description: // Updates the line-number widget whenever the text area changes or // is scrolled. The textChanged flag indicates why the line number // may be changing. // void FieldEditor::updateLineNumbers(FieldInfo *info, SbBool textChanged) const // //////////////////////////////////////////////////////////////////////// { if (textChanged) { // See if the number of lines in the text area has changed. If // so, insert or remove line numbers from the widget. // Count newlines in the text string char *textString = XmTextGetString(info->textWidget); int numLines = 0; char *b; for (b = textString; *b != '\0'; b++) if (*b == '\n') numLines++; // Last line may not end in a newline? if (b > textString && *(b-1) != '\n') numLines++; XtFree(textString); // Make sure line number widget has the correct info char *numberString = XmTextGetString(info->numberWidget); int numNumbers = strlen(numberString) / 6; XtFree(numberString); // Too many line numbers? Remove some. if (numNumbers > numLines) XmTextReplace(info->numberWidget, numLines * 6, numNumbers * 6, NULL); // Too few line numbers? Add some. else if (numNumbers < numLines) { int numNeeded = numLines - numNumbers, i; char *buf = new char [numNeeded * 6 + 1], *b = buf; for (i = 0; i < numNeeded; i++) { sprintf(b, "%5d\n", numNumbers + i); b += strlen(b); } if (numNumbers == 0) XmTextReplace(info->numberWidget, 0, 1, buf); else XmTextInsert(info->numberWidget, numLines * 6, buf); delete [] buf; } } // Find out the scroll bar value and set the line number position // based on it ARG_VARS(1); int topLine; RESET_ARGS(); ADD_ARG(XmNvalue, &topLine); XtGetValues(info->scrollWidget, ARGS); RESET_ARGS(); ADD_ARG(XmNtopCharacter, topLine * 6); XtSetValues(info->numberWidget, ARGS); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback called when the node we are editing changes. The data // pointer points to a FieldEditor instance. // void FieldEditor::nodeSensorCB(void *data, SoSensor *sensor) // //////////////////////////////////////////////////////////////////////// { // Try to update only the field that changed, if there is one FieldEditor *editor = (FieldEditor *) data; SoNodeSensor *nodeSensor = (SoNodeSensor *) sensor; const SoField *trigField = nodeSensor->getTriggerField(); int fieldIndex = -1; if (trigField != NULL) { int i; for (i = 0; i < editor->numFields; i++) { if (editor->fieldInfos[i].field == trigField) { fieldIndex = i; break; } } } editor->revert(fieldIndex); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback called when the "Accept" button in the editor is // activated. The clientData pointer points to a FieldEditor instance. // void FieldEditor::acceptButtonCB(Widget, XtPointer clientData, XtPointer) // //////////////////////////////////////////////////////////////////////// { FieldEditor *editor = (FieldEditor *) clientData; // Make sure latest changes were applied editor->apply(); // Get rid of window editor->windowCloseAction(); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback called when the "Apply" button in the editor is // activated. The clientData pointer points to a FieldEditor instance. // void FieldEditor::applyButtonCB(Widget, XtPointer clientData, XtPointer) // //////////////////////////////////////////////////////////////////////// { ((FieldEditor *) clientData)->apply(); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback called when the "Revert" button in the editor is // activated. The clientData pointer points to a FieldEditor instance. // void FieldEditor::revertButtonCB(Widget, XtPointer clientData, XtPointer) // //////////////////////////////////////////////////////////////////////// { ((FieldEditor *) clientData)->revert(); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback called when the "Cancel" button in the editor is // activated. The clientData pointer points to a FieldEditor instance. // void FieldEditor::cancelButtonCB(Widget, XtPointer clientData, XtPointer) // //////////////////////////////////////////////////////////////////////// { FieldEditor *editor = (FieldEditor *) clientData; // Get rid of window editor->windowCloseAction(); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback called when the "Set Default" button in a field editor // is activated. The clientData pointer points to a FieldInfo // instance for the field to change. // void FieldEditor::defaultButtonCB(Widget, XtPointer clientData, XtPointer) // //////////////////////////////////////////////////////////////////////// { FieldInfo *info = (FieldInfo *) clientData; // Make sure the correct value is displayed in the text widget info->editor->showDefaultValue(info); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback called when the text in a field editor text widget // is changed. The clientData pointer points to a FieldInfo // instance. // void FieldEditor::textChangedCB(Widget, XtPointer clientData, XtPointer callData) // //////////////////////////////////////////////////////////////////////// { FieldInfo *info = (FieldInfo *) clientData; XmTextVerifyPtr tvp = (XmTextVerifyPtr) callData; // If we are inserting a text character, see if it is a carriage return if (tvp->text->length > 0 && *tvp->text->ptr == '\n') { // We don't want the text to be inserted if it's a // single-value field - just act as if we hit the "Apply" // button if (! info->isMultiple) { tvp->doit = FALSE; XtCallActionProc(info->editor->applyButton, "ArmAndActivate", tvp->event, NULL, 0); } } // Multiple-value fields need to scroll correctly when the text // changes if (info->isMultiple) info->editor->updateLineNumbers(info, TRUE); // Reset the setToDefault flag to FALSE if necessary if (info->setToDefault && tvp->doit) { XtSetSensitive(info->defaultButton, TRUE); info->setToDefault = FALSE; } } //////////////////////////////////////////////////////////////////////// // // Description: // Callback called when the value of the scrollbar in a // multiple-value field text scrolling area changes. The clientData // pointer points to a FieldInfo instance. // void FieldEditor::scrollCB(Widget, XtPointer clientData, XtPointer) // //////////////////////////////////////////////////////////////////////// { FieldInfo *info = (FieldInfo *) clientData; // Determine the new top line number from the scroll bar info->editor->updateLineNumbers(info, FALSE); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback used to keep the user from moving the insertion cursor // within the line-number widget. The clientData pointer points to // a FieldInfo instance. // void FieldEditor::lineCB(Widget, XtPointer, XtPointer callData) // //////////////////////////////////////////////////////////////////////// { XmTextVerifyCallbackStruct *cbInfo = (XmTextVerifyCallbackStruct *) callData; // Indicate that no cursor motion should occur cbInfo->doit = False; } //////////////////////////////////////////////////////////////////////// // // Description: // Reallocates fieldBuf and sets fieldBufSize. // void * FieldEditor::reallocBuf(void *ptr, size_t newSize) // //////////////////////////////////////////////////////////////////////// { fieldBufSize = newSize; fieldBuf = (char *) realloc(ptr, newSize); return fieldBuf; } //////////////////////////////////////////////////////////////////////// // // Description: // Handler for read errors that occur when setting values in the // field. Saves the error message to be displayed later. // void FieldEditor::readErrorCB(const SoError *error, void *data) // //////////////////////////////////////////////////////////////////////// { FieldInfo *info = (FieldInfo *) data; // Save only the first error that is found if (info->editor->errorString == NULL) { SbString *err = new SbString; (*err) = "There was an error reading the values for the field named "; (*err) += info->fieldName.getString(); (*err) += ":\n\n"; (*err) += error->getDebugString(); info->editor->errorString = err; } } //////////////////////////////////////////////////////////////////////// // // Description: // Displays error message detected during reading. // void FieldEditor::displayReadError() // //////////////////////////////////////////////////////////////////////// { if (errorString != NULL) { Error *err = new Error(XtParent(getWidget()), errorString->getString()); delete errorString; errorString = NULL; } }