1 /*
2 *
3 * XASTIR, Amateur Station Tracking and Information Reporting
4 * Copyright (C) 1999,2000 Frank Giannandrea
5 * Copyright (C) 2000-2019 The Xastir Group
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program 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, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * Look at the README for more information on the program.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif // HAVE_CONFIG_H
27
28 #include "snprintf.h"
29
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36
37 // Needed for Solaris
38 #ifdef HAVE_STRINGS_H
39 #include <strings.h>
40 #endif // HAVE_STRINGS_H
41
42 #include <ctype.h>
43 #include <sys/types.h>
44
45 #if TIME_WITH_SYS_TIME
46 #include <sys/time.h>
47 #include <time.h>
48 #else // TIME_WITH_SYS_TIME
49 #if HAVE_SYS_TIME_H
50 #include <sys/time.h>
51 #else // HAVE_SYS_TIME_H
52 #include <time.h>
53 #endif // HAVE_SYS_TIME_H
54 #endif // TIME_WITH_SYS_TIME
55
56 #include <Xm/XmAll.h>
57
58 #ifdef HAVE_XBAE_MATRIX_H
59 #include <Xbae/Matrix.h>
60 #endif // HAVE_XBAE_MATRIX_H
61
62 #include <X11/Xatom.h>
63 #include <X11/Shell.h>
64
65 #include "xastir.h"
66 #include "main.h"
67 #include "bulletin_gui.h"
68 #include "interface.h"
69 #include "util.h"
70
71 // Must be last include file
72 #include "leak_detection.h"
73
74
75 extern XmFontList fontlist1; // Menu/System fontlist
76 Widget Display_bulletins_dialog = NULL;
77 Widget Display_bulletins_text = NULL;
78 Widget dist_data = NULL;
79 Widget zero_bulletin_data = NULL;
80
81 static xastir_mutex display_bulletins_dialog_lock;
82
83 int bulletin_range;
84 int new_bulletin_flag = 0;
85 int new_bulletin_count = 0;
86 static time_t first_new_bulletin_time = 0;
87 static time_t last_new_bulletin_time = 0;
88
89
90
91
92
bulletin_gui_init(void)93 void bulletin_gui_init(void)
94 {
95 init_critical_section(&display_bulletins_dialog_lock);
96 }
97
98
99
100
101
102 // Function called from check_for_new_bulletins() if a new bulletin
103 // has come in that's within our range and we have
104 // pop_up_new_bulletins enabled. This causes the Bulletins() dialog
105 // to come up and rescan the message database for all bulletins that
106 // are within the radius specified. By the time this gets called
107 // we've already waited a few seconds to try to get the posit to
108 // come in that matches the bulletin, and have then checked the
109 // database to make sure that the new bulletins received are still
110 // within our range.
popup_bulletins(void)111 void popup_bulletins(void)
112 {
113 if ( Display_bulletins_dialog == NULL ) // Dialog not up
114 {
115
116 // Bring up the dialog
117 Bulletins( (Widget)NULL, (XtPointer)NULL, (XtPointer)NULL );
118 }
119 }
120
121
122
123
124
bulletin_message(char * call_sign,char * tag,char * packet_message,time_t sec_heard)125 void bulletin_message(char *call_sign, char *tag, char *packet_message, time_t sec_heard)
126 {
127 char temp[200];
128 char temp_my_course[10];
129 char temp_text[30];
130 double distance;
131 XmTextPosition pos, eol, eod;
132 struct tm *tmp;
133 time_t timehd;
134 char time_str[20];
135 char *temp_ptr;
136
137
138 timehd=sec_heard;
139 tmp = localtime(&timehd);
140
141 if ( (packet_message != NULL) && (strlen(packet_message) > MAX_MESSAGE_LENGTH) )
142 {
143 if (debug_level & 1)
144 {
145 fprintf(stderr,"bulletin_message: Message length too long\n");
146 }
147 return;
148 }
149
150 (void)strftime(time_str,sizeof(time_str),"%b %d %H:%M",tmp);
151
152 distance = distance_from_my_station(call_sign,temp_my_course);
153 xastir_snprintf(temp, sizeof(temp), "%-9s:%-4s (%s %6.1f %s) %s\n",
154 call_sign, &tag[3], time_str, distance,
155 english_units ? langcode("UNIOP00004"): langcode("UNIOP00005"),
156 packet_message);
157
158 // Operands of <= have incompatible types (double, int):
159 if ( ( ((int)distance <= bulletin_range) && (distance > 0.0) )
160 || (view_zero_distance_bulletins && distance == 0.0)
161 || ( (bulletin_range == 0) && (distance > 0.0) ) )
162 {
163
164 begin_critical_section(&display_bulletins_dialog_lock, "bulletin_gui.c:bulletin_message" );
165
166 if ((Display_bulletins_dialog != NULL) && Display_bulletins_text != NULL) // Dialog is up
167 {
168
169 eod = XmTextGetLastPosition(Display_bulletins_text);
170 memcpy(temp_text, temp, 15);
171 temp_text[14] = '\0'; // Terminate string
172
173 // Look for this bulletin ID. "pos" will hold the first char position if found.
174 if (XmTextFindString(Display_bulletins_text, 0, temp_text, XmTEXT_FORWARD, &pos))
175 {
176
177 // Found it, so now find the end-of-line for it
178 if (XmTextFindString(Display_bulletins_text, pos, "\n", XmTEXT_FORWARD, &eol))
179 {
180 eol++;
181 }
182 else
183 {
184 eol = eod;
185 }
186
187 // And replace the old bulletin with a new copy
188 if (eol == eod)
189 {
190 temp[strlen(temp)-1] = '\0';
191 }
192 XmTextReplace(Display_bulletins_text, pos, eol, temp);
193 }
194 else
195 {
196 for (pos = 0; strlen(temp_text) > 12 && pos < eod;)
197 {
198 if (XmCOPY_SUCCEEDED == XmTextGetSubstring(Display_bulletins_text, pos, 14, 30, temp_text))
199 {
200 if (temp_text[0] && strncmp(temp, temp_text, 14) < 0)
201 {
202 break;
203 }
204 }
205 else
206 {
207 break;
208 }
209
210 if (XmTextFindString(Display_bulletins_text, pos, "\n", XmTEXT_FORWARD, &eol))
211 {
212 pos = ++eol;
213 }
214 else
215 {
216 pos = eod;
217 }
218 }
219 if (pos == eod)
220 {
221 temp[strlen(temp)-1] = '\0'; // End-of-Data remove trailing LF
222 if (pos > 0) // Already have text. Need to insert LF between items
223 {
224 memmove(&temp[1], temp, strlen(temp));
225 temp[0] = '\n';
226 }
227 }
228 XmTextInsert(Display_bulletins_text,pos,temp);
229 }
230 temp_ptr = XmTextFieldGetString(dist_data);
231 bulletin_range = atoi(temp_ptr);
232 XtFree(temp_ptr);
233 }
234
235 end_critical_section(&display_bulletins_dialog_lock, "bulletin_gui.c:bulletin_message" );
236
237 }
238 }
239
240
241
242
243
bulletin_line(Message * fill)244 static void bulletin_line(Message *fill)
245 {
246 bulletin_message(fill->from_call_sign, fill->call_sign, fill->message_line, fill->sec_heard);
247 }
248
249
250
251
252
scan_bulletin_file(void)253 static void scan_bulletin_file(void)
254 {
255 mscan_file(MESSAGE_BULLETIN, bulletin_line);
256 }
257
258
259
260
261
262 // bulletin_data_add
263 //
264 // Adds the bulletin to the message database. Updates the Bulletins
265 // dialog if it is up. Causes Bulletins dialog to pop up if the
266 // bulletin matches certain parameters.
267 //
268 long temp_bulletin_record;
269
bulletin_data_add(char * call_sign,char * from_call,char * data,char * seq,char type,char from)270 void bulletin_data_add(char *call_sign, char *from_call, char *data,
271 char *seq, char type, char from)
272 {
273 int distance = -1;
274
275 // Add to the message database
276 (void)msg_data_add(call_sign,
277 from_call,
278 data,
279 " ", // Need something here. Empty string no good.
280 MESSAGE_BULLETIN,
281 from,
282 &temp_bulletin_record);
283
284 // If we received a NEW bulletin
285 if (temp_bulletin_record == -1L)
286 {
287 char temp[10];
288
289
290 //fprintf(stderr,"We think it's a new bulletin!\n");
291
292 // We add to the distance in order to come up with 0.0
293 // if the distance is not known at all (no position
294 // found yet).
295 distance = (int)(distance_from_my_station(from_call,temp) + 0.9999);
296
297 if ( (bulletin_range == 0)
298 || (distance <= bulletin_range && distance > 0)
299 || (view_zero_distance_bulletins && distance == 0.0) )
300 {
301 // We have a _new_ bulletin that's within our
302 // current range setting. Note that it's also possible
303 // to have a zero distance for the bulletin (we haven't
304 // heard a posit from the sending station yet), then get
305 // a posit later.
306
307 if (debug_level & 1)
308 {
309 fprintf(stderr,"New bulletin:");
310 fprintf(stderr,"%05d:%9s:%c:%c:%9s:%s:%s ",
311 distance,
312 call_sign,
313 type,
314 from,
315 from_call,
316 data,
317 seq);
318 fprintf(stderr," Distance ok:%d miles",distance);
319 }
320
321 if (pop_up_new_bulletins)
322 {
323 //fprintf(stderr,"bulletin_data_add: popping up bulletins\n");
324 popup_bulletins();
325 if (debug_level & 1)
326 {
327 fprintf(stderr,"\n");
328 }
329 }
330 else
331 {
332 if (debug_level & 1)
333 {
334 fprintf(stderr,", but popups disabled!\n");
335 }
336 }
337 }
338 else
339 {
340 // fprintf(stderr,", but distance didn't work out!\n");
341 }
342 }
343 // Update the View->Bulletins dialog if it's up
344 bulletin_message(from_call,
345 call_sign,
346 data,
347 sec_now());
348
349 }
350
351
352
353
354
355 // Find each bulletin that is within our range _and_ within our time
356 // parameters for new bulletins. Count them only. Results returned
357 // in the new_bulletin_count variable.
count_bulletin_messages(char * call_sign,char * packet_message,time_t sec_heard)358 void count_bulletin_messages(char *call_sign, char *packet_message, time_t sec_heard)
359 {
360 char temp_my_course[10];
361 double distance;
362
363 if ( (packet_message != NULL) && (strlen(packet_message) > MAX_MESSAGE_LENGTH) )
364 {
365 if (debug_level & 1)
366 {
367 fprintf(stderr,"bulletin_message: Message length too long\n");
368 }
369 return;
370 }
371
372 distance = distance_from_my_station(call_sign,temp_my_course);
373
374 // Operands of <= have incompatible types (double, int):
375 if ( ( ((int)distance <= bulletin_range) && (distance > 0.0) )
376 || (view_zero_distance_bulletins && distance == 0.0)
377 || ( (bulletin_range == 0) && (distance > 0.0) ) )
378 {
379
380 // Is it newer than our first new_bulletin timestamp?
381 if (sec_heard >= first_new_bulletin_time)
382 {
383 new_bulletin_count++;
384 }
385 }
386 }
387
388
389
390
391
count_bulletin_line(Message * fill)392 static void count_bulletin_line(Message *fill)
393 {
394 count_bulletin_messages(fill->from_call_sign, fill->message_line, fill->sec_heard);
395 }
396
397
398
399
400
count_new_bulletins(void)401 static void count_new_bulletins(void)
402 {
403 mscan_file(MESSAGE_BULLETIN, count_bulletin_line);
404 }
405
406
407
408
409
410 // Function called by mscan_file for each bulletin with zero for the
411 // position_known flag. See next function find_zero_position_bulletins()
412 //
zero_bulletin_processing(Message * fill)413 static void zero_bulletin_processing(Message *fill)
414 {
415 DataRow *p_station; // Pointer to station data
416
417
418 if (!fill->position_known)
419 {
420
421 //fprintf(stderr,"Position unknown: %s:%s\n",
422 // fill->from_call_sign,
423 // fill->message_line);
424
425 // Check to see if we _now_ have a position for this non-new
426 // bulletin. If so, change the position_known flag on that
427 // record to a one, update the record, set the proper timers
428 // and then schedule a popup if it fits within our current
429 // parameters.
430
431 if ( search_station_name(&p_station,fill->from_call_sign,1) )
432 {
433 // Found a bulletin for which we get to fill in a new
434 // position!
435
436 if ( (p_station->coord_lon == 0l)
437 && (p_station->coord_lat == 0l) )
438 {
439 //fprintf(stderr,"Found it but still no valid position!\n");
440 }
441 else // Found valid position for this bulletin
442 {
443
444 //fprintf(stderr,"Found it now! %s:%s\n",
445 // fill->from_call_sign,
446 // fill->message_line);
447
448 // Mark it as found
449 fill->position_known = 1;
450
451 // Fake the timestamp so that we check messages back
452 // to at least this one we just found. Allow for the
453 // fact that we might find several older messages, so
454 // we only want to keep taking the timestamp backwards
455 // in time here.
456 if (first_new_bulletin_time > (fill->sec_heard) )
457 {
458 first_new_bulletin_time = fill->sec_heard;
459 }
460
461 // Check whether we really wish to pop them up
462 if (pop_up_new_bulletins)
463 {
464 int distance;
465 char temp_my_course[10];
466
467 distance = (int)(distance_from_my_station(fill->from_call_sign,
468 temp_my_course) + 0.9999);
469
470 if ( (bulletin_range == 0)
471 || (distance <= bulletin_range && distance > 0) )
472 {
473 if (debug_level & 1)
474 {
475 fprintf(stderr,"Filled in distance for earlier bulletin:%d miles\n",
476 distance);
477 }
478
479 // If view_zero_distance_bulletins was not
480 // turned on, then we probably haven't seen
481 // this bulletin until now. Popup up the
482 // Bulletin dialog.
483 if (!view_zero_distance_bulletins)
484 {
485 //fprintf(stderr,"zero_bulletin_processing: popping up bulletins\n");
486 popup_bulletins();
487 }
488 }
489 }
490 }
491 }
492 else
493 {
494 // No position known for the bulletin. Skip it for now.
495 //fprintf(stderr,"Still not found\n");
496 }
497 }
498 }
499
500
501
502
503
504 // Find all bulletins that have a zero for the position_known flag.
505 // Calls the function above for each bulletin.
506 //
find_zero_position_bulletins(void)507 static void find_zero_position_bulletins(void)
508 {
509 mscan_file(MESSAGE_BULLETIN, zero_bulletin_processing);
510 }
511
512
513
514
515
516 // Function called by main.c:UpdateTime(). Checks whether enough
517 // time has passed since the last new bulletin came in (so that the
518 // posit for it might come in as well). If so, checks for bulletins
519 // that are newer than first_new_bulletin_time and fit within our
520 // range. If any found, it updates the Bulletins dialog.
521 time_t last_bulletin_check = (time_t)0l;
522
check_for_new_bulletins(int curr_sec)523 void check_for_new_bulletins(int curr_sec)
524 {
525
526 // Check every 15 seconds max
527 if ( (last_bulletin_check + 15) > curr_sec )
528 {
529 return;
530 }
531 last_bulletin_check = curr_sec;
532
533 // Look first to see if we might be able to fill in positions on
534 // any older bulletins, then cause a popup for those that fit
535 // our parameters. The below function sets new_bulletin_flag if
536 // it is able to fill in a distance for an older bulletin.
537 // Note: This is time-consuming!
538 find_zero_position_bulletins();
539
540 // Any new bulletins to check? If not, return
541 if (!new_bulletin_flag)
542 {
543 return;
544 }
545
546 // Enough time passed since most recent bulletin? Need to have
547 // enough time to perhaps fill in a distance for each bulletin.
548 if ( (last_new_bulletin_time + 15) > curr_sec )
549 {
550 //fprintf(stderr,"Not enough time has passed\n");
551 return;
552 }
553
554 // If we get to here, then we think we may have at least one new
555 // bulletin, and the latest arrived more than XX seconds ago
556 // (currently 15 seconds). Check for bulletins which have
557 // timestamps equal to or newer than first_new_bulletin_time and
558 // fit within our range.
559
560 new_bulletin_count = 0;
561
562 //fprintf(stderr,"Checking for new bulletins\n");
563
564 count_new_bulletins();
565
566 //fprintf(stderr,"%d new bulletins found\n",new_bulletin_count);
567
568 if (new_bulletin_count)
569 {
570 //fprintf(stderr,"check_for_new_bulletins: popping up bulletins\n");
571 popup_bulletins();
572
573 if (debug_level & 1)
574 {
575 fprintf(stderr,"New bulletins (%d) caused popup!\n",new_bulletin_count);
576 }
577 }
578 else
579 {
580 if (debug_level & 1)
581 {
582 fprintf(stderr,"No bulletin popup generated.\n");
583 }
584 }
585
586 // Reset so that we can do it all over again later. We need
587 // mutex locks protecting these variables.
588 first_new_bulletin_time = last_new_bulletin_time + 1;
589 new_bulletin_flag = 0;
590 }
591
592
593
594
595
Display_bulletins_destroy_shell(Widget UNUSED (widget),XtPointer clientData,XtPointer UNUSED (callData))596 void Display_bulletins_destroy_shell(Widget UNUSED(widget), XtPointer clientData, XtPointer UNUSED(callData) )
597 {
598 Widget shell = (Widget) clientData;
599 char *temp_ptr;
600
601
602 // Keep this. It stores the range in a global variable when we destroy the dialog.
603 temp_ptr = XmTextFieldGetString(dist_data);
604 bulletin_range = atoi(temp_ptr);
605 XtFree(temp_ptr);
606
607 XtPopdown(shell);
608
609 begin_critical_section(&display_bulletins_dialog_lock, "bulletin_gui.c:Display_bulletins_destroy_shell" );
610
611 XtDestroyWidget(shell);
612 Display_bulletins_dialog = (Widget)NULL;
613
614 end_critical_section(&display_bulletins_dialog_lock, "bulletin_gui.c:Display_bulletins_destroy_shell" );
615
616 }
617
618
619
620
621
Display_bulletins_change_range(Widget widget,XtPointer clientData,XtPointer callData)622 void Display_bulletins_change_range(Widget widget, XtPointer clientData, XtPointer callData)
623 {
624 char *temp_ptr;
625
626
627 // Keep this. It stores the range in a global variable when we destroy the dialog.
628 temp_ptr = XmTextFieldGetString(dist_data);
629 bulletin_range = atoi(temp_ptr);
630 XtFree(temp_ptr);
631
632 view_zero_distance_bulletins = (int)XmToggleButtonGetState(zero_bulletin_data);
633 //fprintf(stderr,"%d\n",view_zero_distance_bulletins);
634
635 Display_bulletins_destroy_shell( widget, clientData, callData);
636 Bulletins( widget, clientData, callData);
637 }
638
639
640
641
642
Zero_Bulletin_Data_toggle(Widget widget,XtPointer clientData,XtPointer callData)643 void Zero_Bulletin_Data_toggle( Widget widget, XtPointer clientData, XtPointer callData)
644 {
645 char *which = (char *)clientData;
646 XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *)callData;
647
648 if(state->set)
649 {
650 view_zero_distance_bulletins = atoi(which);
651 }
652 else
653 {
654 view_zero_distance_bulletins = 0;
655 }
656
657 Display_bulletins_destroy_shell( widget, Display_bulletins_dialog, callData);
658 Bulletins( widget, clientData, callData);
659 }
660
661
662
663
664
Bulletins(Widget UNUSED (w),XtPointer UNUSED (clientData),XtPointer UNUSED (callData))665 void Bulletins(Widget UNUSED(w), XtPointer UNUSED(clientData), XtPointer UNUSED(callData) )
666 {
667 Widget pane, form, button_range, button_close, dist, dist_units;
668 unsigned int n;
669 Arg args[50];
670 Atom delw;
671 char temp[10];
672
673
674 if(!Display_bulletins_dialog)
675 {
676
677
678 begin_critical_section(&display_bulletins_dialog_lock, "bulletin_gui.c:Bulletins" );
679
680
681 Display_bulletins_dialog = XtVaCreatePopupShell(langcode("BULMW00001"),
682 xmDialogShellWidgetClass,
683 appshell,
684 XmNdeleteResponse,XmDESTROY,
685 XmNdefaultPosition, FALSE,
686 XmNfontList, fontlist1,
687 NULL);
688
689 pane = XtVaCreateWidget("Bulletins pane",
690 xmPanedWindowWidgetClass,
691 Display_bulletins_dialog,
692 MY_FOREGROUND_COLOR,
693 MY_BACKGROUND_COLOR,
694 NULL);
695
696 form = XtVaCreateWidget("Bulletins form",
697 xmFormWidgetClass,
698 pane,
699 XmNfractionBase, 5,
700 XmNautoUnmanage, FALSE,
701 XmNshadowThickness, 1,
702 MY_FOREGROUND_COLOR,
703 MY_BACKGROUND_COLOR,
704 NULL);
705
706 dist = XtVaCreateManagedWidget(langcode("BULMW00002"),
707 xmLabelWidgetClass,
708 form,
709 XmNtopAttachment, XmATTACH_FORM,
710 XmNtopOffset, 10,
711 XmNbottomAttachment, XmATTACH_NONE,
712 XmNleftAttachment, XmATTACH_FORM,
713 XmNleftOffset, 10,
714 XmNrightAttachment, XmATTACH_NONE,
715 MY_FOREGROUND_COLOR,
716 MY_BACKGROUND_COLOR,
717 XmNfontList, fontlist1,
718 NULL);
719
720 dist_data = XtVaCreateManagedWidget("dist_data",
721 xmTextFieldWidgetClass,
722 form,
723 XmNeditable, TRUE,
724 XmNcursorPositionVisible, TRUE,
725 XmNsensitive, TRUE,
726 XmNshadowThickness, 1,
727 XmNcolumns, 8,
728 XmNwidth, ((8*7)+2),
729 XmNmaxLength, 8,
730 XmNbackground, colors[0x0f],
731 XmNtopAttachment, XmATTACH_FORM,
732 XmNtopOffset, 5,
733 XmNbottomAttachment,XmATTACH_NONE,
734 XmNleftAttachment, XmATTACH_WIDGET,
735 XmNleftWidget, dist,
736 XmNleftOffset, 10,
737 XmNrightAttachment,XmATTACH_NONE,
738 XmNnavigationType, XmTAB_GROUP,
739 XmNfontList, fontlist1,
740 NULL);
741
742 dist_units = XtVaCreateManagedWidget((english_units?langcode("UNIOP00004"):langcode("UNIOP00005")),
743 xmLabelWidgetClass,
744 form,
745 XmNtopAttachment, XmATTACH_FORM,
746 XmNtopOffset, 10,
747 XmNbottomAttachment, XmATTACH_NONE,
748 XmNleftAttachment, XmATTACH_WIDGET,
749 XmNleftWidget, dist_data,
750 XmNleftOffset, 10,
751 XmNrightAttachment, XmATTACH_NONE,
752 MY_FOREGROUND_COLOR,
753 MY_BACKGROUND_COLOR,
754 XmNfontList, fontlist1,
755 NULL);
756
757 button_range = XtVaCreateManagedWidget(langcode("BULMW00003"),
758 xmPushButtonGadgetClass,
759 form,
760 XmNtopAttachment, XmATTACH_FORM,
761 XmNtopOffset, 5,
762 XmNbottomAttachment, XmATTACH_NONE,
763 XmNleftAttachment, XmATTACH_WIDGET,
764 XmNleftWidget, dist_units,
765 XmNleftOffset, 10,
766 XmNrightAttachment, XmATTACH_NONE,
767 XmNnavigationType, XmTAB_GROUP,
768 MY_FOREGROUND_COLOR,
769 MY_BACKGROUND_COLOR,
770 XmNfontList, fontlist1,
771 NULL);
772
773 zero_bulletin_data = XtVaCreateManagedWidget(langcode("WPUPCFD029"),
774 xmToggleButtonWidgetClass,
775 form,
776 XmNtopAttachment, XmATTACH_FORM,
777 XmNtopOffset, 5,
778 XmNbottomAttachment, XmATTACH_NONE,
779 XmNleftAttachment, XmATTACH_WIDGET,
780 XmNleftWidget, button_range,
781 XmNleftOffset,10,
782 XmNrightAttachment, XmATTACH_NONE,
783 XmNnavigationType, XmTAB_GROUP,
784 MY_FOREGROUND_COLOR,
785 MY_BACKGROUND_COLOR,
786 XmNfontList, fontlist1,
787 NULL);
788 XtAddCallback(zero_bulletin_data,XmNvalueChangedCallback,Zero_Bulletin_Data_toggle,"1");
789 if (view_zero_distance_bulletins)
790 {
791 XmToggleButtonSetState(zero_bulletin_data,TRUE,FALSE);
792 }
793 else
794 {
795 XmToggleButtonSetState(zero_bulletin_data,FALSE,FALSE);
796 }
797
798 n=0;
799 XtSetArg(args[n], XmNrows, 15);
800 n++;
801 XtSetArg(args[n], XmNcolumns, 108);
802 n++;
803 XtSetArg(args[n], XmNtraversalOn, FALSE);
804 n++;
805 XtSetArg(args[n], XmNeditable, FALSE);
806 n++;
807 XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT);
808 n++;
809 XtSetArg(args[n], XmNwordWrap, TRUE);
810 n++;
811 XtSetArg(args[n], XmNscrollHorizontal, TRUE);
812 n++;
813 XtSetArg(args[n], XmNscrollVertical, TRUE);
814 n++;
815 XtSetArg(args[n], XmNcursorPositionVisible, FALSE);
816 n++;
817 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET);
818 n++;
819 XtSetArg(args[n], XmNtopWidget, dist);
820 n++;
821 XtSetArg(args[n], XmNtopOffset, 20);
822 n++;
823 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);
824 n++;
825 XtSetArg(args[n], XmNbottomOffset, 30);
826 n++;
827 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);
828 n++;
829 XtSetArg(args[n], XmNleftOffset, 5);
830 n++;
831 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);
832 n++;
833 XtSetArg(args[n], XmNrightOffset, 5);
834 n++;
835 XtSetArg(args[n], XmNforeground, MY_FG_COLOR);
836 n++;
837 XtSetArg(args[n], XmNbackground, MY_BG_COLOR);
838 n++;
839 XtSetArg(args[n], XmNfontList, fontlist1);
840 n++;
841
842
843 Display_bulletins_text = XmCreateScrolledText(form,
844 "Bulletins text",
845 args,
846 n);
847
848 button_close = XtVaCreateManagedWidget(langcode("UNIOP00003"),
849 xmPushButtonGadgetClass,
850 form,
851 XmNtopAttachment, XmATTACH_NONE,
852 XmNbottomAttachment, XmATTACH_FORM,
853 XmNbottomOffset, 5,
854 XmNleftAttachment, XmATTACH_POSITION,
855 XmNleftPosition, 2,
856 XmNrightAttachment, XmATTACH_POSITION,
857 XmNrightPosition, 3,
858 MY_FOREGROUND_COLOR,
859 MY_BACKGROUND_COLOR,
860 XmNfontList, fontlist1,
861 NULL);
862
863 XtAddCallback(button_range, XmNactivateCallback, Display_bulletins_change_range, Display_bulletins_dialog);
864 XtAddCallback(button_close, XmNactivateCallback, Display_bulletins_destroy_shell, Display_bulletins_dialog);
865
866 pos_dialog(Display_bulletins_dialog);
867
868 delw = XmInternAtom(XtDisplay(Display_bulletins_dialog),"WM_DELETE_WINDOW", FALSE);
869 XmAddWMProtocolCallback(Display_bulletins_dialog, delw, Display_bulletins_destroy_shell, (XtPointer)Display_bulletins_dialog);
870
871 xastir_snprintf(temp, sizeof(temp), "%d", bulletin_range);
872 XmTextFieldSetString(dist_data, temp);
873
874 XtManageChild(form);
875 XtManageChild(Display_bulletins_text);
876 XtVaSetValues(Display_bulletins_text, XmNbackground, colors[0x0f], NULL);
877 XtManageChild(pane);
878
879 redraw_on_new_packet_data=1;
880 XtPopup(Display_bulletins_dialog,XtGrabNone);
881
882 end_critical_section(&display_bulletins_dialog_lock, "bulletin_gui.c:Bulletins" );
883
884 scan_bulletin_file();
885
886 // Move focus to the Close button. This appears to
887 // highlight the button fine, but we're not able to hit the
888 // <Enter> key to have that default function happen. Note:
889 // We _can_ hit the <SPACE> key, and that activates the
890 // option.
891 //XmUpdateDisplay(Display_bulletins_dialog);
892 XmProcessTraversal(button_close, XmTRAVERSE_CURRENT);
893
894 }
895 else
896 {
897 (void)XRaiseWindow(XtDisplay(Display_bulletins_dialog), XtWindow(Display_bulletins_dialog));
898 }
899 }
900
901
902