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