1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class RadioGroupKit RadioGroupKit.h
35   \brief The RadioGroupKit class is a radiobutton group.
36 
37   This is a 3D user interface component.
38 
39   Example usage:
40 
41   \code
42   #include <Inventor/Qt/SoQt.h>
43   #include <Inventor/Qt/viewers/SoQtExaminerViewer.h>
44   #include <Inventor/nodes/SoSeparator.h>
45   #include "RadioGroupKit.h"
46 
47 
48   static void
49   myRadioButtonCallback(void * userdata, SoSensor * sensor)
50   {
51     SoFieldSensor * fieldsensor = (SoFieldSensor *)sensor;
52     SoSFInt32 * button = (SoSFInt32 *)fieldsensor->getAttachedField();
53     SoDebugError::postInfo("myRadioButtonCallback",
54                            "Button '%d' clicked",
55                            button->getValue());
56   }
57 
58   int
59   main(int argc, char ** argv)
60   {
61     QWidget * mainwin = SoQt::init(argc, argv, argv[0]);
62     RadioGroupKit::initClass();
63 
64     SoSeparator * root = new SoSeparator;
65     root->ref();
66 
67     // Creating a basic radiobutton group
68     RadioGroupKit * radiogroup = new RadioGroupKit;
69     radiogroup->labels.set1Value(0,"Item #1");
70     radiogroup->labels.set1Value(1,"Item #2");
71     radiogroup->labels.set1Value(2,"Item #3");
72     root->addChild(radiogroup);
73 
74     // Setting up a callback sensor for the radiobuttons
75     SoFieldSensor * radioGroupSensor =
76       new SoFieldSensor(myRadioButtonCallback, NULL);
77     radioGroupSensor->attach(&radiogroup->selected);
78 
79     // Setting up scene
80     SoQtExaminerViewer * viewer = new SoQtExaminerViewer(mainwin);
81     viewer->setSceneGraph(root);
82     viewer->show();
83 
84     SoQt::show(mainwin);
85     SoQt::mainLoop();
86 
87     delete viewer;
88     root->unref();
89 
90     return 0;
91   }
92   \endcode
93 */
94 
95 #include <Inventor/nodes/SoCone.h>
96 #include <Inventor/nodes/SoCube.h>
97 #include <Inventor/nodes/SoTranslation.h>
98 #include <Inventor/nodes/SoSeparator.h>
99 #include <Inventor/nodes/SoTransformSeparator.h>
100 #include <Inventor/nodes/SoMaterial.h>
101 #include <Inventor/nodes/SoText2.h>
102 #include <Inventor/nodes/SoBaseColor.h>
103 #include <Inventor/nodes/SoSphere.h>
104 #include <Inventor/nodes/SoSelection.h>
105 #include <Inventor/nodes/SoShapeHints.h>
106 #include <Inventor/nodes/SoSwitch.h>
107 #include <Inventor/nodes/SoFont.h>
108 #include <Inventor/nodes/SoEventCallback.h>
109 #include <Inventor/events/SoEvent.h>
110 #include <Inventor/events/SoMouseButtonEvent.h>
111 #include <Inventor/nodes/SoTransformation.h>
112 #include <Inventor/lists/SbPList.h>
113 #include <Inventor/nodekits/SoShapeKit.h>
114 #include <Inventor/nodes/SoMarkerSet.h>
115 
116 #include <Inventor/actions/SoGetBoundingBoxAction.h>
117 #include <Inventor/sensors/SoFieldSensor.h>
118 #include <Inventor/errors/SoDebugError.h>
119 
120 #include "RadioGroupKit.h"
121 
122 SO_KIT_SOURCE(RadioGroupKit);
123 
124 static const char RADIOBULLET_radiobulletgeometry[] =
125 "#Inventor V2.1 ascii\n"
126 "\n"
127 "DEF RadioButtons Separator {\n"
128 "\n"
129 " DEF BulletColorActive BaseColor { rgb 0 1 1 }\n"
130 " DEF BulletColor BaseColor { rgb 1 1 1 }\n"
131 "\n"
132 " DEF RadioBulletActive TransformSeparator {\n"
133 "   USE BulletColorActive\n"
134 "   Cube { width 0.2 height 0.2 depth 0.2 }\n"
135 " }\n"
136 " \n"
137 " DEF RadioBullet TransformSeparator {\n"
138 "   USE BulletColor\n"
139 "   Cube { width 0.2 height 0.2 depth 0.2 }\n"
140 " }\n"
141 "}\n"
142 "\n";
143 
144 
145 class RadioGroupKitP {
146 public:
RadioGroupKitP(RadioGroupKit * master)147   RadioGroupKitP(RadioGroupKit * master) {
148     this->master = master;
149   }
150 
151   RadioGroupKit * master;
152   int buttonCounter;
153   SbPList *buttonList;
154   SoSelection *root;
155   SoFieldSensor * labelSensor;
156   SoSelection * groupSelection;
157 
158   void removeAllRadioButtons();
159   void reconstructRadioButtons();
160   void addRadioButton(SbString label);
161   static void buttonClickedCallback(void *userData, SoPath *node);
162   static void radioButtonSensor(void *classObject, SoSensor *sensor);
163 
164 
165 };
166 
167 #define PRIVATE(p) (p->pimpl)
168 #define PUBLIC(p) (p->master)
169 
170 
RadioGroupKit(void)171 RadioGroupKit::RadioGroupKit(void)
172 {
173   SO_KIT_CONSTRUCTOR(RadioGroupKit);
174 
175   PRIVATE(this) = new RadioGroupKitP(this);
176 
177   if (SO_KIT_IS_FIRST_INSTANCE()) {
178     SoInput input;
179     input.setBuffer((void *) RADIOBULLET_radiobulletgeometry, strlen(RADIOBULLET_radiobulletgeometry));
180     SoDB::readAll(&input);
181   }
182 
183   SO_KIT_ADD_CATALOG_ENTRY(radioGroupRoot,SoSelection,FALSE,this, "",TRUE);
184   SO_KIT_ADD_CATALOG_ENTRY(RadioBulletActive, SoTransformSeparator, TRUE, radioGroupRoot, , TRUE);
185   SO_KIT_ADD_CATALOG_ENTRY(RadioBullet, SoTransformSeparator, TRUE, radioGroupRoot, , TRUE);
186   SO_KIT_ADD_CATALOG_ENTRY(BulletColorActive, SoBaseColor, TRUE, radioGroupRoot, , TRUE);
187   SO_KIT_ADD_CATALOG_ENTRY(BulletColor, SoBaseColor, TRUE, radioGroupRoot, , TRUE);
188 
189   SO_KIT_INIT_INSTANCE();
190 
191   this->setPartAsDefault("RadioBullet", "RadioBullet");
192   this->setPartAsDefault("RadioBulletActive", "RadioBulletActive");
193   this->setPartAsDefault("BulletColor", "BulletColor");
194   this->setPartAsDefault("BulletColorActive", "BulletColorActive");
195 
196   PRIVATE(this)->buttonCounter = 0;
197   PRIVATE(this)->buttonList = new SbPList(1);
198 
199   PRIVATE(this)->labelSensor = new SoFieldSensor(PRIVATE(this)->radioButtonSensor,PRIVATE(this));
200   PRIVATE(this)->labelSensor->setPriority(0);
201   PRIVATE(this)->labelSensor->attach(&labels);
202 
203   PRIVATE(this)->root = new SoSelection;
204   this->setPart("radioGroupRoot", PRIVATE(this)->root);
205 
206   // General SoSelector node for this radiogroup
207   PRIVATE(this)->root->addSelectionCallback(&PRIVATE(this)->buttonClickedCallback, PRIVATE(this));
208 
209 }
210 
211 
~RadioGroupKit()212 RadioGroupKit::~RadioGroupKit()
213 {
214   delete PRIVATE(this)->labelSensor;
215   delete PRIVATE(this)->buttonList;
216   delete PRIVATE(this);
217 }
218 
219 
220 void
initClass(void)221 RadioGroupKit::initClass(void)
222 {
223   SO_KIT_INIT_CLASS(RadioGroupKit, SoInteractionKit, "InteractionKit");
224 }
225 
226 
227 SbBool
affectsState() const228 RadioGroupKit::affectsState() const
229 {
230   return(FALSE);
231 }
232 
233 
234 void
buttonClickedCallback(void * userData,SoPath * path)235 RadioGroupKitP::buttonClickedCallback(void * userData, SoPath * path)
236 {
237 
238   RadioGroupKitP * radioGroupP = (RadioGroupKitP *) userData;
239 
240   // Fetch clicked node object
241   SoNode * tail = (SoNode *) ((SoFullPath*)path)->getNodeFromTail(0);
242 
243   // Bullet or the text?
244   if(tail->isOfType(SoText2::getClassTypeId()))
245     tail = (SoNode *) ((SoFullPath*)path)->getNodeFromTail(1);
246   else
247     tail = (SoNode *) ((SoFullPath*)path)->getNodeFromTail(2);
248 
249   int index = (radioGroupP->root->findChild(tail))/2;
250   assert(index != -1); // Failsafe
251 
252   SoTransformSeparator * button = (SoTransformSeparator *) radioGroupP->buttonList->get(index);
253   for(int i=0;i<radioGroupP->buttonList->getLength();++i){
254     SoTransformSeparator *sep = (SoTransformSeparator *) radioGroupP->buttonList->get(i);
255     SoBaseColor *color = (SoBaseColor *) sep->getChild(0);
256     SoBaseColor *bulletColor =  (SoBaseColor*) SO_GET_PART(PUBLIC(radioGroupP), "BulletColor",SoBaseColor);
257     color->rgb = bulletColor->rgb;
258   }
259 
260   // Highlight selected button
261   SoBaseColor * color = (SoBaseColor *) button->getChild(0);
262   SoBaseColor * bulletColor =  (SoBaseColor*) SO_GET_PART(PUBLIC(radioGroupP), "BulletColorActive",SoBaseColor);
263   color->rgb = bulletColor->rgb;
264 
265   // Update selected field
266   PUBLIC(radioGroupP)->selected.setValue(index);
267 
268 
269 #if 0 // debug
270   SoDebugError::postInfo("RadioGroupKitP::buttonClickedCallback","RadioGroupKit::selected = %d", index);
271 #endif // debug
272 
273 }
274 
275 void
radioButtonSensor(void * classObject,SoSensor * sensor)276 RadioGroupKitP::radioButtonSensor(void * classObject, SoSensor * sensor)
277 {
278 
279   RadioGroupKitP * pimpl = (RadioGroupKitP *) classObject;
280 
281   // Button is added
282   if(pimpl->buttonCounter < PUBLIC(pimpl)->labels.getNum()){
283     SbString newlabel = PUBLIC(pimpl)->labels[pimpl->buttonCounter];
284     pimpl->addRadioButton(newlabel);
285     pimpl->buttonCounter++;
286 
287   // Button is removed
288   } else if(pimpl->buttonCounter > PUBLIC(pimpl)->labels.getNum()){
289     pimpl->removeAllRadioButtons();
290     pimpl->reconstructRadioButtons();
291 
292   // Assuming a button is modified
293   } else {
294     pimpl->removeAllRadioButtons();
295     pimpl->reconstructRadioButtons();
296   }
297 
298 }
299 
300 
301 void
removeAllRadioButtons()302 RadioGroupKitP::removeAllRadioButtons()
303 {
304 
305   // Remove all registrated buttons for list
306   for(int i=0;i<this->buttonList->getLength();++i)
307     this->buttonList->remove(i);
308 
309   this->root->removeAllChildren();
310   this->buttonCounter = 0;
311 
312 }
313 
314 
315 void
reconstructRadioButtons()316 RadioGroupKitP::reconstructRadioButtons()
317 {
318   for(int i=0;i<PUBLIC(this)->labels.getNum(); ++i){
319     this->addRadioButton(PUBLIC(this)->labels[i]);
320     this->buttonCounter++;
321   }
322 }
323 
324 
325 void
addRadioButton(SbString label)326 RadioGroupKitP::addRadioButton(SbString label)
327 {
328 
329   SoSeparator * buttonRoot = new SoSeparator;
330   SoShapeHints * hints = new SoShapeHints;
331   hints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE;
332   hints->shapeType = SoShapeHints::SOLID;
333 
334   SoFont * font = new SoFont;
335   font->name.setValue("Times-Roman");
336   font->size.setValue(24.0);
337 
338   buttonRoot->addChild(hints);
339   buttonRoot->addChild(font);
340 
341   SoText2 * buttonLabel = new SoText2;
342   buttonLabel->string = label;
343 
344   SoTransformSeparator * sep;
345 
346   // Make the first added button selected as default
347   if(buttonCounter == 0){
348     sep = (SoTransformSeparator*) SO_GET_PART(PUBLIC(this), "RadioBulletActive",SoTransformSeparator)->copy();
349     PUBLIC(this)->selected.setValue(0);
350   } else {
351     sep = (SoTransformSeparator*) SO_GET_PART(PUBLIC(this), "RadioBullet",SoTransformSeparator)->copy();
352   }
353 
354   this->buttonList->set(buttonCounter,sep);    // Save the separator node for later changes @ callback
355   buttonRoot->addChild(sep);
356 
357   // Fetch bullet boundingbox
358   SbViewportRegion dummyViewport(100,100);
359   SoGetBoundingBoxAction bboxAction(dummyViewport);
360   if(buttonCounter == 0)
361     bboxAction.apply(sep);
362   else
363     bboxAction.apply((SoTransformSeparator *) buttonList->get(buttonCounter-1)); // Previous radiobutton
364 
365   SbBox3f bbox = bboxAction.getBoundingBox();
366   SoTranslation *spacingX = new SoTranslation;
367   SoTranslation *spacingY = new SoTranslation;
368   float dx,dy,dz;
369   bbox.getSize(dx,dy,dz);
370   spacingX->translation.setValue(dx,-dy/2,0);
371   spacingY->translation.setValue(0,-dy*2,0);
372 
373   buttonRoot->addChild(spacingX);
374   buttonRoot->addChild(buttonLabel);
375 
376   this->root->addChild(buttonRoot);
377   this->root->addChild(spacingY);
378 
379 }
380