1 // ----------------------------------------------------------------------------
2 // macroedit.cxx
3 //
4 // Copyright (C) 2007-2009
5 // Dave Freese, W1HKJ
6 // Copyright (C) 2009
7 // Stelios Bounanos, M0GLD
8 //
9 // This file is part of fldigi.
10 //
11 // Fldigi is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // Fldigi is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
23 // ----------------------------------------------------------------------------
24
25 #include <config.h>
26
27 #include <string>
28 #ifndef __MINGW32__
29 # include <glob.h>
30 #endif
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <cstring>
35 #include <cassert>
36
37 #include "macros.h"
38 #include "macroedit.h"
39 #include "globals.h"
40 #include "status.h"
41 #include "fileselect.h"
42 #include "fl_digi.h"
43 #include "main.h"
44 #include "gettext.h"
45 #include "pixmaps.h"
46 #include "configuration.h"
47
48 using namespace std;
49
50 Fl_Double_Window *MacroEditDialog = (Fl_Double_Window *)0;
51
52 Fl_Button *btnMacroEditApply = (Fl_Button *)0;
53 Fl_Button *btnMacroEditClose = (Fl_Button *)0;
54 Fl_Button *btnInsertMacro = (Fl_Button *)0;
55 Fl_Input2 *macrotext = (Fl_Input2 *)0;
56 Fl_Input2 *labeltext = (Fl_Input2 *)0;
57 static int widths[] = {150, 0};
58
59 Fl_Hold_Browser *macroDefs=(Fl_Hold_Browser *)0;
60
61 static int iMacro, iType;
62 static Fl_Input* iInput;
63
64 // fl_color(0) is always the foreground colour
65 #define LINE_SEP "@B0"
66
loadBrowser(Fl_Widget * widget)67 void loadBrowser(Fl_Widget *widget) {
68 Fl_Browser *w = (Fl_Browser *)widget;
69 /* Do not translate the tags lefthand-side */
70 w->add(_("<FREQ>\tmy frequency"));
71 w->add(_("<MODE>\tmode"));
72 w->add(_("<MYCALL>\tmy call"));
73 w->add(_("<MYLOC>\tmy locator"));
74 w->add(_("<MYNAME>\tmy name"));
75 w->add(_("<MYQTH>\tmy QTH"));
76 w->add(_("<MYRST>\tmy RST"));
77 w->add(_("<MYCLASS>\tmy FD class"));
78 w->add(_("<MYSECTION>\tmy FD section"));
79 w->add(_("<MYSTATE>\tmy state"));
80 w->add(_("<MYST>\tmy ST"));
81 w->add(_("<MYCOUNTY>\tmy county"));
82 w->add(_("<MYCNTY>\tmy CNTY"));
83 w->add(_("<ANTENNA>\tmy antenna"));
84 w->add(_("<BAND>\toperating band"));
85 w->add(_("<VER>\tFldigi version"));
86 w->add(_("<DIGI>\tdigital mode (adif)"));
87
88 w->add(LINE_SEP);
89 w->add(_("<CALL>\tother call"));
90 w->add(_("<NAME>\tother name"));
91 w->add(_("<QTH>\tother QTH"));
92 w->add(_("<ST>\tother State"));
93 w->add(_("<PR>\tother Province"));
94 w->add(_("<LOC>\tother locator"));
95 w->add(_("<RST>\tother RST"));
96
97 w->add(LINE_SEP);
98 w->add(_("<INFO1>\tS/N etc."));
99 w->add(_("<INFO2>\tIMD etc."));
100
101 w->add(LINE_SEP);
102 w->add(_("<QSONBR>\t# QSO recs"));
103 w->add(_("<NXTNBR>\tnext QSO rec #"));
104
105 w->add(LINE_SEP);
106 w->add(_("<MAPIT>\tmap on google"));
107 w->add(_("<MAPIT:adr/lat/loc>\tmap by value"));
108
109 w->add(LINE_SEP);
110 w->add(_("<CLRRX>\tclear RX pane"));
111 w->add(_("<CLRTX>\tclear TX pane"));
112 w->add(_("<CLRQSO>\tclear QSO fields"));
113
114 w->add(LINE_SEP);
115 w->add(_("<GET>\ttext to NAME/QTH"));
116
117 #ifdef __WIN32__
118 w->add(LINE_SEP);
119 w->add(_("<TALK:on|off|t>\tDigitalk On, Off, Toggle"));
120 #endif
121
122 w->add(LINE_SEP);
123 w->add(_("<CLRLOG>\tclear log fields"));
124 w->add(_("<LOG>\tsave QSO data"));
125 w->add(_("<LOG:msg>\tsaveQSO data, append msg to notes"));
126 w->add(_("<LNW>\tlog at xmt time"));
127 w->add(_("<LNW:msg>\tsaveQSO data, append msg to notes"));
128 w->add(_("<EQSL>\tlog eQSL"));
129 w->add(_("<EQSL:[msg]>\tlog eQSL optional msg"));
130
131 w->add(LINE_SEP);
132 w->add(_("<QSOTIME>\tQSO time (HHMM))"));
133 w->add(_("<ILDT[:fmt]>\tLDT default '%Y-%m-%d %H:%M%z'"));
134 w->add(_("<LDT[[fmt]>\tLocal datetime, default '%x %H:%M %Z'"));
135 w->add(_("<IZDT[:fmt]>\tZDT default '%Y-%m-%d %H:%MZ'"));
136 w->add(_("<ZDT[:fmt]>\tUTC datetime, default '%x %H:%MZ'"));
137 w->add(_("<LT[:fmt]>\tlocal time, default %H%M"));
138 w->add(_("<ZT[:fmt]>\tzulu time default %H%MZ"));
139 w->add(_("<LD[:fmt]>\tlocal date, default '%Y-%M-%D'"));
140 w->add(_("<ZD[:fmt]>\tzulu date, default '%Y-%M-%D Z'"));
141 w->add(_("<WX>\tget weather data"));
142 w->add(_("<WX:xxxx>\tget weather data for station"));
143
144 w->add(LINE_SEP);
145 w->add(_("<CNTR>\tcontest counter"));
146 w->add(_("<DECR>\tdecrement counter"));
147 w->add(_("<INCR>\tincrement counter"));
148 w->add(_("<XIN>\texchange in"));
149 w->add(_("<XOUT>\texchange out"));
150 w->add(_("<XBEG>\texchange begin"));
151 w->add(_("<XEND>\texchange end"));
152 w->add(_("<SAVEXCHG>\tsave contest out"));
153 w->add(_("<SERNO>\tcurrent contest serno"));
154 w->add(_("<LASTNO>\tlast serno sent"));
155 w->add(_("<FDCLASS>\tFD class"));
156 w->add(_("<FDSECT>\tFD section"));
157 w->add(_("<CLASS>\tcontest class"));
158 w->add(_("<SECTION>\tARRL section"));
159
160 w->add(LINE_SEP);
161 w->add(_("<RX>\treceive"));
162 w->add(_("<TX>\ttransmit"));
163 w->add(_("<TX/RX>\ttoggle T/R"));
164 w->add(_("<SRCHUP>\tsearch UP for signal"));
165 w->add(_("<SRCHDN>\tsearch DOWN for signal"));
166 w->add(_("<GOHOME>\treturn to sweet spot"));
167 w->add(_("<GOFREQ:NNNN>\tmove to freq NNNN Hz"));
168 w->add(_("<QSYTO>\tleft-clk QSY button"));
169 w->add(_("<QSYFM>\tright-clk QSY button"));
170 w->add(_("<QSY:FFF.F[:NNNN]>\tqsy to kHz, Hz"));
171 w->add(_("<QSY+:+/-n.nnn>\tincr/decr xcvr freq"));
172 w->add(_("<RIGMODE:mode>\tvalid xcvr mode"));
173 w->add(_("<FILWID:width>\tvalid xcvr filter width"));
174 w->add(_("<RIGLO:lowcut>\tvalid xcvr low cutoff filter"));
175 w->add(_("<RIGHI:hicut>\tvalid xcvr hi cutoff filter"));
176 w->add(_("<FOCUS>\trig freq has kbd focus"));
177
178 w->add(LINE_SEP);
179 w->add(_("<QRG:text>\tinsert QRG into Rx text"));
180
181 w->add(LINE_SEP);
182 w->add(_("<FILE:>\tinsert text file"));
183 w->add(_("<IMAGE:>\tinsert image file"));
184 w->add(_("<AVATAR>\tsend avatar"));
185 w->add(LINE_SEP);
186
187 w->add(_("<PAUSE>\tpause transmit"));
188 w->add(_("<IDLE:NN.nn>\tidle signal for NN.nn sec"));
189 w->add(_("<TIMER:NN>\trepeat every NN sec"));
190 w->add(_("<AFTER:NN>\trepeat after waiting NN sec"));
191 w->add(_("<TUNE:NN>\ttune signal for NN sec"));
192 w->add(_("<WAIT:NN.n>\tdelay xmt for NN.n sec"));
193 w->add(_("<REPEAT>\trepeat macro continuously"));
194 w->add(_("<SKED:hhmm[ss][:YYYYMMDD]>\tschedule execution for"));
195 w->add(_("<UNTIL:hhmm[ss][:YYYYMMDD]>\tend execution at"));
196 w->add(_("<LOCAL>\tuse local date/time"));
197
198 w->add(LINE_SEP);
199 w->add(_("<TXATTEN:nn.n>\t set xmt attenuator"));
200
201 w->add(LINE_SEP);
202 w->add(_("<CWID>\tCW identifier"));
203 w->add(_("<ID>\tsend mode ID; TX start only"));
204 w->add(_("<TEXT>\ttext at start of TX"));
205 w->add(_("<VIDEO:\tvideo text in TX stream"));
206 w->add(_("<TXRSID:on|off|t>\tTx RSID on,off,toggle"));
207 w->add(_("<RXRSID:on|off|t>\tRx RSID on,off,toggle"));
208 w->add(_("<NRSID:NN>\tTransmit |NN| successive RsID bursts"));
209 w->add(_("<DTMF:[Wn:][Ln:]chrs>\t[Wait][Len](ms)"));
210
211 w->add(LINE_SEP);
212 w->add(_("<AUDIO:>\tXmt audio wav file"));
213
214 w->add(LINE_SEP);
215 w->add(_("<ALERT:[bark][checkout][doesnot][phone][beeboo][diesel][steam_train][dinner_bell][standard_tone]>"));
216 w->add(_("<ALERT:>\talert using external wav file"));
217
218 w->add(LINE_SEP);
219 w->add(_("<POST:+/-nn.n>\tCW QSK post-timing"));
220 w->add(_("<PRE:nn.n>\tCW QSK pre-timing"));
221 w->add(_("<RISE:nn.n>\tCW rise time"));
222 w->add(_("<WPM:NN.nn:FF.nn>\tChar WPM:Text WPM (15.0:5.0)"));
223
224 w->add(LINE_SEP);
225 w->add(_("<RIGCAT:[\"text\"][hex ...]:ret>\tsend CAT cmd"));
226 w->add(_("<FLRIG:[\"text\"][hex ...]>\tsend CAT cmd"));
227
228 w->add(LINE_SEP);
229 w->add(_("<AFC:on|off|t>\tAFC on,off,toggle"));
230 w->add(_("<LOCK:on|off|t>\tLOCK on,off,toggle"));
231 w->add(_("<REV:on|off|t>\tRev on,off,toggle"));
232
233 w->add(LINE_SEP);
234 w->add(_("<MACROS:>\tchange macro defs file"));
235 w->add(_("<SAVE>\tsave current macro file"));
236 w->add(_("<BUFFERED>\trun macro from buffered teext"));
237
238 w->add(LINE_SEP);
239 w->add(_("<COMMENT:comment text>\tignore comment text"));
240 w->add(_("<#comments>\t ignore comments"));
241
242 w->add(LINE_SEP);
243 w->add(_("<CPS_TEST:nn>\tmodem char/sec test on nn chars"));
244 w->add(_("<CPS_N:n>\tmodem timing test, 'n' random 5 char groups"));
245 w->add(_("<CPS_FILE:>\tmodem timing test, spec' file"));
246 w->add(_("<CPS_STRING:s>\tmodem timing test, string 's'"));
247
248 w->add(LINE_SEP);
249 w->add(_("<WAV_TEST>\tWAV file; internal string"));
250 w->add(_("<WAV_N:n>\tWAV file; 'n' random 5 char groups"));
251 w->add(_("<WAV_FILE:>\tWAV file; spec' file"));
252 w->add(_("<WAV_STRING:s>\tWAV file; string 's'"));
253
254 w->add(LINE_SEP);
255 w->add(_("<CSV:on|off|t>\tAnalysis CSV on,off,toggle"));
256
257 w->add(LINE_SEP);
258 w->add(_("<PUSH>\tpush current mode to stack"));
259 w->add(_("<PUSH:m|f\tpush current mode / audio freq to stack"));
260 w->add(_("<POP>\tpop current mode/freq from stack"));
261 w->add(LINE_SEP);
262 assert(MODE_CONTESTIA < MODE_OLIVIA);
263 char s[256];
264 for (trx_mode i = 0; i <= MODE_CONTESTIA; i++) {
265 snprintf(s, sizeof(s), "<MODEM:%s>", mode_info[i].sname);
266 w->add(s);
267 }
268 // add some Contestia macros
269 const char* contestia[] = { "250:8", "250:16", "500:8", "500:16", "1000:8", "1000:16" };
270 for (size_t i = 0; i < sizeof(contestia)/sizeof(*contestia); i++) {
271 snprintf(s, sizeof(s), "<MODEM:%s:%s>", mode_info[MODE_CONTESTIA].sname, contestia[i]);
272 w->add(s);
273 }
274 for (trx_mode i = MODE_CONTESTIA + 1; i <= MODE_OLIVIA; i++) {
275 snprintf(s, sizeof(s), "<MODEM:%s>", mode_info[i].sname);
276 w->add(s);
277 }
278 assert(MODE_OLIVIA < MODE_RTTY);
279 // add some Olivia macros
280 const char* olivia[] = { "250:8", "250:16", "500:8", "500:16", "1000:8", "500:32", "1000:32" };
281 for (size_t i = 0; i < sizeof(olivia)/sizeof(*olivia); i++) {
282 snprintf(s, sizeof(s), "<MODEM:%s:%s>", mode_info[MODE_OLIVIA].sname, olivia[i]);
283 w->add(s);
284 }
285 for (trx_mode i = MODE_OLIVIA + 1; i <= MODE_RTTY; i++) {
286 snprintf(s, sizeof(s), "<MODEM:%s>", mode_info[i].sname);
287 w->add(s);
288 }
289 // add some RTTY macros
290 const char* rtty[] = { "170:45.45:5", "170:50:5", "850:75:5" };
291 for (size_t i = 0; i < sizeof(rtty)/sizeof(*rtty); i++) {
292 snprintf(s, sizeof(s), "<MODEM:%s:%s>", mode_info[MODE_RTTY].sname, rtty[i]);
293 w->add(s);
294 }
295 for (trx_mode i = MODE_RTTY + 1; i < NUM_MODES; i++) {
296 snprintf(s, sizeof(s), "<MODEM:%s>", mode_info[i].sname);
297 w->add(s);
298 }
299
300 #ifndef __MINGW32__
301 glob_t gbuf;
302 glob(string(ScriptsDir).append("*").c_str(), 0, NULL, &gbuf);
303 if (gbuf.gl_pathc == 0) {
304 globfree(&gbuf);
305 return;
306 }
307 w->add(LINE_SEP);
308 struct stat st;
309
310 # if defined(__OpenBSD__)
311 for (int i = 0; i < gbuf.gl_pathc; i++) {
312 # else
313 for (size_t i = 0; i < gbuf.gl_pathc; i++) {
314 # endif
315 if (!(stat(gbuf.gl_pathv[i], &st) == 0
316 && S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR)))
317 continue;
318
319 const char* p;
320 if ((p = strrchr(gbuf.gl_pathv[i], '/'))) {
321 snprintf(s, sizeof(s), "<EXEC>%s</EXEC>", p+1);
322 w->add(s);
323 }
324 }
325 globfree(&gbuf);
326 #else
327 w->add("<EXEC>\tlaunch a program");
328 #endif
329 }
330
331 void cbMacroEditOK(Fl_Widget *w, void *)
332 {
333 if (w == btnMacroEditClose) {
334 MacroEditDialog->hide();
335 return;
336 }
337
338 if (iType == MACRO_EDIT_BUTTON) {
339 update_macro_button(iMacro, macrotext->value(), labeltext->value());
340 }
341 else if (iType == MACRO_EDIT_INPUT)
342 iInput->value(macrotext->value());
343 }
344
345 void update_macro_button(int iMacro, const char *text, const char *name)
346 {
347 macros.text[iMacro].assign(text);
348 macros.name[iMacro].assign(name);
349
350 if (progdefaults.mbar_scheme > MACRO_SINGLE_BAR_MAX) {
351 if (iMacro < NUMMACKEYS) {
352 btnMacro[iMacro]->label( macros.name[iMacro].c_str() );
353 btnMacro[iMacro]->redraw_label();
354 } else if ((iMacro / NUMMACKEYS) == altMacros) {
355 btnMacro[(iMacro % NUMMACKEYS) + NUMMACKEYS]->label( macros.name[iMacro].c_str() );
356 btnMacro[(iMacro % NUMMACKEYS) + NUMMACKEYS]->redraw_label();
357 }
358 } else {
359 btnMacro[iMacro % NUMMACKEYS]->label( macros.name[iMacro].c_str() );
360 btnMacro[iMacro % NUMMACKEYS]->redraw_label();
361 }
362 btnDockMacro[iMacro]->label(macros.name[iMacro].c_str());
363 btnDockMacro[iMacro]->redraw_label();
364
365 macros.changed = true;
366 }
367
368 void cbInsertMacro(Fl_Widget *, void *)
369 {
370 int nbr = macroDefs->value();
371 if (!nbr) return;
372 string edittext = macrotext->value();
373 string text = macroDefs->text(nbr);
374 size_t tab = text.find('\t');
375 if (tab != string::npos)
376 text.erase(tab);
377 if (text == LINE_SEP)
378 return;
379 if (text == "<FILE:>") {
380 string filters = "Text\t*.txt";
381 const char* p = FSEL::select(
382 _("Text file to insert"),
383 filters.c_str(),
384 HomeDir.c_str());
385 if (p && *p) {
386 text.insert(6, p);
387 } else
388 text = "";
389 } else if ((text == "<CPS_FILE:>") || (text == "<WAV_FILE:>")) {
390 string filters = "Text\t*.txt";
391 const char* p = FSEL::select(
392 _("Test text file"),
393 filters.c_str(),
394 HomeDir.c_str());
395 if (p && *p) {
396 text.insert(10, p);
397 } else
398 text = "";
399 } else if (text == "<IMAGE:>") {
400 string filters = "*.{png,jpg,bmp}\t*.png";
401 const char *p = FSEL::select(
402 _("MFSK image file"),
403 filters.c_str(),
404 PicsDir.c_str());
405 if (p && *p) {
406 text.insert(7, p);
407 } else
408 text = "";
409 } else if (text == "<MACROS:>") {
410 string filters = "Macrost\t*.mdf";
411 const char* p = FSEL::select(
412 _("Change to Macro file"),
413 filters.c_str(),
414 MacrosDir.c_str());
415 if (p && *p) {
416 text.insert(8, p);
417 } else
418 text = "";
419 } else if (text == "<ALERT:>") {
420 string filters = "Audio file\t*.{mp3,wav}";
421 const char* p = FSEL::select(
422 _("Select audio file"),
423 filters.c_str(),
424 HomeDir.c_str());
425 if (p && *p) {
426 text.insert(7, p);
427 } else
428 text = "";
429 } else if (text == "<AUDIO:>") {
430 string filters = "Audio file\t*.{mp3,wav}";
431 const char* p = FSEL::select(
432 _("Select audio file"),
433 filters.c_str(),
434 HomeDir.c_str());
435 if (p && *p) {
436 text.insert(7, p);
437 } else
438 text = "";
439 }
440 #ifdef __MINGW32__
441 else if (text == "<EXEC>") {
442 string filters = "Exe\t*.exe";
443 const char* p = FSEL::select(
444 _("Executable file to insert"),
445 filters.c_str(),
446 HomeDir.c_str());
447 if (p && *p) {
448 string exefile = p;
449 exefile.append("</EXEC>");
450 text.insert(6, exefile);
451 } else
452 text = "";
453 }
454 #endif
455 macrotext->insert(text.c_str());
456 macrotext->take_focus();
457 }
458
459 #include <FL/Fl_Tile.H>
460
461 Fl_Double_Window* make_macroeditor(void)
462 {
463 Fl_Double_Window* w = new Fl_Double_Window(800, 190, "");
464
465 Fl_Group *grpA = new Fl_Group(0, 0, 800, 22);
466 Fl_Group *grpB = new Fl_Group(450, 0, 350, 22);
467 btnInsertMacro = new Fl_Button(450, 2, 40, 20);
468 btnInsertMacro->image(new Fl_Pixmap(left_arrow_icon));
469 btnInsertMacro->callback(cbInsertMacro);
470 grpB->end();
471 grpA->end();
472
473 Fl_Group *grpC = new Fl_Group(0, 22, 800, 140);
474 Fl_Tile *tile = new Fl_Tile(0,22,800,140);
475 macrotext = new Fl_Input2(0, 22, 450, 140, _("Macro Text"));
476 macrotext->type(FL_MULTILINE_INPUT);
477 macrotext->textfont(FL_HELVETICA);
478 macrotext->align(FL_ALIGN_TOP);
479
480 macroDefs = new Fl_Hold_Browser(450, 22, 350, 140, _("Select Tag"));
481 macroDefs->column_widths(widths);
482 macroDefs->align(FL_ALIGN_TOP);
483
484 Fl_Box *minbox = new Fl_Box(200, 22, 400, 140);
485 minbox->hide();
486 tile->end();
487 tile->resizable(minbox);
488 grpC->end();
489
490 Fl_Group *grpD = new Fl_Group(0, 164, 452, 24);
491 Fl_Box *box3a = new Fl_Box(0, 164, 327, 24, "");
492 labeltext = new Fl_Input2(337, 164, 115, 24, _("Macro Button Label"));
493 labeltext->textfont(FL_HELVETICA);
494 grpD->end();
495 grpD->resizable(box3a);
496
497 Fl_Group *grpE = new Fl_Group(452, 164, 348, 24);
498 Fl_Box *box4a = new Fl_Box(452, 164, 92, 24, "");
499
500 btnMacroEditApply = new Fl_Button(544, 164, 80, 24, _("Apply"));
501 btnMacroEditApply->callback(cbMacroEditOK);
502
503 btnMacroEditClose = new Fl_Button(626 , 164, 80, 24, _("Close"));
504 btnMacroEditClose->callback(cbMacroEditOK);
505 grpE->end();
506 grpE->resizable(box4a);
507
508 w->end();
509
510 w->resizable(grpC);
511
512 w->size_range( 600, 120);
513
514 w->xclass(PACKAGE_NAME);
515
516 loadBrowser(macroDefs);
517
518 return w;
519 }
520
521 void editMacro(int n, int t, Fl_Input* in)
522 {
523 if (!MacroEditDialog)
524 MacroEditDialog = make_macroeditor();
525 if (t == MACRO_EDIT_BUTTON) {
526 string editor_label;
527 editor_label.append(_("Macro editor - ")).append(progStatus.LastMacroFile);
528 if (editor_label != MacroEditDialog->label())
529 MacroEditDialog->copy_label(editor_label.c_str());
530 macrotext->value(macros.text[n].c_str());
531 labeltext->value(macros.name[n].c_str());
532 labeltext->show();
533 }
534 else if (t == MACRO_EDIT_INPUT) {
535 MacroEditDialog->label(in->label());
536 macrotext->value(in->value());
537 labeltext->hide();
538 }
539 macrotext->textfont(progdefaults.MacroEditFontnbr);
540 macrotext->textsize(progdefaults.MacroEditFontsize);
541 iMacro = n;
542 iType = t;
543 iInput = in;
544 MacroEditDialog->show();
545 }
546
547 void update_macroedit_font()
548 {
549 if (!MacroEditDialog) return;
550 if (!MacroEditDialog->visible()) return;
551 macrotext->textfont(progdefaults.MacroEditFontnbr);
552 macrotext->textsize(progdefaults.MacroEditFontsize);
553 MacroEditDialog->redraw();
554 }
555