1// @configure_input@ 2 3/**************************************************************************\ 4 * Copyright (c) Kongsberg Oil & Gas Technologies AS 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * met: 10 * 11 * Redistributions of source code must retain the above copyright notice, 12 * this list of conditions and the following disclaimer. 13 * 14 * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * Neither the name of the copyright holder nor the names of its 19 * contributors may be used to endorse or promote products derived from 20 * this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33\**************************************************************************/ 34 35// FIXME: overload the getClassName() type functions 36 37#include <Inventor/SbPList.h> 38#include <Inventor/SoSceneManager.h> 39#include <Inventor/fields/SoSFColor.h> 40#include <Inventor/fields/SoMFColor.h> 41#include <Inventor/fields/SoMFUInt32.h> 42#include <Inventor/nodes/SoSeparator.h> 43#include <Inventor/nodes/SoTexture2.h> 44#include <Inventor/sensors/SoFieldSensor.h> 45#include <Inventor/actions/SoGLRenderAction.h> 46 47#include <Inventor/@Gui@/SoAny.h> 48#include <Inventor/@Gui@/nodes/SoGuiViewportFix.h> 49#include <Inventor/@Gui@/nodes/SoGuiColorEditor.h> 50#include <Inventor/@Gui@/editors/So@Gui@ColorEditor.h> 51 52/* 53#include <Inventor/@Gui@/nodes/SoGuiPane.h> 54#include <Inventor/@Gui@/nodes/SoGuiClickCounter.h> 55#include <Inventor/@Gui@/nodes/SoGuiSlider1.h> 56#include <Inventor/@Gui@/nodes/SoGuiSlider2.h> 57*/ 58 59 60/*! 61 \class So@Gui@ColorEditor Inventor/@Gui@/editors/So@Gui@ColorEditor.h 62 \brief The So@Gui@ColorEditor class is a GUI component for interactively 63 editing color fields. 64*/ 65 66/*! 67 \enum So@Gui@ColorEditor::Sliders 68*/ 69 70/*! 71 \val So@Gui@ColorEditor::NONE 72*/ 73 74/*! 75 \val So@Gui@ColorEditor::INTENSITY 76*/ 77 78/*! 79 \val So@Gui@ColorEditor::RGB 80*/ 81 82/*! 83 \val So@Gui@ColorEditor::HSV 84*/ 85 86/*! 87 \val So@Gui@ColorEditor::RGB_V 88*/ 89 90/*! 91 \val So@Gui@ColorEditor::RGB_HSV 92*/ 93 94/*! 95 \enum So@Gui@ColorEditor::UpdateFrequency 96*/ 97 98/*! 99 \val So@Gui@ColorEditor::CONTINUOUS 100*/ 101 102/*! 103 \val So@Gui@ColorEditor::AFTER_ACCEPT 104*/ 105 106// ************************************************************************* 107 108static const SbBool SGI_ATTACHMENT_REF_COMPATIBILITY = TRUE; 109 110enum Attachment { 111 DETACHED, 112 SFCOLOR, 113 MFCOLOR, 114 MFUINT32 115}; 116 117// Name suffix used to avoid conflict with private part of the ColorEditor 118// node kit. 119 120class ColorEditorComponent { 121public: 122 So@Gui@ColorEditor * api; 123 124 static const char * superscene[]; 125 126 SbPList callbacks; 127 128 // attachment is redundant - the existence of the field sensor, and the 129 // field type it is attached to is all the info needed really 130 Attachment attachment; 131 // the field pointers can actually be dropped since the sensor will have 132 // that info 133 SoSFColor * sfcolor; 134 SoMFColor * mfcolor; 135 SoMFUInt32 * mfuint32; 136 int mfindex; 137 138 SoFieldSensor * editor_sensor; 139 static void editor_update_cb(void * closure, SoSensor * sensor); 140 141 SoFieldSensor * attachment_sensor; 142 static void attachment_update_cb(void * closure, SoSensor * sensor); 143 144 SoGuiColorEditor * editor; 145 146 void invokeColorChangeCallbacks(void); 147 SbBool colorsEqual(void); 148}; 149 150// ************************************************************************* 151 152SO@GUI@_OBJECT_SOURCE(So@Gui@ColorEditor); 153 154#define PRIVATE(obj) ((ColorEditorComponent *) ((So@Gui@ColorEditor *) obj)->internals) 155#define PUBLIC(obj) (((ColorEditorComponent *) obj)->api) 156 157So@Gui@ColorEditor::So@Gui@ColorEditor(@WIDGET@ parent, const char * name, SbBool embed) 158 : inherited(parent, name, embed) 159{ 160 this->internals = (void *) new ColorEditorComponent; 161 PRIVATE(this)->api = this; 162 163 PRIVATE(this)->attachment = DETACHED; 164 PRIVATE(this)->sfcolor = NULL; 165 PRIVATE(this)->sfcolor = NULL; 166 PRIVATE(this)->mfcolor = NULL; 167 PRIVATE(this)->mfuint32 = NULL; 168 PRIVATE(this)->mfindex = 0; 169 170 PRIVATE(this)->editor_sensor = NULL; 171 PRIVATE(this)->attachment_sensor = NULL; 172 173 PRIVATE(this)->editor = NULL; 174 175 this->setSize(SbVec2s(320, 256)); 176 177 SoNode * root = SoAny::loadSceneGraph(ColorEditorComponent::superscene); 178 assert(root != NULL); 179 assert(root->isOfType(SoSeparator::getClassTypeId())); 180 SoSeparator * superscene = (SoSeparator *) root; 181 182 PRIVATE(this)->editor = new SoGuiColorEditor; 183 superscene->addChild(PRIVATE(this)->editor); 184 this->setSceneGraph(superscene); 185 186 PRIVATE(this)->attachment_sensor = new SoFieldSensor(ColorEditorComponent::attachment_update_cb, PRIVATE(this)); 187 188 PRIVATE(this)->editor_sensor = new SoFieldSensor(ColorEditorComponent::editor_update_cb, PRIVATE(this)); 189 PRIVATE(this)->editor_sensor->attach(&(PRIVATE(this)->editor->color)); 190} 191 192So@Gui@ColorEditor::~So@Gui@ColorEditor(void) 193{ 194 if ( PRIVATE(this)->attachment != DETACHED ) this->detach(); 195 delete PRIVATE(this)->attachment_sensor; 196 delete PRIVATE(this)->editor_sensor; 197 this->setSceneGraph(NULL); 198 ColorEditorComponent * instance = PRIVATE(this); 199 delete instance; 200} 201 202/*! 203 Attach the editor to a color single field. Any existing attachments are 204 detached. 205 206 The node argument defaults to NULL and is ignored. It is part of the 207 argument list for compatibility reasons. 208*/ 209 210void 211So@Gui@ColorEditor::attach(SoSFColor * color, SoBase * node) 212{ 213 if ( PRIVATE(this)->attachment != DETACHED ) this->detach(); 214 if ( color != NULL ) { 215 if ( SGI_ATTACHMENT_REF_COMPATIBILITY ) { 216 SoFieldContainer * container = color->getContainer(); 217 if ( container != NULL ) container->ref(); 218 } 219 PRIVATE(this)->attachment = SFCOLOR; 220 PRIVATE(this)->sfcolor = color; 221 assert(PRIVATE(this)->attachment_sensor != NULL); 222 PRIVATE(this)->attachment_sensor->attach(color); 223 PRIVATE(this)->editor->color.setValue(color->getValue()); 224 } 225} 226 227/*! 228 Attach the editor to an element in a color multi field. Any existing attachments are 229 detached. 230 231 The node argument defaults to NULL and is ignored. It is part of the 232 argument list for compatibility reasons. 233*/ 234 235void 236So@Gui@ColorEditor::attach(SoMFColor * color, int idx, SoBase * node) 237{ 238 if ( PRIVATE(this)->attachment != DETACHED ) this->detach(); 239 if ( color != NULL ) { 240 if ( SGI_ATTACHMENT_REF_COMPATIBILITY ) { 241 SoFieldContainer * container = color->getContainer(); 242 if ( container != NULL ) container->ref(); 243 } 244 PRIVATE(this)->attachment = MFCOLOR; 245 PRIVATE(this)->mfcolor = color; 246 PRIVATE(this)->mfindex = idx; 247 assert(PRIVATE(this)->attachment_sensor != NULL); 248 PRIVATE(this)->attachment_sensor->attach(color); 249 PRIVATE(this)->editor->color.setValue(color->operator[](idx)); 250 } 251} 252 253/*! 254 Attach the editor to an element in an uint32 multi field. The field 255 is assumed to be of the RGBA packed color format. Any existing attachments are 256 detached. 257 258 The node argument defaults to NULL and is ignored. It is part of the 259 argument list for compatibility reasons. 260*/ 261 262void 263So@Gui@ColorEditor::attach(SoMFUInt32 * color, int idx, SoBase * node) 264{ 265 if ( PRIVATE(this)->attachment != DETACHED ) this->detach(); 266 if ( color != NULL ) { 267 if ( SGI_ATTACHMENT_REF_COMPATIBILITY ) { 268 SoFieldContainer * container = color->getContainer(); 269 if ( container != NULL ) container->ref(); 270 } 271 PRIVATE(this)->attachment = MFUINT32; 272 PRIVATE(this)->mfuint32 = color; 273 PRIVATE(this)->mfindex = idx; 274 assert(PRIVATE(this)->attachment_sensor != NULL); 275 PRIVATE(this)->attachment_sensor->attach(color); 276 SbColor col; 277 float transparency = 0.0f; 278 col.setPackedValue(color->operator[](idx), transparency); 279 PRIVATE(this)->editor->color.setValue(col); 280 } 281} 282 283/*! 284 Detach the editor from the field it is attached to. 285*/ 286 287void 288So@Gui@ColorEditor::detach(void) 289{ 290 if ( PRIVATE(this)->attachment != DETACHED ) { 291 SoField * field = NULL; 292 switch ( PRIVATE(this)->attachment ) { 293 case SFCOLOR: 294 field = PRIVATE(this)->sfcolor; 295 PRIVATE(this)->sfcolor = NULL; 296 break; 297 case MFCOLOR: 298 field = PRIVATE(this)->mfcolor; 299 PRIVATE(this)->mfcolor = NULL; 300 break; 301 case MFUINT32: 302 field = PRIVATE(this)->mfuint32; 303 PRIVATE(this)->mfuint32 = NULL; 304 break; 305 case DETACHED: 306 default: 307 assert(0 && "impossible switch case"); 308 break; 309 } 310 assert(field != NULL); 311 if ( field != NULL ) { 312 assert(PRIVATE(this)->attachment_sensor != NULL); 313 PRIVATE(this)->attachment_sensor->detach(); 314 if ( SGI_ATTACHMENT_REF_COMPATIBILITY ) { 315 SoFieldContainer * container = field->getContainer(); 316 if ( container != NULL ) container->unref(); 317 } 318 } 319 PRIVATE(this)->attachment = DETACHED; 320 } 321} 322 323/*! 324 This method returns whether or not the editor is currently attached to a field. 325*/ 326 327SbBool 328So@Gui@ColorEditor::isAttached(void) const 329{ 330 return (PRIVATE(this)->attachment != DETACHED) ? TRUE : FALSE; 331} 332 333/* 334 Add a callback to be triggered when the color value is changed. 335 336 \sa So@Gui@ColorEditor::setUpdateFrequency 337*/ 338 339void 340So@Gui@ColorEditor::addColorChangedCallback(So@Gui@ColorEditorCB * callback, void * closure) 341{ 342 PRIVATE(this)->callbacks.append((void *) callback); 343 PRIVATE(this)->callbacks.append(closure); 344} 345 346/*! 347 Remove all color change callbacks matching the given arguments. 348*/ 349 350void 351So@Gui@ColorEditor::removeColorChangedCallback(So@Gui@ColorEditorCB * callback, void * closure) 352{ 353 const int len = PRIVATE(this)->callbacks.getLength(); 354 int i; 355 for ( i = 0; i < len; i += 2 ) { 356 So@Gui@ColorEditorCB * cb = 357 (So@Gui@ColorEditorCB *) PRIVATE(this)->callbacks[i]; 358 if ( (callback == cb) && (closure == PRIVATE(this)->callbacks[i+1]) ) { 359 PRIVATE(this)->callbacks.remove(i+1); 360 PRIVATE(this)->callbacks.remove(i); 361 i -= 2; 362 } 363 } 364} 365 366/*! 367 Set a new color value. 368 369 If the field value gets updated, the color change callbacks will be triggered. 370*/ 371 372void 373So@Gui@ColorEditor::setColor(const SbColor & color) 374{ 375 // callbacks are triggered on the sensor rebound... 376 switch ( PRIVATE(this)->attachment ) { 377 case DETACHED: 378 break; 379 case SFCOLOR: 380 assert(PRIVATE(this)->sfcolor != NULL); 381 if ( PRIVATE(this)->sfcolor->getValue() != color ) { 382 PRIVATE(this)->sfcolor->setValue(color); 383 } 384 break; 385 case MFCOLOR: 386 assert(PRIVATE(this)->mfcolor != NULL); 387 if ( PRIVATE(this)->mfcolor->operator[](PRIVATE(this)->mfindex) != color ) { 388 PRIVATE(this)->mfcolor->set1Value(PRIVATE(this)->mfindex, color); 389 } 390 break; 391 case MFUINT32: 392 assert(PRIVATE(this)->mfuint32 != NULL); 393 if ( PRIVATE(this)->mfuint32->operator[](PRIVATE(this)->mfindex) != color.getPackedValue() ) { 394 PRIVATE(this)->mfuint32->set1Value(PRIVATE(this)->mfindex, color.getPackedValue()); 395 } 396 break; 397 } 398 assert(PRIVATE(this)->editor != NULL); 399 PRIVATE(this)->editor->color.setValue(color); 400} 401 402/*! 403 Get the current color value. 404*/ 405 406const SbColor & 407So@Gui@ColorEditor::getColor(void) const 408{ 409 assert(PRIVATE(this)->editor != NULL); 410 return PRIVATE(this)->editor->color.getValue(); 411} 412 413/*! 414 Not implemented yet. 415 416 Sets whether or not the color sliders should be in WYSIWYG mode. 417 When enabled, the color backgrounds in the sliders will be updated to 418 reflect what the color will be, taken all color components into account. 419 When disabled, the color backgrounds only reflect the component the slider 420 controls. 421*/ 422 423void 424So@Gui@ColorEditor::setWYSIWYG(SbBool enable) 425{ 426 assert(PRIVATE(this)->editor != NULL); 427 PRIVATE(this)->editor->wysiwyg.setValue(enable); 428} 429 430/*! 431 Returns whether or not the editor sliders are in WYSIWYG mode. 432*/ 433 434SbBool 435So@Gui@ColorEditor::isWYSIWYG(void) const 436{ 437 assert(PRIVATE(this)->editor != NULL); 438 return PRIVATE(this)->editor->wysiwyg.getValue(); 439} 440 441/*! 442 Sets which if the slider sets is to be used. 443 444 \sa So@Gui@ColorEditor::Sliders 445*/ 446 447void 448So@Gui@ColorEditor::setCurrentSliders(So@Gui@ColorEditor::Sliders which) 449{ 450 assert(PRIVATE(this)->editor != NULL); 451 PRIVATE(this)->editor->sliders.setValue((So@Gui@ColorEditor::Sliders) which); 452} 453 454/*! 455 Returns which slider sets is being used. 456 457 \sa So@Gui@ColorEditor::Sliders 458*/ 459 460So@Gui@ColorEditor::Sliders 461So@Gui@ColorEditor::getCurrentSliders(void) const 462{ 463 assert(PRIVATE(this)->editor != NULL); 464 return (So@Gui@ColorEditor::Sliders) PRIVATE(this)->editor->sliders.getValue(); 465} 466 467/*! 468 Sets the update-frequency setting, which affects when color change callbacks 469 will be triggered. 470 471 \sa So@Gui@ColorEditor::UpdateFrequency 472*/ 473 474void 475So@Gui@ColorEditor::setUpdateFrequency(So@Gui@ColorEditor::UpdateFrequency freq) 476{ 477 assert(PRIVATE(this)->editor != NULL); 478 PRIVATE(this)->editor->update.setValue((So@Gui@ColorEditor::UpdateFrequency) freq); 479} 480 481/*! 482 Returns the update-frequency setting. 483 484 \sa So@Gui@ColorEditor::UpdateFrequency 485*/ 486 487So@Gui@ColorEditor::UpdateFrequency 488So@Gui@ColorEditor::getUpdateFrequency(void) const 489{ 490 assert(PRIVATE(this)->editor != NULL); 491 return (So@Gui@ColorEditor::UpdateFrequency) PRIVATE(this)->editor->update.getValue(); 492} 493 494SoGuiColorEditor * 495So@Gui@ColorEditor::getEditor(void) const 496{ 497 return PRIVATE(this)->editor; 498} 499 500// ************************************************************************* 501 502const char * 503So@Gui@ColorEditor::getDefaultWidgetName(void) const 504{ 505 static const char widgetName[] = "So@Gui@ColorEditor"; 506 return widgetName; 507} 508 509const char * 510So@Gui@ColorEditor::getDefaultTitle(void) const 511{ 512 static const char title[] = "ColorEditor"; 513 return title; 514} 515 516const char * 517So@Gui@ColorEditor::getDefaultIconTitle(void) const 518{ 519 static const char iconTitle[] = "ColEd"; 520 return iconTitle; 521} 522 523// ************************************************************************* 524// ColorEditorComponent 525// ************************************************************************* 526 527const char * 528ColorEditorComponent::superscene[] = 529{ 530 "#Inventor V2.1 ascii", 531 "", 532 "Separator {", 533 " DirectionalLight { direction 0 0 -1 color 1 1 1 intensity 0.8 }", 534 " OrthographicCamera { }", 535 " DEF viewportfix SoGuiViewportFix { }", 536 " Material { ambientColor 0.8 0.8 0.8 }", 537 "}", 538 NULL 539}; 540 541// ************************************************************************* 542 543void 544ColorEditorComponent::invokeColorChangeCallbacks(void) 545{ 546 int i; 547 for ( i = 0; i < this->callbacks.getLength(); i += 2 ) { 548 So@Gui@ColorEditorCB * callback = (So@Gui@ColorEditorCB *) this->callbacks[i]; 549 void * closure = this->callbacks[i+1]; 550 callback(closure, &this->editor->color.getValue()); 551 } 552} 553 554SbBool 555ColorEditorComponent::colorsEqual(void) 556{ 557 SbColor attachmentColor; 558 switch ( this->attachment ) { 559 case SFCOLOR: 560 assert(this->sfcolor != NULL); 561 attachmentColor = this->sfcolor->getValue(); 562 break; 563 case MFCOLOR: 564 assert(this->mfcolor != NULL); 565 attachmentColor = this->mfcolor->operator[](this->mfindex); 566 break; 567 case MFUINT32: 568 assert(this->mfcolor != NULL); 569 do { 570 float transparency = 0.0f; 571 attachmentColor.setPackedValue(this->mfuint32->operator[](this->mfindex), transparency); 572 } while ( FALSE ); 573 break; 574 case DETACHED: 575 default: 576 return TRUE; 577 } 578 return (attachmentColor == this->editor->color.getValue()) ? TRUE : FALSE; 579} 580 581void 582ColorEditorComponent::attachment_update_cb(void * closure, SoSensor * sensor) 583{ 584 assert(closure != NULL); 585 ColorEditorComponent * me = (ColorEditorComponent *) closure; 586 if ( me->colorsEqual() ) return; 587 588 switch ( me->attachment ) { 589 case SFCOLOR: 590 assert(me->sfcolor != NULL); 591 me->editor->color.setValue(me->sfcolor->getValue()); 592 break; 593 case MFCOLOR: 594 assert(me->mfcolor != NULL); 595 me->editor->color.setValue(me->mfcolor->operator[](me->mfindex)); 596 break; 597 case MFUINT32: 598 assert(me->mfcolor != NULL); 599 do { 600 SbColor color; 601 float transparency = 0.0f; 602 color.setPackedValue(me->mfuint32->operator[](me->mfindex), transparency); 603 me->editor->color.setValue(color); 604 } while ( FALSE ); 605 break; 606 case DETACHED: 607 default: 608 break; 609 } 610} 611 612void 613ColorEditorComponent::editor_update_cb(void * closure, SoSensor * sensor) 614{ 615 assert(closure != NULL); 616 ColorEditorComponent * me = (ColorEditorComponent *) closure; 617 if ( me->colorsEqual() ) return; 618 619 SbColor color = me->editor->color.getValue(); 620 621 switch ( me->attachment ) { 622 case SFCOLOR: 623 assert(me->sfcolor != NULL); 624 me->sfcolor->setValue(color); 625 break; 626 case MFCOLOR: 627 assert(me->mfcolor != NULL); 628 me->mfcolor->set1Value(me->mfindex, color); 629 break; 630 case MFUINT32: 631 assert(me->mfuint32 != NULL); 632 me->mfuint32->set1Value(me->mfindex, color.getPackedValue()); 633 break; 634 case DETACHED: 635 default: 636 break; 637 } 638 639 if ( me->editor->update.getValue() == SoGuiColorEditor::CONTINUOUS ) 640 me->invokeColorChangeCallbacks(); 641} 642 643// ************************************************************************* 644 645#undef PRIVATE 646#undef PUBLIC 647 648