1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2014
3 // David Freese, W1HKJ
4 //
5 // This file is part of flmsg
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 <stdlib.h>
22 #include <iostream>
23 #include <fstream>
24 #include <cstring>
25 #include <ctime>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdio.h>
29 #include <errno.h>
30
31 #include <FL/Fl.H>
32 #include <FL/Enumerations.H>
33 #include <FL/Fl_Window.H>
34 #include <FL/Fl_Button.H>
35 #include <FL/Fl_Group.H>
36 #include <FL/Fl_Sys_Menu_Bar.H>
37 #include <FL/x.H>
38 #include <FL/Fl_Help_Dialog.H>
39 #include <FL/Fl_Menu_Item.H>
40 #include <FL/Fl_File_Icon.H>
41
42 #include "config.h"
43 #include "flmsg_config.h"
44
45 #include "flmsg.h"
46 #include "templates.h"
47 #include "debug.h"
48 #include "util.h"
49 #include "gettext.h"
50 #include "flmsg_dialog.h"
51 #include "flinput2.h"
52 #include "date.h"
53 #include "calendar.h"
54 #include "icons.h"
55 #include "fileselect.h"
56 #include "wrap.h"
57 #include "status.h"
58 #include "parse_xml.h"
59
60 #ifdef WIN32
61 # include "flmsgrc.h"
62 # include "compat.h"
63 # define dirent fl_dirent_no_thanks
64 #endif
65
66 #include <FL/filename.H>
67 #include "dirent-check.h"
68
69 #include <FL/x.H>
70 #include <FL/Fl_Pixmap.H>
71 #include <FL/Fl_Image.H>
72
73 using namespace std;
74
75 // ICS213 fields
76 // compatibility fields required to read older data files
77
78 string a_213_inc = "<inc:";
79 string a_213_to = "<to:";
80 string a_213_fm = "<fm:";
81 string a_213_p1 = "<p1:";
82 string a_213_p2 = "<p2:";
83 string a_213_subj = "<sb:";
84 string a_213_d1 = "<d1:";
85 string a_213_t1 = "<t1:";
86 string a_213_msg = "<mg:";
87 string a_213_s1 = "<s1:";
88 string a_213_p3 = "<p3:";
89 string a_213_reply = "<rp:";
90 string a_213_d2 = "<d2:";
91 string a_213_t2 = "<t2:";
92 string a_213_s2 = "<s2:";
93 string a_213_p4 = "<p4:";
94
95 FIELD afields[] = {
96 { a_213_inc, "", (void **)&txt_213_inc, 't' },
97 { a_213_to, "", (void **)&txt_213_to, 't' },
98 { a_213_p1, "", (void **)&txt_213_p1, 't' },
99 { a_213_fm, "", (void **)&txt_213_fm, 't' },
100 { a_213_p2, "", (void **)&txt_213_p2, 't' },
101 { a_213_d1, "", (void **)&txt_213_d1, 'd' },
102 { a_213_t1, "", (void **)&txt_213_t1, 't' },
103 { a_213_subj, "", (void **)&txt_213_subj, 't' },
104 { a_213_s1, "", (void **)&txt_213_s1, 't' },
105 { a_213_p3, "", (void **)&txt_213_p3, 't' },
106 { a_213_s2, "", (void **)&txt_213_s2, 't' },
107 { a_213_p4, "", (void **)&txt_213_p4, 't' },
108 { a_213_d2, "", (void **)&txt_213_d2, 'd' },
109 { a_213_t2, "", (void **)&txt_213_t2, 't' },
110 { a_213_msg, "", (void **)&txt_213_msg, 'e' },
111 { a_213_reply, "", (void **)&txt_213_reply, 'e' } };
112
113 string _213_inc = ":inc:";
114 string _213_to = ":to:";
115 string _213_fm = ":fm:";
116 string _213_p1 = ":p1:";
117 string _213_p2 = ":p2:";
118 string _213_subj = ":sb:";
119 string _213_d1 = ":d1:";
120 string _213_t1 = ":t1:";
121 string _213_msg = ":mg:";
122 string _213_s1 = ":s1:";
123 string _213_p3 = ":p3:";
124 string _213_reply = ":rp:";
125 string _213_d2 = ":d2:";
126 string _213_t2 = ":t2:";
127 string _213_s2 = ":s2:";
128 string _213_p4 = ":p4:";
129
130 string ICS_213_msg = "";
131 string ICS_213_reply = "";
132 string buffer;
133
134 FIELD fields[] = {
135 { _213_inc, "", (void **)&txt_213_inc, 't' },
136 { _213_to, "", (void **)&txt_213_to, 't' },
137 { _213_p1, "", (void **)&txt_213_p1, 't' },
138 { _213_fm, "", (void **)&txt_213_fm, 't' },
139 { _213_p2, "", (void **)&txt_213_p2, 't' },
140 { _213_d1, "", (void **)&txt_213_d1, 'd' },
141 { _213_t1, "", (void **)&txt_213_t1, 't' },
142 { _213_subj, "", (void **)&txt_213_subj, 't' },
143 { _213_s1, "", (void **)&txt_213_s1, 't' },
144 { _213_p3, "", (void **)&txt_213_p3, 't' },
145 { _213_s2, "", (void **)&txt_213_s2, 't' },
146 { _213_p4, "", (void **)&txt_213_p4, 't' },
147 { _213_d2, "", (void **)&txt_213_d2, 'd' },
148 { _213_t2, "", (void **)&txt_213_t2, 't' },
149 { _213_msg, "", (void **)&txt_213_msg, 'e' },
150 { _213_reply, "", (void **)&txt_213_reply, 'e' } };
151
152 bool using_213Template = false;
153
154 string base_213_filename = "";
155 string def_213_filename = "";
156 string def_213_TemplateName = "";
157
158 int numfields = sizeof(fields) / sizeof(FIELD);
159
cb_SetDate1()160 void cb_SetDate1()
161 {
162 txt_213_d1->value(szDate(progStatus.dtformat));
163 }
164
cb_SetDate2()165 void cb_SetDate2()
166 {
167 txt_213_d2->value(szDate(progStatus.dtformat));
168 }
169
cb_SetTime1()170 void cb_SetTime1()
171 {
172 txt_213_t1->value(szTime(progStatus.UTC));
173 }
174
cb_SetTime2()175 void cb_SetTime2()
176 {
177 txt_213_t2->value(szTime(progStatus.UTC));
178 }
179
clear_fields()180 void clear_fields()
181 {
182 for (int i = 0; i < numfields; i++)
183 fields[i].f_data.clear();
184 }
185
check_fields()186 bool check_fields()
187 {
188 for (int i = 0; i < numfields; i++) {
189 if (fields[i].w_type == 'd') {
190 if (fields[i].f_data != ((Fl_DateInput *)(*fields[i].w))->value())
191 return true;
192 } else if (fields[i].w_type == 't') {
193 if (fields[i].f_data != ((Fl_Input2 *)(*fields[i].w))->value())
194 return true;
195 } else if (fields[i].w_type == 'e') {
196 if (fields[i].f_data!= ((FTextEdit *)(*fields[i].w))->buffer()->text())
197 return true;
198 }
199 }
200 return false;
201 }
202
update_fields()203 void update_fields()
204 {
205 bool changed = false;
206 for (int i = 0; i < numfields; i++) {
207 if (fields[i].w_type == 'd') {
208 if (fields[i].f_data != ((Fl_DateInput *)(*fields[i].w))->value()) {
209 changed = true;;
210 fields[i].f_data = ((Fl_DateInput *)(*fields[i].w))->value();
211 }
212 } else if (fields[i].w_type == 't') {
213 if (fields[i].f_data != ((Fl_Input2 *)(*fields[i].w))->value()) {
214 changed = true;;
215 fields[i].f_data = ((Fl_Input2 *)(*fields[i].w))->value();
216 }
217 } else if (fields[i].w_type == 'e') {
218 if (fields[i].f_data!= ((FTextEdit *)(*fields[i].w))->buffer()->text()) {
219 changed = true;;
220 fields[i].f_data = ((FTextEdit *)(*fields[i].w))->buffer()->text();
221 }
222 }
223 }
224 if (changed) update_header(CHANGED);
225 }
226
clear_213_form()227 void clear_213_form()
228 {
229 clear_fields();
230 txt_213_inc->value("");
231 txt_213_to->value("");
232 txt_213_p1->value("");
233 txt_213_fm->value("");
234 txt_213_p2->value("");
235 txt_213_subj->value("");
236 txt_213_d1->value("");
237 txt_213_t1->value("");
238 txt_213_msg->clear();
239 txt_213_s1->value("");
240 txt_213_p3->value("");
241 txt_213_reply->clear();
242 txt_213_s2->value("");
243 txt_213_d2->value("");
244 txt_213_t2->value("");
245 txt_213_p4->value("");
246 update_fields();
247 }
248
update_form213()249 void update_form213()
250 {
251 for (int i = 0; i < numfields; i++) {
252 if (fields[i].w_type == 'd')
253 ((Fl_DateInput *)(*fields[i].w))->value(fields[i].f_data.c_str());
254 else if (fields[i].w_type == 't')
255 ((Fl_Input2 *)(*fields[i].w))->value(fields[i].f_data.c_str());
256 else if (fields[i].w_type == 'e') {
257 ((FTextEdit *)(*fields[i].w))->clear();
258 ((FTextEdit *)(*fields[i].w))->add(fields[i].f_data.c_str());
259 }
260 }
261 }
262
make_buffer(bool compress=false)263 void make_buffer(bool compress = false)
264 {
265 string mbuff;
266 mbuff.clear();
267 for (int i = 0; i < numfields; i++)
268 mbuff.append( lineout( fields[i].f_type, fields[i].f_data ) );
269 if (compress) compress_maybe(mbuff);
270 buffer.append(mbuff);
271 }
272
read_213_buffer(string data)273 void read_213_buffer(string data)
274 {
275 bool data_ok = false;
276 clear_fields();
277 read_header(data);
278 for (int i = 0; i < numfields; i++) {
279 fields[i].f_data = findstr(data, fields[i].f_type);
280 if (!fields[i].f_data.empty()) data_ok = true;
281 }
282 if (!data_ok)
283 for (int i = 0; i < numfields; i++)
284 fields[i].f_data = findstr(data, afields[i].f_type);
285
286 update_form213();
287 }
288
cb_213_new()289 void cb_213_new()
290 {
291 if (check_fields()) {
292 if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 1) {
293 update_header(CHANGED);
294 cb_213_save();
295 }
296 }
297 clear_213_form();
298 clear_header();
299 def_213_filename = ICS_msg_dir;
300 def_213_filename.append("new").append(F213_EXT);
301 using_213Template = false;
302 show_filename(def_213_filename);
303 }
304
cb_213_import()305 void cb_213_import()
306 {
307 string def_213_filename = ICS_dir;
308 def_213_filename.append("DEFAULT.XML");
309 const char *p = FSEL::select(
310 "Open Qforms xml file",
311 "Qforms xml\t*.{xml,XML}",
312 def_213_filename.c_str());
313 if (p){
314 clear_213_form();
315 clear_header();
316 qform_ics_import(p);
317 using_213Template = false;
318 }
319 }
320
cb_213_export()321 void cb_213_export()
322 {
323 string def_213_filename = ICS_dir;
324 def_213_filename.append(base_213_filename);
325 def_213_filename.append(".XML");
326 const char *p = FSEL::saveas(
327 "Open Qforms xml file",
328 "Qforms xml\t*.{xml,XML}",
329 def_213_filename.c_str());
330 if (p) {
331 const char *pext = fl_filename_ext(p);
332 def_213_filename = p;
333 update_fields();
334 if (strlen(pext) == 0) def_213_filename.append(".XML");
335 qform_ics_export(def_213_filename);
336 }
337 }
338
cb_213_wrap_import(string wrapfilename,string inpbuffer)339 void cb_213_wrap_import(string wrapfilename, string inpbuffer)
340 {
341 clear_213_form();
342 read_213_buffer(inpbuffer);
343 def_213_filename = ICS_msg_dir;
344 def_213_filename.append(wrapfilename);
345 using_213Template = false;
346 show_filename(def_213_filename);
347 }
348
eval_213_fsize()349 int eval_213_fsize()
350 {
351 Ccrc16 chksum;
352 evalstr.assign("[WRAP:beg][WRAP:lf][WRAP:fn ");
353 evalstr.append(base_213_filename).append("]");
354 update_fields();
355 update_header(FROM);
356 evalstr.append(header("<ics213>"));
357 buffer.clear();
358 make_buffer(true);
359 if (buffer.empty()) return 0;
360 compress_maybe( buffer );
361 evalstr.append( buffer );
362 evalstr.append("[WRAP:chksum ").append(chksum.scrc16(evalstr)).append("][WRAP:end]");
363 return evalstr.length();
364 }
365
cb_213_wrap_export()366 void cb_213_wrap_export()
367 {
368 if (check_fields()) {
369 if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 0)
370 return;
371 update_header(CHANGED);
372 }
373
374 if (base_213_filename == string("new").append(F213_EXT) ||
375 base_213_filename == string("default").append(F213_EXT) )
376 if (!cb_213_save_as()) return;
377
378 string wrapfilename = WRAP_send_dir;
379 wrapfilename.append(base_213_filename);
380 wrapfilename.append(WRAP_EXT);
381 const char *p = FSEL::saveas(
382 "Save as wrap file",
383 "Wrap file\t*.{wrap,WRAP}",
384 wrapfilename.c_str());
385 if (p) {
386 string pext = fl_filename_ext(p);
387 wrapfilename = p;
388 update_header(FROM);
389 update_fields();
390 buffer.assign(header("<ics213>"));
391 make_buffer(true);
392 export_wrapfile(base_213_filename, wrapfilename, buffer, pext != WRAP_EXT);
393
394 buffer.assign(header("<ics213>"));
395 make_buffer(false);
396 write_213(def_213_filename);
397 }
398 }
399
cb_213_wrap_autosend()400 void cb_213_wrap_autosend()
401 {
402 if (check_fields()) {
403 if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 0)
404 return;
405 update_header(CHANGED);
406 }
407
408 if (base_213_filename == string("new").append(F213_EXT) ||
409 base_213_filename == string("default").append(F213_EXT) )
410 if (!cb_213_save_as()) return;
411
412 update_header(FROM);
413 update_fields();
414 buffer.assign(header("<ics213>"));
415 make_buffer(true);
416 xfr_via_socket(base_213_filename, buffer);
417
418 buffer.assign(header("<ics213>"));
419 make_buffer(false);
420 write_213(def_213_filename);
421 }
422
cb_213_load_template()423 void cb_213_load_template()
424 {
425 string def_213_filename = def_213_TemplateName;
426 const char *p = FSEL::select(
427 "Open template file",
428 string("Template file\t*").append(T213_EXT).c_str(),
429 def_213_filename.c_str());
430 if (p) {
431 clear_213_form();
432 read_data_file(p);
433 def_213_TemplateName = p;
434 show_filename(def_213_TemplateName);
435 using_213Template = true;
436 }
437 }
438
cb_213_save_template()439 void cb_213_save_template()
440 {
441 if (!using_213Template) {
442 cb_save_as_template();
443 return;
444 }
445 string def_213_filename = def_213_TemplateName;
446 const char *p = FSEL::saveas(
447 "Save template file",
448 string("Template file\t*").append(T213_EXT).c_str(),
449 def_213_filename.c_str());
450 if (p) {
451 update_header(CHANGED);
452 update_fields();
453 buffer.assign(header("<ics213>"));
454 make_buffer();
455 write_213(p);
456 }
457 }
458
cb_213_save_as_template()459 void cb_213_save_as_template()
460 {
461 string def_213_filename = def_213_TemplateName;
462 const char *p = FSEL::saveas(
463 "Save as template file",
464 string("Template file\t*").append(T213_EXT).c_str(),
465 def_213_filename.c_str());
466 if (p) {
467 const char *pext = fl_filename_ext(p);
468 def_213_TemplateName = p;
469 if (strlen(pext) == 0) def_213_TemplateName.append(T213_EXT);
470 remove_spaces_from_filename(def_213_TemplateName);
471 clear_header();
472 update_header(CHANGED);
473 update_fields();
474 buffer.assign(header("<ics213>"));
475 make_buffer();
476 write_213(def_213_TemplateName);
477 show_filename(def_213_TemplateName);
478 using_213Template = true;
479 }
480 }
481
cb_213_open()482 void cb_213_open()
483 {
484 const char *p = FSEL::select(_("Open data file"), "ICS-213\t*.{213,f2s}",
485 def_213_filename.c_str());
486 if (!p) return;
487 if (strlen(p) == 0) return;
488 clear_213_form();
489 read_data_file(p);
490 using_213Template = false;
491 def_213_filename = p;
492 show_filename(def_213_filename);
493 }
494
write_213(string s)495 void write_213(string s)
496 {
497 FILE *icsfile = fopen(s.c_str(), "w");
498 if (!icsfile) return;
499
500 fwrite(buffer.c_str(), buffer.length(), 1, icsfile);
501 fclose(icsfile);
502 }
503
cb_213_save_as()504 bool cb_213_save_as()
505 {
506 const char *p;
507 string newfilename;
508
509 string name = named_file();
510 if (!name.empty()) {
511 name.append(F213_EXT);
512 newfilename = ICS_msg_dir;
513 newfilename.append(name);
514 } else
515 newfilename = def_213_filename;
516
517 p = FSEL::saveas(_("Save data file"), "ICS-213\t*.{213T,f2t}",
518 newfilename.c_str());
519
520 if (!p) return false;
521 if (strlen(p) == 0) return false;
522
523 if (progStatus.sernbr_fname) update_sernbr();
524
525 const char *pext = fl_filename_ext(p);
526 def_213_filename = p;
527 if (strlen(pext) == 0) def_213_filename.append(F213_EXT);
528
529 remove_spaces_from_filename(def_213_filename);
530 update_header(NEW);
531 update_fields();
532 buffer.assign(header("<ics213>"));
533 make_buffer();
534 write_213(def_213_filename);
535
536 using_213Template = false;
537 show_filename(def_213_filename);
538 return true;
539 }
540
cb_213_save()541 void cb_213_save()
542 {
543 if (base_213_filename == std::string("new").append(F213_EXT) ||
544 base_213_filename == std::string("default").append(F213_EXT) ||
545 using_213Template == true) {
546 cb_213_save_as();
547 return;
548 }
549 if (check_fields()) update_header(CHANGED);
550 update_fields();
551 buffer.assign(header("<ics213>"));
552 make_buffer();
553 write_213(def_213_filename);
554 using_213Template = false;
555 }
556
cb_213_html()557 void cb_213_html()
558 {
559 string fname_name = fl_filename_name(def_213_filename.c_str());
560 size_t p = fname_name.rfind('.');
561 if (p != string::npos) fname_name.erase(p);
562
563 string icsname = ICS_dir;
564 icsname.append(fname_name);
565 icsname.append(".html");
566
567 string html_text = "";
568
569 update_fields();
570 string form = ics213_html_template;
571
572 replacestr(form, TITLE, fname_name);
573 for (int i = 0; i < numfields; i++) {
574 if (fields[i].w_type != 'e')
575 replacestr( form, fields[i].f_type, fields[i].f_data );
576 else {
577 string ww = wordwrap(fields[i].f_data, progStatus.charcount);
578 int lfcount = 0;
579 size_t pos = 0;
580 while ((pos = ww.find("\n", pos)) != string::npos){
581 lfcount++; pos++;
582 }
583 if (lfcount < 10) for (int i = 0; i < (10 - lfcount); i++) ww.append("\n");
584 html_text.assign("<pre>");
585 html_text.append(ww);
586 html_text.append("</pre>");
587 replacestr( form, fields[i].f_type, html_text );
588 }
589 }
590
591 for (int i = 0; i < numfields; i++)
592 replacestr( form, fields[i].f_type, fields[i].f_data );
593
594 FILE *icsfile = fopen(icsname.c_str(), "w");
595 fprintf(icsfile,"%s", form.c_str());
596 fclose(icsfile);
597
598 open_url(icsname.c_str());
599 }
600
cb_213_textout()601 void cb_213_textout()
602 {
603 string icsname = ICS_dir;
604 icsname.append("ics213.txt");
605
606 update_fields();
607 string form = ics213_text_template;
608
609 for (int i = 0; i < numfields; i++)
610 replacestr( form, fields[i].f_type, fields[i].f_data);
611
612 FILE *icsfile = fopen(icsname.c_str(), "w");
613 fprintf(icsfile,"%s", form.c_str());
614 fclose(icsfile);
615
616 open_url(icsname.c_str());
617 }
618