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 <stdlib.h>
32 #include <math.h>
33 
34 #include <Xm/XmAll.h>
35 
36 #include "xastir.h"
37 #include "database.h"
38 #include "draw_symbols.h"
39 #include "main.h"
40 #include "util.h"
41 #include "color.h"
42 #include "maps.h"
43 
44 // Must be last include file
45 #include "leak_detection.h"
46 
47 
48 extern XmFontList fontlist1;    // Menu/System fontlist
49 
50 
51 #define ANGLE_UPDOWN 45         /* prefer horizontal cars if less than 45 degrees */
52 
53 int symbols_loaded = 0;
54 int symbols_cache[5] = {0,0,0,0,0};
55 Widget select_symbol_dialog = (Widget)NULL;
56 static xastir_mutex select_symbol_dialog_lock;
57 Pixmap select_icons[(126-32)*2];    //33 to 126 with both '/' and '\' symbols (94 * 2) or 188
58 int symbol_change_requested_from = 0;
59 
60 
61 
62 
63 
draw_symbols_init(void)64 void draw_symbols_init(void)
65 {
66   init_critical_section( &select_symbol_dialog_lock );
67 }
68 
69 
70 
71 
72 
73 /*** symbol data ***/
74 
clear_symbol_data(void)75 void clear_symbol_data(void)
76 {
77   int my_size;
78   int i;
79   char *data_ptr;
80 
81   data_ptr = (char *)symbol_data;
82   my_size = (int)sizeof(SymbolData);
83   for (i=0; i<my_size; i++)
84   {
85     *data_ptr++ = '\0';
86   }
87   symbols_loaded = 0;
88 }
89 
90 
91 
92 
93 
94 /*
95  *  Draw nice looking text
96  *
97  *  Modified to take into account the font metrics - N7IPB
98  *  4/8/2016
99  */
draw_nice_string(Widget w,Pixmap where,int style,long x,long y,char * text,int bgcolor,int fgcolor,int length)100 void draw_nice_string(Widget w, Pixmap where, int style, long x, long y, char *text, int bgcolor, int fgcolor, int length)
101 {
102   GContext gcontext;
103   XFontStruct *xfs_ptr;
104   int font_width, font_height;
105 
106   if (x > screen_width)
107   {
108     return;
109   }
110   if (x < 0)
111   {
112     return;
113   }
114   if (y > screen_height)
115   {
116     return;
117   }
118   if (y < 0)
119   {
120     return;
121   }
122 
123   // With a large font, the background rectangle is too small.  Need
124   // to include the font metrics in this drawing algorithm.
125 
126   gcontext = XGContextFromGC(gc);
127   xfs_ptr = XQueryFont(XtDisplay(w), gcontext);
128   font_width = (int)((xfs_ptr->max_bounds.width
129                       + xfs_ptr->max_bounds.width
130                       + xfs_ptr->max_bounds.width
131                       + xfs_ptr->min_bounds.width) / 4);
132 
133   font_height = xfs_ptr->max_bounds.ascent
134                 + xfs_ptr->max_bounds.descent;
135 
136   switch (style)
137   {
138 
139     case 0:
140       // make outline style
141       (void)XSetForeground(XtDisplay(w),gc,colors[bgcolor]);
142       // draw an outline 1 pixel bigger than text
143       (void)XDrawString(XtDisplay(w),where,gc,x+1,y-1,text,length);
144       (void)XDrawString(XtDisplay(w),where,gc,x+1,y,text,length);
145       (void)XDrawString(XtDisplay(w),where,gc,x+1,y+1,text,length);
146       (void)XDrawString(XtDisplay(w),where,gc,x-1,y,text,length);
147       (void)XDrawString(XtDisplay(w),where,gc,x-1,y-1,text,length);
148       (void)XDrawString(XtDisplay(w),where,gc,x-1,y+1,text,length);
149       (void)XDrawString(XtDisplay(w),where,gc,x,y+1,text,length);
150       (void)XDrawString(XtDisplay(w),where,gc,x,y-1,text,length);
151       break;
152 
153     case 1:
154       // draw text the old way in a gray box
155       // Leave this next one hard-coded to 0xff.  This keeps
156       // the background as gray.
157 
158       (void)XSetForeground(XtDisplay(w),gc,colors[0xff]);
159       (void)XFillRectangle( XtDisplay(w),
160                             where,
161                             gc,
162                             x,                    // X
163                             y-(font_height-(font_height/4)),          // Y
164                             // Get the actual width of the text in pixels
165                             get_text_width(w,text),  // width
166                             font_height);         // height
167 
168       (void)XSetForeground(XtDisplay(w),gc,colors[bgcolor]);
169       (void)XDrawString(XtDisplay(w),where,gc,x+(font_height/10),y+(font_width/8),text,length);
170 
171       break;
172 
173     case 2:
174       // draw white or colored text in a black box
175 
176       (void)XSetForeground( XtDisplay(w),
177                             gc,
178                             GetPixelByName(w,"black") );
179 
180       (void)XFillRectangle( XtDisplay(w),
181                             where,
182                             gc,
183                             x,                    // X
184                             y-(font_height-(font_height/4)),          // Y
185                             // Get the actual width of the text in pixels
186                             get_text_width(w,text),
187                             font_height);         // height
188 
189       break;
190 
191     // Case three will be used in a future release with an additional
192     // Station Text Style selection
193     case 3:
194     default:
195       // Real Shadow text on a transparent background
196 
197       (void)XSetForeground(XtDisplay(w),gc,colors[bgcolor]);
198       (void)XDrawString(XtDisplay(w),where,gc,x+(font_height/10),y+(font_width/8),text,length);
199 
200 
201       break;
202 
203   }
204 
205   // finally draw the text
206   (void)XSetForeground(XtDisplay(w),gc,colors[fgcolor]);
207   (void)XDrawString(XtDisplay(w),where,gc,x,y,text,length);
208 
209   // And free our font info
210 
211   if (xfs_ptr)
212   {
213     // This leaks memory if the last parameter is a "0"
214     XFreeFontInfo(NULL, xfs_ptr, 1);
215   }
216 
217 
218 }
219 
220 /* symbol drawing routines */
221 
222 // Function to draw a line between a WP symbol "\/" and the
223 // transmitting station.  We pass it the WP symbol.  It does a
224 // lookup for the transmitting station callsign to get those
225 // coordinates, then draws a line between the two symbols if
226 // possible (both on screen).
227 //
228 // If the symbol was a Waypoint symbol, "\/", we need to draw a line
229 // from the transmitting station to the waypoint symbol according to
230 // the spec.  Take care to not draw the line any longer than needed
231 // (don't exercise the X11 long-line drawing bug).  According to the
232 // spec we also need to change the symbol to just a red dot, but
233 // it's nice having the "WP" above it so we can differentiate it
234 // from the other red dot symbol.
235 //
236 // We should skip drawing the line if the object/item is not being drawn.
237 // Should we skip it if the origination station isn't being drawn?
238 // Do we need to add yet another togglebutton to enable/disable this line?
239 //
240 // Note that this type of operation, making a relation between two
241 // symbols, breaks our paradism quite a bit.  Until now all symbols
242 // have been independent of each other.  Perhaps we should store the
243 // location of one symbol in the data of the other so that we won't
244 // have to compare back and forth.  This won't help much if either
245 // or both symbols are moving.  Probably better to just do a lookup
246 // of the originating station by callsign through our lists and then
247 // draw the line between the two coordinates each time.
248 //
draw_WP_line(DataRow * p_station,int ambiguity_flag,long ambiguity_coord_lon,long ambiguity_coord_lat,Pixmap where,Widget UNUSED (w))249 void draw_WP_line(DataRow *p_station,
250                   int ambiguity_flag,
251                   long ambiguity_coord_lon,
252                   long ambiguity_coord_lat,
253                   Pixmap where,
254                   Widget UNUSED(w) )
255 {
256   DataRow *transmitting_station = NULL;
257   int my_course;
258   long x_long, y_lat;
259   long x_long2, y_lat2;
260   double lat_m;
261   long x, y;
262   long x2, y2;
263   double temp;
264   int color = trail_colors[p_station->trail_color];
265   float temp_latitude, temp_latitude2;
266   float temp_longitude, temp_longitude2;
267 
268 
269   // Compute screen position of waypoint symbol
270   if (ambiguity_flag)
271   {
272     x_long = ambiguity_coord_lon;
273     y_lat = ambiguity_coord_lat;
274   }
275   else
276   {
277     x_long = p_station->coord_lon;
278     y_lat = p_station->coord_lat;
279   }
280 
281   // x & y are screen location of waypoint symbol
282   x = (x_long - NW_corner_longitude)/scale_x;
283   y = (y_lat - NW_corner_latitude)/scale_y;
284 
285   // Find transmitting station, get it's position.
286   // p_station->origin contains the callsign for the transmitting
287   // station.  Do a lookup on that callsign through our database
288   // to get the position of that station.
289 
290   if (!search_station_name(&transmitting_station,p_station->origin,1))
291   {
292     // Can't find call,
293     return;
294   }
295 
296   x_long2 = transmitting_station->coord_lon;
297   y_lat2 = transmitting_station->coord_lat;
298 
299   // x2 & y2 are screen location of transmitting station
300   x2 = (x_long2 - NW_corner_longitude)/scale_x;
301   y2 = (y_lat2 - NW_corner_latitude)/scale_y;
302 
303 
304   /*
305       if ((x2 - x) > 0) {
306           my_course = (int)( 57.29578
307               * atan( (double)((y2-(y*1.0)) / (x2-(x*1.0) ) ) ) );
308       }
309       else {
310           my_course = (int)( 57.29578
311               * atan( (double)((y2-(y*1.0)) / (x-(x2*1.0) ) ) ) );
312       }
313   */
314 
315   // Use the mid-latitude formulas for calculating the rhumb line
316   // course.  Modified to minimize the number of conversions we
317   // need to do.
318 //    lat_m = (double)( (y_lat + y_lat2) / 2.0 );
319 
320   // Convert from Xastir coordinate system
321 //    lat_m = (double)( -((lat_m - 32400000l) / 360000.0) );
322 
323   lat_m = -((y_lat - 32400000l) / 360000.0)
324           + -((y_lat2 - 32400000l) / 360000.0);
325   lat_m = lat_m / 2.0;
326 
327   convert_from_xastir_coordinates(&temp_longitude2,
328                                   &temp_latitude2,
329                                   x_long2,
330                                   y_lat2);
331 
332   convert_from_xastir_coordinates(&temp_longitude,
333                                   &temp_latitude,
334                                   x_long,
335                                   y_lat);
336 
337   temp = (double)( (temp_longitude2 - temp_longitude)
338                    / (temp_latitude2 - temp_latitude) );
339 // Check for divide-by-zero here????
340 
341   // Calculate course and convert to degrees
342   my_course = (int)( 57.29578 * atan( cos(lat_m) * temp) );
343 
344   // The arctan function returns values between -90 and +90.  To
345   // obtain the true course we apply the following rules:
346   if (temp_latitude2 > temp_latitude
347       && temp_longitude2 > temp_longitude)
348   {
349     // Do nothing.
350   }
351   else if (temp_latitude2 < temp_latitude
352            && temp_longitude2 > temp_longitude)
353   {
354     my_course = 180 - my_course;
355   }
356   else if (temp_latitude2 < temp_latitude
357            && temp_longitude2 < temp_longitude)
358   {
359     my_course = 180 + my_course;
360   }
361   else if (temp_latitude2 > temp_latitude
362            && temp_longitude2 < temp_longitude)
363   {
364     my_course = 360 - my_course;
365   }
366   else
367   {
368     // ??
369     // Do nothing.
370   }
371 
372 //fprintf(stderr,"course:%d\n", my_course);
373 
374   // Convert to screen angle
375 //    my_course = my_course + 90;
376 
377   // Compute whether either of them are on-screen.  If so, draw at
378   // least part of the line between them.
379   (void)XSetLineAttributes(XtDisplay(da), gc, 0, LineOnOffDash, CapButt,JoinMiter);
380   (void)XSetForeground(XtDisplay(da),gc,color); // red3
381 
382 // Check that our parameters are within spec for XDrawLine.  We'll
383 // stick to 16-bit values here due to warnings on the man-page
384 // regarding XSegment structs and the protocol only handling
385 // short's/unsigned short's, just in case.
386 
387   (void)XDrawLine(XtDisplay(da),where,gc,
388                   l16(x),      // int
389                   l16(y),      // int
390                   l16(x2),     // int
391                   l16(y2));    // int
392 }
393 
394 
395 
396 
397 
398 //draw_pod_circle(64000000l, 32400000l, 10, colors[0x44], pixmap_final);
399 //
400 // Probability of Detection circle:  A circle around the point last
401 // seen drawn at the distance that a person of that description
402 // could travel since they were last seen.  It helps to limit a
403 // search to a reasonable area.
404 //
405 // It'd be nice to have some method of showing where the center of
406 // the circle was as well, in case we don't have a PLS object placed
407 // there also.  Perhaps a small dot and/or four lines going from
408 // that point to the edge of the circle?
409 //
410 // range is in miles
411 // x_long/y_lat are in Xastir lat/lon units
412 //
draw_pod_circle(long x_long,long y_lat,double range,int color,Pixmap where,int sec_heard)413 void draw_pod_circle(long x_long, long y_lat, double range, int color, Pixmap where, int sec_heard)
414 {
415   double diameter;
416 
417   if ( ((sec_old+sec_heard)>sec_now()) || Select_.old_data )
418   {
419 
420 
421 // Prevents it from being drawn when the symbol is off-screen.
422 // It'd be better to check for lat/long +/- range to see if it's on the screen.
423 
424 //    if ((x_long>NW_corner_longitude) && (x_long<SE_corner_longitude)) {
425 
426 //        if ((y_lat>NW_corner_latitude) && (y_lat<SE_corner_latitude)) {
427 
428 //            if ((x_long < 0) || (x_long > 129600000l))
429 //                return;
430 
431 //            if ((y_lat < 0) || (y_lat > 64800000l))
432 //                return;
433 
434     // Range is in miles.  Bottom term is in meters before the 0.0006214
435     // multiplication factor which converts it to miles.
436     // Equation is:  2 * ( range(mi) / x-distance across window(mi) )
437     diameter = 2.0 * ( range/
438                        (scale_x * calc_dscale_x(center_longitude,center_latitude) * 0.0006214 ) );
439 
440     // If less than 4 pixels across, skip drawing it.
441     if (diameter <= 4.0)
442     {
443       return;
444     }
445 
446     //fprintf(stderr,"Range:%f\tDiameter:%f\n",range,diameter);
447 
448     (void)XSetLineAttributes(XtDisplay(da), gc, 2, LineSolid, CapButt,JoinMiter);
449     //(void)XSetForeground(XtDisplay(da),gc,colors[0x0a]);
450     //(void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
451     (void)XSetForeground(XtDisplay(da),gc,color);
452 
453 // Check that our parameters are within spec for XDrawArc.  Tricky
454 // 'cuz the XArc struct has short's and unsigned short's, while
455 // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
456 // just to make sure.
457 
458     (void)XDrawArc(XtDisplay(da),where,gc,
459                    l16(((x_long-NW_corner_longitude)/scale_x)-(diameter/2)), // int
460                    l16(((y_lat-NW_corner_latitude)/scale_y)-(diameter/2)),   // int
461                    lu16(diameter),         // unsigned int
462                    lu16(diameter),         // unsigned int
463                    l16(0),                 // int
464                    l16(64*360));           // int
465 
466 // We may need to check for the lat/long being way too far
467 // off-screen, refusing to draw the circles if so, if and only if we
468 // get into X11 drawing bugs as-is.
469 
470 
471 //        }
472 //    }
473   }
474 }
475 
476 
477 
478 
479 
480 // range is in centimeters (0 to 65535 representing 0 to 655.35 meters)
481 // x_long/y_lat are in Xastir lat/lon units
482 // lat_precision/lon-precision are in 100ths of seconds of lat/lon
483 //
draw_precision_rectangle(long x_long,long y_lat,double UNUSED (range),unsigned int lat_precision,unsigned int lon_precision,int color,Pixmap where)484 void draw_precision_rectangle(long x_long,
485                               long y_lat,
486                               double UNUSED(range), // Not implemented yet
487                               unsigned int lat_precision,
488                               unsigned int lon_precision,
489                               int color,
490                               Pixmap where)
491 {
492 
493 // Prevents it from being drawn when the symbol is off-screen.
494 // It'd be better to check for lat/long +/- range to see if it's on the screen.
495 
496   if ((x_long>NW_corner_longitude) && (x_long<SE_corner_longitude))
497   {
498 
499     if ((y_lat>NW_corner_latitude) && (y_lat<SE_corner_latitude))
500     {
501       long x2, y2;
502 
503 
504 //            if ((x_long < 0) || (x_long > 129600000l))
505 //                return;
506 
507 //            if ((y_lat < 0) || (y_lat > 64800000l))
508 //                return;
509 
510       (void)XSetLineAttributes(XtDisplay(da), gc, 2, LineSolid, CapButt,JoinMiter);
511       //(void)XSetForeground(XtDisplay(da),gc,colors[0x0a]);
512       //(void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
513       (void)XSetForeground(XtDisplay(da),gc,color);
514 
515       if (x_long > 64800000L)
516       {
517         // Eastern hemisphere, add X's (go further east)
518         x2 = x_long + lon_precision;
519       }
520       else
521       {
522         // Western hemisphere, subtract X's (go further west)
523         x2 = x_long - lon_precision;
524       }
525 
526       if (y_lat > 32400000L)
527       {
528         // Southern hemisphere, add Y's (go further north)
529         y2 = y_lat + lat_precision;
530       }
531       else
532       {
533         // Northern hemisphere, subtract Y's (go further south)
534         y2 = y_lat - lat_precision;
535       }
536 
537       draw_vector(da, x_long, y_lat, x_long, y2, gc, where, 0); // x_long constant
538       draw_vector(da, x_long, y2, x2, y2, gc, where, 0); // y2 constant
539       draw_vector(da, x2, y2, x2, y_lat, gc, where, 0); // x2 constant
540       draw_vector(da, x2, y_lat, x_long, y_lat, gc, where, 0); // y_lat constant
541     }
542   }
543 }
544 
545 
546 
547 
548 
draw_phg_rng(long x_long,long y_lat,char * phg,time_t sec_heard,Pixmap where)549 void draw_phg_rng(long x_long, long y_lat, char *phg, time_t sec_heard, Pixmap where)
550 {
551   double range, diameter;
552   int offx,offy;
553   double tilt;
554   char is_rng;
555   char *strp;
556 
557 
558   if ( ((sec_old+sec_heard)>sec_now()) || Select_.old_data )
559   {
560     tilt=0.0;
561     is_rng=0;
562     offx=0;
563     offy=0;
564 
565     if (phg[0] == 'R' && phg[1] == 'N' && phg[2] == 'G')
566     {
567       is_rng = 1;
568     }
569 
570 
571     if (is_rng)
572     {
573       strp = &phg[3];
574       range = atof(strp);
575     }
576     else
577     {
578       range = phg_range(phg[3],phg[4],phg[5]);
579     }
580 
581     // Range is in miles.  Bottom term is in meters before the 0.0006214
582     // multiplication factor which converts it to miles.
583     // Equation is:  2 * ( range(mi) / x-distance across window(mi) )
584     diameter = 2.0 * ( range/
585                        (scale_x * calc_dscale_x(center_longitude,center_latitude) * 0.0006214 ) );
586 
587     // If less than 4 pixels across, skip drawing it.
588     if (diameter <= 4.0)
589     {
590       return;
591     }
592 
593     if (!is_rng)    // Figure out the directivity, if outside range of 0-8 it's declared to be an omni
594     {
595 
596       switch (phg[6]-'0')
597       {
598 
599         case(0):
600           offx=0;
601           offy=0;
602           break;
603 
604         case(1):    //  45
605           offx=-1*(diameter/6);
606           offy=diameter/6;
607           tilt=5.49778;
608           break;
609 
610         case(2):    //  90
611           offx=-1*(diameter/6);
612           offy=0;
613           tilt=0;
614           break;
615 
616         case(3):    // 135
617           offx=-1*(diameter/6);
618           offy=-1*(diameter/6);
619           tilt=.78539;
620           break;
621 
622         case(4):    // 180
623           offx=0;
624           offy=-1*(diameter/6);
625           tilt=1.5707;
626           break;
627 
628         case(5):    // 225
629           offx=diameter/6;
630           offy=-1*(diameter/6);
631           tilt=2.3561;
632           break;
633 
634         case(6):    // 270
635           offx=diameter/6;
636           offy=0;
637           tilt=3.14159;
638           break;
639 
640         case(7):    // 315
641           offx=diameter/6;
642           offy=diameter/6;
643           tilt=3.92699;
644           break;
645 
646         case(8):    // 360
647           offx=0;
648           offy=diameter/6;
649           tilt=4.71238;
650           break;
651 
652         default:
653           offx=0;
654           offy=0;
655           break;
656       }   // End of switch
657     }
658 
659     (void)XSetLineAttributes(XtDisplay(da), gc, 1, LineSolid, CapButt,JoinMiter);
660 
661     if ((sec_old+sec_heard)>sec_now())
662     {
663       (void)XSetForeground(XtDisplay(da),gc,colors[0x0a]);
664     }
665     else
666     {
667       (void)XSetForeground(XtDisplay(da),gc,colors[0x52]);
668     }
669 
670     if (is_rng || phg[6]=='0')      // Draw circl
671     {
672 
673       // Check that our parameters are within spec for XDrawArc.  Tricky
674       // 'cuz the XArc struct has short's and unsigned short's, while
675       // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
676       // just to make sure.
677 
678       (void)XDrawArc(XtDisplay(da),where,gc,
679                      l16(((x_long-NW_corner_longitude)/scale_x)-(diameter/2)), // int
680                      l16(((y_lat-NW_corner_latitude)/scale_y)-(diameter/2)),   // int
681                      lu16(diameter), // unsigned int
682                      lu16(diameter), // unsigned int
683                      l16(0),         // int
684                      l16(64*360));   // int
685     }
686     else      // Draw oval to depict beam heading
687     {
688 
689       // If phg[6] != '0' we still draw a circle, but the center
690       // is offset in the indicated direction by 1/3 the radius.
691 
692       // This debug statement will almost never wind
693       // up selected, because 4095 means "all possible debugging"
694       //  It is being placed here SOLELY so that I can
695       // leave the setting of "tilt" (which should determine
696       // how an oval would be tilted to indicate directivity)
697       // without having GCC 6.x whine about it not being used.
698       if (debug_level == 4095)
699       {
700         fprintf(stderr,"If we had tilted ovals implemented, would have tilted one by %lf\n",tilt);
701       }
702 
703       // Draw Circle
704 
705       // Check that our parameters are within spec for XDrawArc.  Tricky
706       // 'cuz the XArc struct has short's and unsigned short's, while
707       // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
708       // just to make sure.
709 
710       (void)XDrawArc(XtDisplay(da),where,gc,
711                      l16(((x_long-NW_corner_longitude)/scale_x)-(diameter/2) - offx),  // int
712                      l16(((y_lat-NW_corner_latitude)/scale_y)-(diameter/2) - offy),    // int
713                      lu16(diameter), // unsigned int
714                      lu16(diameter), // unsigned int
715                      l16(0),         // int
716                      l16(64*360));   // int
717     }
718   }
719 }
720 
721 
722 
723 
724 
725 // Function to draw DF circles around objects/stations for DF'ing purposes.
726 //
727 // We change from filled circles to open circles at zoom level 128 for speed purposes.
728 //
draw_DF_circle(long x_long,long y_lat,char * shgd,time_t sec_heard,Pixmap where)729 void draw_DF_circle(long x_long, long y_lat, char *shgd, time_t sec_heard, Pixmap where)
730 {
731   double range, diameter;
732   int offx,offy;
733   double tilt;
734 
735 
736   if ( ((sec_old+sec_heard)>sec_now()) || Select_.old_data )
737   {
738     tilt=0.0;
739     offx=0;
740     offy=0;
741 
742     range = shg_range(shgd[3],shgd[4],shgd[5]);
743 
744     // Range is in miles.  Bottom term is in meters before the 0.0006214
745     // multiplication factor which converts it to miles.
746     // Equation is:  2 * ( range(mi) / x-distance across window(mi) )
747     //
748     diameter = 2.0 * ( range/
749                        (scale_x * calc_dscale_x(center_longitude,center_latitude) * 0.0006214 ) );
750 
751     // If less than 4 pixels across, skip drawing it.
752     if (diameter <= 4.0)
753     {
754       return;
755     }
756 
757     // Figure out the directivity, if outside range of 0-8 it's declared to be an omni
758     switch (shgd[6]-'0')
759     {
760       case(0):
761         offx=0;
762         offy=0;
763         break;
764 
765       case(1):    //  45
766         offx=-1*(diameter/6);
767         offy=diameter/6;
768         tilt=5.49778;
769         break;
770 
771       case(2):    //  90
772         offx=-1*(diameter/6);
773         offy=0;
774         tilt=0;
775         break;
776 
777       case(3):    // 135
778         offx=-1*(diameter/6);
779         offy=-1*(diameter/6);
780         tilt=.78539;
781         break;
782 
783       case(4):    // 180
784         offx=0;
785         offy=-1*(diameter/6);
786         tilt=1.5707;
787         break;
788 
789       case(5):    // 225
790         offx=diameter/6;
791         offy=-1*(diameter/6);
792         tilt=2.3561;
793         break;
794 
795       case(6):    // 270
796         offx=diameter/6;
797         offy=0;
798         tilt=3.14159;
799         break;
800 
801       case(7):    // 315
802         offx=diameter/6;
803         offy=diameter/6;
804         tilt=3.92699;
805         break;
806 
807       case(8):    // 360
808         offx=0;
809         offy=diameter/6;
810         tilt=4.71238;
811         break;
812 
813       default:
814         offx=0;
815         offy=0;
816         break;
817     }
818 
819     if (scale_y > 128)   // Don't fill in circle if zoomed in too far (too slow!)
820     {
821       (void)XSetLineAttributes(XtDisplay(da), gc_stipple, 1, LineSolid, CapButt,JoinMiter);
822     }
823     else
824     {
825       (void)XSetLineAttributes(XtDisplay(da), gc_stipple, 8, LineSolid, CapButt,JoinMiter);
826     }
827 
828     // Stipple the area instead of obscuring the map underneath
829     (void)XSetStipple(XtDisplay(da), gc_stipple, pixmap_50pct_stipple);
830     (void)XSetFillStyle(XtDisplay(da), gc_stipple, FillStippled);
831 
832     // Choose the color for the DF'ing circle
833     // We try to choose similar colors to those used in DOSaprs,
834     // which are qbasic or gwbasic colors.
835     switch (shgd[3])
836     {
837 
838       case '9':   // Light Red
839         if ((sec_old+sec_heard)>sec_now())  // New
840         {
841           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x25]);
842         }
843         else                                // Old
844         {
845           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x25]);
846         }
847         break;
848 
849       case '8':   // Red
850         if ((sec_old+sec_heard)>sec_now())  // New
851         {
852           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x2d]);
853         }
854         else                                // Old
855         {
856           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x2d]);
857         }
858         break;
859 
860       case '7':   // Light Magenta
861         if ((sec_old+sec_heard)>sec_now())  // New
862         {
863           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x26]);
864         }
865         else                                // Old
866         {
867           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x26]);
868         }
869         break;
870 
871       case '6':   // Magenta
872         if ((sec_old+sec_heard)>sec_now())  // New
873         {
874           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x2e]);
875         }
876         else                                // Old
877         {
878           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x2e]);
879         }
880         break;
881 
882       case '5':   // Light Cyan
883         if ((sec_old+sec_heard)>sec_now())  // New
884         {
885           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x24]);
886         }
887         else                                // Old
888         {
889           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x24]);
890         }
891         break;
892 
893       case '4':   // Cyan
894         if ((sec_old+sec_heard)>sec_now())  // New
895         {
896           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x2c]);
897         }
898         else                                // Old
899         {
900           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x2c]);
901         }
902         break;
903 
904       case '3':   // White
905         if ((sec_old+sec_heard)>sec_now())  // New
906         {
907           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x0f]);
908         }
909         else                                // Old
910         {
911           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x0f]);
912         }
913         break;
914 
915       case '2':   // Light Blue
916         if ((sec_old+sec_heard)>sec_now())  // New
917         {
918           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x22]);
919         }
920         else                                // Old
921         {
922           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x22]);
923         }
924         break;
925 
926       case '1':   // Blue
927         if ((sec_old+sec_heard)>sec_now())  // New
928         {
929           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x2a]);
930         }
931         else                                // Old
932         {
933           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x2a]);
934         }
935         break;
936 
937       case '0':   // DarkGray (APRSdos) or Black (looks better). We use Black.
938       default:
939         if ((sec_old+sec_heard)>sec_now())  // New (was 0x30)
940         {
941           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x08]);
942         }
943         else                                // Old
944         {
945           (void)XSetForeground(XtDisplay(da),gc_stipple,colors[0x08]);
946         }
947         break;
948     }
949 
950     // If shgd[6] != '0' we still draw a circle, but the center
951     // is offset in the indicated direction by 1/3 the radius.
952 
953     // This debug statement will almost never wind
954     // up selected, because 4095 means "all possible debugging"
955     //  It is being placed here SOLELY so that I can
956     // leave the setting of "tilt" (which should determine
957     // how an oval would be tilted to indicate directivity)
958     // without having GCC 6.x whine about it not being used.
959     if (debug_level & 4095)
960     {
961       fprintf(stderr,"If we had tilted ovals implemented, would have tilted one by %lf\n",tilt);
962     }
963 
964     // Draw Circle
965 
966     // Check that our parameters are within spec for XDrawArc.  Tricky
967     // 'cuz the XArc struct has short's and unsigned short's, while
968     // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
969     // just to make sure.
970 
971     (void)XDrawArc(XtDisplay(da),where,gc_stipple,
972                    l16(((x_long-NW_corner_longitude)/scale_x)-(diameter/2) - offx),  // int
973                    l16(((y_lat-NW_corner_latitude)/scale_y)-(diameter/2) - offy),    // int
974                    lu16(diameter), // unsigned int
975                    lu16(diameter), // unsigned int
976                    l16(0),         // int
977                    l16(64*360));   // int
978 
979     if (scale_y > 128)   // Don't fill in circle if zoomed in too far (too slow!)
980     {
981 
982       while (diameter > 1.0)
983       {
984         diameter = diameter - 1.0;
985 
986         // Check that our parameters are within spec for XDrawArc.  Tricky
987         // 'cuz the XArc struct has short's and unsigned short's, while
988         // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
989         // just to make sure.
990 
991         (void)XDrawArc(XtDisplay(da),where,gc_stipple,
992                        l16(((x_long-NW_corner_longitude)/scale_x)-(diameter/2) - offx),  // int
993                        l16(((y_lat-NW_corner_latitude)/scale_y)-(diameter/2) - offy),    // int
994                        lu16(diameter),  // unsigned int
995                        lu16(diameter),  // unsigned int
996                        l16(0),         // int
997                        l16(64*360));   // int
998       }
999     }
1000   }
1001   // Change back to non-stipple for whatever drawing occurs after this
1002   (void)XSetFillStyle(XtDisplay(da), gc_stipple, FillSolid);
1003 }
1004 
1005 
1006 
1007 
1008 
1009 // Draw the ALOHA circle
1010 // Identical to draw_pod_circle when this was first written, but separated
1011 // just in case that POD functionality ever changes per the comments in it
draw_aloha_circle(long x_long,long y_lat,double range,int color,Pixmap where)1012 void draw_aloha_circle(long x_long, long y_lat, double range, int color, Pixmap where)
1013 {
1014   double diameter;
1015   long width, height;
1016 
1017 
1018   // Range is in miles.  Bottom term is in meters before the
1019   // 0.0006214 multiplication factor which converts it to miles.
1020   // Equation is:  2 * ( range(mi) / x-distance across window(mi) )
1021   diameter = 2.0 * ( range/
1022                      (scale_x * calc_dscale_x(center_longitude,center_latitude) * 0.0006214 ) );
1023 
1024   // If less than 4 pixels across, skip drawing it.
1025   if (diameter <= 4.0)
1026   {
1027     return;
1028   }
1029 
1030   width = (((x_long-NW_corner_longitude)/scale_x)-(diameter/2));
1031   height = (((y_lat-NW_corner_latitude)/scale_y)-(diameter/2));
1032 
1033   (void)XSetLineAttributes(XtDisplay(da), gc, 2, LineSolid, CapButt,JoinMiter);
1034   (void)XSetForeground(XtDisplay(da),gc,color);
1035 
1036   // Check that our parameters are within spec for XDrawArc.  Tricky
1037   // 'cuz the XArc struct has short's and unsigned short's, while
1038   // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
1039   // just to make sure.
1040 
1041   (void)XDrawArc(XtDisplay(da),where,gc,
1042                  l16(width),     // int
1043                  l16(height),    // int
1044                  lu16(diameter), // unsigned int
1045                  lu16(diameter), // unsigned int
1046                  l16(0),         // int
1047                  l16(64*360));   // int
1048 }
1049 
1050 
1051 
1052 
1053 
1054 static int barb_len;
1055 static int barb_spacing;
1056 
1057 
1058 
1059 
1060 
1061 // Change barb parameters based on our current zoom level, so the
1062 // barbs don't get too long as we zoom out.
set_barb_parameters(void)1063 void set_barb_parameters(void)
1064 {
1065   float factor = 1.0;
1066 
1067   // Initial settings
1068   barb_len = 16;
1069   barb_spacing = 16;
1070 
1071   // Scale factor
1072   if      (scale_y > 80000)
1073   {
1074     factor = 3.0;
1075   }
1076   else if (scale_y > 40000)
1077   {
1078     factor = 2.5;
1079   }
1080   else if (scale_y > 20000)
1081   {
1082     factor = 2.0;
1083   }
1084   else if (scale_y > 10000)
1085   {
1086     factor = 1.5;
1087   }
1088 
1089   // Scale them, plus use poor man's rounding
1090   barb_len = (int)((barb_len / factor) + 0.5);
1091   barb_spacing = (int)((barb_spacing / factor) + 0.5);;
1092 }
1093 
1094 
1095 
1096 
1097 
draw_half_barbs(int * i,int quantity,float bearing_radians,long x,long y,char * UNUSED (course),Pixmap where)1098 void draw_half_barbs(int *i, int quantity, float bearing_radians, long x, long y, char * UNUSED(course), Pixmap where)
1099 {
1100   float barb_radians = bearing_radians + ( (45/360.0) * 2.0 * M_PI);
1101   int j;
1102   long start_x, start_y, off_x, off_y;
1103 
1104 
1105   for (j = 0; j < quantity; j++)
1106   {
1107     // Starting point for barb is (*i * barb_spacing) pixels
1108     // along bearing_radians vector
1109     *i = *i + barb_spacing;
1110     off_x = *i * cos(bearing_radians);
1111     off_y = *i * sin(bearing_radians);
1112     start_y = y + off_y;
1113     start_x = x + off_x;
1114 
1115     // Set off in the barb direction now
1116     off_y = (long)( (barb_len / 2) * sin(barb_radians) );
1117     off_x = (long)( (barb_len / 2) * cos(barb_radians) );
1118 
1119     (void)XSetLineAttributes(XtDisplay(da), gc, 0, LineSolid, CapButt,JoinMiter);
1120     (void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
1121 
1122 // Check that our parameters are within spec for XDrawLine.  We'll
1123 // stick to 16-bit values here due to warnings on the man-page
1124 // regarding XSegment structs and the protocol only handling
1125 // short's/unsigned short's, just in case.
1126 
1127     (void)XDrawLine(XtDisplay(da),where,gc,
1128                     l16(start_x),           // int
1129                     l16(start_y),           // int
1130                     l16(start_x + off_x),   // int
1131                     l16(start_y + off_y));  // int
1132   }
1133 }
1134 
1135 
1136 
1137 
1138 
draw_full_barbs(int * i,int quantity,float bearing_radians,long x,long y,char * UNUSED (course),Pixmap where)1139 void draw_full_barbs(int *i, int quantity, float bearing_radians, long x, long y, char * UNUSED(course), Pixmap where)
1140 {
1141   float barb_radians = bearing_radians + ( (45/360.0) * 2.0 * M_PI);
1142   int j;
1143   long start_x, start_y, off_x, off_y;
1144 
1145 
1146   for (j = 0; j < quantity; j++)
1147   {
1148     // Starting point for barb is (*i * barb_spacing) pixels
1149     // along bearing_radians vector
1150     *i = *i + barb_spacing;
1151     off_x = *i * cos(bearing_radians);
1152     off_y = *i * sin(bearing_radians);
1153     start_y = y + off_y;
1154     start_x = x + off_x;
1155 
1156     // Set off in the barb direction now
1157     off_y = (long)( barb_len * sin(barb_radians) );
1158     off_x = (long)( barb_len * cos(barb_radians) );
1159 
1160     (void)XSetLineAttributes(XtDisplay(da), gc, 0, LineSolid, CapButt,JoinMiter);
1161     (void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
1162 
1163 // Check that our parameters are within spec for XDrawLine.  We'll
1164 // stick to 16-bit values here due to warnings on the man-page
1165 // regarding XSegment structs and the protocol only handling
1166 // short's/unsigned short's, just in case.
1167 
1168     (void)XDrawLine(XtDisplay(da),where,gc,
1169                     l16(start_x),           // int
1170                     l16(start_y),           // int
1171                     l16(start_x + off_x),   // int
1172                     l16(start_y + off_y));  // int
1173   }
1174 }
1175 
1176 
1177 
1178 
1179 
draw_triangle_flags(int * i,int quantity,float bearing_radians,long x,long y,char * UNUSED (course),Pixmap where)1180 void draw_triangle_flags(int *i, int quantity, float bearing_radians, long x, long y, char * UNUSED(course), Pixmap where)
1181 {
1182   float barb_radians = bearing_radians + ( (45/360.0) * 2.0 * M_PI);
1183   int j;
1184   long start_x, start_y, off_x, off_y, off_x2, off_y2;
1185   XPoint points[3];
1186 
1187 
1188   for (j = 0; j < quantity; j++)
1189   {
1190     // Starting point for barb is (*i * barb_spacing) pixels
1191     // along bearing_radians vector
1192     *i = *i + barb_spacing;
1193     off_x = *i * cos(bearing_radians);
1194     off_y = *i * sin(bearing_radians);
1195     start_y = y + off_y;
1196     start_x = x + off_x;
1197 
1198     // Calculate 2nd point along staff
1199     off_x2 = (barb_spacing/2) * cos(bearing_radians);
1200     off_y2 = (barb_spacing/2) * sin(bearing_radians);
1201 
1202     // Set off in the barb direction now
1203     off_y = (long)( barb_len * sin(barb_radians) );
1204     off_x = (long)( barb_len * cos(barb_radians) );
1205 
1206     (void)XSetLineAttributes(XtDisplay(da), gc, 0, LineSolid, CapButt,JoinMiter);
1207     (void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
1208 
1209     points[0].x = start_x;
1210     points[0].y = start_y;
1211     points[1].x = start_x + off_x;
1212     points[1].y = start_y + off_y;
1213     points[2].x = start_x + off_x2;
1214     points[2].y = start_y + off_y2;
1215 
1216     // Number of points is always 3 here, so we don't need to
1217     // check first before calling XFillPolygon().
1218     (void)XFillPolygon(XtDisplay(da), where, gc, points, 3, Convex, CoordModeOrigin);
1219   }
1220 }
1221 
1222 
1223 
1224 
1225 
draw_square_flags(int * i,int quantity,float bearing_radians,long x,long y,char * UNUSED (course),Pixmap where)1226 void draw_square_flags(int *i, int quantity, float bearing_radians, long x, long y, char * UNUSED(course), Pixmap where)
1227 {
1228   float barb_radians = bearing_radians + ( (90/360.0) * 2.0 * M_PI);
1229   int j;
1230   long start_x, start_y, off_x, off_y, off_x2, off_y2;
1231   XPoint points[4];
1232 
1233 
1234   for (j = 0; j < quantity; j++)
1235   {
1236     // Starting point for barb is (*i * barb_spacing) pixels
1237     // along bearing_radians vector
1238     *i = *i + barb_spacing;
1239     off_x = *i * cos(bearing_radians);
1240     off_y = *i * sin(bearing_radians);
1241     start_y = y + off_y;
1242     start_x = x + off_x;
1243 
1244     // Calculate 2nd point along staff
1245     off_x2 = (barb_spacing/2) * cos(bearing_radians);
1246     off_y2 = (barb_spacing/2) * sin(bearing_radians);
1247 
1248     // Set off in the barb direction now
1249     off_y = (long)( barb_len * sin(barb_radians) );
1250     off_x = (long)( barb_len * cos(barb_radians) );
1251 
1252     (void)XSetLineAttributes(XtDisplay(da), gc, 0, LineSolid, CapButt,JoinMiter);
1253     (void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
1254 
1255     points[0].x = start_x;
1256     points[0].y = start_y;
1257     points[1].x = start_x + off_x;
1258     points[1].y = start_y + off_y;
1259     points[2].x = start_x + off_x + off_x2;
1260     points[2].y = start_y + off_y + off_y2;
1261     points[3].x = start_x + off_x2;
1262     points[3].y = start_y + off_y2;
1263 
1264     // Number of points is always 4 here, so we don't need to
1265     // check first before calling XFillPolygon().
1266     (void)XFillPolygon(XtDisplay(da), where, gc, points, 4, Convex, CoordModeOrigin);
1267   }
1268 }
1269 
1270 
1271 
1272 
1273 
1274 // Function to draw wind barbs.  Use speed in knots to determine the
1275 // flags and barbs to draw along the shaft.  Course is in true
1276 // degrees, in the direction that the wind is coming from.
1277 //
1278 //   Square flag = 100 knots
1279 // Triangle flag = 50 knots
1280 //     Full barb = 10 knots
1281 //     Half barb = 5 knots
1282 //
draw_wind_barb(long x_long,long y_lat,char * speed,char * course,time_t sec_heard,Pixmap where)1283 void draw_wind_barb(long x_long, long y_lat, char *speed,
1284                     char *course, time_t sec_heard, Pixmap where)
1285 {
1286   int square_flags = 0;
1287   int triangle_flags = 0;
1288   int full_barbs = 0;
1289   int half_barbs = 0;
1290   int shaft_length = 0;
1291   int my_speed = atoi(speed);     // In mph (so far)
1292   int my_course = atoi(course);   // In ° true
1293   float bearing_radians;
1294   long off_x,off_y;
1295   long x,y;
1296   int i;
1297 
1298 
1299   // Ghost the wind barb if sec_heard is too long.
1300   if ( ((sec_old+sec_heard)<=sec_now()) && !Select_.old_data )
1301   {
1302     return;
1303   }
1304 
1305 
1306 
1307 // What to do if my_speed is zero?  Blank out any wind barbs
1308 // that were written before?
1309 
1310 
1311   // Prevents it from being drawn when the symbol is off-screen.
1312   // It'd be better to check for lat/long +/- range to see if it's
1313   // on the screen.
1314 
1315   if ((x_long>NW_corner_longitude) && (x_long<SE_corner_longitude))
1316   {
1317 
1318     if ((y_lat>NW_corner_latitude) && (y_lat<SE_corner_latitude))
1319     {
1320 
1321 //            if ((x_long < 0) || (x_long > 129600000l))
1322 //                return;
1323 
1324 //            if ((y_lat < 0) || (y_lat > 64800000l))
1325 //                return;
1326 
1327       // Ok to draw wind barb
1328 
1329     }
1330     else
1331     {
1332       return;
1333     }
1334   }
1335   else
1336   {
1337     return;
1338   }
1339 
1340   // Set up the constants for our zoom level
1341   set_barb_parameters();
1342 
1343   // Convert from mph to knots for wind speed.
1344   my_speed = my_speed * 0.8689607;
1345 
1346   //fprintf(stderr,"mph:%s, knots:%d\n",speed,my_speed);
1347 
1348   // Adjust so that it fits our screen angles.  We're off by
1349   // 90 degrees.
1350   my_course = (my_course - 90) % 360;
1351 
1352   square_flags = (int)(my_speed / 100);
1353   my_speed = my_speed % 100;
1354 
1355   triangle_flags = (int)(my_speed / 50);
1356   my_speed = my_speed % 50;
1357 
1358   full_barbs = (int)(my_speed / 10);
1359   my_speed = my_speed % 10;
1360 
1361   half_barbs = (int)(my_speed / 5);
1362 
1363   shaft_length = barb_spacing * (square_flags + triangle_flags + full_barbs
1364                                  + half_barbs + 1);
1365 
1366   // Set a minimum length for the shaft?
1367   if (shaft_length < 2)
1368   {
1369     shaft_length = 2;
1370   }
1371 
1372   if (debug_level & 128)
1373   {
1374     fprintf(stderr,"Course:%d,\tL:%d,\tsq:%d,\ttr:%d,\tfull:%d,\thalf:%d\n",
1375             atoi(course),
1376             shaft_length,
1377             square_flags,
1378             triangle_flags,
1379             full_barbs,
1380             half_barbs);
1381   }
1382 
1383   // Draw shaft at proper angle.
1384   bearing_radians = (my_course/360.0) * 2.0 * M_PI;
1385 
1386   off_y = (long)( shaft_length * sin(bearing_radians) );
1387   off_x = (long)( shaft_length * cos(bearing_radians) );
1388 
1389   x = (x_long - NW_corner_longitude)/scale_x;
1390   y = (y_lat - NW_corner_latitude)/scale_y;
1391 
1392   (void)XSetLineAttributes(XtDisplay(da), gc, 0, LineSolid, CapButt,JoinMiter);
1393   (void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
1394 
1395 // Check that our parameters are within spec for XDrawLine.  We'll
1396 // stick to 16-bit values here due to warnings on the man-page
1397 // regarding XSegment structs and the protocol only handling
1398 // short's/unsigned short's, just in case.
1399 
1400   (void)XDrawLine(XtDisplay(da),where,gc,
1401                   l16(x),             // int
1402                   l16(y),             // int
1403                   l16(x + off_x),     // int
1404                   l16(y + off_y));    // int
1405 
1406   // Increment along shaft and draw filled polygons at:
1407   // "(angle + 45) % 360" degrees to create flags.
1408 
1409   i = barb_spacing;
1410   // Draw half barbs if any
1411   if (half_barbs)
1412     draw_half_barbs(&i,
1413                     half_barbs,
1414                     bearing_radians,
1415                     x,
1416                     y,
1417                     course,
1418                     where);
1419 
1420   // Draw full barbs if any
1421   if (full_barbs)
1422     draw_full_barbs(&i,
1423                     full_barbs,
1424                     bearing_radians,
1425                     x,
1426                     y,
1427                     course,
1428                     where);
1429 
1430   // Draw triangle flags if any
1431   if (triangle_flags)
1432     draw_triangle_flags(&i,
1433                         triangle_flags,
1434                         bearing_radians,
1435                         x,
1436                         y,
1437                         course,
1438                         where);
1439 
1440   // Draw rectangle flags if any
1441   if (square_flags)
1442     draw_square_flags(&i,
1443                       square_flags,
1444                       bearing_radians,
1445                       x,
1446                       y,
1447                       course,
1448                       where);
1449 }
1450 
1451 
1452 
1453 
1454 
1455 // Function to draw beam headings for DF'ing purposes.  Separates NRQ into its
1456 // components, which are Number/Range/Quality.
1457 //
1458 // If N is 0, then the NRQ value is meaningless.  1 through 8 are hits per period
1459 // of time (auto-DF'ing equipment).  A value of 8 means all samples possible got
1460 // a hit.  A value of 9 means that the report is manual.
1461 //
1462 // Range limits the length of the line to the original map's scale of the sending
1463 // station.  The range is 2**R, so for R=4 the range would be 16 miles.
1464 //
1465 // Q is a single digit from 0-9 and provides indication of bearing accuracy:
1466 // 0 Useless
1467 // 1 <240 deg (worst)
1468 // 2 <120 deg
1469 // 3 <64  deg
1470 // 4 <32  deg
1471 // 5 <16  deg
1472 // 6 <8   deg
1473 // 7 <4   deg
1474 // 8 <2   deg
1475 // 9 <1   deg (best)
1476 //
1477 //
1478 // TODO: Should we draw with XOR for this function?  Would appear on
1479 // most maps that way, and we wouldn't have to worry much about
1480 // color.
1481 //
1482 // TODO: If Q between 1 and 8, shade the entire area to show the
1483 // beam width?
1484 //
1485 //
1486 // Latest:  We ignore the color parameter and draw everything using
1487 // red3.
1488 //
1489 // The distance calculations below use nautical miles.  Here we
1490 // ignore the difference between nautical and statute miles as it
1491 // really doesn't make much difference how long we draw the vectors:
1492 // The angle is what is important here.
1493 //
draw_bearing(long x_long,long y_lat,char * course,char * bearing,char * NRQ,int UNUSED (color),int draw_beamwidth,int draw_bearing,time_t sec_heard,Pixmap where)1494 void draw_bearing(long x_long, long y_lat, char *course,
1495                   char *bearing, char *NRQ, int UNUSED(color), int draw_beamwidth,
1496                   int draw_bearing,
1497                   time_t sec_heard, Pixmap where)
1498 {
1499   double range = 0;
1500   double real_bearing = 0.0;
1501   double real_bearing_min = 0.0;
1502   double real_bearing_max = 0.0;
1503   int width = 0;
1504   long x_long2, x_long3, x_long4, y_lat2, y_lat3, y_lat4;
1505   double screen_miles;
1506 
1507 
1508   if ( ((sec_old+sec_heard)>sec_now()) || Select_.old_data )
1509   {
1510 
1511     // Check for a zero value for N.  If found, the NRQ value is meaningless
1512     // and we need to assume some working default values.
1513     if (NRQ[0] != '0')
1514     {
1515 
1516       // "range" as used below is in nautical miles.
1517       range = (double)( pow(2.0,NRQ[1] - '0') );
1518 
1519       switch (NRQ[2])
1520       {
1521         case('1'):
1522           width = 240;    // Degrees of beam width.  What's the point?
1523           break;
1524         case('2'):
1525           width = 120;    // Degrees of beam width.  What's the point?
1526           break;
1527         case('3'):
1528           width = 64; // Degrees of beam width.  What's the point?
1529           break;
1530         case('4'):
1531           width = 32; // Degrees of beam width.  Starting to be usable.
1532           break;
1533         case('5'):
1534           width = 16; // Degrees of beam width.  Usable.
1535           break;
1536         case('6'):
1537           width = 8;  // Degrees of beam width.  Usable.
1538           break;
1539         case('7'):
1540           width = 4;  // Degrees of beam width.  Nice!
1541           break;
1542         case('8'):
1543           width = 2;  // Degrees of beam width.  Nice!
1544           break;
1545         case('9'):
1546           width = 1;  // Degrees of beam width.  Very Nice!
1547           break;
1548         case('0'):  // "Useless" beam width according to spec
1549         default:
1550           return; // Exit routine without drawing vectors
1551           break;
1552       }
1553     }
1554     else    // Assume some default values.
1555     {
1556       range = 512.0;  // Assume max range of 512 nautical miles
1557       width = 8;      // Assume 8 degrees for beam width
1558     }
1559 
1560     // We have the course of the vehicle and the bearing from the
1561     // vehicle.  Now we need the real bearing.
1562     //
1563     if (atoi(course) != 0)
1564     {
1565       real_bearing = atoi(course) + atoi(bearing);
1566       real_bearing_min = real_bearing + 360.0 - (width/2.0);
1567       real_bearing_max = real_bearing + (width/2.0);
1568     }
1569     else
1570     {
1571       real_bearing = atoi(bearing);
1572       real_bearing_min = real_bearing + 360.0 - (width/2.0);
1573       real_bearing_max = real_bearing + (width/2.0);
1574     }
1575 
1576     while (real_bearing > 360.0)
1577     {
1578       real_bearing -= 360.0;
1579     }
1580 
1581     while (real_bearing_min > 360.0)
1582     {
1583       real_bearing_min -= 360.0;
1584     }
1585 
1586     while (real_bearing_max > 360.0)
1587     {
1588       real_bearing_max -= 360.0;
1589     }
1590 
1591     // want this in nautical miles
1592     screen_miles = scale_x * calc_dscale_x(center_longitude,center_latitude)
1593                    * .5400;
1594 
1595     // Shorten range to more closely fit the screen
1596     if ( range > (3.0 * screen_miles) )
1597     {
1598       range = 3.0 * screen_miles;
1599     }
1600 
1601     // We now have a distance and a bearing for each vector.
1602     // Compute the end points via dead-reckoning here.  It will give
1603     // us points between which we can draw a vector and makes the
1604     // rest of the code much easier.  Need to skip adding 270
1605     // degrees if we use that method.
1606     //
1607     if (draw_beamwidth)
1608     {
1609       compute_DR_position(x_long,       // input (long)
1610                           y_lat,        // input (long)
1611                           range,        // input in nautical miles (double)
1612                           real_bearing_min,     // input in ° true (double)
1613                           &x_long2,             // output (*long)
1614                           &y_lat2);             // output (*long)
1615 
1616       compute_DR_position(x_long,       // input (long)
1617                           y_lat,        // input (long)
1618                           range,        // input in nautical miles (double)
1619                           real_bearing_max,    // input in ° true (double)
1620                           &x_long3,            // output (*long)
1621                           &y_lat3);            // output (*long)
1622     }
1623 
1624     if (draw_bearing)
1625     {
1626       compute_DR_position(x_long,       // input (long)
1627                           y_lat,        // input (long)
1628                           range,        // input in nautical miles (double)
1629                           real_bearing,   // input in ° true (double)
1630                           &x_long4,           // output (*long)
1631                           &y_lat4);           // output (*long)
1632     }
1633 
1634     (void)XSetLineAttributes(XtDisplay(da), gc, 2, LineSolid, CapButt,JoinMiter);
1635     //(void)XSetForeground(XtDisplay(da),gc,colors[0x0a]);
1636     if (draw_beamwidth)
1637     {
1638       (void)XSetForeground(XtDisplay(da),gc,colors[0x4a]); // red2
1639       draw_vector(da, x_long, y_lat, x_long2, y_lat2, gc, where, 0);
1640       draw_vector(da, x_long, y_lat, x_long3, y_lat3, gc, where, 0);
1641     }
1642 
1643     if (draw_bearing)
1644     {
1645       (void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
1646       draw_vector(da, x_long, y_lat, x_long4, y_lat4, gc, where, 0);
1647     }
1648   }
1649 
1650   // Change back to non-stipple for whatever drawing occurs after this
1651 //    (void)XSetFillStyle(XtDisplay(da), gc_tint, FillSolid);
1652 }
1653 
1654 
1655 
1656 
1657 
1658 // TODO:  Pass back the modified x_long/y_lat to the calling routine
1659 // and use the new lat/long to place the symbol.  This will knock
1660 // off the digits on the right that the ambiguity specifies.  We
1661 // then add 1/2 the rectangle offsets in order to get the symbol
1662 // placed in the middle of the rectangle.
1663 //
draw_ambiguity(long x_long,long y_lat,char amb,long * amb_x_long,long * amb_y_lat,time_t sec_heard,Pixmap UNUSED (where))1664 void draw_ambiguity(long x_long, long y_lat, char amb, long *amb_x_long, long *amb_y_lat, time_t sec_heard, Pixmap UNUSED(where) )
1665 {
1666   unsigned long left, right, top, bottom;
1667   long offset_lat, offset_long;
1668   int scale_limit;
1669 
1670 
1671   // Assign these first in case we do a sudden return from the
1672   // function.
1673   *amb_x_long = x_long;
1674   *amb_y_lat = y_lat;
1675 
1676 //    if ((x_long < 0) || (x_long > 129600000l))
1677 //        return;
1678 
1679 //    if ((y_lat < 0) || (y_lat > 64800000l))
1680 //        return;
1681 
1682   switch (amb)
1683   {
1684     case 1: // +- 1/10th minute
1685       offset_lat = offset_long = 600;
1686       scale_limit = 256;
1687 
1688       // Truncate digits off the right
1689       x_long = (long)(x_long / 600);
1690       x_long = x_long * 600;
1691       y_lat = (long)(y_lat / 600);
1692       y_lat = y_lat * 600;
1693       break;
1694 
1695     case 2: // +- 1 minute
1696       offset_lat = offset_long = 6000;
1697       scale_limit = 2048;
1698 
1699       // Truncate digits off the right
1700       x_long = (long)(x_long / 6000);
1701       x_long = x_long * 6000;
1702       y_lat = (long)(y_lat / 6000);
1703       y_lat = y_lat * 6000;
1704       break;
1705 
1706     case 3: // +- 10 minutes
1707       offset_lat = offset_long = 60000;
1708       scale_limit = 16384;
1709 
1710       // Truncate digits off the right
1711       x_long = (long)(x_long / 60000);
1712       x_long = x_long * 60000;
1713       y_lat = (long)(y_lat / 60000);
1714       y_lat = y_lat * 60000;
1715       break;
1716 
1717     case 4: // +- 1 degree
1718       offset_lat = offset_long = 360000;
1719       scale_limit = 65536;
1720 
1721       // Truncate digits off the right
1722       x_long = (long)(x_long / 360000);
1723       x_long = x_long * 360000;
1724       y_lat = (long)(y_lat / 360000);
1725       y_lat = y_lat * 360000;
1726       break;
1727 
1728 // TODO:  The last two cases need fixing up like the above
1729 
1730     case 5: // grid square: 2.5min lat x 5min lon
1731       offset_lat  = 360000.0 * 1.25 / 60.0;
1732       offset_long = 360000.0 * 2.50 / 60.0;
1733       scale_limit = 1024;
1734       break;
1735 
1736     case 6: // grid square: 1deg lat x 2deg lon
1737       offset_lat  = 360000.0 * 0.5;
1738       offset_long = 360000.0 * 1.0;
1739       scale_limit = 16384;
1740       break;
1741 
1742     case 0:
1743     default:
1744       return; // if no ambiguity, do nothing
1745       break;
1746 
1747   }
1748 
1749   // Re-assign them here as they should have been truncated on the
1750   // right by the above code.  We'll use these new values
1751   // to draw the symbols and the other associated symbol data
1752   // (external to this function).
1753   //
1754   *amb_x_long = x_long + (offset_long/2);
1755   *amb_y_lat = y_lat + (offset_lat / 2);
1756 
1757 
1758   if (scale_y > scale_limit)
1759   {
1760     // Ambiguity box will be smaller than smallest symbol so
1761     // don't draw it.
1762 //fprintf(stderr,"scale_y > scale_limit\n");
1763     return;
1764   }
1765 
1766   if ( ((sec_old+sec_heard)<=sec_now()) && !Select_.old_data )
1767   {
1768     return;
1769   }
1770 
1771   left   = x_long;
1772   top    = y_lat;
1773   right  = x_long + offset_long;
1774   bottom = y_lat  + offset_lat;
1775 
1776 
1777   (void)XSetForeground(XtDisplay(da), gc, colors[0x08]);
1778 
1779   // Draw rectangle (unfilled) plus vectors from symbol to
1780   // corners.
1781 
1782   (void)XSetLineAttributes(XtDisplay(da), gc,
1783                            2, LineOnOffDash, CapButt,JoinMiter);
1784 
1785   // Top line of rectangle
1786   draw_vector(da,left,top,right,top,gc,pixmap_final, 0);
1787 
1788   // Bottom line of rectangle
1789   draw_vector(da,left,bottom,right,bottom,gc,pixmap_final, 1);
1790 
1791   // Left line of rectangle
1792   draw_vector(da,left,top,left,bottom,gc,pixmap_final, 1);
1793 
1794   // Right line of rectangle
1795   draw_vector(da,right,top,right,bottom,gc,pixmap_final, 1);
1796 
1797   // Diagonal lines
1798   draw_vector(da,left,top,right,bottom,gc,pixmap_final, 1);
1799   draw_vector(da,right,top,left,bottom,gc,pixmap_final, 1);
1800 }
1801 
1802 
1803 
1804 
1805 
1806 // Function which specifies whether any part of a bounding box fits
1807 // on the screen, using screen coordinates as inputs.
1808 //
onscreen(long left,long right,long top,long bottom)1809 static __inline__ int onscreen(long left, long right, long top, long bottom)
1810 {
1811   // This checks to see if any part of a box is on the screen.
1812   if (left > screen_width || right < 0 || top > screen_height || bottom < 0)
1813   {
1814     return 0;
1815   }
1816   else
1817   {
1818     return 1;
1819   }
1820 }
1821 
1822 
1823 
1824 
1825 
1826 // According to the spec, the lat/long point is the upper left
1827 // corner of the object, and the offsets are down and to the right
1828 // (except for one line type where it's down and to the left).  This
1829 // doesn't appear to be the case in dos/winAPRS.  Matching what they
1830 // do:
1831 //
1832 // Type 0 Circle:    Tie = center, offsets = vert/horiz. sizes.
1833 // Type 1 Line:      Tie = bottom right, offsets = left and up.
1834 // Type 2 Ellipse:   Tie = center, offsets = vert/horiz. sizes.
1835 // Type 3 Triangle:  Tie = bottom right, offsets = height/width.
1836 // Type 4 Rectangle: Tie = lower right, offsets = left and up.
1837 // Type 5 Circle:    Tie = center, offsets = vert/horiz. sizes.
1838 // Type 6 Line:      Tie = bottom left, offsets = right and up.
1839 // Type 7 Ellipse:   Tie = center, offsets = vert/horiz. sizes.
1840 // Type 8 Triangle:  Tie = bottom right, offsets = height/width.
1841 // Type 9 Rectangle: Tie = lower right, offsets = left and up.
1842 //
1843 // Exceptions to this are the triangle, ellipse, and circle.  The
1844 // ellipse and circle have the lat/long as the center point.  The
1845 // triangle is an isosceles triangle with the lat/long point being
1846 // the bottom right and the bottom of the triangle being horizontal.
1847 //
draw_area(long x_long,long y_lat,char type,char color,char sqrt_lat_off,char sqrt_lon_off,unsigned int width,time_t sec_heard,Pixmap where)1848 void draw_area(long x_long, long y_lat, char type, char color,
1849                char sqrt_lat_off, char sqrt_lon_off, unsigned int width, time_t sec_heard, Pixmap where)
1850 {
1851   long left, top, right, bottom, xoff, yoff;
1852   int  c;
1853   XPoint points[4];
1854 
1855 
1856   // Ghost the area object if sec_heard is too long.
1857   if ( ((sec_old+sec_heard)<=sec_now()) && !Select_.old_data )
1858   {
1859     return;
1860   }
1861 
1862 
1863 //    if ((x_long < 0) || (x_long > 129600000l) ||
1864 //        (y_lat  < 0) || (y_lat  > 64800000l))
1865 //        return;
1866 
1867   xoff = 360000.0 / 1500.0 * (sqrt_lon_off * sqrt_lon_off) / scale_x;
1868   yoff = 360000.0 / 1500.0 * (sqrt_lat_off * sqrt_lat_off) / scale_y;
1869 
1870   right  = (x_long - NW_corner_longitude) / scale_x;
1871   bottom = (y_lat  - NW_corner_latitude)  / scale_y;
1872   left   = right  - xoff;
1873   top    = bottom - yoff;
1874 
1875   // colors[0x21] is the first in the list of area object colors in main.c
1876   c = colors[0x21 + color];
1877 
1878   (void)XSetForeground(XtDisplay(da), gc, c);
1879   if (xoff < 20 || yoff < 20)
1880   {
1881     (void)XSetLineAttributes(XtDisplay(da), gc, 1, LineSolid, CapButt,JoinMiter);
1882   }
1883   else
1884   {
1885     (void)XSetLineAttributes(XtDisplay(da), gc, 2, LineSolid, CapButt,JoinMiter);
1886   }
1887   (void)XSetFillStyle(XtDisplay(da), gc, FillSolid); // just in case
1888   (void)XSetStipple(XtDisplay(da), gc, pixmap_50pct_stipple);
1889 
1890   switch (type)
1891   {
1892     case AREA_OPEN_BOX:
1893       if (onscreen(left, right, top, bottom))
1894       {
1895 
1896 // Check that our parameters are within spec for XDrawRectangle
1897 // Tricky 'cuz the XRectangle struct has short's and unsigned short's,
1898 // while XDrawRectangle man-page says int's/unsigned int's.  We'll
1899 // stick to 16-bit just to make sure.
1900 
1901         (void)XDrawRectangle(XtDisplay(da), where, gc,
1902                              l16(left),     // int
1903                              l16(top),      // int
1904                              lu16(xoff),    // unsigned int
1905                              lu16(yoff));   // unsigned int
1906       }
1907       break;
1908     case AREA_FILLED_BOX:
1909       if (onscreen(left, right, top, bottom))
1910       {
1911         (void)XSetFillStyle(XtDisplay(da), gc, FillStippled);
1912         (void)XFillRectangle(XtDisplay(da), where, gc, l16(left), l16(top), l16(xoff), l16(yoff));
1913       }
1914       break;
1915     /* For the rest of the objects, the l16 limiting of the values is inadequate because the
1916        shapes will still be draw wrong if the value actually was limited down.
1917        However, this is slightly better than passing long or int values that would just be
1918        used as 16bit by X (i.e.: truncated) until I/we come up with some sort of clipping algorithm.
1919        In real use, what I'm talking about will occur based on two things: the size of the area and
1920        the zoom level being used.  The more the extents of the area go beyond the edges of the screen,
1921        the more this will happen. N7TAP */
1922     case AREA_OPEN_CIRCLE:
1923     case AREA_OPEN_ELLIPSE:
1924       right  += xoff;
1925       bottom += yoff;
1926       if (onscreen(left, right, top, bottom))
1927       {
1928 
1929 // Check that our parameters are within spec for XDrawArc.  Tricky
1930 // 'cuz the XArc struct has short's and unsigned short's, while
1931 // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
1932 // just to make sure.
1933 
1934         (void)XDrawArc(XtDisplay(da), where, gc,
1935                        l16(left),         // int
1936                        l16(top),          // int
1937                        lu16(2*xoff),      // unsigned int
1938                        lu16(2*yoff),      // unsigned int
1939                        l16(0),            // int
1940                        l16(64 * 360));    // int
1941       }
1942       break;
1943     case AREA_FILLED_CIRCLE:
1944     case AREA_FILLED_ELLIPSE:
1945       right  += xoff;
1946       bottom += yoff;
1947       if (onscreen(left, right, top, bottom))
1948       {
1949         (void)XSetFillStyle(XtDisplay(da), gc, FillStippled);
1950         (void)XFillArc(XtDisplay(da), where, gc, l16(left), l16(top), l16(2*xoff), l16(2*yoff), 0, 64 * 360);
1951       }
1952       break;
1953     case AREA_LINE_RIGHT:
1954       left  += xoff;
1955       right += xoff;
1956       if (width > 0)
1957       {
1958         double angle = atan((float)xoff/(float)yoff);
1959 // Check for divide-by-zero here???
1960 
1961         int conv_width = width/(scale_x*calc_dscale_x(center_longitude,center_latitude)*0.0006214);
1962         points[0].x = l16(left-(conv_width * cos(angle))+xoff);
1963         points[0].y = l16(top -(conv_width * sin(angle)));
1964         points[1].x = l16(left-(conv_width * cos(angle)));
1965         points[1].y = l16(top -(conv_width * sin(angle))+yoff);
1966         points[2].x = l16(left+(conv_width * cos(angle)));
1967         points[2].y = l16(top +(conv_width * sin(angle))+yoff);
1968         points[3].x = l16(left+(conv_width * cos(angle))+xoff);
1969         points[3].y = l16(top +(conv_width * sin(angle)));
1970         if (onscreen(points[1].x, points[3].x, points[0].y, points[2].y))
1971         {
1972           (void)XSetFillStyle(XtDisplay(da), gc, FillStippled);
1973 
1974           // Number of points is always 4 here, so we don't
1975           // need to check first before calling
1976           // XFillPolygon().
1977           (void)XFillPolygon(XtDisplay(da), where, gc, points, 4, Convex, CoordModeOrigin);
1978         }
1979       }
1980       if (onscreen(left, right, top, bottom))
1981       {
1982         (void)XSetFillStyle(XtDisplay(da), gc, FillSolid);
1983 
1984 // Check that our parameters are within spec for XDrawLine.  We'll
1985 // stick to 16-bit values here due to warnings on the man-page
1986 // regarding XSegment structs and the protocol only handling
1987 // short's/unsigned short's, just in case.
1988 
1989         (void)XDrawLine(XtDisplay(da), where, gc,
1990                         l16(left),      // int
1991                         l16(bottom),    // int
1992                         l16(right),     // int
1993                         l16(top));      // int
1994       }
1995       break;
1996     case AREA_LINE_LEFT:
1997       if (width > 0)
1998       {
1999         double angle = atan((float)xoff/(float)yoff);
2000 // Check for divide-by-zero here???
2001 
2002         int conv_width = width/(scale_x*calc_dscale_x(center_longitude,center_latitude)*0.0006214);
2003         points[0].x = l16(left+(conv_width * cos(angle)));
2004         points[0].y = l16(top -(conv_width * sin(angle)));
2005         points[1].x = l16(left+(conv_width * cos(angle))+xoff);
2006         points[1].y = l16(top -(conv_width * sin(angle))+yoff);
2007         points[2].x = l16(left-(conv_width * cos(angle))+xoff);
2008         points[2].y = l16(top +(conv_width * sin(angle))+yoff);
2009         points[3].x = l16(left-(conv_width * cos(angle)));
2010         points[3].y = l16(top +(conv_width * sin(angle)));
2011         if (onscreen(points[3].x, points[1].x, points[0].y, points[2].y))
2012         {
2013           (void)XSetFillStyle(XtDisplay(da), gc, FillStippled);
2014 
2015           // Number of points is always 4 here, so we don't
2016           // need to check first before calling
2017           // XFillPolygon().
2018           (void)XFillPolygon(XtDisplay(da), where, gc, points, 4, Convex, CoordModeOrigin);
2019         }
2020       }
2021       if (onscreen(left, right, top, bottom))
2022       {
2023         (void)XSetFillStyle(XtDisplay(da), gc, FillSolid);
2024 
2025 // Check that our parameters are within spec for XDrawLine.  We'll
2026 // stick to 16-bit values here due to warnings on the man-page
2027 // regarding XSegment structs and the protocol only handling
2028 // short's/unsigned short's, just in case.
2029 
2030         (void)XDrawLine(XtDisplay(da), where, gc,
2031                         l16(right),     // int
2032                         l16(bottom),    // int
2033                         l16(left),      // int
2034                         l16(top));      // int
2035       }
2036       break;
2037     case AREA_OPEN_TRIANGLE:
2038       left -= xoff;
2039       points[0].x = l16(right);
2040       points[0].y = l16(bottom);
2041       points[1].x = l16(left+xoff);
2042       points[1].y = l16(top);
2043       points[2].x = l16(left);
2044       points[2].y = l16(bottom);
2045       points[3].x = l16(right);
2046       points[3].y = l16(bottom);
2047       if (onscreen(left, right, top, bottom))
2048       {
2049 
2050 // Check that our parameters are within spec for XDrawLines.  We'll
2051 // stick to 16-bit values here due to warnings on the man-page
2052 // regarding XSegment structs and the protocol only handling
2053 // short's/unsigned short's, just in case.
2054 
2055         (void)XDrawLines(XtDisplay(da), where, gc,
2056                          points,             // XPoint *
2057                          l16(4),             // int
2058                          CoordModeOrigin);   // int
2059       }
2060       break;
2061     case AREA_FILLED_TRIANGLE:
2062       left -= xoff;
2063       points[0].x = l16(right);
2064       points[0].y = l16(bottom);
2065       points[1].x = l16(left+xoff);
2066       points[1].y = l16(top);
2067       points[2].x = l16(left);
2068       points[2].y = l16(bottom);
2069       if (onscreen(left, right, top, bottom))
2070       {
2071         (void)XSetFillStyle(XtDisplay(da), gc, FillStippled);
2072 
2073         // Number of points is always 3 here, so we don't need
2074         // to check first before calling XFillPolygon().
2075         (void)XFillPolygon(XtDisplay(da), where, gc, points, 3, Convex, CoordModeOrigin);
2076       }
2077       break;
2078     default:
2079       break;
2080   }
2081   (void)XSetFillStyle(XtDisplay(da), gc, FillSolid);
2082 }
2083 
2084 
2085 
2086 
2087 
2088 /* DK7IN: Statistics for colors in all symbols (as of 16.03.2001)
2089 60167 .
2090  6399 q
2091  3686 m
2092  3045 c
2093  2034 j
2094  1903 h
2095  1726 l
2096  1570 k
2097  1063 g
2098  1051 #
2099   840 p
2100   600 ~
2101   477 i
2102   443 n
2103   430 a
2104   403 o
2105   337 f
2106   250 b
2107   207 e
2108   169 d
2109 */
2110 
2111 
2112 
2113 
2114 
2115 // read pixels from file, speeding it up by smart ordering of switches
read_symbol_from_file(FILE * f,char * pixels,char table_char)2116 void read_symbol_from_file(FILE *f, char *pixels, char table_char)
2117 {
2118   int x,y;
2119   int color;
2120   char line[100];
2121   char pixels_copy[400];
2122   char *p,*q;
2123   unsigned char a, b, c;
2124 
2125   for (y=0; y<20; y++)
2126   {
2127     (void)get_line(f,line,100);
2128     for (x=0; x<20; x++)
2129     {
2130       switch (line[x])
2131       {
2132         case('.'):       // transparent
2133           color=0xff;
2134           break;
2135         case('q'):       // #000000  black   0%
2136           color=0x51;
2137           break;
2138         case('m'):       // #FFFFFF  white 100%
2139           color=0x4d;
2140           break;
2141         case('c'):       // #CCCCCC  gray80 80%
2142           color=0x43;
2143           break;
2144         case('j'):       // #EE0000  red2
2145           color=0x4a;
2146           break;
2147         case('h'):       // #00BFFF  Deep sky blue
2148           color=0x48;
2149           break;
2150         case('l'):       // #0000CD  mediumblue
2151           color=0x4c;
2152           break;
2153         case('k'):       // #00CD00  green3
2154           color=0x4b;
2155           break;
2156         case('g'):       // #00008B  blue4
2157           color=0x47;
2158           break;
2159         case('#'):       // #FFFF00  yellow
2160           color=0x40;
2161           break;
2162         case('p'):       // #454545  gray27 27%
2163           color=0x50;
2164           break;
2165         case('~'):       // used in the last two symbols in the file
2166           color=0xff;  // what should it be? was transparent before...
2167           break;
2168         case('i'):       // #006400  Dark Green
2169           color=0x49;
2170           break;
2171         case('n'):       // #878787  gray53 52%
2172           color=0x4e;
2173           break;
2174         case('a'):       // #CD6500  darkorange2
2175           color=0x41;
2176           break;
2177         case('o'):       // #5A5A5A  gray59 35%
2178           color=0x4f;
2179           break;
2180         case('f'):       // #CD3333  brown3
2181           color=0x46;
2182           break;
2183         case('b'):       // #A020F0  purple
2184           color=0x42;
2185           break;
2186         case('e'):       // #FF4040  brown1
2187           color=0x45;
2188           break;
2189         case('d'):       // #CD0000  red3
2190           color=0x44;
2191           break;
2192         case('r'):       //          LimeGreen  DK7IN: saw this in the color definitions...
2193           color=0x52;                         // so we could use it
2194           break;
2195         default:
2196           color=0xff;
2197           break;
2198       }
2199       pixels[y*20+x] = (char)(color);
2200     }
2201   }
2202 
2203   // Create outline on icons, if needed
2204   // Do not change the overlays and "number" tables
2205   if((icon_outline_style != 0) && (table_char != '~') && (table_char != '#'))
2206   {
2207     switch(icon_outline_style)
2208     {
2209       case 1:
2210         color = 0x51; // Black
2211         break;
2212       //case 2:  color = 0x43; // Grey 80%
2213       case 2:
2214         color = 0x4e; // Grey 52%
2215         break;
2216       case 3:
2217         color = 0x4d; // White
2218         break;
2219       default:
2220         color = 0xff; // Transparent
2221         break;
2222     }
2223 
2224     p = pixels;
2225     q = &pixels_copy[0];
2226 
2227     for (y=0; y<20; y++)
2228     {
2229       for (x=0; x<20; x++)
2230       {
2231         *q = *p; // copy current color
2232 
2233         // If transparent see if the pixel is on the edge
2234         if(*q == (char) 0xff)
2235         {
2236           //check if left or right is none transparent
2237           b = c = 0xff;
2238 
2239           // left (left only possible if x > 0)
2240           if(x > 0)
2241           {
2242             b = p[-1];
2243           }
2244           // right (right only possible if x < 19)
2245           if(x < 19)
2246           {
2247             c = p[+1];
2248           }
2249 
2250           // if non-transparent color is found change pixel
2251           // to outline color
2252           if((b != (unsigned char) 0xff)
2253               || (c != (unsigned char) 0xff))
2254           {
2255             // change to icon outline color
2256             *q = color;
2257           }
2258 
2259           if((y > 0) && (*q == (char) 0xff))
2260           {
2261             //check if left-up, up or right-up is none transparent
2262             //"up" only possible if y > 0
2263             a = b = c = 0xff;
2264 
2265             // left-up (left only possible if x > 0)
2266             if(x > 0)
2267             {
2268               a = p[-21];
2269             }
2270             // up
2271             b = p[-20];
2272             // right-up (right only possible if x < 19)
2273             if(x < 19)
2274             {
2275               c = p[-19];
2276             }
2277 
2278             // if non-transparent color is found change pixel
2279             // to outline color
2280             if((a != (unsigned char) 0xff)
2281                 || (b != (unsigned char) 0xff)
2282                 || (c != (unsigned char) 0xff))
2283             {
2284               // change to icon outline color
2285               *q = color;
2286             }
2287           }
2288 
2289           if((y < 19) && (*q == (char) 0xff))
2290           {
2291             //check if left-down, down or right-down is none transparent
2292             //"down" only possible if y < 19
2293             a = b = c = 0xff;
2294 
2295             // left-down (left only possible if x > 0)
2296             if(x > 0)
2297             {
2298               a = p[+19];
2299             }
2300             // down
2301             b = p[+20];
2302             // right-down (right only possible if x < 19)
2303             if(x < 19)
2304             {
2305               c = p[+21];
2306             }
2307 
2308             // if non-transparent color is found change pixel
2309             // to outline color
2310             if((a != (unsigned char) 0xff)
2311                 || (b != (unsigned char) 0xff)
2312                 || (c != (unsigned char) 0xff))
2313             {
2314               // change to icon outline color
2315               *q = color;
2316             }
2317           }
2318         }
2319 
2320         p++;
2321         q++;
2322       }
2323     }
2324     memcpy(pixels, pixels_copy, 400);
2325   }
2326 }
2327 
2328 
2329 
2330 
2331 
2332 /* read in symbol table */
load_pixmap_symbol_file(char * filename,int reloading)2333 void load_pixmap_symbol_file(char *filename, int reloading)
2334 {
2335   FILE *f;
2336   char filen[500];
2337   char line[100];
2338   char table_char;
2339   char symbol_char;
2340   int done;
2341   char pixels[400];
2342   char orient;
2343 
2344   busy_cursor(appshell);
2345   symbols_loaded = 0;
2346   table_char = '\0';
2347   symbol_char = '\0';
2348   done = 0;
2349   xastir_snprintf(filen, sizeof(filen), "%s/%s", SYMBOLS_DIR, filename);
2350   f = fopen(filen,"r");
2351   if (f!=NULL)
2352   {
2353     while (!feof(f) && !done)
2354     {
2355       (void)get_line(f,line,100);
2356       if (strncasecmp("TABLE ",line,6)==0)
2357       {
2358         table_char=line[6];
2359         /*fprintf(stderr,"TABLE %c\n",table_char);*/
2360       }
2361       else
2362       {
2363         if (strncasecmp("DONE",line,4)==0)
2364         {
2365           done=1;
2366           /*fprintf(stderr,"DONE\n");*/
2367         }
2368         else
2369         {
2370           if (strncasecmp("APRS ",line,5)==0)
2371           {
2372             symbol_char=line[5];
2373             if (strlen(line)>=20 && line[19] == 'l')     // symbol with orientation ?
2374             {
2375               orient = 'l';  // should be 'l' for left
2376             }
2377             else
2378             {
2379               orient = ' ';
2380             }
2381             read_symbol_from_file(f, pixels, table_char);                      // read pixels for one symbol
2382             insert_symbol(table_char,symbol_char,pixels,270,orient,reloading); // always have normal orientation
2383             if (orient == 'l')
2384             {
2385               insert_symbol(table_char,symbol_char,pixels, 0,'u',reloading); // create other orientations
2386               insert_symbol(table_char,symbol_char,pixels, 90,'r',reloading);
2387               insert_symbol(table_char,symbol_char,pixels,180,'d',reloading);
2388             }
2389           }
2390         }
2391       }
2392     }
2393   }
2394   else
2395   {
2396     fprintf(stderr,"Error opening symbol file %s\n",filen);
2397     popup_message("Error opening symbol file","Error opening symbol file");
2398   }
2399 
2400   if (f != NULL)
2401   {
2402     (void)fclose(f);
2403   }
2404 }
2405 
2406 
2407 
2408 
2409 
2410 // add a symbol to the end of the symbol table.
2411 //
2412 // Here we actually draw the pixels into the SymbolData struct,
2413 // which contains separate Pixmap's for the icon, the transparent
2414 // background, and the ghost image.
2415 //
insert_symbol(char table,char symbol,char * pixel,int deg,char orient,int reloading)2416 void insert_symbol(char table, char symbol, char *pixel, int deg, char orient, int reloading)
2417 {
2418   int x,y,idx,old_next,color,last_color,last_gc2;
2419 
2420   if (symbols_loaded < MAX_SYMBOLS)
2421   {
2422     // first time loading, -> create pixmap...
2423     // when reloading -> reuse already created pixmaps...
2424     if(reloading == 0)
2425     {
2426       symbol_data[symbols_loaded].pix=XCreatePixmap(XtDisplay(appshell),
2427                                       RootWindowOfScreen(XtScreen(appshell)),
2428                                       20,
2429                                       20,
2430                                       DefaultDepthOfScreen(XtScreen(appshell)));
2431 
2432       symbol_data[symbols_loaded].pix_mask=XCreatePixmap(XtDisplay(appshell),
2433                                            RootWindowOfScreen(XtScreen(appshell)),
2434                                            20,
2435                                            20,
2436                                            1);
2437 
2438       symbol_data[symbols_loaded].pix_mask_old=XCreatePixmap(XtDisplay(appshell),
2439           RootWindowOfScreen(XtScreen(appshell)),
2440           20,
2441           20,
2442           1);
2443     }
2444 
2445     old_next=0;
2446     last_color = -1;    // Something bogus
2447     last_gc2 = -1;      // Also bogus
2448 
2449     for (y=0; y<20; y++)
2450     {
2451       for (x=0; x<20; x++)
2452       {
2453         switch (deg)
2454         {
2455           case(0):
2456             idx = 20* (19-x) +   y;
2457             break;
2458           case(90):
2459             idx = 20*   y    + (19-x);
2460             break;
2461           case(180):
2462             idx = 20* (19-x) + (19-y);
2463             break;
2464           default:
2465             idx = 20*   y    +   x;
2466             break;
2467         }
2468         color = (int)(pixel[idx]);
2469         if (color<0)
2470         {
2471           color = 0xff;
2472         }
2473 
2474 // Change to new color only when necessary.  We use two different
2475 // GC's here, one for the main icon pixmap, and one for the symbol
2476 // mask and ghost layer.
2477 
2478 
2479         // DK7IN: is (da) correct or should this be (appshell) ?
2480         if (color != last_color)
2481         {
2482           (void)XSetForeground(XtDisplay(da),gc,colors[color]);
2483           last_color = color;
2484         }
2485 
2486 // Check that our parameters are within spec for XDrawPoint.  Tricky
2487 // 'cuz the XPoint struct uses short's, while XDrawPoint manpage
2488 // specifies int's.  We'll stick to 16-bit numbers just to make
2489 // sure.
2490 
2491         (void)XDrawPoint(XtDisplay(da),
2492                          symbol_data[symbols_loaded].pix,
2493                          gc,
2494                          l16(x),     // int
2495                          l16(y));    // int
2496         // DK7IN
2497 
2498 
2499         // Create symbol mask
2500         if (color != 0xff)
2501         {
2502           if (last_gc2 != 1)
2503           {
2504             (void)XSetForeground(XtDisplay(appshell),gc2,1);  // active bit
2505             last_gc2 = 1;
2506           }
2507         }
2508         else
2509         {
2510           if (last_gc2 != 0)
2511           {
2512             (void)XSetForeground(XtDisplay(appshell),gc2,0);  // transparent.
2513             last_gc2 = 0;
2514           }
2515         }
2516 
2517 // Check that our parameters are within spec for XDrawPoint.  Tricky
2518 // 'cuz the XPoint struct uses short's, while XDrawPoint manpage
2519 // specifies int's.  We'll stick to 16-bit numbers just to make
2520 // sure.
2521 
2522         (void)XDrawPoint(XtDisplay(appshell),
2523                          symbol_data[symbols_loaded].pix_mask,
2524                          gc2,
2525                          l16(x),     // int
2526                          l16(y));    // int
2527 
2528 
2529         // Create ghost symbol mask by setting every 2nd bit
2530         // to transparent
2531         old_next++;
2532         if (old_next>1)
2533         {
2534           old_next=0;
2535           if (last_gc2 != 0)
2536           {
2537             (void)XSetForeground(XtDisplay(appshell),gc2,0);
2538             last_gc2 = 0;
2539           }
2540         }
2541 
2542 // Check that our parameters are within spec for XDrawPoint.  Tricky
2543 // 'cuz the XPoint struct uses short's, while XDrawPoint manpage
2544 // specifies int's.  We'll stick to 16-bit numbers just to make
2545 // sure.
2546 
2547         (void)XDrawPoint(XtDisplay(appshell),
2548                          symbol_data[symbols_loaded].pix_mask_old,
2549                          gc2,
2550                          l16(x),     // int
2551                          l16(y));    // int
2552       }
2553       old_next++;    // shift one bit every scan line for ghost image
2554       if (old_next>1)
2555       {
2556         old_next=0;
2557       }
2558     }
2559     symbol_data[symbols_loaded].active = SYMBOL_ACTIVE;
2560     symbol_data[symbols_loaded].table  = table;
2561     symbol_data[symbols_loaded].symbol = symbol;
2562     symbol_data[symbols_loaded].orient = orient;
2563     symbols_loaded++;
2564   }
2565 }
2566 
2567 
2568 
2569 
2570 
2571 // calculate symbol orientation from course
2572 // ' ' = left
2573 // 'd' = down
2574 // 'r' = right
2575 // 'u' = up
symbol_orient(char * course)2576 char symbol_orient(char *course)
2577 {
2578   char orient;
2579   float mydir;
2580 
2581   orient = ' '; // Default = left
2582   if (strlen(course))
2583   {
2584     mydir = (float)atof(course);
2585     if (mydir > 0)
2586     {
2587 
2588       while (mydir > 360.0)
2589       {
2590         mydir = mydir - 360.0;
2591       }
2592 
2593       while (mydir < 0.0)
2594       {
2595         mydir = mydir + 360.0;
2596       }
2597 
2598       if (mydir == 360.0)
2599       {
2600         mydir = 0.0;
2601       }
2602 
2603       orient = 'u';                               // 000-045 = up
2604       if ( mydir > (float)( 0+ANGLE_UPDOWN ) )    // 046-135 = right
2605       {
2606         orient = 'r';
2607       }
2608       if ( mydir > (float)( 90+ANGLE_UPDOWN ) )   // 136-225 = down
2609       {
2610         orient = 'd';
2611       }
2612       if ( mydir > (float)( 180+ANGLE_UPDOWN) )   // 226-314 = left
2613       {
2614         orient = ' ';
2615       }
2616       if ( mydir >= (float)(270+ANGLE_UPDOWN) )   // 315-360 = up
2617       {
2618         orient = 'u';
2619       }
2620     }
2621   }
2622   return(orient);
2623 }
2624 
2625 
2626 
2627 
2628 
2629 // Storage for an index into the symbol table that we may need
2630 // later.
2631 int nosym_index = -1;
2632 
2633 
2634 // Look through our symbol table for a match.
2635 //
symbol(Widget w,int ghost,char symbol_table,char symbol_id,char symbol_overlay,Pixmap where,int mask,long x_offset,long y_offset,char orient)2636 void symbol(Widget w, int ghost, char symbol_table, char symbol_id, char symbol_overlay, Pixmap where,
2637             int mask, long x_offset, long y_offset, char orient)
2638 {
2639   int i;
2640   int found;
2641   int alphanum_index = -1;
2642 
2643 
2644   if (x_offset > screen_width)
2645   {
2646     return;
2647   }
2648   if (x_offset < 0)
2649   {
2650     return;
2651   }
2652   if (y_offset > screen_height)
2653   {
2654     return;
2655   }
2656   if (y_offset < 0)
2657   {
2658     return;
2659   }
2660 
2661   /* DK7IN: orient  is ' ','l','r','u','d'  for left/right/up/down symbol orientation */
2662   // if symbol could be rotated, normal symbol orientation in symbols.dat is to the left
2663 
2664 
2665   // Find the nosymbol index if we haven't filled it in by now.
2666   // This "for" loop should get run only once during Xastir's
2667   // entire runtime, so it shouldn't contribute much to CPU
2668   // loading.
2669   if (nosym_index == -1)
2670   {
2671     for ( i = 0; i < symbols_loaded; i++ )
2672     {
2673       if (symbol_data[i].active == SYMBOL_ACTIVE)
2674       {
2675         if (symbol_data[i].table == '!'
2676             && symbol_data[i].symbol == '#')
2677         {
2678           nosym_index = i;       // index of special symbol (if none available)
2679           break;
2680         }
2681       }
2682     }
2683   }
2684 
2685 
2686   // Handle the overlay character.  The "for" loop below gets run
2687   // once every time we encounter an overlay character, which
2688   // isn't all that often.
2689   if (symbol_overlay == '\0' || symbol_overlay == ' ')
2690   {
2691     alphanum_index = 0; // we don't want an overlay
2692   }
2693   else    // Find the overlay character index
2694   {
2695     for ( i = 0; i < symbols_loaded; i++ )
2696     {
2697       if (symbol_data[i].active == SYMBOL_ACTIVE)
2698       {
2699         if (symbol_data[i].table == '#'
2700             && symbol_data[i].symbol == symbol_overlay)
2701         {
2702           alphanum_index = i; // index of symbol for character overlay
2703           break;
2704         }
2705       }
2706     }
2707   }
2708 
2709 
2710   found = -1;
2711 
2712   // Check last few symbols we used to see if we can shortcut
2713   // looking through the entire index.  The symbols array really
2714   // should be turned into a hash to save time.  Basically we've
2715   // implemented a very short cache here, but it keeps us from
2716   // looking through the entire symbol array sometimes.
2717   //
2718   for ( i = 0; i < 5; i++ )
2719   {
2720 //fprintf(stderr,"Checking symbol cache\n");
2721     if (symbol_data[symbols_cache[i]].table == symbol_table
2722         && symbol_data[symbols_cache[i]].symbol == symbol_id)
2723     {
2724       // We found the matching symbol in the cache
2725       found = symbols_cache[i];  // index of symbol
2726 //fprintf(stderr,"Symbol cache hit:%d\n",found);
2727       break;
2728     }
2729   }
2730 
2731   if (found == -1)    // Not found in symbols cache
2732   {
2733 
2734     for ( i = 0; i < symbols_loaded; i++ )
2735     {
2736       if (symbol_data[i].active == SYMBOL_ACTIVE)
2737       {
2738         if (symbol_data[i].table == symbol_table
2739             && symbol_data[i].symbol == symbol_id)
2740         {
2741           // We found the matching symbol
2742           found = i;  // index of symbol
2743 
2744           // Save newly found symbol in cache, shift other
2745           // cache entries down by one so that newest is
2746           // at the beginning for the cache search.
2747 //fprintf(stderr,"Saving in cache\n");
2748           symbols_cache[4] = symbols_cache[3];
2749           symbols_cache[3] = symbols_cache[2];
2750           symbols_cache[2] = symbols_cache[1];
2751           symbols_cache[1] = symbols_cache[0];
2752           symbols_cache[0] = i;
2753 
2754           break;
2755         }
2756       }
2757     }
2758   }
2759 
2760   if (found == -1)    // Didn't find a matching symbol
2761   {
2762     found = nosym_index;
2763     if (symbol_table && symbol_id && debug_level & 128)
2764     {
2765       fprintf(stderr,"No Symbol Yet! %2x:%2x\n", (unsigned int)symbol_table, (unsigned int)symbol_id);
2766     }
2767   }
2768   else                        // maybe we want a rotated symbol
2769   {
2770 
2771 
2772 // It looks like we originally did not want to rotate the symbol if
2773 // it was ghosted?  Why?  For dead-reckoning we do want it to be
2774 // rotated when ghosted.
2775 //      if (!(orient == ' ' || orient == 'l' || symbol_data[found].orient == ' ' || ghost)) {
2776     if (!(orient == ' ' || orient == 'l' || symbol_data[found].orient == ' '))
2777     {
2778       for (i = found; i < symbols_loaded; i++)
2779       {
2780         if (symbol_data[i].active == SYMBOL_ACTIVE)
2781         {
2782           if (symbol_data[i].table == symbol_table && symbol_data[i].symbol == symbol_id
2783               && symbol_data[i].orient == orient)
2784           {
2785             found=i;  // index of rotated symbol
2786             break;
2787           }
2788         }
2789       }
2790     }
2791   }
2792 
2793 
2794   if (mask)
2795   {
2796     if (ghost)
2797     {
2798       (void)XSetClipMask(XtDisplay(w),gc,symbol_data[found].pix_mask_old);
2799     }
2800     else
2801     {
2802       (void)XSetClipMask(XtDisplay(w),gc,symbol_data[found].pix_mask);
2803     }
2804   }
2805   (void)XSetClipOrigin(XtDisplay(w),gc,x_offset,y_offset);
2806   (void)XCopyArea(XtDisplay(w),symbol_data[found].pix,where,gc,0,0,20,20,x_offset,y_offset);
2807 
2808 
2809   if(alphanum_index > 0)
2810   {
2811     if (ghost)
2812     {
2813       (void)XSetClipMask(XtDisplay(w),gc,symbol_data[alphanum_index].pix_mask_old);
2814     }
2815     else
2816     {
2817       (void)XSetClipMask(XtDisplay(w),gc,symbol_data[alphanum_index].pix_mask);
2818     }
2819 
2820     (void)XSetClipOrigin(XtDisplay(w),gc,x_offset,y_offset);
2821     (void)XCopyArea(XtDisplay(w),symbol_data[alphanum_index].pix,where,gc,0,0,20,20,x_offset,y_offset); // rot
2822   }
2823 
2824   (void)XSetClipMask(XtDisplay(w),gc,None);
2825 }
2826 
2827 // Calculate the width in pixels of the actual text
2828 // This helps us take into account proportional or non-proportional fonts
get_text_width(Widget w,char * text)2829 long get_text_width(Widget w,char *text)
2830 {
2831   long width;
2832 
2833   GContext gcontext;
2834   XFontStruct *xfs_ptr;
2835   int dir, asc, desc;   // parameters returned by XTextExtents, but not used here.
2836   XCharStruct overall;  // description of the space occupied by the string.
2837 
2838   gcontext = XGContextFromGC(gc);
2839   xfs_ptr = XQueryFont(XtDisplay(w), gcontext);
2840 
2841   XTextExtents(xfs_ptr, text, strlen(text), &dir, &asc, &desc, &overall);
2842   //printf("%s Width = %d\n",text,overall.width);
2843   width =overall.width;
2844 
2845   if (xfs_ptr)
2846   {
2847     // This leaks memory if the last parameter is a "0"
2848     XFreeFontInfo(NULL, xfs_ptr, 1);
2849   }
2850   return width;
2851 }
2852 
2853 
2854 
2855 // Speed is in converted units by this point (kph or mph)
draw_symbol(Widget w,char symbol_table,char symbol_id,char symbol_overlay,long x_long,long y_lat,char * callsign_text,char * alt_text,char * course_text,char * speed_text,char * my_distance,char * my_course,char * wx_temp,char * wx_wind,time_t sec_heard,int temp_show_last_heard,Pixmap where,char orient,char area_type,char * signpost,char * gauge_data,int bump_count)2856 void draw_symbol(Widget w, char symbol_table, char symbol_id, char symbol_overlay,
2857                  long x_long,long y_lat, char *callsign_text, char *alt_text, char *course_text,
2858                  char *speed_text, char *my_distance, char *my_course, char *wx_temp,
2859                  char* wx_wind, time_t sec_heard, int temp_show_last_heard, Pixmap where,
2860                  char orient, char area_type, char *signpost, char *gauge_data, int bump_count)
2861 {
2862   long x_offset,y_offset;
2863   int length;
2864   int ghost;
2865   int posyl;
2866   int posyr;
2867   long txt_width;
2868 
2869 // Added to allow dynamic offsets for the text around a symbol
2870 // Originally it was hard coded offsets, this bases the offset on
2871 // the font height and width
2872 // N7IPB - 4/7/2016
2873 //
2874   GContext gcontext;
2875   XFontStruct *xfs_ptr;
2876   int font_width, font_height;
2877 
2878   gcontext = XGContextFromGC(gc);
2879 
2880   xfs_ptr = XQueryFont(XtDisplay(w), gcontext);
2881 
2882   font_width = (int)((xfs_ptr->max_bounds.width
2883                       + xfs_ptr->max_bounds.width
2884                       + xfs_ptr->max_bounds.width
2885                       + xfs_ptr->min_bounds.width) / 4);
2886 
2887   // Get the font height and use it for the distance between lineis of text
2888   font_height = xfs_ptr->max_bounds.ascent;
2889 
2890   // Free the info to avoid a memory leak
2891   if (xfs_ptr)
2892   {
2893     // This leaks memory if the last parameter is a "0"
2894     XFreeFontInfo(NULL, xfs_ptr, 1);
2895   }
2896 
2897   if ((x_long>NW_corner_longitude) && (x_long<SE_corner_longitude))
2898   {
2899 
2900     if ((y_lat>NW_corner_latitude) && (y_lat<SE_corner_latitude))
2901     {
2902 
2903       x_offset=((x_long-NW_corner_longitude)/scale_x)-(8);
2904       y_offset=((y_lat-NW_corner_latitude) /scale_y)-(8);
2905 //N7IPB - check for aircraft and if so check aircraft timeout
2906 // ghost if timeout - else use normal ghost time
2907       if ((aircraft_sec_clear != 0) &&((symbol_id == '^') || (symbol_id == '\'') || (symbol_id == 'X')))
2908       {
2909         ghost = (int)(((sec_heard+aircraft_sec_clear)) < sec_now());
2910       }
2911       else
2912       {
2913         ghost = (int)(((sec_old+sec_heard)) < sec_now());
2914       }
2915       if (bump_count)
2916       {
2917         currently_selected_stations++;
2918       }
2919 
2920       if (Display_.symbol)
2921       {
2922         symbol(w,ghost,symbol_table,symbol_id,symbol_overlay,where,1,x_offset,y_offset,orient);
2923       }
2924 
2925       posyr = font_height;      // align symbols vertically centered to the right
2926       if ( (!ghost || Select_.old_data) && strlen(alt_text)>0)
2927       {
2928         posyr -= font_height/2;
2929       }
2930       if (strlen(callsign_text)>0)
2931       {
2932         posyr -= font_height/2;
2933       }
2934       if ( (!ghost || Select_.old_data) && strlen(speed_text)>0)
2935       {
2936         posyr -= font_height/2 ;
2937       }
2938       if ( (!ghost || Select_.old_data) && strlen(course_text)>0)
2939       {
2940         posyr -= font_height/2;
2941       }
2942       if (area_type == AREA_LINE_RIGHT)
2943       {
2944         posyr += 9;
2945       }
2946       if (signpost[0] != '\0')    // Signpost data?
2947       {
2948         posyr -=font_height/2;
2949       }
2950       // we may eventually have more adjustments for different types of areas
2951 
2952       length=(int)strlen(alt_text);
2953       if ( (!ghost || Select_.old_data) && length>0)
2954       {
2955         x_offset=((x_long-NW_corner_longitude)/scale_x)+13;
2956         y_offset=((y_lat -NW_corner_latitude) /scale_y)+posyr;
2957         draw_nice_string(w,where,letter_style,x_offset,y_offset,alt_text,0x08,0x48,length);
2958         posyr += font_height;
2959       }
2960 
2961       length=(int)strlen(callsign_text);
2962       if ( (!ghost || Select_.old_data) && length>0)
2963       {
2964         x_offset=((x_long-NW_corner_longitude)/scale_x)+13;
2965         y_offset=((y_lat -NW_corner_latitude) /scale_y)+posyr;
2966         draw_nice_string(w,where,letter_style,x_offset,y_offset,callsign_text,0x08,0x0f,length);
2967         posyr += font_height;
2968       }
2969 
2970       length=(int)strlen(speed_text);
2971       if ( (!ghost || Select_.old_data) && length>0)
2972       {
2973         x_offset=((x_long-NW_corner_longitude)/scale_x)+13;
2974         y_offset=((y_lat -NW_corner_latitude) /scale_y)+posyr;
2975         draw_nice_string(w,where,letter_style,x_offset,y_offset,speed_text,0x08,0x4a,length);
2976         posyr += font_height;
2977       }
2978 
2979       length=(int)strlen(course_text);
2980       if ( (!ghost || Select_.old_data) && length>0)
2981       {
2982         x_offset=((x_long-NW_corner_longitude)/scale_x)+13;
2983         y_offset=((y_lat -NW_corner_latitude) /scale_y)+posyr;
2984         draw_nice_string(w,where,letter_style,x_offset,y_offset,course_text,0x08,0x52,length);
2985         posyr += font_height;
2986       }
2987 
2988       length=(int)strlen(signpost);   // Make it white like callsign?
2989       if ( (!ghost || Select_.old_data) && length>0)
2990       {
2991         x_offset=((x_long-NW_corner_longitude)/scale_x)+13;
2992         y_offset=((y_lat -NW_corner_latitude) /scale_y)+posyr;
2993         draw_nice_string(w,where,letter_style,x_offset,y_offset,signpost,0x08,0x0f,length);
2994         posyr += font_height;
2995       }
2996 
2997       posyl = font_height; // distance and direction goes to the left.
2998       // Also minutes last heard.
2999       if ( (!ghost || Select_.old_data) && strlen(my_distance)>0)
3000       {
3001         posyl -= font_height/2;
3002       }
3003       if ( (!ghost || Select_.old_data) && strlen(my_course)>0)
3004       {
3005         posyl -= font_height/2;
3006       }
3007       if ( (!ghost || Select_.old_data) && temp_show_last_heard)
3008       {
3009         posyl -= font_height/2;
3010       }
3011 
3012       length=(int)strlen(my_distance);
3013       txt_width=get_text_width(w,my_distance);
3014       if ( (!ghost || Select_.old_data) && length>0)
3015       {
3016         x_offset=(((x_long-NW_corner_longitude)/scale_x)-(txt_width+9));
3017         y_offset=((y_lat  -NW_corner_latitude) /scale_y)+posyl;
3018         draw_nice_string(w,where,letter_style,x_offset,y_offset,my_distance,0x08,0x0f,length);
3019         posyl += font_height;
3020       }
3021       length=(int)strlen(my_course);
3022       txt_width=get_text_width(w,my_course);
3023       if ( (!ghost || Select_.old_data) && length>0)
3024       {
3025         x_offset=(((x_long-NW_corner_longitude)/scale_x)-(txt_width+9));
3026         y_offset=((y_lat  -NW_corner_latitude) /scale_y)+posyl;
3027         draw_nice_string(w,where,letter_style,x_offset,y_offset,my_course,0x08,0x0f,length);
3028         posyl += font_height;
3029       }
3030       if ( (!ghost || Select_.old_data) && temp_show_last_heard)
3031       {
3032         char age[20];
3033         float minutes;
3034         float hours;
3035         int fgcolor;
3036 
3037 
3038         // Color code the time string based on
3039         // time since last heard:
3040         //  Green:  0-29 minutes
3041         // Yellow: 30-59 minutes
3042         //    Red: 60 minutes to 1 day
3043         //  White: 1 day or later
3044 
3045         minutes = (float)( (sec_now() - sec_heard) / 60.0);
3046         hours = minutes / 60.0;
3047 
3048         // Heard from this station within the
3049         // last 30 minutes?
3050         if (minutes < 30.0)
3051         {
3052           xastir_snprintf(age,
3053                           sizeof(age),
3054                           "%d%s",
3055                           (int)minutes,
3056                           langcode("UNIOP00034"));    // min
3057           fgcolor = 0x52; // green
3058         }
3059         // 30 to 59 minutes?
3060         else if (minutes < 60.0)
3061         {
3062           xastir_snprintf(age,
3063                           sizeof(age),
3064                           "%d%s",
3065                           (int)minutes,
3066                           langcode("UNIOP00034"));    // min
3067           fgcolor = 0x40; // yellow
3068         }
3069         // 1 hour to 1 day old?
3070         else if (hours <= 24.0)
3071         {
3072           xastir_snprintf(age,
3073                           sizeof(age),
3074                           "%.1f%s",
3075                           hours,
3076                           langcode("UNIOP00035"));    // hr
3077           fgcolor = 0x4a; // red
3078         }
3079         // More than a day old
3080         else
3081         {
3082           xastir_snprintf(age,
3083                           sizeof(age),
3084                           "%.1f%s",
3085                           hours / 24.0,
3086                           langcode("UNIOP00036"));    // day
3087           fgcolor = 0x0f; // white
3088         }
3089 
3090         length = strlen(age);
3091         txt_width = get_text_width(w,age);
3092         x_offset=(((x_long-NW_corner_longitude)/scale_x)-(txt_width)-9);
3093         y_offset=((y_lat  -NW_corner_latitude) /scale_y)+posyl;
3094         draw_nice_string(w,where,letter_style,x_offset,y_offset,age,0x08,fgcolor,length);
3095         posyl += font_height;
3096       }
3097 
3098       // weather goes to the bottom, centered horizontally.
3099       // Start off making sure it's below the symbol
3100       posyr += 10;
3101       if (posyr < posyl)
3102       {
3103         posyr = posyl;
3104       }
3105       if (posyr < font_height)
3106       {
3107         posyr = font_height;
3108       }
3109 
3110       length=(int)strlen(wx_temp);
3111       txt_width = get_text_width(w,wx_temp);
3112       if ( (!ghost || Select_.old_data) && length>0)
3113       {
3114         x_offset=((x_long-NW_corner_longitude)/scale_x)-(txt_width/2);
3115         y_offset=((y_lat -NW_corner_latitude) /scale_y)+posyr;
3116         draw_nice_string(w,where,letter_style,x_offset,y_offset,wx_temp,0x08,0x40,length);
3117         posyr += font_height;
3118       }
3119 
3120       length=(int)strlen(wx_wind);
3121       txt_width = get_text_width(w,wx_wind);
3122       if ( (!ghost || Select_.old_data) && length>0)
3123       {
3124         x_offset=((x_long-NW_corner_longitude)/scale_x)-(txt_width/2);
3125         y_offset=((y_lat -NW_corner_latitude) /scale_y)+posyr;
3126         draw_nice_string(w,where,letter_style,x_offset,y_offset,wx_wind,0x08,0x40,length);
3127       }
3128 
3129       if (gauge_data != NULL)
3130       {
3131         // Gauge data goes on the bottom, centered
3132         // horizontally.  White.
3133         if (posyr < posyl)
3134         {
3135           posyr = posyl;
3136         }
3137         if (posyr < font_width)
3138         {
3139           posyr = font_width;
3140         }
3141 
3142         length=(int)strlen(gauge_data);
3143         txt_width=get_text_width(w,gauge_data);
3144         if ( (!ghost || Select_.old_data) && length>0)
3145         {
3146           x_offset=((x_long-NW_corner_longitude)/scale_x)-(txt_width/2);
3147           y_offset=((y_lat -NW_corner_latitude) /scale_y)+posyr;
3148           draw_nice_string(w,where,letter_style,x_offset,y_offset,gauge_data,0x08,0x0f,length);
3149         }
3150       }
3151     }
3152   }
3153 }
3154 
3155 
3156 
3157 
3158 
3159 /*
3160  * Looks at the style to determine what color to use.
3161  * KG4NBB
3162  */
getLineColor(char styleChar)3163 static int getLineColor(char styleChar)
3164 {
3165   int color;
3166 
3167   switch (styleChar)
3168   {
3169     case 'a':
3170     case 'b':
3171     case 'c':
3172       color = colors[0x0c];   // red
3173       break;
3174 
3175     case 'd':
3176     case 'e':
3177     case 'f':
3178       color = colors[0x0e];   // yellow
3179       break;
3180 
3181     case 'g':
3182     case 'h':
3183     case 'i':
3184       color = colors[0x09];   // blue
3185       break;
3186 
3187     case 'j':
3188     case 'k':
3189     case 'l':
3190       color = colors[0x0a];   // green
3191       break;
3192 
3193     default:
3194       color = colors[0x0a];   // green
3195       break;
3196   }
3197 
3198   return color;
3199 }
3200 
3201 
3202 
3203 
3204 
3205 /*
3206  * Looks at the style to determine what line type to use.
3207  * KG4NBB
3208  */
getLineStyle(char styleChar)3209 static int getLineStyle(char styleChar)
3210 {
3211   int style;
3212 
3213   switch (styleChar)
3214   {
3215     case 'a':
3216     case 'd':
3217     case 'g':
3218     case 'j':
3219       style = LineSolid;
3220       break;
3221 
3222     case 'b':
3223     case 'e':
3224     case 'h':
3225     case 'k':
3226       style = LineOnOffDash;
3227       break;
3228 
3229     case 'c':
3230     case 'f':
3231     case 'i':
3232     case 'l':
3233       style = LineDoubleDash;
3234       break;
3235 
3236     default:
3237       style = LineSolid;
3238       break;
3239   }
3240 
3241   return style;
3242 }
3243 
3244 
3245 
3246 
3247 
3248 /*
3249  * Draw the other points associated with the station.
3250  * KG4NBB
3251  */
draw_multipoints(long UNUSED (x_long),long UNUSED (y_lat),int numpoints,long mypoints[][2],char type,char style,time_t sec_heard,Pixmap where)3252 void draw_multipoints(long UNUSED(x_long), long UNUSED(y_lat), int numpoints, long mypoints[][2], char type, char style, time_t sec_heard, Pixmap where)
3253 {
3254   int ghost;
3255   int skip_duplicates;
3256 
3257 
3258   // See if we should draw multipoints for this station. This only happens
3259   // if there are points to draw and the object has not been cleared (or
3260   // we're supposed to show old data).
3261 
3262   // Per Dale Huguley in e-mail 07/10/2003, a good interval for
3263   // the severe weather polygons to disappear is 10 minutes.  We
3264   // hard-code it here so the user can't mess it up too badly with
3265   // the ghosting interval.
3266 //    ghost = (int)(((sec_old+sec_heard)) < sec_now());
3267   ghost = (int)( ( sec_heard + (10 * 60) ) < sec_now() );
3268 
3269   // We don't want to draw them if the ghost interval is up, no
3270   // matter whether Include Expired Data is checked.
3271   //if ( (!ghost || Select_.old_data) && (numpoints > 0) ) {
3272   if ( !ghost  && (numpoints > 0) )
3273   {
3274 
3275     //long x_offset, y_offset;
3276     int  i;
3277 //        XPoint xpoints[MAX_MULTIPOINTS + 1];
3278 
3279 #if 0
3280     long mostNorth, mostSouth, mostWest, mostEast;
3281 
3282     // Check to see if the object is onscreen.
3283     // Look for the coordinates that are farthest north, farthest south,
3284     // farthest west, and farthest east. Then check to see if any of that
3285     // box is onscreen. If so, proceed with drawing. This is all done in
3286     // Xastir coordinates.
3287 
3288     mostNorth = mostSouth = y_lat;
3289     mostWest = mostEast = x_long;
3290 
3291     for (i = 0; i < numpoints; ++i)
3292     {
3293       if (mypoints[i][0] < mostNorth)
3294       {
3295         mostNorth = mypoints[i][0];
3296       }
3297       if (mypoints[i][0] > mostSouth)
3298       {
3299         mostSouth = mypoints[i][0];
3300       }
3301       if (mypoints[i][1] < mostWest)
3302       {
3303         mostWest = mypoints[i][1];
3304       }
3305       if (mypoints[i][1] > mostEast)
3306       {
3307         mostEast = mypoints[i][1];
3308       }
3309     }
3310 
3311     if (onscreen(mostWest, mostEast, mostNorth, mostSouth))
3312 #else   // 0
3313 
3314     // See if the station icon is on the screen. If so, draw the associated
3315     // points. The drawback to this approach is that if the station icon is
3316     // scrolled off the edge of the display the points aren't drawn even if
3317     // one or more of them is on the display.
3318 
3319 //        if( (x_long > NW_corner_longitude) && (x_long < SE_corner_longitude)
3320 //            && (y_lat > NW_corner_latitude) && (y_lat < SE_corner_latitude) )
3321 #endif  // 0
3322     {
3323       //x_offset = (x_long - NW_corner_longitude) / scale_x;
3324       //y_offset = (y_lat - NW_corner_latitude) / scale_y;
3325 
3326       // Convert each of the points from Xastir coordinates to
3327       // screen coordinates and fill in the xpoints array.
3328 
3329 //            for (i = 0; i < numpoints; ++i) {
3330 //                xpoints[i].x = (mypoints[i][0] - NW_corner_longitude) / scale_x;
3331 //                xpoints[i].y = (mypoints[i][1] - NW_corner_latitude) / scale_y;
3332 //                // fprintf(stderr,"   %d: %d,%d\n", i, xpoints[i].x, xpoints[i].y);
3333 //            }
3334 
3335       // The type parameter determines how the points will be used.
3336       // After determining the type, use the style parameter to
3337       // get the color and line style.
3338 
3339       switch (type)
3340       {
3341 
3342         case '0':           // closed polygon
3343         default:
3344           // Repeat the first point so the polygon will be closed.
3345 
3346 //                    xpoints[numpoints].x = xpoints[0].x;
3347 //                    xpoints[numpoints].y = xpoints[0].y;
3348 
3349           // First draw a wider black line.
3350           (void)XSetForeground(XtDisplay(da), gc, colors[0x08]);  // black
3351           (void)XSetLineAttributes(XtDisplay(da), gc, 4, LineSolid, CapButt, JoinMiter);
3352 
3353           skip_duplicates = 0;
3354           for (i = 0; i < numpoints-1; i++)
3355           {
3356 //                        (void)XDrawLines(XtDisplay(da), where, gc, xpoints, numpoints+1, CoordModeOrigin);
3357             draw_vector(da, mypoints[i][0],
3358                         mypoints[i][1],
3359                         mypoints[i+1][0],
3360                         mypoints[i+1][1],
3361                         gc,
3362                         where,
3363                         skip_duplicates);
3364 
3365             skip_duplicates = 1;
3366           }
3367           // Close the polygon
3368           draw_vector(da,
3369                       mypoints[i][0],
3370                       mypoints[i][1],
3371                       mypoints[0][0],
3372                       mypoints[0][1],
3373                       gc,
3374                       where,
3375                       skip_duplicates);
3376 
3377           // Then draw the appropriate colored line on top of it.
3378           (void)XSetForeground(XtDisplay(da), gc, getLineColor(style));
3379           (void)XSetLineAttributes(XtDisplay(da), gc, 2, getLineStyle(style), CapButt, JoinMiter);
3380 
3381           skip_duplicates = 0;
3382           for (i = 0; i < numpoints-1; i++)
3383           {
3384 //                        (void)XDrawLines(XtDisplay(da), where, gc, xpoints, numpoints+1, CoordModeOrigin);
3385             draw_vector(da, mypoints[i][0],
3386                         mypoints[i][1],
3387                         mypoints[i+1][0],
3388                         mypoints[i+1][1],
3389                         gc,
3390                         where,
3391                         skip_duplicates);
3392 
3393             skip_duplicates = 1;
3394           }
3395           // Close the polygon
3396           draw_vector(da,
3397                       mypoints[i][0],
3398                       mypoints[i][1],
3399                       mypoints[0][0],
3400                       mypoints[0][1],
3401                       gc,
3402                       where,
3403                       skip_duplicates);
3404 
3405           break;
3406 
3407         case '1':           // line segments
3408 
3409           (void)XSetForeground(XtDisplay(da), gc, getLineColor(style));
3410           (void)XSetLineAttributes(XtDisplay(da), gc, 2, getLineStyle(style), CapButt, JoinMiter);
3411 
3412           skip_duplicates = 0;
3413           for (i = 0; i < numpoints-1; i++)
3414           {
3415 //                        (void)XDrawLines(XtDisplay(da), where, gc, xpoints, numpoints, CoordModeOrigin);
3416             draw_vector(da, mypoints[i][0],
3417                         mypoints[i][1],
3418                         mypoints[i+1][0],
3419                         mypoints[i+1][1],
3420                         gc,
3421                         where,
3422                         skip_duplicates);
3423 
3424             skip_duplicates = 1;
3425           }
3426 
3427           break;
3428 
3429           // Other types have yet to be implemented.
3430       }
3431     }
3432   }
3433 }
3434 
3435 
3436 
3437 
3438 
Select_symbol_destroy_shell(Widget UNUSED (widget),XtPointer clientData,XtPointer UNUSED (callData))3439 void Select_symbol_destroy_shell( Widget UNUSED(widget), XtPointer clientData, XtPointer UNUSED(callData) )
3440 {
3441   Widget shell = (Widget) clientData;
3442   int i;
3443 
3444   XtPopdown(shell);
3445 
3446   // Free all 188 symbol pixmaps
3447   for ( i = 0; i < (126-32)*2; i++ )
3448   {
3449     (void)XFreePixmap(XtDisplay(appshell),select_icons[i]);
3450   }
3451 
3452   begin_critical_section(&select_symbol_dialog_lock, "draw_symbols.c:Select_symbol_destroy_shell" );
3453 
3454   XtDestroyWidget(shell);
3455   select_symbol_dialog = (Widget)NULL;
3456 
3457   end_critical_section(&select_symbol_dialog_lock, "draw_symbols.c:Select_symbol_destroy_shell" );
3458 
3459 }
3460 
3461 
3462 
3463 
3464 
Select_symbol_change_data(Widget widget,XtPointer clientData,XtPointer callData)3465 void Select_symbol_change_data(Widget widget, XtPointer clientData, XtPointer callData)
3466 {
3467   char table[2];
3468   char symbol[2];
3469   int i = XTPOINTER_TO_INT(clientData);
3470 
3471   //fprintf(stderr,"Selected a symbol: %d\n", clientData);
3472 
3473   if ( i > 0)
3474   {
3475     //fprintf(stderr,"Symbol is from primary symbol table: /%c\n",(char)i);
3476     table[0] = '/';
3477     symbol[0] = (char)i;
3478   }
3479   else
3480   {
3481     //fprintf(stderr,"Symbol is from secondary symbol table: \\%c\n",(char)(-i));
3482     table[0] = '\\';
3483     symbol[0] = (char)(-i);
3484   }
3485   table[1] = '\0';
3486   symbol[1] = '\0';
3487 
3488 
3489   if (symbol_change_requested_from == 1)          // Configure->Station Dialog
3490   {
3491     symbol_change_requested_from = 0;
3492     //fprintf(stderr,"Updating Configure->Station Dialog\n");
3493 
3494     XmTextFieldSetString(station_config_group_data,table);
3495     XmTextFieldSetString(station_config_symbol_data,symbol);
3496     updateSymbolPictureCallback(widget,clientData,callData);
3497   }
3498   else if (symbol_change_requested_from == 2)     // Create->Object/Item Dialog
3499   {
3500     symbol_change_requested_from = 0;
3501     //fprintf(stderr,"Updating Create->Object/Item Dialog\n");
3502 
3503     XmTextFieldSetString(object_group_data,table);
3504     XmTextFieldSetString(object_symbol_data,symbol);
3505     updateObjectPictureCallback(widget,clientData,callData);
3506   }
3507   else    // Do nothing.  We shouldn't be here.
3508   {
3509     symbol_change_requested_from = 0;
3510   }
3511 
3512   Select_symbol_destroy_shell(widget,select_symbol_dialog,callData);
3513 }
3514 
3515 
3516 
3517 
3518 
Select_symbol(Widget UNUSED (w),XtPointer UNUSED (clientData),XtPointer UNUSED (callData))3519 void Select_symbol( Widget UNUSED(w), XtPointer UNUSED(clientData), XtPointer UNUSED(callData) )
3520 {
3521   static Widget  pane, my_form, my_form2, my_form3, button_cancel,
3522          frame, frame2, b1, scrollwindow;
3523   int i;
3524   Atom delw;
3525 
3526 
3527   if (!select_symbol_dialog)
3528   {
3529 
3530 
3531     begin_critical_section(&select_symbol_dialog_lock, "draw_symbols.c:Select_symbol" );
3532 
3533 
3534     select_symbol_dialog = XtVaCreatePopupShell(langcode("SYMSEL0001"),
3535                            xmDialogShellWidgetClass, appshell,
3536                            XmNdeleteResponse,XmDESTROY,
3537                            XmNdefaultPosition, FALSE,
3538                            XmNfontList, fontlist1,
3539                            NULL);
3540 
3541     pane = XtVaCreateWidget("Select_symbol pane",
3542                             xmPanedWindowWidgetClass,
3543                             select_symbol_dialog,
3544                             MY_FOREGROUND_COLOR,
3545                             MY_BACKGROUND_COLOR,
3546                             NULL);
3547 
3548     scrollwindow = XtVaCreateManagedWidget("Select_symbol scrollwindow",
3549                                            xmScrolledWindowWidgetClass,
3550                                            pane,
3551                                            XmNscrollingPolicy, XmAUTOMATIC,
3552                                            NULL);
3553 
3554     my_form =  XtVaCreateWidget("Select_symbol my_form",
3555                                 xmFormWidgetClass,
3556                                 scrollwindow,
3557                                 XmNfractionBase, 5,
3558                                 XmNautoUnmanage, FALSE,
3559                                 XmNshadowThickness, 1,
3560                                 MY_FOREGROUND_COLOR,
3561                                 MY_BACKGROUND_COLOR,
3562                                 NULL);
3563 
3564     frame = XtVaCreateManagedWidget("Select_symbol frame",
3565                                     xmFrameWidgetClass,
3566                                     my_form,
3567                                     XmNtopAttachment,XmATTACH_FORM,
3568                                     XmNtopOffset,10,
3569                                     XmNbottomAttachment,XmATTACH_NONE,
3570                                     XmNleftAttachment, XmATTACH_FORM,
3571                                     XmNleftOffset, 10,
3572                                     XmNrightAttachment,XmATTACH_NONE,
3573                                     MY_FOREGROUND_COLOR,
3574                                     MY_BACKGROUND_COLOR,
3575                                     NULL);
3576 
3577     // Discard the return value of this function, we're not using it.
3578     // GCC 6.x *hates* when we assign to variables we never use.
3579     XtVaCreateManagedWidget(langcode("SYMSEL0002"),
3580                             xmLabelWidgetClass,
3581                             frame,
3582                             XmNchildType, XmFRAME_TITLE_CHILD,
3583                             MY_FOREGROUND_COLOR,
3584                             MY_BACKGROUND_COLOR,
3585                             XmNfontList, fontlist1,
3586                             NULL);
3587 
3588     frame2 = XtVaCreateManagedWidget("Select_symbol frame",
3589                                      xmFrameWidgetClass,
3590                                      my_form,
3591                                      XmNtopAttachment,XmATTACH_FORM,
3592                                      XmNtopOffset,10,
3593                                      XmNbottomAttachment,XmATTACH_NONE,
3594                                      XmNleftAttachment, XmATTACH_WIDGET,
3595                                      XmNleftWidget, frame,
3596                                      XmNleftOffset, 10,
3597                                      XmNrightAttachment,XmATTACH_FORM,
3598                                      XmNrightOffset, 10,
3599                                      MY_FOREGROUND_COLOR,
3600                                      MY_BACKGROUND_COLOR,
3601                                      NULL);
3602 
3603     // Discard the return value of this function call, we're not using it.
3604     // GCC 6.x *hates* when we assign to variables we never use.
3605     XtVaCreateManagedWidget(langcode("SYMSEL0003"),
3606                             xmLabelWidgetClass,
3607                             frame2,
3608                             XmNchildType, XmFRAME_TITLE_CHILD,
3609                             MY_FOREGROUND_COLOR,
3610                             MY_BACKGROUND_COLOR,
3611                             XmNfontList, fontlist1,
3612                             NULL);
3613 
3614     my_form2 =  XtVaCreateWidget("Select_symbol my_form2",
3615                                  xmRowColumnWidgetClass,
3616                                  frame,
3617                                  XmNorientation, XmHORIZONTAL,
3618                                  XmNpacking, XmPACK_COLUMN,
3619                                  XmNnumColumns, 10,
3620                                  XmNautoUnmanage, FALSE,
3621                                  XmNshadowThickness, 1,
3622                                  MY_FOREGROUND_COLOR,
3623                                  MY_BACKGROUND_COLOR,
3624                                  NULL);
3625 
3626     my_form3 =  XtVaCreateWidget("Select_symbol my_form3",
3627                                  xmRowColumnWidgetClass,
3628                                  frame2,
3629                                  XmNorientation, XmHORIZONTAL,
3630                                  XmNpacking, XmPACK_COLUMN,
3631                                  XmNnumColumns, 10,
3632                                  XmNautoUnmanage, FALSE,
3633                                  XmNshadowThickness, 1,
3634                                  MY_FOREGROUND_COLOR,
3635                                  MY_BACKGROUND_COLOR,
3636                                  NULL);
3637 
3638     // Symbols:  33 to 126, for both '/' and '\' tables (94 * 2)
3639     // 33 = start of icons in ASCII table, 126 = end
3640 
3641     // Draw the primary symbol set
3642     for ( i = 33; i < 127; i++ )
3643     {
3644 
3645       select_icons[i-33] = XCreatePixmap(XtDisplay(appshell),
3646                                          RootWindowOfScreen(XtScreen(appshell)),
3647                                          20,
3648                                          20,
3649                                          DefaultDepthOfScreen(XtScreen(appshell)));
3650 
3651       b1 = XtVaCreateManagedWidget("symbol button",
3652                                    xmPushButtonWidgetClass,
3653                                    my_form2,
3654                                    XmNlabelType,               XmPIXMAP,
3655                                    XmNlabelPixmap,             select_icons[i-33],
3656                                    XmNnavigationType, XmTAB_GROUP,
3657                                    MY_FOREGROUND_COLOR,
3658                                    MY_BACKGROUND_COLOR,
3659                                    XmNfontList, fontlist1,
3660                                    NULL);
3661 
3662       symbol(b1,0,'/',(char)i,' ',select_icons[i-33],0,0,0,' ');  // create icon
3663 
3664       // Here we send back the ascii number of the symbol.  We need to keep it within
3665       // the range of short int's.
3666       XtAddCallback(b1,
3667                     XmNactivateCallback,
3668                     Select_symbol_change_data,
3669                     INT_TO_XTPOINTER(i) );
3670     }
3671 
3672     // Draw the alternate symbol set
3673     for ( i = 33+94; i < 127+94; i++ )
3674     {
3675 
3676       select_icons[i-33] = XCreatePixmap(XtDisplay(appshell),
3677                                          RootWindowOfScreen(XtScreen(appshell)),
3678                                          20,
3679                                          20,
3680                                          DefaultDepthOfScreen(XtScreen(appshell)));
3681 
3682       b1 = XtVaCreateManagedWidget("symbol button",
3683                                    xmPushButtonWidgetClass,
3684                                    my_form3,
3685                                    XmNlabelType,               XmPIXMAP,
3686                                    XmNlabelPixmap,             select_icons[i-33],
3687                                    XmNnavigationType, XmTAB_GROUP,
3688                                    MY_FOREGROUND_COLOR,
3689                                    MY_BACKGROUND_COLOR,
3690                                    XmNfontList, fontlist1,
3691                                    NULL);
3692 
3693       symbol(b1,0,'\\',(char)i-94,' ',select_icons[i-33],0,0,0,' ');  // create icon
3694 
3695       // Here we send back the ascii number of the symbol negated.  We need to keep it
3696       // within the range of short int's.
3697       XtAddCallback(b1,
3698                     XmNactivateCallback,
3699                     Select_symbol_change_data,
3700                     INT_TO_XTPOINTER(-(i-94)) );
3701     }
3702 
3703     button_cancel = XtVaCreateManagedWidget(langcode("UNIOP00002"),
3704                                             xmPushButtonGadgetClass,
3705                                             my_form,
3706                                             XmNtopAttachment, XmATTACH_WIDGET,
3707                                             XmNtopWidget, frame,
3708                                             XmNtopOffset, 5,
3709                                             XmNbottomAttachment, XmATTACH_FORM,
3710                                             XmNbottomOffset, 5,
3711                                             XmNleftAttachment, XmATTACH_FORM,
3712                                             XmNleftOffset, 5,
3713                                             XmNrightAttachment, XmATTACH_FORM,
3714                                             XmNrightOffset, 5,
3715                                             XmNnavigationType, XmTAB_GROUP,
3716                                             MY_FOREGROUND_COLOR,
3717                                             MY_BACKGROUND_COLOR,
3718                                             XmNfontList, fontlist1,
3719                                             NULL);
3720 
3721     XtAddCallback(button_cancel, XmNactivateCallback, Select_symbol_destroy_shell, select_symbol_dialog);
3722 
3723     pos_dialog(select_symbol_dialog);
3724 
3725     delw = XmInternAtom(XtDisplay(select_symbol_dialog),"WM_DELETE_WINDOW", FALSE);
3726     XmAddWMProtocolCallback(select_symbol_dialog, delw, Select_symbol_destroy_shell, (XtPointer)select_symbol_dialog);
3727     XtManageChild(my_form3);
3728     XtManageChild(my_form2);
3729     XtManageChild(my_form);
3730     XtManageChild(pane);
3731 
3732     resize_dialog(my_form, select_symbol_dialog);
3733 
3734     XtPopup(select_symbol_dialog,XtGrabNone);
3735 
3736     // Move focus to the Close button.  This appears to highlight the
3737     // button fine, but we're not able to hit the <Enter> key to
3738     // have that default function happen.  Note:  We _can_ hit the
3739     // <SPACE> key, and that activates the option.
3740 //        XmUpdateDisplay(select_symbol_dialog);
3741     XmProcessTraversal(button_cancel, XmTRAVERSE_CURRENT);
3742 
3743 
3744     end_critical_section(&select_symbol_dialog_lock, "draw_symbols.c:Select_symbol" );
3745 
3746 
3747   }
3748   else
3749   {
3750     (void)XRaiseWindow(XtDisplay(select_symbol_dialog), XtWindow(select_symbol_dialog));
3751   }
3752 }
3753 
3754 
3755 
3756 
3757 
3758 // Function to draw dead-reckoning symbols.
3759 //
draw_deadreckoning_features(DataRow * p_station,Pixmap where,Widget w)3760 void draw_deadreckoning_features(DataRow *p_station,
3761                                  Pixmap where,
3762                                  Widget w)
3763 {
3764   double my_course;
3765   long x_long, y_lat;
3766   long x_long2, y_lat2;
3767   long x, y;
3768   long x2, y2;
3769   double diameter;
3770   int color = trail_colors[p_station->trail_color];
3771 //    int symbol_on_screen = 0;
3772   int ghosted_symbol_on_screen = 0;
3773 
3774 
3775 // This function takes a bit of CPU if we are zoomed out.  It'd be
3776 // best to check first whether the zoom level and the speed make it
3777 // worth computing DR at all for this station.  As a first
3778 // approximation, we could turn off DR if we're at zoom 8000 or
3779 // higher:
3780 //
3781 //    if (scale_y > 8000)
3782 //        return;
3783 
3784 
3785   x_long = p_station->coord_lon;
3786   y_lat = p_station->coord_lat;
3787 
3788   // x/y are screen locations for start position
3789   x = (x_long - NW_corner_longitude)/scale_x;
3790   y = (y_lat - NW_corner_latitude)/scale_y;
3791 
3792   y_lat2 = y_lat;
3793   x_long2 = x_long;
3794 
3795   // Compute the latest DR'ed position for the object
3796   compute_current_DR_position(p_station,
3797                               &x_long2,
3798                               &y_lat2);
3799 
3800   // x2/y2 are screen location for ghost symbol (DR'ed position)
3801   x2 = (x_long2 - NW_corner_longitude)/scale_x;
3802   y2 = (y_lat2 - NW_corner_latitude)/scale_y;
3803 
3804 
3805   // Check DR'ed symbol position
3806   if (    (x_long2>NW_corner_longitude) &&
3807           (x_long2<SE_corner_longitude) &&
3808           (y_lat2>NW_corner_latitude) &&
3809           (y_lat2<SE_corner_latitude) &&
3810           ((x_long>=0) && (x_long<=129600000l)) &&
3811           ((y_lat>=0) && (y_lat<=64800000l)))
3812   {
3813 
3814     ghosted_symbol_on_screen++;
3815   }
3816 
3817 
3818   // Draw the DR arc
3819   //
3820   if (Display_.dr_arc && ghosted_symbol_on_screen)
3821   {
3822 
3823     double xdiff, ydiff;
3824 
3825 
3826     xdiff = (x2-x) * 1.0;
3827     ydiff = (y2-y) * 1.0;
3828 
3829     // a squared + b squared = c squared
3830     diameter = 2.0 * sqrt( (double)( (ydiff*ydiff) + (xdiff*xdiff) ) );
3831 
3832     //fprintf(stderr,"Range:%f\tDiameter:%f\n",range,diameter);
3833 
3834     if (diameter > 10.0)
3835     {
3836       int arc_degrees = (sec_now() - p_station->sec_heard) * 90 / (5*60);
3837 
3838       if (arc_degrees > 360)
3839       {
3840         arc_degrees = 360;
3841       }
3842 
3843       (void)XSetLineAttributes(XtDisplay(da), gc, 1, LineOnOffDash, CapButt,JoinMiter);
3844       //(void)XSetForeground(XtDisplay(da),gc,colors[0x0a]);
3845       //(void)XSetForeground(XtDisplay(da),gc,colors[0x44]); // red3
3846       (void)XSetForeground(XtDisplay(da),gc,color);
3847 
3848 
3849       // Compute angle from the two screen positions.
3850       //
3851       if (xdiff != 0)
3852       {
3853 //We should guard against divide-by-zero here!
3854         my_course = 57.29578 * atan(xdiff/ ydiff);
3855       }
3856       else
3857       {
3858         if (ydiff >= 0)
3859         {
3860           my_course = 180.0;
3861         }
3862         else
3863         {
3864           my_course = 0.0;
3865         }
3866       }
3867 
3868 
3869 //fprintf(stderr,"my_course:%f\n", my_course);
3870       // The arctan function returns values between -90 and +90.  To
3871       // obtain the true course we apply the following rules:
3872       if (ydiff > 0 && xdiff > 0)
3873       {
3874 //fprintf(stderr,"1\n");  // Lower-right quadrant
3875         my_course = 360.0 - my_course;
3876       }
3877       else if (ydiff < 0.0 && xdiff > 0.0)
3878       {
3879 //fprintf(stderr,"2\n");  // Upper-right quadrant
3880         my_course = 180.0 - my_course;
3881       }
3882       else if (ydiff < 0.0 && xdiff < 0.0)
3883       {
3884 //fprintf(stderr,"3\n");  // Upper-left quadrant
3885         my_course = 180.0 - my_course;
3886       }
3887       else if (ydiff > 0.0 && xdiff < 0.0)
3888       {
3889 //fprintf(stderr,"4\n");  // Lower-left quadrant
3890         my_course = 360.0 - my_course;
3891       }
3892       else    // 0/90/180/270
3893       {
3894 //fprintf(stderr,"5\n");
3895         my_course = 180.0 + my_course;
3896       }
3897 
3898 
3899       // Convert to screen angle for XDrawArc routines:
3900       my_course = my_course + 90.0;
3901 
3902       if (my_course > 360.0)
3903       {
3904         my_course = my_course - 360.0;
3905       }
3906 
3907 //fprintf(stderr,"\tmy_course2:%f\n", my_course);
3908 
3909 // Check that our parameters are within spec for XDrawArc.  Tricky
3910 // 'cuz the XArc struct has short's and unsigned short's, while
3911 // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
3912 // just to make sure.
3913 
3914       (void)XDrawArc(XtDisplay(da),where,gc,
3915                      l16(x-(diameter/2)),    // int
3916                      l16(y-(diameter/2)),    // int
3917                      lu16(diameter),         // unsigned int
3918                      lu16(diameter),         // unsigned int
3919                      l16(-64*my_course),     // int
3920                      l16(64/2*arc_degrees)); // int
3921 
3922 // Check that our parameters are within spec for XDrawArc.  Tricky
3923 // 'cuz the XArc struct has short's and unsigned short's, while
3924 // XDrawArc man-page says int's/unsigned int's.  We'll stick to 16-bit
3925 // just to make sure.
3926 
3927       (void)XDrawArc(XtDisplay(da),where,gc,
3928                      l16(x-(diameter/2)),        // int
3929                      l16(y-(diameter/2)),        // int
3930                      lu16(diameter),             // unsigned int
3931                      lu16(diameter),             // unsigned int
3932                      l16(-64*my_course),         // int
3933                      l16(-64/2*arc_degrees));    // int
3934     }
3935   }
3936 
3937 
3938 // Note that for the DR course, if we're in the middle of the symbol
3939 // and the DR'ed symbol (ghosted symbol), but neither of them are
3940 // on-screen, the DR'ed course won't display.
3941 //
3942   // Draw the DR'ed course if either the symbol or the DR'ed
3943   // symbol (ghosted symbol) are on-screen.
3944   //
3945   if (Display_.dr_course)
3946   {
3947 
3948     (void)XSetLineAttributes(XtDisplay(da), gc, 0, LineOnOffDash, CapButt,JoinMiter);
3949     (void)XSetForeground(XtDisplay(da),gc,color); // red3
3950 
3951     // This one changes the angle as the vector gets longer, by
3952     // about 10 degrees (A test at 133 degrees -> 143 degrees).
3953     // draw_vector() needs to truncate the line in this case,
3954     // maintaining the same slope.  This behavior is _much_
3955     // better than the XDrawLine above though!
3956     //
3957     draw_vector(w,
3958                 x_long,
3959                 y_lat,
3960                 x_long2,
3961                 y_lat2,
3962                 gc,
3963                 where,
3964                 0);
3965   }
3966 
3967 
3968   // Draw the DR'ed symbol (ghosted symbol) if enabled and if
3969   // on-screen.
3970   //
3971   if (Display_.dr_symbol && ghosted_symbol_on_screen)
3972   {
3973 
3974     draw_symbol(w,
3975                 p_station->aprs_symbol.aprs_type,
3976                 p_station->aprs_symbol.aprs_symbol,
3977                 p_station->aprs_symbol.special_overlay,
3978                 x_long2,
3979                 y_lat2,
3980                 "",
3981                 "",
3982                 "",
3983                 "",
3984                 "",
3985                 "",
3986                 "",
3987                 "",
3988                 p_station->sec_heard-sec_old,   // Always draw it ghosted
3989                 0,
3990                 where,
3991                 symbol_orient(p_station->course),
3992                 p_station->aprs_symbol.area_object.type,
3993                 p_station->signpost,
3994                 NULL,
3995                 0);  // Don't bump the station count
3996   }
3997 }
3998 
3999 
4000