1 /* MessageBox.cpp
2 * Copyright (C) 2018, 2019 Sven Jähnichen
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "MessageBox.hpp"
19
20 namespace BWidgets
21 {
MessageBox()22 MessageBox::MessageBox () : MessageBox (0.0, 0.0, 0.0, 0.0, "messagebox", "", "MessageBox") {}
23
MessageBox(const double x,const double y,const double width,const double height,const std::string & text,std::vector<std::string> buttons)24 MessageBox::MessageBox (const double x, const double y, const double width, const double height,
25 const std::string& text, std::vector<std::string> buttons) :
26 MessageBox (x, y, width, height, text, "", text, buttons) {}
27
MessageBox(const double x,const double y,const double width,const double height,const std::string & title,const std::string & text,std::vector<std::string> buttons)28 MessageBox::MessageBox (const double x, const double y, const double width, const double height,
29 const std::string& title, const std::string& text, std::vector<std::string> buttons) :
30 MessageBox (x, y, width, height, title, title, text, buttons) {}
31
MessageBox(const double x,const double y,const double width,const double height,const std::string & name,const std::string & title,const std::string & text,std::vector<std::string> buttonLabels)32 MessageBox::MessageBox (const double x, const double y, const double width, const double height,
33 const std::string& name, const std::string& title, const std::string& text, std::vector<std::string> buttonLabels) :
34 ValueWidget (x, y, width, height, name, 0.0),
35 titleBox (0, 0, 0, 0, name + BWIDGETS_DEFAULT_MESSAGEBOX_TITLE_NAME, ""),
36 textBox (0, 0, 0, 0, name + BWIDGETS_DEFAULT_MESSAGEBOX_TEXT_NAME, ""),
37 okButton (0, 0, BWIDGETS_DEFAULT_BUTTON_WIDTH, BWIDGETS_DEFAULT_BUTTON_HEIGHT, name + BWIDGETS_DEFAULT_MESSAGEBOX_BUTTON_NAME, "OK", 0.0),
38 buttons ()
39 {
40 // Title
41 setTitle (title);
42 titleBox.setClickable (false);
43 add (titleBox);
44
45 // Text
46 setText (text);
47 textBox.setClickable (false);
48 add (textBox);
49
50 // Buttons
51 if (!buttonLabels. empty ()) addButtons (buttonLabels);
52
53 // Or at least the OK button
54 if (buttons.empty ())
55 {
56 okButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, MessageBox::redirectPostValueChanged);
57 buttons.push_back (&okButton);
58 add (okButton);
59 }
60
61 background_ = BWIDGETS_DEFAULT_MENU_BACKGROUND;
62 border_ = BWIDGETS_DEFAULT_MENU_BORDER;
63 setDraggable (true);
64 }
65
MessageBox(const MessageBox & that)66 MessageBox::MessageBox (const MessageBox& that) : ValueWidget (that)
67 {
68 titleBox = that.titleBox;
69 add (titleBox);
70 textBox = that.textBox;
71 add (textBox);
72
73 for (TextButton* b : that.buttons)
74 {
75 addButton (b->getLabel ()->getText ());
76 }
77
78 okButton = that.okButton;
79 if (buttons.empty ())
80 {
81 okButton.getLabel ()->setTextColors(BColors::darks);
82 okButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, MessageBox::redirectPostValueChanged);
83 buttons.push_back (&okButton);
84 add (okButton);
85 }
86
87 update ();
88 }
89
~MessageBox()90 MessageBox::~MessageBox ()
91 {
92 while (!buttons.empty ())
93 {
94 TextButton* b = buttons.back ();
95 if (b && (b != &okButton)) delete b;
96 else release (b);
97 buttons.pop_back ();
98 }
99 }
100
operator =(const MessageBox & that)101 MessageBox& MessageBox::operator= (const MessageBox& that)
102 {
103 titleBox = that.titleBox;
104 textBox = that.textBox;
105 okButton = that.okButton;
106
107 // Clean buttons first
108 while (!buttons.empty ())
109 {
110 TextButton* b = buttons.back ();
111 if (b && (b != &okButton)) delete b;
112 else release (b);
113 buttons.pop_back ();
114 }
115
116 // Hard copy buttons
117 for (TextButton* b : that.buttons)
118 {
119 addButton (b->getLabel ()->getText ());
120 }
121
122 // At least an OK button
123 if (buttons.empty ())
124 {
125 okButton.getLabel ()->setTextColors(BColors::darks);
126 okButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, MessageBox::redirectPostValueChanged);
127 buttons.push_back (&okButton);
128 add (okButton);
129 }
130
131 Widget::operator= (that);
132 return *this;
133 }
134
clone() const135 Widget* MessageBox::clone () const {return new MessageBox (*this);}
136
setTitle(const std::string & title)137 void MessageBox::setTitle (const std::string& title) {titleBox.setText (title);}
138
getTitle() const139 std::string MessageBox::getTitle () const {return titleBox.getText ();}
140
setText(const std::string & text)141 void MessageBox::setText (const std::string& text) {textBox.setText (text);}
142
getText() const143 std::string MessageBox::getText () const {return textBox.getText ();}
144
addButton(const std::string & label)145 void MessageBox::addButton (const std::string& label)
146 {
147 TextButton* b = new TextButton(0, 0, BWIDGETS_DEFAULT_BUTTON_WIDTH, BWIDGETS_DEFAULT_BUTTON_HEIGHT,
148 name_ + BWIDGETS_DEFAULT_MESSAGEBOX_BUTTON_NAME, label, 0.0);
149 if (b)
150 {
151 cairo_t* cr = cairo_create (widgetSurface_);
152 cairo_text_extents_t ext = b->getLabel()->getFont()->getTextExtents (cr, label);
153 cairo_destroy (cr);
154 b->setWidth (ext.width > BWIDGETS_DEFAULT_BUTTON_WIDTH - BWIDGETS_DEFAULT_MENU_PADDING ?
155 ext.width + BWIDGETS_DEFAULT_MENU_PADDING :
156 BWIDGETS_DEFAULT_BUTTON_WIDTH);
157 b->setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, MessageBox::redirectPostValueChanged);
158 buttons.push_back (b);
159 add (*b);
160 }
161 }
162
addButtons(std::vector<std::string> labels)163 void MessageBox::addButtons (std::vector<std::string> labels)
164 {
165 for (std::string label : labels) addButton (label);
166 }
167
removeButton(const std::string & label)168 void MessageBox::removeButton (const std::string& label)
169 {
170 for (std::vector<TextButton*>::iterator it = buttons.begin (); it != buttons.end (); ++it)
171 {
172 TextButton* b = (TextButton*) *it;
173 if (b && (b->getLabel ()->getText () == label))
174 {
175 if (b != &okButton) delete b;
176 buttons.erase (it);
177 return;
178 }
179 }
180 }
181
getButtonValue(const std::string & label) const182 double MessageBox::getButtonValue (const std::string& label) const
183 {
184 double nr = 1;
185 for (TextButton* b : buttons)
186 {
187 if (b)
188 {
189 if (b->getLabel ()->getText () == label) return nr;
190 nr++;
191 }
192 }
193 return 0;
194 }
195
getButtonText(const double value) const196 std::string MessageBox::getButtonText (const double value) const
197 {
198 size_t v = value;
199 if ((v < 1) || (v > buttons.size ())) return "";
200 if (!buttons[v - 1]) return "";
201 return buttons[v - 1]->getLabel ()-> getText ();
202 }
203
setTextColors(const BColors::ColorSet & colorset)204 void MessageBox::setTextColors (const BColors::ColorSet& colorset) {textBox.setTextColors (colorset);}
205
getTextColors()206 BColors::ColorSet* MessageBox::getTextColors () {return textBox.getTextColors ();}
207
setFont(const BStyles::Font & font)208 void MessageBox::setFont (const BStyles::Font& font) {textBox.setFont (font);}
209
getFont()210 BStyles::Font* MessageBox::getFont () {return textBox.getFont ();}
211
applyTheme(BStyles::Theme & theme)212 void MessageBox::applyTheme (BStyles::Theme& theme) {applyTheme (theme, name_);}
213
applyTheme(BStyles::Theme & theme,const std::string & name)214 void MessageBox::applyTheme (BStyles::Theme& theme, const std::string& name)
215 {
216 Widget::applyTheme (theme, name);
217 titleBox.applyTheme (theme, name + BWIDGETS_DEFAULT_MESSAGEBOX_TITLE_NAME);
218 textBox.applyTheme (theme, name + BWIDGETS_DEFAULT_MESSAGEBOX_TEXT_NAME);
219 for (TextButton* b : buttons)
220 {
221 if (b) b->applyTheme (theme, name + BWIDGETS_DEFAULT_MESSAGEBOX_BUTTON_NAME);
222 }
223 update ();
224 }
225
update()226 void MessageBox::update ()
227 {
228 // Update super widget first
229 Widget::update ();
230
231 double width = getEffectiveWidth ();
232 double height = getEffectiveHeight ();
233
234 // Title
235 if (titleBox.getText () != "")
236 {
237 titleBox.setWidth (width > 2 * BWIDGETS_DEFAULT_MENU_PADDING ? width - 2 * BWIDGETS_DEFAULT_MENU_PADDING : 0);
238 titleBox.setHeight
239 (
240 height > 3 * BWIDGETS_DEFAULT_MENU_PADDING + BWIDGETS_DEFAULT_BUTTON_HEIGHT ?
241 height - (3 * BWIDGETS_DEFAULT_MENU_PADDING + BWIDGETS_DEFAULT_BUTTON_HEIGHT) :
242 0
243 );
244 titleBox.getFont ()->setFontWeight (CAIRO_FONT_WEIGHT_BOLD);
245 double titleheight = titleBox.getTextBlockHeight(titleBox.getTextBlock ());
246 titleBox.setHeight
247 (
248 titleheight < height - (3 * BWIDGETS_DEFAULT_MENU_PADDING + BWIDGETS_DEFAULT_BUTTON_HEIGHT) ?
249 titleheight :
250 (
251 height > (3 * BWIDGETS_DEFAULT_MENU_PADDING + BWIDGETS_DEFAULT_BUTTON_HEIGHT) ?
252 height - (3 * BWIDGETS_DEFAULT_MENU_PADDING + BWIDGETS_DEFAULT_BUTTON_HEIGHT) :
253 0
254 )
255 );
256 titleBox.moveTo (BWIDGETS_DEFAULT_MENU_PADDING, BWIDGETS_DEFAULT_MENU_PADDING);
257 }
258 else
259 {
260 titleBox.setWidth (0);
261 titleBox.setHeight (0);
262 titleBox.moveTo (0, 0);
263 }
264
265 // Text
266 if (textBox.getText () != "")
267 {
268 double titleheight = titleBox.getHeight () + BWIDGETS_DEFAULT_MENU_PADDING;
269 textBox.setWidth (width > 2 * BWIDGETS_DEFAULT_MENU_PADDING ? width - 2 * BWIDGETS_DEFAULT_MENU_PADDING : 0);
270 textBox.setHeight
271 (
272 height > (3 * BWIDGETS_DEFAULT_MENU_PADDING + BWIDGETS_DEFAULT_BUTTON_HEIGHT) + titleheight ?
273 height - ((3 * BWIDGETS_DEFAULT_MENU_PADDING + BWIDGETS_DEFAULT_BUTTON_HEIGHT) + titleheight) :
274 0
275 );
276 textBox.moveTo (BWIDGETS_DEFAULT_MENU_PADDING, BWIDGETS_DEFAULT_MENU_PADDING + titleheight);
277 }
278 else
279 {
280 textBox.setWidth (0);
281 textBox.setHeight (0);
282 textBox.moveTo (0, 0);
283 }
284
285 // Buttons
286 // Calculate total width
287 int nrbuttons = 0;
288 double totalbuttonwidth = 0.0;
289 for (TextButton* b : buttons)
290 {
291 if (b)
292 {
293 ++nrbuttons;
294 totalbuttonwidth += b->getWidth ();
295 }
296 }
297
298 // Calculate spaces and offset
299 double buttonspace = (width - totalbuttonwidth) / (nrbuttons + 1);
300 if (buttonspace < BWIDGETS_DEFAULT_MENU_PADDING) buttonspace = BWIDGETS_DEFAULT_MENU_PADDING;
301 double buttonxpos = (totalbuttonwidth + (nrbuttons + 1) * buttonspace < width ?
302 buttonspace :
303 width / 2 - (totalbuttonwidth + (nrbuttons - 1) * buttonspace) / 2);
304
305 // Rearrange
306 for (TextButton* b : buttons)
307 {
308 if (b)
309 {
310 b->moveTo (buttonxpos, height - BWIDGETS_DEFAULT_MENU_PADDING - BWIDGETS_DEFAULT_BUTTON_HEIGHT);
311 buttonxpos = buttonxpos + buttonspace + b->getWidth ();
312 }
313 }
314 }
315
redirectPostValueChanged(BEvents::Event * event)316 void MessageBox::redirectPostValueChanged (BEvents::Event* event)
317 {
318 if (event && (event->getEventType () == BEvents::EventType::VALUE_CHANGED_EVENT) && event->getWidget ())
319 {
320 BEvents::ValueChangedEvent* ev = (BEvents::ValueChangedEvent*) event;
321 TextButton* w = (TextButton*) ev->getWidget ();
322 if (w->getParent ())
323 {
324 std::string label = w->getLabel ()->getText ();
325 MessageBox* p = (MessageBox*) w->getParent ();
326 if (p->getMainWindow ())
327 {
328 // Button pressed ?
329 if (w->getValue ())
330 {
331 double v = p->getButtonValue (label);
332
333 // Emit value changed event and close (hide) message box
334 if (v)
335 {
336 p->setValue (v);
337 p->postCloseRequest ();
338 }
339 }
340 }
341 }
342 }
343 }
344
345 }
346