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