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