1 // ----------------------------------------------------------------------------
2 // flmisc.cxx
3 //
4 // Copyright (C) 2008-2010
5 // Stelios Bounanos, M0GLD
6 //
7 // This file is part of fldigi.
8 //
9 // Fldigi is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // Fldigi is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
21 // ----------------------------------------------------------------------------
22
23 #include <config.h>
24
25 #include <string>
26 #include <cstdlib>
27 #include <cstring>
28 #include <cstdarg>
29 #include <iostream>
30
31 #include <FL/Fl.H>
32 #include <FL/Enumerations.H>
33 #include <FL/Fl_Menu_Item.H>
34 #include <FL/Fl_Tooltip.H>
35
36 #include <FL/Fl_Pixmap.H>
37 #include <FL/Fl_Window.H>
38 #include <FL/Fl_Group.H>
39 #include <FL/Fl_Box.H>
40 #include <FL/Fl_Dial.H>
41 #include <FL/Fl_Dial.H>
42 #include <FL/Fl_Return_Button.H>
43 #include <FL/fl_draw.H>
44
45 #include <FL/x.H>
46
47 #include "flmisc.h"
48 #include "pixmaps.h"
49
50 using namespace std;
51
quick_choice_menu(const char * title,unsigned sel,const Fl_Menu_Item * menu)52 unsigned quick_choice_menu(const char* title, unsigned sel, const Fl_Menu_Item* menu)
53 {
54 unsigned n = menu->size();
55 sel = CLAMP(sel - 1, 0, n - 1);
56 int t = Fl_Tooltip::enabled();
57 Fl_Tooltip::disable();
58 const Fl_Menu_Item* p = menu->popup(Fl::event_x(), Fl::event_y(), title, menu + sel);
59 Fl_Tooltip::enable(t);
60 return p ? p - menu + 1 : 0;
61 }
62
quick_choice(const char * title,unsigned sel,...)63 unsigned quick_choice(const char* title, unsigned sel, ...)
64 {
65 const char* item;
66 const Fl_Menu_Item* menu = NULL;
67 Fl_Menu_Item* p = NULL;
68
69 va_list ap;
70 va_start(ap, sel);
71 for (size_t n = 0; (item = va_arg(ap, const char*)); n++) {
72 if ((p = (Fl_Menu_Item*)realloc(p, (n+2) * sizeof(Fl_Menu_Item))) == NULL) {
73 free((Fl_Menu_Item*)menu);
74 va_end(ap);
75 return 0;
76 }
77 memset(p + n, 0, 2 * sizeof(Fl_Menu_Item));
78 p[n].label(item);
79 p[n+1].label(NULL);
80 menu = p;
81 }
82 va_end(ap);
83
84 sel = quick_choice_menu(title, sel, menu);
85 free(p);
86 return sel;
87 }
88
89 // Adjust and return fg color to ensure good contrast with bg
adjust_color(Fl_Color fg,Fl_Color bg)90 Fl_Color adjust_color(Fl_Color fg, Fl_Color bg)
91 {
92 Fl_Color adj;
93 unsigned max = 24;
94 while ((adj = fl_contrast(fg, bg)) != fg && max--)
95 fg = (adj == FL_WHITE) ? fl_color_average(fg, FL_WHITE, .9)
96 : fl_color_average(fg, FL_BLACK, .9);
97 return fg;
98 }
99
100 // invert colour (bg1r, bg1g, bg1b); return def if new colour does not make
101 // good contrast with bg2
adjust_color_inv(unsigned char & bg1r,unsigned char & bg1g,unsigned char & bg1b,Fl_Color bg2,Fl_Color def)102 void adjust_color_inv(unsigned char& bg1r, unsigned char& bg1g, unsigned char& bg1b,
103 Fl_Color bg2, Fl_Color def)
104 {
105 bg1r = 255 - bg1r; bg1g = 255 - bg1g; bg1b = 255 - bg1b;
106 Fl_Color adj = fl_rgb_color(bg1r, bg1g, bg1b);
107 if (fl_contrast(adj, bg2) != adj)
108 Fl::get_color((def >= 1 ? def : adj), bg1r, bg1g, bg1b);
109 }
110
111 #if !defined(__APPLE__) && !defined(__WOE32__) && USE_X
112 # include <FL/Fl_Window.H>
113 # include <FL/Fl_Pixmap.H>
114 # include <FL/fl_draw.H>
make_pixmap(Pixmap * xpm,const char ** data,int argc,char ** argv)115 void make_pixmap(Pixmap *xpm, const char **data, int argc, char** argv)
116 {
117 // We need a displayed window to provide a GC for X_CreatePixmap
118 Fl_Window w(0, 0, PACKAGE_NAME);
119 w.xclass(PACKAGE_NAME);
120 w.border(0);
121 w.show(argc, argv);
122
123 Fl_Pixmap icon(data);
124 int maxd = MAX(icon.w(), icon.h());
125 w.make_current();
126 *xpm = fl_create_offscreen(maxd, maxd);
127 w.hide();
128
129 fl_begin_offscreen(*xpm);
130 // Fl_Color(FL_BACKGROUND_COLOR);
131 // fl_rectf(0, 0, maxd, maxd);
132 icon.draw(maxd - icon.w(), maxd - icon.h());
133 fl_end_offscreen();
134 }
135 #endif
136
137 dialog_positions notify_dialog::positions[11] = {
138 {0, 50, 50}, //0
139 {0, 150, 200}, //1
140 {0, 200, 350}, //2
141 {0, 150, 50}, //3
142 {0, 150, 200}, //4
143 {0, 200, 350}, //5
144 {0, 250, 50}, //6
145 {0, 350, 200}, //7
146 {0, 450, 350}, //8
147 {0, 350, 50}, //9
148 {0, 400, 300} //10 centered on screen, all dialogs past 10
149 };
150
notify_dialog(int X,int Y)151 notify_dialog::notify_dialog(int X, int Y)
152 : Fl_Window(X, Y, 410, 103, ""), icon(10, 10, 50, 50), message(70, 25, 330, 35),
153 dial(277, 70, 23, 23), button(309, 70, 90, 23, "Close"), resize_box(399, 26, 1, 1)
154 {
155 set_non_modal();
156
157 for (dialog_number = 0; dialog_number < 10; dialog_number++)
158 if (positions[dialog_number].used == 0) break;
159 positions[dialog_number].used = 1;
160
161 position (positions[dialog_number].X, positions[dialog_number].Y);
162
163 icon.image(new Fl_Pixmap(dialog_information_48_icon));
164
165 message.type (FL_MULTILINE_OUTPUT);
166 message.box (FL_FLAT_BOX);
167 message.color (FL_BACKGROUND_COLOR);
168
169 button.callback (button_cb);
170 newx = button.x();
171
172 dial.box(FL_FLAT_BOX);
173 dial.type(FL_FILL_DIAL);
174 dial.selection_color(adjust_color(fl_lighter(FL_BACKGROUND_COLOR), FL_BACKGROUND_COLOR));
175 dial.angle1(180);
176 dial.angle2(-180);
177 dial.minimum(0.0);
178
179 user_button = (Fl_Button *)0;
180
181 xclass(PACKAGE_TARNAME);
182 resizable(resize_box);
183
184 end();
185 hide();
186 }
187
~notify_dialog()188 notify_dialog::~notify_dialog()
189 {
190 Fl::remove_timeout(dial_timer, &dial);
191 this->hide();
192 delete icon.image();
193 delete user_button;
194 positions[dialog_number].used = 0;
195 }
196
handle(int event)197 int notify_dialog::handle(int event)
198 {
199 if (event == FL_PUSH) {
200 dial.hide();
201 return Fl_Window::handle(event);
202 }
203 return Fl_Window::handle(event);
204 }
205
button_cb(Fl_Widget * w,void *)206 void notify_dialog::button_cb(Fl_Widget* w, void*)
207 {
208 w->window()->hide();
209 }
210
dial_timer(void * arg)211 void notify_dialog::dial_timer(void* arg)
212 {
213 Fl_Dial* dial = reinterpret_cast<Fl_Dial*>(arg);
214 double v = dial->value();
215 if (!dial->visible())
216 return;
217 if (v == dial->minimum())
218 return dial->window()->hide();
219 dial->value(dial->clamp(v - 0.05));
220 return Fl::repeat_timeout(0.05, dial_timer, arg);
221 }
222
make_button(int W,int H)223 Fl_Button* notify_dialog::make_button(int W, int H)
224 {
225 Fl_Group* cur = Fl_Group::current();
226 Fl_Group::current(this);
227
228 if (user_button) return user_button;
229
230 int pad = 10;
231 int X = newx - pad - W;
232 if (X - pad - dial.w() > 0) {
233 user_button = new Fl_Button(newx = X, button.y(), W, H);
234 dial.position(user_button->x() - dial.w() - pad, dial.y());
235 }
236
237 Fl_Group::current(cur);
238 return user_button;
239 }
240
notify(const char * msg,double timeout)241 void notify_dialog::notify(const char* msg, double timeout)
242 {
243 message.value(msg);
244 _timeout = timeout;
245 const char* p;
246 if ((p = strchr(msg, '\n'))) { // use first line as label
247 string l(msg, p - msg);
248 copy_label(l.c_str());
249 }
250 else
251 label("Notification");
252
253 fl_font(message.textfont(), message.textsize());
254 int H = 0;
255 for (const char* p = msg; (p = strchr(p, '\n')); p++)
256 H++;
257
258 int nuh = 103 + max(H-1, 0) * fl_height();
259 resize(x(), y(), w(), nuh);
260 }
261
show_notifier(notify_dialog * me)262 void show_notifier(notify_dialog *me)
263 {
264 if (me->_timeout > 0.0) {
265 me->dial.maximum(me->_timeout);
266 me->dial.value(me->_timeout);
267 me->dial.show();
268 Fl::add_timeout(0.0, notify_dialog::dial_timer, &me->dial);
269 }
270 else
271 me->dial.hide();
272 me->button.take_focus();
273 me->show();
274 }
275
276 // =============================================================================
277
278 #ifdef BUILD_FLDIGI
279
280 #include "icons.h"
281 #include "gettext.h"
282
Mode_Browser(void)283 Mode_Browser::Mode_Browser(void)
284 : Fl_Double_Window(170, 460), changed_cb(NULL), changed_args(NULL)
285 {
286 int bw = 80, bh = 20, pad = 2;
287
288 modes = new Fl_Check_Browser(pad, pad, w() - pad, h() - 2 * (bh + 2 * pad));
289 for (int i = 0; i < NUM_MODES; i++)
290 modes->add(mode_info[i].name);
291 modes->callback(modes_cb, this);
292 modes->when(FL_WHEN_CHANGED);
293
294 all_button = new Fl_Button(modes->x(), modes->y() + modes->h() + pad,
295 bw, bh, _("Select All"));
296 all_button->callback(button_cb, this);
297
298 none_button = new Fl_Button(all_button->x(), all_button->y() + all_button->h() + pad,
299 all_button->w(), all_button->h(), _("Clear All"));
300 none_button->callback(button_cb, this);
301
302 close_button = new Fl_Button(w() - none_button->w() - pad, none_button->y(),
303 none_button->w(), none_button->h(),
304 icons::make_icon_label(_("Close"), close_icon));
305 icons::set_icon_label(close_button);
306 close_button->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
307 close_button->callback(button_cb, this);
308
309 end();
310 resizable(modes);
311 xclass(PACKAGE_TARNAME);
312 }
313
~Mode_Browser(void)314 Mode_Browser::~Mode_Browser(void)
315 {
316 icons::free_icon_label(close_button);
317 delete close_button;
318 delete all_button;
319 delete none_button;
320 delete modes;
321 }
322
show_(mode_set_t * b)323 void Mode_Browser::show_(mode_set_t* b)
324 {
325 store = b;
326 modes->check_none();
327 for (size_t i = 0; i < b->size(); i++)
328 modes->checked(i + 1, store->test(i));
329 modes->position(0);
330 Fl_Double_Window::show();
331 }
332
callback(Fl_Callback * cb,void * args)333 void Mode_Browser::callback(Fl_Callback* cb, void* args)
334 {
335 changed_cb = cb;
336 changed_args = args;
337 }
338
modes_cb(Fl_Widget * w,void * arg)339 void Mode_Browser::modes_cb(Fl_Widget* w, void* arg)
340 {
341 Mode_Browser* m = static_cast<Mode_Browser*>(arg);
342 int sel = m->modes->value();
343 m->store->set(sel - 1, m->modes->checked(sel));
344 if (m->changed_cb)
345 m->changed_cb(m, m->changed_args);
346 }
347
button_cb(Fl_Widget * w,void * arg)348 void Mode_Browser::button_cb(Fl_Widget* w, void* arg)
349 {
350 Mode_Browser* m = static_cast<Mode_Browser*>(arg);
351 if (w == m->close_button)
352 m->hide();
353 else {
354 if (w == m->all_button) {
355 m->store->set();
356 m->modes->check_all();
357 }
358 else {
359 m->store->reset();
360 m->modes->check_none();
361 }
362 if (m->changed_cb)
363 m->changed_cb(m, m->changed_args);
364 }
365 }
366
367 #endif // BUILD_FLDIGI
368