(no commit message)
[geda-pcb/pcjc2.git] / src / report.c
blobfe75421915dc722fd36a3326c509470238544c12
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
7 * This module, report.c, was written and is Copyright (C) 1997 harry eaton
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 * Contact addresses for paper mail and Email:
24 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
25 * Thomas.Nau@rz.uni-ulm.de
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
34 #include <math.h>
36 #include "report.h"
37 #include "crosshair.h"
38 #include "data.h"
39 #include "drill.h"
40 #include "error.h"
41 #include "search.h"
42 #include "misc.h"
43 #include "mymem.h"
44 #include "rats.h"
45 #include "rtree.h"
46 #include "strflags.h"
47 #include "macro.h"
48 #include "undo.h"
49 #include "find.h"
50 #include "draw.h"
51 #include "pcb-printf.h"
52 #ifdef HAVE_REGEX_H
53 #include <regex.h>
54 #endif
56 #ifdef HAVE_REGCOMP
57 #undef HAVE_RE_COMP
58 #endif
60 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
61 #define USE_RE
62 #endif
64 #ifdef HAVE_LIBDMALLOC
65 #include <dmalloc.h>
66 #endif
68 #define USER_UNITMASK (Settings.grid_unit->allow)
70 static int
71 ReportDrills (int argc, char **argv, Coord x, Coord y)
73 DrillInfoType *AllDrills;
74 Cardinal n;
75 char *stringlist, *thestring;
76 int total_drills = 0;
78 AllDrills = GetDrillInfo (PCB->Data);
79 RoundDrillInfo (AllDrills, 100);
81 for (n = 0; n < AllDrills->DrillN; n++)
83 total_drills += AllDrills->Drill[n].PinCount;
84 total_drills += AllDrills->Drill[n].ViaCount;
85 total_drills += AllDrills->Drill[n].UnplatedCount;
88 stringlist = (char *)malloc (512L + AllDrills->DrillN * 64L);
90 /* Use tabs for formatting since can't count on a fixed font anymore.
91 | And even that probably isn't going to work in all cases.
93 sprintf (stringlist,
94 _("There are %d different drill sizes used in this layout, %d holes total\n\n"
95 "Drill Diam. (%s)\t# of Pins\t# of Vias\t# of Elements\t# Unplated\n"),
96 AllDrills->DrillN, total_drills, Settings.grid_unit->suffix);
97 thestring = stringlist;
98 while (*thestring != '\0')
99 thestring++;
100 for (n = 0; n < AllDrills->DrillN; n++)
102 pcb_sprintf (thestring,
103 "%10m*\t\t%d\t\t%d\t\t%d\t\t%d\n",
104 Settings.grid_unit->suffix,
105 AllDrills->Drill[n].DrillSize,
106 AllDrills->Drill[n].PinCount, AllDrills->Drill[n].ViaCount,
107 AllDrills->Drill[n].ElementN,
108 AllDrills->Drill[n].UnplatedCount);
109 while (*thestring != '\0')
110 thestring++;
112 FreeDrillInfo (AllDrills);
113 /* create dialog box */
114 gui->report_dialog (_("Drill Report"), stringlist);
116 free (stringlist);
117 return 0;
121 static const char reportdialog_syntax[] = N_("ReportDialog()");
123 static const char reportdialog_help[] =
124 N_("Report on the object under the crosshair");
126 /* %start-doc actions ReportDialog
128 This is a shortcut for @code{Report(Object)}.
130 %end-doc */
132 static int
133 ReportDialog (int argc, char **argv, Coord x, Coord y)
135 void *ptr1, *ptr2, *ptr3;
136 int type;
137 char report[2048];
139 type = SearchScreen (x, y, REPORT_TYPES, &ptr1, &ptr2, &ptr3);
140 if (type == NO_TYPE)
141 type =
142 SearchScreen (x, y, REPORT_TYPES | LOCKED_TYPE, &ptr1, &ptr2, &ptr3);
144 switch (type)
146 case VIA_TYPE:
148 PinType *via;
149 #ifndef NDEBUG
150 if (gui->shift_is_pressed ())
152 __r_dump_tree (PCB->Data->via_tree->root, 0);
153 return 0;
155 #endif
156 via = (PinType *) ptr2;
157 if (TEST_FLAG (HOLEFLAG, via))
158 pcb_sprintf (&report[0], _("%m+VIA ID# %ld; Flags:%s\n"
159 "(X,Y) = %$mD.\n"
160 "It is a pure hole of diameter %$mS.\n"
161 "Name = \"%s\"."
162 "%s"), USER_UNITMASK, via->ID, flags_to_string (via->Flags, VIA_TYPE),
163 via->X, via->Y, via->DrillingHole, EMPTY (via->Name),
164 TEST_FLAG (LOCKFLAG, via) ? _("It is LOCKED.\n") : "");
165 else
166 pcb_sprintf (&report[0], _("%m+VIA ID# %ld; Flags:%s\n"
167 "(X,Y) = %$mD.\n"
168 "Copper width = %$mS. Drill width = %$mS.\n"
169 "Clearance width in polygons = %$mS.\n"
170 "Annulus = %$mS.\n"
171 "Solder mask hole = %$mS (gap = %$mS).\n"
172 "Name = \"%s\"."
173 "%s"), USER_UNITMASK, via->ID, flags_to_string (via->Flags, VIA_TYPE),
174 via->X, via->Y,
175 via->Thickness,
176 via->DrillingHole,
177 via->Clearance / 2,
178 (via->Thickness - via->DrillingHole) / 2,
179 via->Mask,
180 (via->Mask - via->Thickness) / 2,
181 EMPTY (via->Name), TEST_FLAG (LOCKFLAG, via) ?
182 _("It is LOCKED.\n") : "");
183 break;
185 case PIN_TYPE:
187 PinType *Pin;
188 ElementType *element;
189 #ifndef NDEBUG
190 if (gui->shift_is_pressed ())
192 __r_dump_tree (PCB->Data->pin_tree->root, 0);
193 return 0;
195 #endif
196 Pin = (PinType *) ptr2;
197 element = (ElementType *) ptr1;
199 PIN_LOOP (element);
201 if (pin == Pin)
202 break;
204 END_LOOP;
205 if (TEST_FLAG (HOLEFLAG, Pin))
206 pcb_sprintf (&report[0], _("%m+PIN ID# %ld; Flags:%s\n"
207 "(X,Y) = %$mD.\n"
208 "It is a mounting hole. Drill width = %$mS.\n"
209 "It is owned by element %$mS.\n"
210 "%s"), USER_UNITMASK, Pin->ID, flags_to_string (Pin->Flags, PIN_TYPE),
211 Pin->X, Pin->Y, Pin->DrillingHole,
212 EMPTY (element->Name[1].TextString),
213 TEST_FLAG (LOCKFLAG, Pin) ? _("It is LOCKED.\n") : "");
214 else
215 pcb_sprintf (&report[0],
216 _("%m+PIN ID# %ld; Flags:%s\n" "(X,Y) = %$mD.\n"
217 "Copper width = %$mS. Drill width = %$mS.\n"
218 "Clearance width to Polygon = %$mS.\n"
219 "Annulus = %$mS.\n"
220 "Solder mask hole = %$mS (gap = %$mS).\n"
221 "Name = \"%s\".\n"
222 "It is owned by element %s\n as pin number %s.\n"
223 "%s"), USER_UNITMASK,
224 Pin->ID, flags_to_string (Pin->Flags, PIN_TYPE),
225 Pin->X, Pin->Y, Pin->Thickness,
226 Pin->DrillingHole,
227 Pin->Clearance / 2,
228 (Pin->Thickness - Pin->DrillingHole) / 2,
229 Pin->Mask,
230 (Pin->Mask - Pin->Thickness) / 2,
231 EMPTY (Pin->Name),
232 EMPTY (element->Name[1].TextString), EMPTY (Pin->Number),
233 TEST_FLAG (LOCKFLAG, Pin) ? _("It is LOCKED.\n") : "");
234 break;
236 case LINE_TYPE:
238 LineType *line;
239 #ifndef NDEBUG
240 if (gui->shift_is_pressed ())
242 LayerType *layer = (LayerType *) ptr1;
243 __r_dump_tree (layer->line_tree->root, 0);
244 return 0;
246 #endif
247 line = (LineType *) ptr2;
248 pcb_sprintf (&report[0], _("%m+LINE ID# %ld; Flags:%s\n"
249 "FirstPoint(X,Y) = %$mD, ID = %ld.\n"
250 "SecondPoint(X,Y) = %$mD, ID = %ld.\n"
251 "Width = %$mS.\nClearance width in polygons = %$mS.\n"
252 "It is on layer %d\n"
253 "and has name \"%s\".\n"
254 "%s"), USER_UNITMASK,
255 line->ID, flags_to_string (line->Flags, LINE_TYPE),
256 line->Point1.X, line->Point1.Y, line->Point1.ID,
257 line->Point2.X, line->Point2.Y, line->Point2.ID,
258 line->Thickness, line->Clearance / 2,
259 GetLayerNumber (PCB->Data, (LayerType *) ptr1),
260 UNKNOWN (line->Number),
261 TEST_FLAG (LOCKFLAG, line) ? _("It is LOCKED.\n") : "");
262 break;
264 case RATLINE_TYPE:
266 RatType *line;
267 #ifndef NDEBUG
268 if (gui->shift_is_pressed ())
270 __r_dump_tree (PCB->Data->rat_tree->root, 0);
271 return 0;
273 #endif
274 line = (RatType *) ptr2;
275 pcb_sprintf (&report[0], _("%m+RAT-LINE ID# %ld; Flags:%s\n"
276 "FirstPoint(X,Y) = %$mD; ID = %ld; "
277 "connects to layer group %d.\n"
278 "SecondPoint(X,Y) = %$mD; ID = %ld; "
279 "connects to layer group %d.\n"),
280 USER_UNITMASK, line->ID, flags_to_string (line->Flags, LINE_TYPE),
281 line->Point1.X, line->Point1.Y,
282 line->Point1.ID, line->group1,
283 line->Point2.X, line->Point2.Y,
284 line->Point2.ID, line->group2);
285 break;
287 case ARC_TYPE:
289 ArcType *Arc;
290 BoxType *box;
291 #ifndef NDEBUG
292 if (gui->shift_is_pressed ())
294 LayerType *layer = (LayerType *) ptr1;
295 __r_dump_tree (layer->arc_tree->root, 0);
296 return 0;
298 #endif
299 Arc = (ArcType *) ptr2;
300 box = GetArcEnds (Arc);
302 pcb_sprintf (&report[0], _("%m+ARC ID# %ld; Flags:%s\n"
303 "CenterPoint(X,Y) = %$mD.\n"
304 "Radius = %$mS, Thickness = %$mS.\n"
305 "Clearance width in polygons = %$mS.\n"
306 "StartAngle = %ma degrees, DeltaAngle = %ma degrees.\n"
307 "Bounding Box is %$mD, %$mD.\n"
308 "That makes the end points at %$mD and %$mD.\n"
309 "It is on layer %d.\n"
310 "%s"), USER_UNITMASK, Arc->ID, flags_to_string (Arc->Flags, ARC_TYPE),
311 Arc->X, Arc->Y,
312 Arc->Width, Arc->Thickness,
313 Arc->Clearance / 2, Arc->StartAngle, Arc->Delta,
314 Arc->BoundingBox.X1, Arc->BoundingBox.Y1,
315 Arc->BoundingBox.X2, Arc->BoundingBox.Y2,
316 box->X1, box->Y1,
317 box->X2, box->Y2,
318 GetLayerNumber (PCB->Data, (LayerType *) ptr1),
319 TEST_FLAG (LOCKFLAG, Arc) ? _("It is LOCKED.\n") : "");
320 break;
322 case POLYGON_TYPE:
324 PolygonType *Polygon;
325 #ifndef NDEBUG
326 if (gui->shift_is_pressed ())
328 LayerType *layer = (LayerType *) ptr1;
329 __r_dump_tree (layer->polygon_tree->root, 0);
330 return 0;
332 #endif
333 Polygon = (PolygonType *) ptr2;
335 pcb_sprintf (&report[0], _("%m+POLYGON ID# %ld; Flags:%s\n"
336 "Its bounding box is %$mD %$mD.\n"
337 "It has %d points and could store %d more\n"
338 " without using more memory.\n"
339 "It has %d holes and resides on layer %d.\n"
340 "%s"), USER_UNITMASK, Polygon->ID,
341 flags_to_string (Polygon->Flags, POLYGON_TYPE),
342 Polygon->BoundingBox.X1, Polygon->BoundingBox.Y1,
343 Polygon->BoundingBox.X2, Polygon->BoundingBox.Y2,
344 Polygon->PointN, Polygon->PointMax - Polygon->PointN,
345 Polygon->HoleIndexN,
346 GetLayerNumber (PCB->Data, (LayerType *) ptr1),
347 TEST_FLAG (LOCKFLAG, Polygon) ? _("It is LOCKED.\n") : "");
348 break;
350 case PAD_TYPE:
352 Coord len;
353 PadType *Pad;
354 ElementType *element;
355 #ifndef NDEBUG
356 if (gui->shift_is_pressed ())
358 __r_dump_tree (PCB->Data->pad_tree->root, 0);
359 return 0;
361 #endif
362 Pad = (PadType *) ptr2;
363 element = (ElementType *) ptr1;
365 PAD_LOOP (element);
368 if (pad == Pad)
369 break;
372 END_LOOP;
373 len = Distance (Pad->Point1.X, Pad->Point1.Y, Pad->Point2.X, Pad->Point2.Y);
374 pcb_sprintf (&report[0], _("%m+PAD ID# %ld; Flags:%s\n"
375 "FirstPoint(X,Y) = %$mD; ID = %ld.\n"
376 "SecondPoint(X,Y) = %$mD; ID = %ld.\n"
377 "Width = %$mS. Length = %$mS.\n"
378 "Clearance width in polygons = %$mS.\n"
379 "Solder mask = %$mS x %$mS (gap = %$mS).\n"
380 "Name = \"%s\".\n"
381 "It is owned by SMD element %s\n"
382 " as pin number %s and is on the %s\n"
383 "side of the board.\n"
384 "%s"), USER_UNITMASK, Pad->ID,
385 flags_to_string (Pad->Flags, PAD_TYPE),
386 Pad->Point1.X, Pad->Point1.Y, Pad->Point1.ID,
387 Pad->Point2.X, Pad->Point2.Y, Pad->Point2.ID,
388 Pad->Thickness, len + Pad->Thickness,
389 Pad->Clearance / 2,
390 Pad->Mask, len + Pad->Mask,
391 (Pad->Mask - Pad->Thickness) / 2,
392 EMPTY (Pad->Name),
393 EMPTY (element->Name[1].TextString),
394 EMPTY (Pad->Number),
395 TEST_FLAG (ONSOLDERFLAG,
396 Pad) ? _("solder (bottom)") : _("component"),
397 TEST_FLAG (LOCKFLAG, Pad) ? _("It is LOCKED.\n") : "");
398 break;
400 case ELEMENT_TYPE:
402 ElementType *element;
403 #ifndef NDEBUG
404 if (gui->shift_is_pressed ())
406 __r_dump_tree (PCB->Data->element_tree->root, 0);
407 return 0;
409 #endif
410 element = (ElementType *) ptr2;
411 pcb_sprintf (&report[0], _("%m+ELEMENT ID# %ld; Flags:%s\n"
412 "BoundingBox %$mD %$mD.\n"
413 "Descriptive Name \"%s\".\n"
414 "Name on board \"%s\".\n"
415 "Part number name \"%s\".\n"
416 "It is %$mS tall and is located at (X,Y) = %$mD %s.\n"
417 "Mark located at point (X,Y) = %$mD.\n"
418 "It is on the %s side of the board.\n"
419 "%s"), USER_UNITMASK,
420 element->ID, flags_to_string (element->Flags, ELEMENT_TYPE),
421 element->BoundingBox.X1, element->BoundingBox.Y1,
422 element->BoundingBox.X2, element->BoundingBox.Y2,
423 EMPTY (element->Name[0].TextString),
424 EMPTY (element->Name[1].TextString),
425 EMPTY (element->Name[2].TextString),
426 SCALE_TEXT (FONT_CAPHEIGHT, element->Name[1].Scale),
427 element->Name[1].X, element->Name[1].Y,
428 TEST_FLAG (HIDENAMEFLAG, element) ? _(",\n but it's hidden") : "",
429 element->MarkX, element->MarkY,
430 TEST_FLAG (ONSOLDERFLAG, element) ? _("solder (bottom)") : _("component"),
431 TEST_FLAG (LOCKFLAG, element) ? _("It is LOCKED.\n") : "");
432 break;
434 case TEXT_TYPE:
435 #ifndef NDEBUG
436 if (gui->shift_is_pressed ())
438 LayerType *layer = (LayerType *) ptr1;
439 __r_dump_tree (layer->text_tree->root, 0);
440 return 0;
442 #endif
443 case ELEMENTNAME_TYPE:
445 char laynum[32];
446 TextType *text;
447 #ifndef NDEBUG
448 if (gui->shift_is_pressed ())
450 __r_dump_tree (PCB->Data->name_tree[NAME_INDEX (PCB)]->root, 0);
451 return 0;
453 #endif
454 text = (TextType *) ptr2;
456 if (type == TEXT_TYPE)
457 sprintf (laynum, _("It is on layer %d."),
458 GetLayerNumber (PCB->Data, (LayerType *) ptr1));
459 pcb_sprintf (&report[0], _("%m+TEXT ID# %ld; Flags:%s\n"
460 "Located at (X,Y) = %$mD.\n"
461 "Characters are %$mS tall.\n"
462 "Value is \"%s\".\n"
463 "Direction is %d.\n"
464 "The bounding box is %$mD %$mD.\n"
465 "%s\n"
466 "%s"), USER_UNITMASK, text->ID, flags_to_string (text->Flags, TEXT_TYPE),
467 text->X, text->Y, SCALE_TEXT (FONT_CAPHEIGHT, text->Scale),
468 text->TextString, text->Direction,
469 text->BoundingBox.X1, text->BoundingBox.Y1,
470 text->BoundingBox.X2, text->BoundingBox.Y2,
471 (type == TEXT_TYPE) ? laynum : _("It is an element name."),
472 TEST_FLAG (LOCKFLAG, text) ? _("It is LOCKED.\n") : "");
473 break;
475 case LINEPOINT_TYPE:
476 case POLYGONPOINT_TYPE:
478 PointType *point = (PointType *) ptr2;
479 pcb_sprintf (&report[0], _("%m+POINT ID# %ld.\n"
480 "Located at (X,Y) = %$mD.\n"
481 "It belongs to a %s on layer %d.\n"), USER_UNITMASK, point->ID,
482 point->X, point->Y,
483 (type == LINEPOINT_TYPE) ?
484 C_("report", "line") : C_("report", "polygon"),
485 GetLayerNumber (PCB->Data, (LayerType *) ptr1));
486 break;
488 case NO_TYPE:
489 report[0] = '\0';
490 break;
492 default:
493 sprintf (&report[0], _("Unknown\n"));
494 break;
497 if (report[0] == '\0')
499 Message (_("Nothing found to report on\n"));
500 return 1;
502 /* create dialog box */
503 gui->report_dialog (_("Report"), &report[0]);
505 return 0;
508 static int
509 ReportFoundPins (int argc, char **argv, Coord x, Coord y)
511 static DynamicStringType list;
512 char temp[64];
513 int col = 0;
515 DSClearString (&list);
516 DSAddString (&list, _("The following pins/pads are FOUND:\n"));
517 ELEMENT_LOOP (PCB->Data);
519 PIN_LOOP (element);
521 if (TEST_FLAG (FOUNDFLAG, pin))
523 sprintf (temp, _("%s-%s,%c"),
524 NAMEONPCB_NAME (element),
525 pin->Number,
526 ((col++ % (COLUMNS + 1)) == COLUMNS) ? '\n' : ' ');
527 DSAddString (&list, temp);
530 END_LOOP;
531 PAD_LOOP (element);
533 if (TEST_FLAG (FOUNDFLAG, pad))
535 sprintf (temp, _("%s-%s,%c"),
536 NAMEONPCB_NAME (element), pad->Number,
537 ((col++ % (COLUMNS + 1)) == COLUMNS) ? '\n' : ' ');
538 DSAddString (&list, temp);
541 END_LOOP;
543 END_LOOP;
545 gui->report_dialog (_("Report"), list.Data);
546 return 0;
549 /* Assumes that we start with a blank connection state,
550 * e.g. ClearFlagOnAllObjects() has been run.
551 * Does not add its own changes to the undo system
553 static double
554 XYtoNetLength (Coord x, Coord y, int *found)
556 double length;
558 length = 0;
559 *found = 0;
561 /* NB: The third argument here, 'false' ensures LookupConnection
562 * does not add its changes to the undo system.
564 LookupConnection (x, y, false, PCB->Grid, FOUNDFLAG, true);
566 ALLLINE_LOOP (PCB->Data);
568 if (TEST_FLAG (FOUNDFLAG, line))
570 double l;
571 int dx, dy;
572 dx = line->Point1.X - line->Point2.X;
573 dy = line->Point1.Y - line->Point2.Y;
574 l = sqrt ((double)dx*dx + (double)dy*dy);
575 length += l;
576 *found = 1;
579 ENDALL_LOOP;
581 ALLARC_LOOP (PCB->Data);
583 if (TEST_FLAG (FOUNDFLAG, arc))
585 double l;
586 /* FIXME: we assume width==height here */
587 l = M_PI * 2*arc->Width * abs(arc->Delta)/360.0;
588 length += l;
589 *found = 1;
592 ENDALL_LOOP;
594 return length;
597 static int
598 ReportAllNetLengths (int argc, char **argv, Coord x, Coord y)
600 int ni;
601 int found;
603 /* Reset all connection flags and save an undo-state to get back
604 * to the state the board was in when we started this function.
606 * After this, we don't add any changes to the undo system, but
607 * ensure we get back to a point where we can Undo() our changes
608 * by resetting the connections with ClearFlagOnAllObjects() before
609 * calling Undo() at the end of the procedure.
611 ClearFlagOnAllObjects (true, FOUNDFLAG);
612 IncrementUndoSerialNumber ();
614 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
616 char *netname = PCB->NetlistLib.Menu[ni].Name + 2;
617 char *ename = PCB->NetlistLib.Menu[ni].Entry[0].ListEntry;
618 char *pname;
619 bool got_one = 0;
621 ename = strdup (ename);
622 pname = strchr (ename, '-');
623 if (! pname)
625 free (ename);
626 continue;
628 *pname++ = 0;
630 ELEMENT_LOOP (PCB->Data);
632 char *es = element->Name[NAMEONPCB_INDEX].TextString;
633 if (es && strcmp (es, ename) == 0)
635 PIN_LOOP (element);
637 if (strcmp (pin->Number, pname) == 0)
639 x = pin->X;
640 y = pin->Y;
641 got_one = 1;
642 break;
645 END_LOOP;
646 PAD_LOOP (element);
648 if (strcmp (pad->Number, pname) == 0)
650 x = (pad->Point1.X + pad->Point2.X) / 2;
651 y = (pad->Point1.Y + pad->Point2.Y) / 2;
652 got_one = 1;
653 break;
656 END_LOOP;
659 END_LOOP;
661 if (got_one)
663 char buf[50];
664 const char *units_name = argv[0];
665 Coord length;
667 if (argc < 1)
668 units_name = Settings.grid_unit->suffix;
670 length = XYtoNetLength (x, y, &found);
672 /* Reset connectors for the next lookup */
673 ClearFlagOnAllObjects (false, FOUNDFLAG);
675 pcb_sprintf(buf, _("%$m*"), units_name, length);
676 gui->log(_("Net %s length %s\n"), netname, buf);
680 ClearFlagOnAllObjects (false, FOUNDFLAG);
681 Undo (true);
682 return 0;
685 static int
686 ReportNetLength (int argc, char **argv, Coord x, Coord y)
688 Coord length = 0;
689 char *netname = 0;
690 int found = 0;
692 gui->get_coords (_("Click on a connection"), &x, &y);
694 /* Reset all connection flags and save an undo-state to get back
695 * to the state the board was in when we started this function.
697 * After this, we don't add any changes to the undo system, but
698 * ensure we get back to a point where we can Undo() our changes
699 * by resetting the connections with ClearFlagOnAllObjects() before
700 * calling Undo() at the end of the procedure.
702 ClearFlagOnAllObjects (true, FOUNDFLAG);
703 IncrementUndoSerialNumber ();
705 length = XYtoNetLength (x, y, &found);
707 if (!found)
709 ClearFlagOnAllObjects (false, FOUNDFLAG);
710 Undo (true);
711 gui->log (_("No net under cursor.\n"));
712 return 1;
715 ELEMENT_LOOP (PCB->Data);
717 PIN_LOOP (element);
719 if (TEST_FLAG (FOUNDFLAG, pin))
721 int ni, nei;
722 char *ename = element->Name[NAMEONPCB_INDEX].TextString;
723 char *pname = pin->Number;
724 char *n;
726 if (ename && pname)
728 n = Concat (ename, _("-"), pname, NULL);
729 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
730 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
732 if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
734 netname = PCB->NetlistLib.Menu[ni].Name + 2;
735 goto got_net_name; /* four for loops deep */
741 END_LOOP;
742 PAD_LOOP (element);
744 if (TEST_FLAG (FOUNDFLAG, pad))
746 int ni, nei;
747 char *ename = element->Name[NAMEONPCB_INDEX].TextString;
748 char *pname = pad->Number;
749 char *n;
751 if (ename && pname)
753 n = Concat (ename, _("-"), pname, NULL);
754 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
755 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
757 if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
759 netname = PCB->NetlistLib.Menu[ni].Name + 2;
760 goto got_net_name; /* four for loops deep */
766 END_LOOP;
768 END_LOOP;
770 got_net_name:
771 ClearFlagOnAllObjects (false, FOUNDFLAG);
772 Undo (true);
775 char buf[50];
776 pcb_sprintf(buf, _("%$m*"), Settings.grid_unit->suffix, length);
777 if (netname)
778 gui->log (_("Net \"%s\" length: %s\n"), netname, buf);
779 else
780 gui->log (_("Net length: %s\n"), buf);
783 return 0;
786 static int
787 ReportNetLengthByName (char *tofind, int x, int y)
789 int result;
790 char *netname = 0;
791 Coord length = 0;
792 int found = 0;
793 int i;
794 LibraryMenuType *net;
795 ConnectionType conn;
796 int net_found = 0;
797 #if defined(USE_RE)
798 int use_re = 0;
799 #endif
800 #if defined(HAVE_REGCOMP)
801 regex_t elt_pattern;
802 regmatch_t match;
803 #endif
804 #if defined(HAVE_RE_COMP)
805 char *elt_pattern;
806 #endif
808 if (!PCB)
809 return 1;
811 if (!tofind)
812 return 1;
814 #if defined(USE_RE)
815 use_re = 1;
816 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
818 net = PCB->NetlistLib.Menu + i;
819 if (strcasecmp (tofind, net->Name + 2) == 0)
820 use_re = 0;
822 if (use_re)
824 #if defined(HAVE_REGCOMP)
825 result =
826 regcomp (&elt_pattern, tofind,
827 REG_EXTENDED | REG_ICASE | REG_NOSUB);
828 if (result)
830 char errorstring[128];
832 regerror (result, &elt_pattern, errorstring, 128);
833 Message (_("regexp error: %s\n"), errorstring);
834 regfree (&elt_pattern);
835 return (1);
837 #endif
838 #if defined(HAVE_RE_COMP)
839 if ((elt_pattern = re_comp (tofind)) != NULL)
841 Message (_("re_comp error: %s\n"), elt_pattern);
842 return (1);
844 #endif
846 #endif
848 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
850 net = PCB->NetlistLib.Menu + i;
852 #if defined(USE_RE)
853 if (use_re)
855 #if defined(HAVE_REGCOMP)
856 if (regexec (&elt_pattern, net->Name + 2, 1, &match, 0) != 0)
857 continue;
858 #endif
859 #if defined(HAVE_RE_COMP)
860 if (re_exec (net->Name + 2) != 1)
861 continue;
862 #endif
864 else
865 #endif
866 if (strcasecmp (net->Name + 2, tofind))
867 continue;
869 if (SeekPad (net->Entry, &conn, false))
871 switch (conn.type)
873 case PIN_TYPE:
874 x = ((PinType *) (conn.ptr2))->X;
875 y = ((PinType *) (conn.ptr2))->Y;
876 net_found=1;
877 break;
878 case PAD_TYPE:
879 x = ((PadType *) (conn.ptr2))->Point1.X;
880 y = ((PadType *) (conn.ptr2))->Point1.Y;
881 net_found=1;
882 break;
884 if (net_found)
885 break;
889 if (!net_found)
891 gui->log (_("No net named %s\n"), tofind);
892 return 1;
895 #ifdef HAVE_REGCOMP
896 if (use_re)
897 regfree (&elt_pattern);
898 #endif
900 /* Reset all connection flags and save an undo-state to get back
901 * to the state the board was in when we started.
903 * After this, we don't add any changes to the undo system, but
904 * ensure we get back to a point where we can Undo() our changes
905 * by resetting the connections with ClearFlagOnAllObjects() before
906 * calling Undo() when we are finished.
908 ClearFlagOnAllObjects (true, FOUNDFLAG);
909 IncrementUndoSerialNumber ();
911 length = XYtoNetLength (x, y, &found);
912 netname = net->Name + 2;
914 ClearFlagOnAllObjects (false, FOUNDFLAG);
915 Undo (true);
917 if (!found)
919 if (net_found)
920 gui->log (_("Net found, but no lines or arcs were flagged.\n"));
921 else
922 gui->log (_("Net not found.\n"));
924 return 1;
928 char buf[50];
929 pcb_sprintf(buf, _("%$m*"), Settings.grid_unit->suffix, length);
930 if (netname)
931 gui->log (_("Net \"%s\" length: %s\n"), netname, buf);
932 else
933 gui->log (_("Net length: %s\n"), buf);
936 return 0;
939 /* ---------------------------------------------------------------------------
940 * reports on an object
941 * syntax:
944 static const char report_syntax[] =
945 N_("Report(Object|DrillReport|FoundPins|NetLength|AllNetLengths|[,name])");
947 static const char report_help[] = N_("Produce various report.");
949 /* %start-doc actions Report
951 @table @code
953 @item Object
954 The object under the crosshair will be reported, describing various
955 aspects of the object.
957 @item DrillReport
958 A report summarizing the number of drill sizes used, and how many of
959 each, will be produced.
961 @item FoundPins
962 A report listing all pins and pads which are marked as ``found'' will
963 be produced.
965 @item NetLength
966 The name and length of the net under the crosshair will be reported to
967 the message log.
969 @item AllNetLengths
970 The name and length of the net under the crosshair will be reported to
971 the message log. An optional parameter specifies mm, mil, pcb, or in
972 units
974 @end table
976 %end-doc */
978 static int
979 Report (int argc, char **argv, Coord x, Coord y)
981 if ((argc < 1) || (argc > 2))
982 AUSAGE (report);
983 else if (strcasecmp (argv[0], "Object") == 0)
985 gui->get_coords (_("Click on an object"), &x, &y);
986 return ReportDialog (argc - 1, argv + 1, x, y);
988 else if (strcasecmp (argv[0], "DrillReport") == 0)
989 return ReportDrills (argc - 1, argv + 1, x, y);
990 else if (strcasecmp (argv[0], "FoundPins") == 0)
991 return ReportFoundPins (argc - 1, argv + 1, x, y);
992 else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 1))
993 return ReportNetLength (argc - 1, argv + 1, x, y);
994 else if (strcasecmp (argv[0], "AllNetLengths") == 0)
995 return ReportAllNetLengths (argc - 1, argv + 1, x, y);
996 else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 2))
997 return ReportNetLengthByName (argv[1], x, y);
998 else if (argc == 2)
999 AUSAGE (report);
1000 else
1001 AFAIL (report);
1002 return 1;
1005 HID_Action report_action_list[] = {
1006 {"ReportObject", N_("Click on an object"), ReportDialog,
1007 reportdialog_help, reportdialog_syntax}
1009 {"Report", 0, Report,
1010 report_help, report_syntax}
1013 REGISTER_ACTIONS (report_action_list)