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 // ---------------------------------------------------------------------
76 // ics 216 field variables and template variables
77 // ---------------------------------------------------------------------
78 
79 string ics216_incident					= ":inc:";
80 string ics216_date						= ":dat:";
81 string ics216_time						= ":tim:";
82 string ics216_branch					= ":br:";
83 string ics216_agc						= ":agc:";
84 string ics216_op_period					= ":opp:";
85 string ics216_tac_freq					= ":tfr:";
86 
87 string ics216_div_grp					= ":div[n]:";
88 string ics216_agency					= ":agy[n]:";
89 
90 string ics216_ag						= ":ag[n]:";
91 string ics216_id						= ":id[n]:";
92 string ics216_rr						= ":rr[n]:";
93 
94 string ics216_prepared_by				= ":pre:";
95 
96 string s216_incident;
97 string s216_date;
98 string s216_time;
99 string s216_branch;
100 string s216_agc;
101 string s216_op_period;
102 string s216_tac_freq;
103 
104 string s216_div_grp[4];
105 string s216_agency[4];
106 string s216_ag[36];
107 string s216_id[36];
108 string s216_rr[36];
109 
110 string s216_prepared_by;
111 
112 // =====================================================================
113 
114 string buff216;
115 string def_216_filename = "";
116 string base_216_filename = "";
117 string def_216_TemplateName = "";
118 bool using_ics216_template = false;
119 
cb_216_set_date()120 void cb_216_set_date()
121 {
122 	txt_216_date->value(szDate(progStatus.dtformat));
123 }
124 
cb_216_set_time()125 void cb_216_set_time()
126 {
127 	txt_216_time->value(szTime(progStatus.UTC));
128 }
129 
clear_216fields()130 void clear_216fields()
131 {
132 	s216_incident.clear();
133 	s216_date.clear();
134 	s216_time.clear();
135 	s216_branch.clear();
136 	s216_agc.clear();
137 	s216_op_period.clear();
138 	s216_tac_freq.clear();
139 	s216_prepared_by.clear();
140 
141 	for (int i = 0; i < 4; i++) {
142 		s216_div_grp[i].clear();
143 		s216_agency[i].clear();
144 	}
145 	for (int i = 0; i < 36; i++) {
146 		s216_ag[i].clear();
147 		s216_id[i].clear();
148 		s216_rr[i].clear();
149 	}
150 }
151 
check_216fields()152 bool check_216fields()
153 {
154 	if (s216_incident != txt_216_incident->value())
155 		return true;
156 	if (s216_date != txt_216_date->value())
157 		return true;
158 	if (s216_time != txt_216_time->value())
159 		return true;
160 	if (s216_branch != txt_216_branch->value())
161 		return true;
162 	if (s216_agc != txt_216_agc->value())
163 		return true;
164 	if (s216_op_period != txt_216_op_period->value())
165 		return true;
166 	if (s216_tac_freq != txt_216_tac_freq->value())
167 		return true;
168 	if (s216_prepared_by != txt_216_prepared_by->value())
169 		return true;
170 
171 	for (int i = 0; i < 4; i++) {
172 		if (s216_div_grp[i] != txt_216_div_grp[i]->value())
173 			return true;
174 		if (s216_agency[i] != txt_216_agency[i]->value())
175 			return true;
176 	}
177 
178 	for (int i = 0; i < 36; i++) {
179 		if (s216_ag[i] != txt_216_ag[i]->value())
180 			return true;
181 		if (s216_id[i] != txt_216_id[i]->value())
182 			return true;
183 		if (s216_rr[i] != txt_216_rr[i]->value())
184 			return true;
185 	}
186 	return false;
187 }
188 
update_216fields()189 void update_216fields()
190 {
191 	s216_incident = txt_216_incident->value();
192 	s216_date = txt_216_date->value();
193 	s216_time = txt_216_time->value();
194 	s216_branch = txt_216_branch->value();
195 	s216_agc = txt_216_agc->value();
196 	s216_op_period = txt_216_op_period->value();
197 	s216_tac_freq = txt_216_tac_freq->value();
198 	s216_prepared_by = txt_216_prepared_by->value();
199 
200 	for (int i = 0; i < 4; i++) {
201 		s216_div_grp[i] = txt_216_div_grp[i]->value();
202 		s216_agency[i] = txt_216_agency[i]->value();
203 	}
204 
205 	for (int i = 0; i < 36; i++) {
206 		s216_ag[i] = txt_216_ag[i]->value();
207 		s216_id[i] = txt_216_id[i]->value();
208 		s216_rr[i] = txt_216_rr[i]->value();
209 	}
210 }
211 
update_216form()212 void update_216form()
213 {
214 	txt_216_incident->value(s216_incident.c_str());
215 	txt_216_date->value(s216_date.c_str());
216 	txt_216_time->value(s216_time.c_str());
217 	txt_216_branch->value(s216_branch.c_str());
218 	txt_216_agc->value(s216_agc.c_str());
219 	txt_216_op_period->value(s216_op_period.c_str());
220 	txt_216_tac_freq->value(s216_tac_freq.c_str());
221 	txt_216_prepared_by->value(s216_prepared_by.c_str());
222 
223 	for (int i = 0; i < 4; i++) {
224 		txt_216_div_grp[i]->value(s216_div_grp[i].c_str());
225 		txt_216_agency[i]->value(s216_agency[i].c_str());
226 	}
227 	for (int i = 0; i < 36; i++) {
228 		txt_216_ag[i]->value(s216_ag[i].c_str());
229 		txt_216_id[i]->value(s216_id[i].c_str());
230 		txt_216_rr[i]->value(s216_rr[i].c_str());
231 	}
232 }
233 
clear_216_form()234 void clear_216_form()
235 {
236 	clear_216fields();
237 	update_216form();
238 }
239 
ics_216_nn(string & subst,int n)240 string &ics_216_nn(string & subst, int n)
241 {
242 	static string garbage = "#$^*!";
243 	static string ics;
244 	ics.clear();
245 	ics = subst;
246 	size_t pos = ics.find("[");
247 	if (pos == string::npos) return garbage;
248 	pos++;
249 	if (n < 10)
250 		ics[pos] = '0' + n;
251 	else {
252 		if (n < 20)
253 			ics[pos] = '1';
254 		else if ( n < 30)
255 			ics[pos] = '2';
256 		else if (n < 40)
257 			ics[pos] = '3';
258 		ics[pos+1] = '0' + n % 10;
259 		ics[pos+2] = ']';
260 		ics += ':';
261 	}
262 	return ics;
263 }
264 
make_buff216(bool compress=false)265 void make_buff216(bool compress = false)
266 {
267 	string mbuff;
268 	mbuff.clear();
269 	mbuff.append( lineout( ics216_incident, s216_incident ) );
270 	mbuff.append( lineout( ics216_date, s216_date ) );
271 	mbuff.append( lineout( ics216_time, s216_time ) );
272 	mbuff.append( lineout( ics216_branch, s216_branch ) );
273 	mbuff.append( lineout( ics216_agc, s216_agc ) );
274 	mbuff.append( lineout( ics216_op_period, s216_op_period ) );
275 	mbuff.append( lineout( ics216_tac_freq, s216_tac_freq ) );
276 	mbuff.append( lineout( ics216_prepared_by, s216_prepared_by ) );
277 
278 	for (int i = 0; i < 4; i++) {
279 		mbuff.append( lineout( ics_216_nn( ics216_div_grp, i), s216_div_grp[i] ) );
280 		mbuff.append( lineout( ics_216_nn( ics216_agency, i), s216_agency[i] ) );
281 	}
282 	for (int i = 0; i < 36; i++) {
283 		mbuff.append( lineout( ics_216_nn( ics216_ag, i), s216_ag[i] ) );
284 		mbuff.append( lineout( ics_216_nn( ics216_id, i), s216_id[i] ) );
285 		mbuff.append( lineout( ics_216_nn( ics216_rr, i), s216_rr[i] ) );
286 	}
287 	if (compress) compress_maybe(mbuff);
288 	buff216.append(mbuff);
289 }
290 
read_216_buffer(string data)291 void read_216_buffer(string data)
292 {
293 	clear_216fields();
294 	read_header(data);
295 
296 	s216_incident = findstr( data, ics216_incident );
297 	s216_date = findstr( data, ics216_date );
298 	s216_time = findstr( data, ics216_time );
299 	s216_branch = findstr( data, ics216_branch );
300 	s216_agc = findstr( data, ics216_agc );
301 	s216_op_period = findstr( data, ics216_op_period );
302 	s216_tac_freq = findstr( data, ics216_tac_freq );
303 	s216_prepared_by = findstr( data, ics216_prepared_by );
304 
305 	for (int i = 0; i < 4; i++) {
306 		s216_div_grp[i] = findstr( data, ics_216_nn( ics216_div_grp, i ) );
307 		s216_agency[i] = findstr( data, ics_216_nn( ics216_agency, i ) );
308 	}
309 	for (int i = 0; i < 36; i++) {
310 		s216_ag[i] = findstr( data, ics_216_nn( ics216_ag, i ) );
311 		s216_id[i] = findstr( data, ics_216_nn( ics216_id, i ) );
312 		s216_rr[i] = findstr( data, ics_216_nn( ics216_rr, i ) );
313 	}
314 
315 	update_216form();
316 }
317 
cb_216_new()318 void cb_216_new()
319 {
320 	if (check_216fields()) {
321 		if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 1) {
322 			update_header(CHANGED);
323 			cb_216_save();
324 		}
325 	}
326 	clear_216_form();
327 	clear_header();
328 	def_216_filename = ICS_msg_dir;
329 	def_216_filename.append("new").append(F216_EXT);
330 	show_filename(def_216_filename);
331 	using_ics216_template = false;
332 }
333 
cb_216_import()334 void cb_216_import()
335 {
336 	fl_alert2("Not implemented");
337 }
338 
cb_216_export()339 void cb_216_export()
340 {
341 	fl_alert2("Not implemented");
342 }
343 
cb_216_wrap_import(string wrapfilename,string inpbuffer)344 void cb_216_wrap_import(string wrapfilename, string inpbuffer)
345 {
346 	clear_216_form();
347 	read_216_buffer(inpbuffer);
348 	def_216_filename = ICS_msg_dir;
349 	def_216_filename.append(wrapfilename);
350 	show_filename(def_216_filename);
351 	using_ics216_template = false;
352 }
353 
eval_216_fsize()354 int eval_216_fsize()
355 {
356 	Ccrc16 chksum;
357 	evalstr.assign("[WRAP:beg][WRAP:lf][WRAP:fn ");
358 	evalstr.append(base_216_filename).append("]");
359 	update_216fields();
360 	update_header(FROM);
361 	evalstr.append(header("<ics216>"));
362 	buff216.clear();
363 	make_buff216(true);
364 	if (buff216.empty()) return 0;
365 	compress_maybe( buff216 );
366 	evalstr.append( buff216 );
367 	evalstr.append("[WRAP:chksum ").append(chksum.scrc16(evalstr)).append("][WRAP:end]");
368 	return evalstr.length();
369 }
370 
cb_216_wrap_export()371 void cb_216_wrap_export()
372 {
373 	if (check_216fields()) {
374 		if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 0)
375 			return;
376 		update_header(CHANGED);
377 	}
378 	update_216fields();
379 
380 	if (base_216_filename == string("new").append(F216_EXT) ||
381 		base_216_filename == string("default").append(F216_EXT) )
382 		if (!cb_216_save_as()) return;
383 
384 	string wrapfilename = WRAP_send_dir;
385 	wrapfilename.append(base_216_filename);
386 	wrapfilename.append(".wrap");
387 	const char *p = FSEL::saveas(
388 			"Save as wrap file",
389 			"Wrap file\t*.{wrap,WRAP}",
390 			wrapfilename.c_str());
391 	if (p) {
392 		string pext = fl_filename_ext(p);
393 		wrapfilename = p;
394 		update_header(FROM);
395 		buff216.assign(header("<ics216>"));
396 		make_buff216(true);
397 		export_wrapfile(base_216_filename, wrapfilename, buff216, pext != ".wrap");
398 
399 		buff216.assign(header("<ics216>"));
400 		make_buff216(false);
401 		write_216(def_216_filename);
402 	}
403 }
404 
cb_216_wrap_autosend()405 void cb_216_wrap_autosend()
406 {
407 	if (check_216fields()) {
408 		if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 0)
409 			return;
410 		update_header(CHANGED);
411 	}
412 	update_216fields();
413 
414 	if (base_216_filename == string("new").append(F216_EXT) ||
415 		base_216_filename == string("default").append(F216_EXT) )
416 		if (!cb_216_save_as()) return;
417 
418 	update_header(FROM);
419 	buff216.assign(header("<ics216>"));
420 	make_buff216(true);
421 	xfr_via_socket(base_216_filename, buff216);
422 
423 	buff216.assign(header("<ics216>"));
424 	make_buff216(false);
425 	write_216(def_216_filename);
426 }
427 
cb_216_load_template()428 void cb_216_load_template()
429 {
430 	string def_216_filename = def_216_TemplateName;
431 	const char *p = FSEL::select(
432 			"Open template file",
433 			string("Template file\t*").append(T216_EXT).c_str(),
434 			def_216_filename.c_str());
435 	if (p) {
436 		clear_216_form();
437 		read_data_file(p);
438 		def_216_TemplateName = p;
439 		show_filename(def_216_TemplateName);
440 		using_ics216_template = true;
441 	}
442 }
443 
cb_216_save_template()444 void cb_216_save_template()
445 {
446 	if (!using_ics216_template) {
447 		cb_216_save_as_template();
448 		return;
449 	}
450 	string def_216_filename = def_216_TemplateName;
451 	const char *p = FSEL::saveas(
452 			"Save template file",
453 			string("Template file\t*").append(T216_EXT).c_str(),
454 			def_216_filename.c_str());
455 	if (p) {
456 		update_header(CHANGED);
457 		buff216.assign(header("<ics216>"));
458 		make_buff216();
459 		write_216(p);
460 	}
461 }
462 
cb_216_save_as_template()463 void cb_216_save_as_template()
464 {
465 	string def_216_filename = def_216_TemplateName;
466 	const char *p = FSEL::saveas(
467 			"Save as template file",
468 			string("Template file\t*").append(T216_EXT).c_str(),
469 			def_216_filename.c_str());
470 	if (p) {
471 		const char *pext = fl_filename_ext(p);
472 		def_216_TemplateName = p;
473 		if (strlen(pext) == 0) def_216_TemplateName.append(T216_EXT);
474 		remove_spaces_from_filename(def_216_TemplateName);
475 		clear_header();
476 		update_header(CHANGED);
477 		buff216.assign(header("<ics216>"));
478 		make_buff216();
479 		write_216(def_216_TemplateName);
480 		show_filename(def_216_TemplateName);
481 		using_ics216_template = true;
482 	}
483 }
484 
cb_216_open()485 void cb_216_open()
486 {
487 	const char *p = FSEL::select(
488 			_("Open data file"),
489 			string("ICS-216\t*").append(F216_EXT).c_str(),
490 			def_216_filename.c_str());
491 	if (!p) return;
492 	if (strlen(p) == 0) return;
493 	clear_216_form();
494 	read_data_file(p);
495 	using_ics216_template = false;
496 	def_216_filename = p;
497 	show_filename(def_216_filename);
498 }
499 
write_216(string s)500 void write_216(string s)
501 {
502 	FILE *file216 = fopen(s.c_str(), "w");
503 	if (!file216) return;
504 
505 	fwrite(buff216.c_str(), buff216.length(), 1, file216);
506 	fclose(file216);
507 }
508 
cb_216_save_as()509 bool cb_216_save_as()
510 {
511 	const char *p;
512 	string newfilename;
513 
514 	string name = named_file();
515 	if (!name.empty()) {
516 		name.append(F216_EXT);
517 		newfilename = ICS_msg_dir;
518 		newfilename.append(name);
519 	} else
520 		newfilename = def_216_filename;
521 
522 	p = FSEL::saveas(
523 			_("Save data file"),
524 			string("ICS-216\t*").append(F216_EXT).c_str(),
525 			newfilename.c_str());
526 
527 	if (!p) return false;
528 	if (strlen(p) == 0) return false;
529 
530 	if (progStatus.sernbr_fname) update_sernbr();
531 
532 	const char *pext = fl_filename_ext(p);
533 	def_216_filename = p;
534 	if (strlen(pext) == 0) def_216_filename.append(F216_EXT);
535 
536 	remove_spaces_from_filename(def_216_filename);
537 	update_216fields();
538 	update_header(NEW);
539 	buff216.assign(header("<ics216>"));
540 	make_buff216();
541 	write_216(def_216_filename);
542 
543 	using_ics216_template = false;
544 	show_filename(def_216_filename);
545 	return true;
546 }
547 
cb_216_save()548 void cb_216_save()
549 {
550 	if (base_216_filename == string("new").append(F216_EXT) ||
551 		base_216_filename == string("default").append(F216_EXT) ||
552 		using_ics216_template == true) {
553 		cb_216_save_as();
554 		return;
555 	}
556 	if (check_216fields()) update_header(CHANGED);
557 	update_216fields();
558 	buff216.assign(header("<ics216>"));
559 	make_buff216();
560 	write_216(def_216_filename);
561 	using_ics216_template = false;
562 }
563 
cb_216_html()564 void cb_216_html()
565 {
566 	string fname_name = fl_filename_name(def_216_filename.c_str());
567 	size_t p = fname_name.rfind('.');
568 	if (p != string::npos) fname_name.erase(p);
569 
570 	string ics216_fname = ICS_dir;
571 	ics216_fname.append(fname_name);
572 	ics216_fname.append(".html");
573 
574 	string html_text = "";
575 
576 	update_216fields();
577 	string form216 = ics216_html_template;
578 
579 	replacestr(form216, TITLE, fname_name);
580 	replacestr(form216, ics216_incident, s216_incident );
581 	replacestr(form216, ics216_date, s216_date );
582 	replacestr(form216, ics216_time, s216_time );
583 	replacestr(form216, ics216_branch, s216_branch );
584 	replacestr(form216, ics216_agc, s216_agc );
585 	replacestr(form216, ics216_op_period, s216_op_period );
586 	replacestr(form216, ics216_tac_freq, s216_tac_freq );
587 	replacestr(form216, ics216_prepared_by, s216_prepared_by );
588 
589 	for (int i = 0; i < 4; i++) {
590 		replacestr(form216, ics_216_nn( ics216_div_grp, i), s216_div_grp[i] );
591 		replacestr(form216, ics_216_nn( ics216_agency, i), s216_agency[i] );
592 	}
593 	for (int i = 0; i < 36; i++) {
594 		replacestr(form216, ics_216_nn( ics216_ag, i), s216_ag[i] );
595 		replacestr(form216, ics_216_nn( ics216_id, i), s216_id[i] );
596 		replacestr(form216, ics_216_nn( ics216_rr, i), s216_rr[i] );
597 	}
598 
599 	FILE *file216 = fopen(ics216_fname.c_str(), "w");
600 	fprintf(file216,"%s", form216.c_str());
601 	fclose(file216);
602 
603 	open_url(ics216_fname.c_str());
604 }
605 
cb_216_msg_type()606 void cb_216_msg_type()
607 {
608 	if (tabs_msg_type->value() == tab_ics216 ) {
609 		tab_ics216_type->value(tab_216_1);
610 		show_filename(def_216_filename);
611 	}
612 }
613 
cb_216_textout()614 void cb_216_textout()
615 {
616 	string ics216_fname = ICS_dir;
617 	ics216_fname.append("ics216.txt");
618 
619 	update_216fields();
620 	string form216 = ics216_text_template;
621 
622 	replacestr(form216, ics216_incident, s216_incident );
623 	replacestr(form216, ics216_date, s216_date );
624 	replacestr(form216, ics216_time, s216_time );
625 	replacestr(form216, ics216_branch, s216_branch );
626 	replacestr(form216, ics216_agc, s216_agc );
627 	replacestr(form216, ics216_op_period, s216_op_period );
628 	replacestr(form216, ics216_tac_freq, s216_tac_freq );
629 	replacestr(form216, ics216_prepared_by, s216_prepared_by );
630 
631 	for (int i = 0; i < 4; i++) {
632 		replacestr(form216, ics_216_nn( ics216_div_grp, i), s216_div_grp[i] );
633 		replacestr(form216, ics_216_nn( ics216_agency, i), s216_agency[i] );
634 	}
635 	for (int i = 0; i < 36; i++) {
636 		replacestr(form216, ics_216_nn( ics216_ag, i), s216_ag[i] );
637 		replacestr(form216, ics_216_nn( ics216_id, i), s216_id[i] );
638 		replacestr(form216, ics_216_nn( ics216_rr, i), s216_rr[i] );
639 	}
640 
641 	FILE *file216 = fopen(ics216_fname.c_str(), "w");
642 	fprintf(file216,"%s", form216.c_str());
643 	fclose(file216);
644 
645 	open_url(ics216_fname.c_str());
646 }
647