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