1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2018
3 // David Freese, W1HKJ
4 //
5 // This file is part of flrig.
6 //
7 // flrig is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // flrig 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, see <http://www.gnu.org/licenses/>.
19 // ----------------------------------------------------------------------------
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Single_Window.H>
23 #include <FL/fl_draw.H>
24 #include "pl_tones.h"
25
26 #define BOXWIDTH 50
27 #define BOXHEIGHT 20
28 #define BORDER 2
29
30 /**
31 This widget creates a modal window for selecting a tone from a CTCSS map.
32 */
33 static const char *szTONES[] = {
34 "67.0", "69.3", "71.9", "74.4", "77.0",
35 "79.7", "82.5", "85.4", "88.5", "91.5",
36 "94.8", "97.4", "100.0", "103.5", "107.2",
37 "110.9", "114.8", "118.8", "123.0", "127.3",
38 "131.8", "136.5", "141.3", "146.2", "151.4",
39 "156.7", "159.8", "162.2", "165.5", "167.9",
40 "171.3", "173.8", "177.3", "179.9", "183.5",
41 "186.2", "189.9", "192.8", "196.6", "199.5",
42 "203.5", "206.5", "210.7", "218.1", "225.7",
43 "229.1", "233.6", "241.8", "250.3", "254.1" };
44
TonePicker(int oldtone,Fl_Color clr1,Fl_Color clr2)45 TonePicker::TonePicker(int oldtone, Fl_Color clr1, Fl_Color clr2) :
46 Fl_Window(BOXWIDTH*5 + 1 + 2*BORDER, BOXHEIGHT*10 + 1 + 2*BORDER),
47 clr_bkgnd(clr1),
48 clr_select(clr2)
49 {
50 clear_border();
51 set_modal();
52 initial = which = oldtone;
53 }
54
drawbox(int c)55 void TonePicker::drawbox(int c) {
56 int X = (c % 5) * BOXWIDTH + BORDER;
57 int Y = (c / 5) * BOXHEIGHT + BORDER;
58 fl_draw_box(c == which ? FL_DOWN_BOX : FL_BORDER_BOX,
59 X, Y, BOXWIDTH + 1, BOXHEIGHT + 1,
60 c == which ? clr_select : clr_bkgnd);
61 fl_color(c == which ? FL_WHITE : FL_FOREGROUND_COLOR);
62 fl_draw(szTONES[c],
63 X, Y, BOXWIDTH + 1, BOXHEIGHT + 1,
64 (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_INSIDE) );
65 }
66
draw()67 void TonePicker::draw() {
68 if (damage() != FL_DAMAGE_CHILD) {
69 fl_draw_box(FL_UP_BOX,0,0,w(),h(),color());
70 for (int c = 0; c < 50; c++) drawbox(c);
71 } else {
72 drawbox(previous);
73 drawbox(which);
74 }
75 previous = which;
76 }
77
handle(int e)78 int TonePicker::handle(int e) {
79 int c = which;
80 switch (e) {
81 case FL_PUSH:
82 case FL_DRAG: {
83 int X = (Fl::event_x_root() - x() - BORDER);
84 if (X >= 0) X = X / BOXWIDTH;
85 int Y = (Fl::event_y_root() - y() - BORDER);
86 if (Y >= 0) Y = Y / BOXHEIGHT;
87 if (X >= 0 && X < 5 && Y >= 0 && Y < 10)
88 c = 5*Y + X;
89 else
90 c = initial;
91 } break;
92 case FL_RELEASE:
93 done = 1;
94 return 1;
95 case FL_KEYBOARD:
96 switch (Fl::event_key()) {
97 case FL_Up: if (c > 5) c -= 5; break;
98 case FL_Down: if (c < 45) c += 5; break;
99 case FL_Left: if (c > 0) c--; break;
100 case FL_Right: if (c < 50) c++; break;
101 case FL_Escape: which = initial; done = 1; return 1;
102 case FL_KP_Enter:
103 case FL_Enter: done = 1; return 1;
104 default: return 0;
105 }
106 break;
107 default:
108 return 0;
109 }
110 if (c != which) {
111 which = c; damage(FL_DAMAGE_CHILD);
112 int bx = (c % 5) * BOXWIDTH + BORDER;
113 int by = (c / 5) * BOXHEIGHT + BORDER;
114 int px = x();
115 int py = y();
116 int scr_x, scr_y, scr_w, scr_h;
117 Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h);
118 if (px < scr_x) px = scr_x;
119 if (px + bx + BOXWIDTH + BORDER >= scr_x+scr_w)
120 px = scr_x + scr_w - bx - BOXWIDTH-BORDER;
121 if (py < scr_y) py = scr_y;
122 if (py + by + BOXHEIGHT + BORDER >= scr_y+scr_h)
123 py = scr_y + scr_h - by - BOXHEIGHT - BORDER;
124 if (px + bx < BORDER) px = BORDER - bx;
125 if (py + by < BORDER) py = BORDER - by;
126 position(px,py);
127 }
128 return 1;
129 }
130
run()131 int TonePicker::run() {
132 if (which > 50) {
133 position(
134 Fl::event_x_root() - w()/2, Fl::event_y_root() - y()/2);
135 } else {
136 position(
137 Fl::event_x_root() - (initial % 5)*BOXWIDTH - BOXWIDTH/2 - BORDER,
138 Fl::event_y_root() - (initial / 10)*BOXHEIGHT - BOXHEIGHT/2 - BORDER);
139 }
140 show();
141 Fl::grab(*this);
142 done = 0;
143 while (!done) Fl::wait();
144 Fl::grab(0);
145 return which;
146 }
147
btn_cb(Fl_Button * v,void * d)148 void btn_cb (Fl_Button *v, void *d)
149 {
150 Fl_PL_tone *p = (Fl_PL_tone *)(v->parent());
151 TonePicker m(p->val, p->clr_bkgnd, p->clr_select);
152 int val = m.run();
153 p->val = val;
154 p->valbox->label(szTONES[val]);
155 p->valbox->redraw();
156 }
157
Fl_PL_tone(int X,int Y,int W,int H,const char * label)158 Fl_PL_tone::Fl_PL_tone(int X, int Y, int W, int H, const char *label)
159 : Fl_Group (X, Y, W, H, label)
160 {
161 valbox = new Fl_Box (FL_DOWN_BOX, X, Y, W-H, H, "");
162 valbox->align(FL_ALIGN_INSIDE | FL_ALIGN_LEFT);
163 valbox->color(FL_BACKGROUND2_COLOR);
164
165 btn = new Fl_Button (X + W - H + 1, Y, H - 1, H, "@2>");
166 btn->callback ((Fl_Callback *)btn_cb, 0);
167
168 clr_bkgnd = FL_BACKGROUND_COLOR;
169 clr_select = FL_SELECTION_COLOR;
170
171 val = 0;
172 }
173
~Fl_PL_tone()174 Fl_PL_tone::~Fl_PL_tone()
175 {
176 delete valbox;
177 delete btn;
178 }
179
value()180 int Fl_PL_tone::value()
181 {
182 return val;
183 }
184
value(int val_)185 void Fl_PL_tone::value(int val_)
186 {
187 val = val_;
188 valbox->label(szTONES[val]);
189 }
190
background(Fl_Color clr)191 void Fl_PL_tone::background(Fl_Color clr)
192 {
193 clr_bkgnd = clr;
194 }
195
color_select(Fl_Color clr)196 void Fl_PL_tone::color_select(Fl_Color clr)
197 {
198 clr_select = clr;
199 }
200