1 /*
2 * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
3 *
4 * Copyright (C) 2004, 2005, 2007, 2008 Jan Reucker (original author)
5 * Copyright (C) 2008 Jens Wilhelm Wulf
6 * 2012 Joel Lienard
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 */
23
24
25 #include "../i18n.h"
26 #include "crrc_dialog.h"
27
28 #include <iostream>
29
30 /**
31 * Constants for unified dialog style.
32 */
33 const int DLG_DEF_SPACE = 10;
34 const int DLG_DEF_BUTTON_WIDTH = 70;
35 const int DLG_DEF_BUTTON_HEIGHT = 25;
36 const int DLG_CHECK_W = 15;
37 const int DLG_CHECK_H = 15;
38
39 #define DLG_COLOR_SHIFT 0.1
40
41 /** \brief A list of all currently existing CRRCDialogs.
42 *
43 * This vector keeps a pointer to each instance of CRRCDialog.
44 * We need it to determine the dialog which shall receive
45 * a click after a button callback was activated. PLIB only
46 * provides a pointer to the actual button, but we need
47 * a pointer to the dialog which contains the button.
48 */
49 std::vector<CRRCDialog*> CRRCDialog::instances;
50
51
52 // Prototypes for this file
53 static void CRRCDialogOKCallback(puObject* obj);
54 static void CRRCDialogCancelCallback(puObject* obj);
55 static void CRRCDialogCallback(puObject *obj);
56
57
58 /** \brief Construct the dialog frame and up to two buttons.
59 *
60 * The created dialog will be at least big enough to hold
61 * two buttons and can be enlarged by providing the
62 * width and height arguments.
63 * \param width Dialog's width (must be greater than the space
64 * occupied by the buttons to take effect)
65 * \param height Dialog's height (must be greater than the space
66 * occupied by the buttons to take effect)
67 * \param style Can be CRRC_DIALOG_OK (dialog has an OK button),
68 * CRRC_DIALOG_CANCEL (dialog has a Cancel button)
69 * or both (bitwise OR'ed, which also
70 * is the default).
71 */
CRRCDialog(int width,int height,int style)72 CRRCDialog::CRRCDialog(int width, int height, int style)
73 : puDialogBox(10, 10),
74 butCancel(NULL), butOK(NULL)
75 {
76 // calculate sizes and position
77 int min_width, min_height;
78 int window_width, window_height;
79
80 // Calculate minimum sizes:
81 // one button and two spaces high
82 min_height = 2 * DLG_DEF_SPACE + DLG_DEF_BUTTON_HEIGHT;
83 // two buttons and 3 spaces wide
84 min_width = 2 * DLG_DEF_BUTTON_WIDTH + 3 * DLG_DEF_SPACE;
85
86 width = (min_width > width) ? min_width : width;
87 height = (min_height > height) ? min_height : height;
88
89 // Place message box on screen: horiz. centered, 1/3 from top
90 puGetWindowSize(&window_width, &window_height);
91 setPosition(window_width/2 - width/2,
92 window_height*2/3 - height/2);
93
94 // Create the dialog's frame
95 dlgFrame = new puFrame(0, 0, width, height);
96
97 // Just make sure nothing weird happens if anyone calls this
98 // ctor with an invalid style parameter --> fall back to default
99 if (!(style & CRRC_DIALOG_OK) && !(style & CRRC_DIALOG_CANCEL))
100 {
101 style = CRRC_DIALOG_OK | CRRC_DIALOG_CANCEL;
102 }
103
104 // Create buttons if wanted
105 if (style & CRRC_DIALOG_OK)
106 {
107 butOK = new puOneShot(0, DLG_DEF_SPACE,
108 DLG_DEF_BUTTON_WIDTH,
109 DLG_DEF_SPACE + DLG_DEF_BUTTON_HEIGHT);
110 butOK->setCallback(CRRCDialogOKCallback);
111 butOK->setLegend(_("OK"));
112 butOK->setUserData(this);
113 // makeReturnDefault lets the button look ugly in SMALL_BEVELLED...
114 //butOK->makeReturnDefault(true);
115 }
116 if (style & CRRC_DIALOG_CANCEL)
117 {
118 butCancel = new puOneShot(0, DLG_DEF_SPACE,
119 DLG_DEF_BUTTON_WIDTH,
120 DLG_DEF_SPACE + DLG_DEF_BUTTON_HEIGHT);
121 butCancel->setCallback(CRRCDialogCancelCallback);
122 butCancel->setLegend(_("Cancel"));
123 butCancel->setUserData(this);
124 }
125 // Place the buttons.
126 centerButtons();
127
128 // get default dialog color and define color options
129 // to be used by other items (e.g. popup menu)
130 getColour(PUCOL_FOREGROUND, &dlgCol[0], &dlgCol[1], &dlgCol[2], &dlgCol[3]);
131 puSetColour(dlgCol1, dlgCol);
132 dlgCol1[0] += dlgCol1[0] < 1. - DLG_COLOR_SHIFT ? DLG_COLOR_SHIFT : 1. - dlgCol1[0];
133 dlgCol1[1] += dlgCol1[1] < 1. - DLG_COLOR_SHIFT ? DLG_COLOR_SHIFT : 1. - dlgCol1[1];
134 dlgCol1[2] += dlgCol1[2] < 1. - DLG_COLOR_SHIFT ? DLG_COLOR_SHIFT : 1. - dlgCol1[2];
135
136 // Set the deault dialog's callback, likely replaced by user
137 setCallback(CRRCDialogCallback);
138
139 CRRCDialog::instances.push_back(this);
140 reveal();
141 }
142
143
144 /** \brief Destroy the object.
145 *
146 * Deallocates the object's dynamic memory and removes
147 * the dialog from the internal list of all dialog instances.
148 */
~CRRCDialog()149 CRRCDialog::~CRRCDialog()
150 {
151 // Delete dialog from internal list of instances
152 std::vector<CRRCDialog*>::iterator it = CRRCDialog::instances.begin();
153 while(it != CRRCDialog::instances.end())
154 {
155 if (*it == this)
156 {
157 instances.erase(it);
158 break;
159 }
160 it++;
161 }
162
163 puDeleteObject(butOK);
164 puDeleteObject(butCancel);
165 }
166
hideOthers()167 void CRRCDialog::hideOthers()
168 {
169 std::vector<CRRCDialog*>::iterator it = CRRCDialog::instances.begin();
170 while(it != CRRCDialog::instances.end())
171 {
172 if (*it != this)
173 {
174 CRRCDialog* d = *it;
175 d->hide();
176 }
177 it++;
178 }
179 }
180 /** \brief Set visible all the dialogues
181 */
revealAll()182 void CRRCDialog::revealAll()
183 {
184 std::vector<CRRCDialog*>::iterator it = CRRCDialog::instances.begin();
185 while(it != CRRCDialog::instances.end())
186 {
187 CRRCDialog* d = *it;
188 if( !(d->isVisible() )) d->reveal();
189 it++;
190 }
191 }
192 /** \brief Set the dialog's transparency.
193 * \param t transparency: 0 (opaque) to 1 (full trasparent).
194 */
setTransparency(float t)195 void CRRCDialog::setTransparency(float t)
196 {
197 float r, g, b, a;
198 a = 1 - t;
199 dlgFrame->getColour ( PUCOL_FOREGROUND, &r, &g, &b);
200 dlgFrame->setColour ( PUCOL_FOREGROUND, r, g, b, a ) ;
201 }
202
203
204 /** \brief Set the dialog's size.
205 *
206 * This method overloads the inherited puObject::setSize() method.
207 * In addition to resizing the base object it resizes the
208 * underlying puFrame.
209 * \param w The dialog's new width.
210 * \param h The dialog's new height.
211 */
setSize(int w,int h)212 void CRRCDialog::setSize(int w, int h)
213 {
214 puDialogBox::setSize(w, h);
215 dlgFrame->setSize(w, h);
216 centerButtons();
217 }
218
219
220 /** \brief Place the dialog on screen.
221 *
222 * Make sure that the dialog isn't placed off-screen, then
223 * just delegate to our parent's setPosition method.
224 */
setPosition(int x,int y)225 void CRRCDialog::setPosition(int x, int y)
226 {
227 if (x < 0)
228 {
229 x = 0;
230 }
231 if (y < 0)
232 {
233 y = 0;
234 }
235 puDialogBox::setPosition(x, y);
236 }
237
238
239 /** \brief Re-center the dialog's buttons.
240 *
241 * If both buttons are activated, the following layout is applied:
242 * The "OK" button will be placed DLG_DEF_SPACE/2 pixels
243 * left of the dialog's horizontal center. The dialog's
244 * Cancel button will be placed DLG_DEF_SPACE/2 pixels
245 * right of the center.
246 *
247 * If only one button is activated it will be centered.
248 *
249 * In both cases, the buttons will be placed DLG_DEF_SPACE
250 * pixels from the dialog's bottom edge.
251 */
centerButtons()252 void CRRCDialog::centerButtons()
253 {
254 int w, h, ok_x, ok_y = 0, can_x, can_y = 0;
255 int buttons = 0;
256
257 puDialogBox::getSize(&w, &h);
258 if (butOK != NULL)
259 {
260 buttons++;
261 butOK->getPosition(&ok_x, &ok_y);
262 }
263 if (butCancel != NULL)
264 {
265 buttons++;
266 butCancel->getPosition(&can_x, &can_y);
267 }
268
269 switch (buttons)
270 {
271 case 1:
272 // only one button --> centered
273 ok_x = can_x = (w - DLG_DEF_BUTTON_WIDTH) / 2;
274 break;
275
276 case 2:
277 default:
278 // two buttons
279 ok_x = (w - DLG_DEF_SPACE)/2 - DLG_DEF_BUTTON_WIDTH;
280 can_x = (w + DLG_DEF_SPACE)/2;
281 break;
282 }
283
284 if (butOK != NULL)
285 {
286 butOK->setPosition(ok_x, ok_y);
287 }
288 if (butCancel != NULL)
289 {
290 butCancel->setPosition(can_x, can_y);
291 }
292 }
293
294
295 /** \brief Center the dialog on screen
296 *
297 * This method tries to place the dialog exactly in the middle of
298 * the screen.
299 *
300 * It doesn't work well, as combo boxes, when expanded upwards,
301 * seem to influence getABox -- but also getBBox, so using that doesn't help.
302 */
centerOnScreen()303 void CRRCDialog::centerOnScreen()
304 {
305 int wwidth, wheight;
306 int current_width = getABox()->max[0] - getABox()->min[0];
307 int current_height = getABox()->max[1] - getABox()->min[1];
308
309 puGetWindowSize(&wwidth, &wheight);
310 setPosition(wwidth/2 - current_width/2, wheight/2 - current_height/2);
311 }
312
313
314 /** \brief The generic dialog callback.
315 *
316 * This callback is invoked if one of the dialog's button is
317 * activated. It simply close the dialog.
318 */
CRRCDialogCallback(puObject * obj)319 void CRRCDialogCallback(puObject* obj)
320 {
321 puDeleteObject(obj);
322 }
323
324
325 /** \brief The generic OK callback.
326 *
327 * This callback is invoked if the dialog's OK button is
328 * activated. It searches for the dialog the button belongs
329 * to, sets the dialog's value to CRRC_DIALOG_OK and
330 * invokes the dialog's callback.
331 */
CRRCDialogOKCallback(puObject * obj)332 void CRRCDialogOKCallback(puObject* obj)
333 {
334 CRRCDialog *dlg = static_cast<CRRCDialog*>(obj->getUserData());
335 dlg->setValue(CRRC_DIALOG_OK);
336 dlg->invokeCallback();
337 }
338
339
340 /** \brief The generic Cancel callback.
341 *
342 * This callback is invoked if the dialog's Cancel button is
343 * activated. It searches for the dialog the button belongs
344 * to, sets the dialog's value to CRRC_DIALOG_OK and
345 * invokes the dialog's callback.
346 */
CRRCDialogCancelCallback(puObject * obj)347 void CRRCDialogCancelCallback(puObject* obj)
348 {
349 CRRCDialog *dlg = static_cast<CRRCDialog*>(obj->getUserData());
350 dlg->setValue(CRRC_DIALOG_CANCEL);
351 dlg->invokeCallback();
352 }
353
354
355 /** \brief Set the OK button legend to a different text.
356 *
357 * This method is useful if the default "OK"/"Cancel"
358 * button pair does not suit your needs, e.g. when you
359 * need "Yes"/"No" buttons.
360 */
setOKButtonLegend(const char * text)361 void CRRCDialog::setOKButtonLegend(const char *text)
362 {
363 butOK->setLegend(text);
364 }
365
366
367 /** \brief Set the Cancel button legend to a different text.
368 *
369 * This method is useful if the default "OK"/"Cancel"
370 * button pair does not suit your needs, e.g. when you
371 * need "Yes"/"No" buttons.
372 */
setCancelButtonLegend(const char * text)373 void CRRCDialog::setCancelButtonLegend(const char *text)
374 {
375 butCancel->setLegend(text);
376 }
377
378
379 /** \brief Get a pointer to the top-most dialog on screen.
380 *
381 * This method returns a pointer to the top-most dialog
382 * on screen, or NULL if no dialog is visible.
383 */
getToplevel()384 CRRCDialog* CRRCDialog::getToplevel()
385 {
386 CRRCDialog *ret = NULL;
387
388 if (instances.size() > 0)
389 {
390 ret = instances.back();
391 }
392
393 return ret;
394 }
395
396
397 /** \brief Determine if the dialog has an OK button.
398 *
399 *
400 */
hasOKButton()401 bool CRRCDialog::hasOKButton()
402 {
403 bool ret = false;
404
405 if (butOK != NULL)
406 {
407 ret = true;
408 }
409
410 return ret;
411 }
412
413 /** \brief Determine if the dialog has a Cancel button.
414 *
415 *
416 */
hasCancelButton()417 bool CRRCDialog::hasCancelButton()
418 {
419 bool ret = false;
420
421 if (butCancel != NULL)
422 {
423 ret = true;
424 }
425
426 return ret;
427 }
428
429 /** \brief Lock the dialog.
430 *
431 * Greys out the buttons so the user can't close the
432 * dialog.
433 */
lock()434 void CRRCDialog::lock()
435 {
436 if (butOK != NULL)
437 {
438 butOK->greyOut();
439 }
440 if (butCancel != NULL)
441 {
442 butCancel->greyOut();
443 }
444 }
445
446 /** \brief Unlock the dialog.
447 *
448 * Reactivates the dialog's buttons.
449 */
unlock()450 void CRRCDialog::unlock()
451 {
452 if (butOK != NULL)
453 {
454 butOK->activate();
455 }
456 if (butCancel != NULL)
457 {
458 butCancel->activate();
459 }
460 }
461