pcb 4.1.1
An interactive printed circuit board layout editor.
|
00001 00035 #ifdef HAVE_CONFIG_H 00036 #include "config.h" 00037 #endif 00038 00039 #include <stdlib.h> 00040 #include <stdarg.h> 00041 #ifdef HAVE_STRING_H 00042 #include <string.h> 00043 #endif 00044 #include <memory.h> 00045 #include <ctype.h> 00046 #include <signal.h> 00047 #include <sys/param.h> 00048 #include <sys/stat.h> 00049 #include <sys/types.h> 00050 #include <math.h> 00051 #ifdef HAVE_UNISTD_H 00052 #include <unistd.h> 00053 #endif 00054 #ifdef HAVE_PWD_H 00055 #include <pwd.h> 00056 #endif 00057 00058 #include "global.h" 00059 00060 #include "box.h" 00061 #include "crosshair.h" 00062 #include "create.h" 00063 #include "data.h" 00064 #include "draw.h" 00065 #include "file.h" 00066 #include "error.h" 00067 #include "mymem.h" 00068 #include "misc.h" 00069 #include "move.h" 00070 #include "pcb-printf.h" 00071 #include "polygon.h" 00072 #include "remove.h" 00073 #include "rtree.h" 00074 #include "rotate.h" 00075 #include "rubberband.h" 00076 #include "search.h" 00077 #include "set.h" 00078 #include "undo.h" 00079 #include "action.h" 00080 00081 #ifdef HAVE_LIBDMALLOC 00082 #include <dmalloc.h> 00083 #endif 00084 00085 /* forward declarations */ 00086 static char *BumpName (char *); 00087 static void GetGridLockCoordinates (int, void *, void *, void *, 00088 Coord *, Coord *); 00089 00090 00091 /* Local variables */ 00092 00097 static struct 00098 { 00099 bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn; 00100 int LayerStack[MAX_ALL_LAYER]; 00101 bool LayerOn[MAX_ALL_LAYER]; 00102 int cnt; 00103 } SavedStack; 00104 00109 double 00110 Distance (double x1, double y1, double x2, double y2) 00111 { 00112 return hypot(x2 - x1, y2 - y1); 00113 } 00114 00118 Angle 00119 NormalizeAngle (Angle a) 00120 { 00121 while (a < 0) 00122 a += 360.0; 00123 while (a >= 360.0) 00124 a -= 360.0; 00125 return a; 00126 } 00127 00133 double 00134 GetValue (const char *val, const char *units, bool * absolute) 00135 { 00136 return GetValueEx(val, units, absolute, NULL, "cmil"); 00137 } 00138 00139 double 00140 GetValueEx (const char *val, const char *units, bool * absolute, UnitList extra_units, const char *default_unit) 00141 { 00142 double value; 00143 int n = -1; 00144 bool scaled = 0; 00145 bool dummy; 00146 00147 /* Allow NULL to be passed for absolute */ 00148 if(absolute == NULL) 00149 absolute = &dummy; 00150 00151 /* if the first character is a sign we have to add the 00152 * value to the current one 00153 */ 00154 if (*val == '=') 00155 { 00156 *absolute = true; 00157 if (sscanf (val+1, "%lf%n", &value, &n) < 1) 00158 return 0; 00159 n++; 00160 } 00161 else 00162 { 00163 if (isdigit ((int) *val)) 00164 *absolute = true; 00165 else 00166 *absolute = false; 00167 if (sscanf (val, "%lf%n", &value, &n) < 1) 00168 return 0; 00169 } 00170 if (!units && n > 0) 00171 units = val + n; 00172 00173 while (units && *units == ' ') 00174 units ++; 00175 00176 if (units && *units) 00177 { 00178 int i; 00179 const Unit *unit = get_unit_struct (units); 00180 if (unit != NULL) 00181 { 00182 value = unit_to_coord (unit, value); 00183 scaled = 1; 00184 } 00185 if (extra_units) 00186 { 00187 for (i = 0; *extra_units[i].suffix; ++i) 00188 { 00189 if (strncmp (units, extra_units[i].suffix, strlen(extra_units[i].suffix)) == 0) 00190 { 00191 value *= extra_units[i].scale; 00192 if (extra_units[i].flags & UNIT_PERCENT) 00193 value /= 100.0; 00194 scaled = 1; 00195 } 00196 } 00197 } 00198 } 00199 /* Apply default unit */ 00200 if (!scaled && default_unit && *default_unit) 00201 { 00202 int i; 00203 const Unit *unit = get_unit_struct (default_unit); 00204 if (extra_units) 00205 for (i = 0; *extra_units[i].suffix; ++i) 00206 if (strcmp (extra_units[i].suffix, default_unit) == 0) 00207 { 00208 value *= extra_units[i].scale; 00209 if (extra_units[i].flags & UNIT_PERCENT) 00210 value /= 100.0; 00211 scaled = 1; 00212 } 00213 if (!scaled && unit != NULL) 00214 value = unit_to_coord (unit, value); 00215 } 00216 00217 return value; 00218 } 00219 00233 double GetUnitlessValue (const char *val, bool *absolute) { 00234 double value; 00235 00236 if (*val == '=') 00237 { 00238 *absolute = true; 00239 val++; 00240 } 00241 else 00242 { 00243 if (isdigit ((int) *val)) 00244 *absolute = true; 00245 else 00246 *absolute = false; 00247 } 00248 00249 if (sscanf (val, "%lf", &value) < 1) 00250 return 0.; 00251 00252 return value; 00253 } 00254 00258 void 00259 SetPointBoundingBox (PointType *Pnt) 00260 { 00261 Pnt->X2 = Pnt->X + 1; 00262 Pnt->Y2 = Pnt->Y + 1; 00263 } 00264 00268 void 00269 SetPinBoundingBox (PinType *Pin) 00270 { 00271 Coord width; 00272 00273 /* the bounding box covers the extent of influence 00274 * so it must include the clearance values too 00275 */ 00276 width = MAX (Pin->Clearance + PIN_SIZE (Pin), Pin->Mask) / 2; 00277 00278 /* Adjust for our discrete polygon approximation */ 00279 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5; 00280 00281 Pin->BoundingBox.X1 = Pin->X - width; 00282 Pin->BoundingBox.Y1 = Pin->Y - width; 00283 Pin->BoundingBox.X2 = Pin->X + width; 00284 Pin->BoundingBox.Y2 = Pin->Y + width; 00285 close_box(&Pin->BoundingBox); 00286 } 00287 00291 void 00292 SetPadBoundingBox (PadType *Pad) 00293 { 00294 Coord width; 00295 Coord deltax; 00296 Coord deltay; 00297 00298 /* the bounding box covers the extent of influence 00299 * so it must include the clearance values too 00300 */ 00301 width = (Pad->Thickness + Pad->Clearance + 1) / 2; 00302 width = MAX (width, (Pad->Mask + 1) / 2); 00303 deltax = Pad->Point2.X - Pad->Point1.X; 00304 deltay = Pad->Point2.Y - Pad->Point1.Y; 00305 00306 if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0) 00307 { 00308 /* slanted square pad */ 00309 double theta; 00310 Coord btx, bty; 00311 00312 /* T is a vector half a thickness long, in the direction of 00313 one of the corners. */ 00314 theta = atan2 (deltay, deltax); 00315 btx = width * cos (theta + M_PI/4) * sqrt(2.0); 00316 bty = width * sin (theta + M_PI/4) * sqrt(2.0); 00317 00318 00319 Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty), 00320 MIN (Pad->Point2.X + btx, Pad->Point2.X + bty)); 00321 Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty), 00322 MAX (Pad->Point2.X + btx, Pad->Point2.X + bty)); 00323 Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty), 00324 MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty)); 00325 Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty), 00326 MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty)); 00327 } 00328 else 00329 { 00330 /* Adjust for our discrete polygon approximation */ 00331 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5; 00332 00333 Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width; 00334 Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width; 00335 Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width; 00336 Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width; 00337 } 00338 close_box(&Pad->BoundingBox); 00339 } 00340 00344 void 00345 SetLineBoundingBox (LineType *Line) 00346 { 00347 Coord width = (Line->Thickness + Line->Clearance + 1) / 2; 00348 00349 /* Adjust for our discrete polygon approximation */ 00350 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5; 00351 00352 Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width; 00353 Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width; 00354 Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width; 00355 Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width; 00356 close_box(&Line->BoundingBox); 00357 SetPointBoundingBox (&Line->Point1); 00358 SetPointBoundingBox (&Line->Point2); 00359 } 00360 00364 void 00365 SetPolygonBoundingBox (PolygonType *Polygon) 00366 { 00367 Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD; 00368 Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0; 00369 POLYGONPOINT_LOOP (Polygon); 00370 { 00371 MAKEMIN (Polygon->BoundingBox.X1, point->X); 00372 MAKEMIN (Polygon->BoundingBox.Y1, point->Y); 00373 MAKEMAX (Polygon->BoundingBox.X2, point->X); 00374 MAKEMAX (Polygon->BoundingBox.Y2, point->Y); 00375 } 00376 /* boxes don't include the lower right corner */ 00377 close_box(&Polygon->BoundingBox); 00378 END_LOOP; 00379 } 00380 00384 void 00385 SetElementBoundingBox (DataType *Data, ElementType *Element, 00386 FontType *Font) 00387 { 00388 BoxType *box, *vbox; 00389 00390 if (Data && Data->element_tree) 00391 r_delete_entry (Data->element_tree, (BoxType *) Element); 00392 /* first update the text objects */ 00393 ELEMENTTEXT_LOOP (Element); 00394 { 00395 if (Data && Data->name_tree[n]) 00396 r_delete_entry (Data->name_tree[n], (BoxType *) text); 00397 SetTextBoundingBox (Font, text); 00398 if (Data && !Data->name_tree[n]) 00399 Data->name_tree[n] = r_create_tree (NULL, 0, 0); 00400 if (Data) 00401 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0); 00402 } 00403 END_LOOP; 00404 00405 /* do not include the elementnames bounding box which 00406 * is handled separately 00407 */ 00408 box = &Element->BoundingBox; 00409 vbox = &Element->VBox; 00410 box->X1 = box->Y1 = MAX_COORD; 00411 box->X2 = box->Y2 = 0; 00412 ELEMENTLINE_LOOP (Element); 00413 { 00414 SetLineBoundingBox (line); 00415 MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2); 00416 MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2); 00417 MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2); 00418 MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2); 00419 MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2); 00420 MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2); 00421 MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2); 00422 MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2); 00423 } 00424 END_LOOP; 00425 ARC_LOOP (Element); 00426 { 00427 SetArcBoundingBox (arc); 00428 MAKEMIN (box->X1, arc->BoundingBox.X1); 00429 MAKEMIN (box->Y1, arc->BoundingBox.Y1); 00430 MAKEMAX (box->X2, arc->BoundingBox.X2); 00431 MAKEMAX (box->Y2, arc->BoundingBox.Y2); 00432 } 00433 END_LOOP; 00434 *vbox = *box; 00435 PIN_LOOP (Element); 00436 { 00437 if (Data && Data->pin_tree) 00438 r_delete_entry (Data->pin_tree, (BoxType *) pin); 00439 SetPinBoundingBox (pin); 00440 if (Data) 00441 { 00442 if (!Data->pin_tree) 00443 Data->pin_tree = r_create_tree (NULL, 0, 0); 00444 r_insert_entry (Data->pin_tree, (BoxType *) pin, 0); 00445 } 00446 MAKEMIN (box->X1, pin->BoundingBox.X1); 00447 MAKEMIN (box->Y1, pin->BoundingBox.Y1); 00448 MAKEMAX (box->X2, pin->BoundingBox.X2); 00449 MAKEMAX (box->Y2, pin->BoundingBox.Y2); 00450 MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2); 00451 MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2); 00452 MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2); 00453 MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2); 00454 } 00455 END_LOOP; 00456 PAD_LOOP (Element); 00457 { 00458 if (Data && Data->pad_tree) 00459 r_delete_entry (Data->pad_tree, (BoxType *) pad); 00460 SetPadBoundingBox (pad); 00461 if (Data) 00462 { 00463 if (!Data->pad_tree) 00464 Data->pad_tree = r_create_tree (NULL, 0, 0); 00465 r_insert_entry (Data->pad_tree, (BoxType *) pad, 0); 00466 } 00467 MAKEMIN (box->X1, pad->BoundingBox.X1); 00468 MAKEMIN (box->Y1, pad->BoundingBox.Y1); 00469 MAKEMAX (box->X2, pad->BoundingBox.X2); 00470 MAKEMAX (box->Y2, pad->BoundingBox.Y2); 00471 MAKEMIN (vbox->X1, 00472 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2); 00473 MAKEMIN (vbox->Y1, 00474 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2); 00475 MAKEMAX (vbox->X2, 00476 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2); 00477 MAKEMAX (vbox->Y2, 00478 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2); 00479 } 00480 END_LOOP; 00481 /* now we set the EDGE2FLAG of the pad if Point2 00482 * is closer to the outside edge than Point1 00483 */ 00484 PAD_LOOP (Element); 00485 { 00486 if (pad->Point1.Y == pad->Point2.Y) 00487 { 00488 /* horizontal pad */ 00489 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1) 00490 SET_FLAG (EDGE2FLAG, pad); 00491 else 00492 CLEAR_FLAG (EDGE2FLAG, pad); 00493 } 00494 else 00495 { 00496 /* vertical pad */ 00497 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1) 00498 SET_FLAG (EDGE2FLAG, pad); 00499 else 00500 CLEAR_FLAG (EDGE2FLAG, pad); 00501 } 00502 } 00503 END_LOOP; 00504 00505 /* mark pins with component orientation */ 00506 if ((box->X2 - box->X1) > (box->Y2 - box->Y1)) 00507 { 00508 PIN_LOOP (Element); 00509 { 00510 SET_FLAG (EDGE2FLAG, pin); 00511 } 00512 END_LOOP; 00513 } 00514 else 00515 { 00516 PIN_LOOP (Element); 00517 { 00518 CLEAR_FLAG (EDGE2FLAG, pin); 00519 } 00520 END_LOOP; 00521 } 00522 close_box(box); 00523 close_box(vbox); 00524 if (Data && !Data->element_tree) 00525 Data->element_tree = r_create_tree (NULL, 0, 0); 00526 if (Data) 00527 r_insert_entry (Data->element_tree, box, 0); 00528 } 00529 00533 void 00534 SetTextBoundingBox (FontType *FontPtr, TextType *Text) 00535 { 00536 SymbolType *symbol = FontPtr->Symbol; 00537 unsigned char *s = (unsigned char *) Text->TextString; 00538 int i; 00539 int space; 00540 00541 Coord minx, miny, maxx, maxy, tx; 00542 Coord min_final_radius; 00543 Coord min_unscaled_radius; 00544 bool first_time = true; 00545 00546 minx = miny = maxx = maxy = tx = 0; 00547 00548 /* Calculate the bounding box based on the larger of the thicknesses 00549 * the text might clamped at on silk or copper layers. 00550 */ 00551 min_final_radius = MAX (PCB->minWid, PCB->minSlk) / 2; 00552 00553 /* Pre-adjust the line radius for the fact we are initially computing the 00554 * bounds of the un-scaled text, and the thickness clamping applies to 00555 * scaled text. 00556 */ 00557 min_unscaled_radius = UNSCALE_TEXT (min_final_radius, Text->Scale); 00558 00559 /* calculate size of the bounding box */ 00560 for (; s && *s; s++) 00561 { 00562 if (*s <= MAX_FONTPOSITION && symbol[*s].Valid) 00563 { 00564 LineType *line = symbol[*s].Line; 00565 for (i = 0; i < symbol[*s].LineN; line++, i++) 00566 { 00567 /* Clamp the width of text lines at the minimum thickness. 00568 * NB: Divide 4 in thickness calculation is comprised of a factor 00569 * of 1/2 to get a radius from the center-line, and a factor 00570 * of 1/2 because some stupid reason we render our glyphs 00571 * at half their defined stroke-width. 00572 */ 00573 Coord unscaled_radius = MAX (min_unscaled_radius, line->Thickness / 4); 00574 00575 if (first_time) 00576 { 00577 minx = maxx = line->Point1.X; 00578 miny = maxy = line->Point1.Y; 00579 first_time = false; 00580 } 00581 00582 minx = MIN (minx, line->Point1.X - unscaled_radius + tx); 00583 miny = MIN (miny, line->Point1.Y - unscaled_radius); 00584 minx = MIN (minx, line->Point2.X - unscaled_radius + tx); 00585 miny = MIN (miny, line->Point2.Y - unscaled_radius); 00586 maxx = MAX (maxx, line->Point1.X + unscaled_radius + tx); 00587 maxy = MAX (maxy, line->Point1.Y + unscaled_radius); 00588 maxx = MAX (maxx, line->Point2.X + unscaled_radius + tx); 00589 maxy = MAX (maxy, line->Point2.Y + unscaled_radius); 00590 } 00591 space = symbol[*s].Delta; 00592 } 00593 else 00594 { 00595 BoxType *ds = &FontPtr->DefaultSymbol; 00596 Coord w = ds->X2 - ds->X1; 00597 00598 minx = MIN (minx, ds->X1 + tx); 00599 miny = MIN (miny, ds->Y1); 00600 minx = MIN (minx, ds->X2 + tx); 00601 miny = MIN (miny, ds->Y2); 00602 maxx = MAX (maxx, ds->X1 + tx); 00603 maxy = MAX (maxy, ds->Y1); 00604 maxx = MAX (maxx, ds->X2 + tx); 00605 maxy = MAX (maxy, ds->Y2); 00606 00607 space = w / 5; 00608 } 00609 tx += symbol[*s].Width + space; 00610 } 00611 00612 /* scale values */ 00613 minx = SCALE_TEXT (minx, Text->Scale); 00614 miny = SCALE_TEXT (miny, Text->Scale); 00615 maxx = SCALE_TEXT (maxx, Text->Scale); 00616 maxy = SCALE_TEXT (maxy, Text->Scale); 00617 00618 /* set upper-left and lower-right corner; 00619 * swap coordinates if necessary (origin is already in 'swapped') 00620 * and rotate box 00621 */ 00622 00623 if (TEST_FLAG (ONSOLDERFLAG, Text)) 00624 { 00625 Text->BoundingBox.X1 = Text->X + minx; 00626 Text->BoundingBox.Y1 = Text->Y - miny; 00627 Text->BoundingBox.X2 = Text->X + maxx; 00628 Text->BoundingBox.Y2 = Text->Y - maxy; 00629 RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y, 00630 (4 - Text->Direction) & 0x03); 00631 } 00632 else 00633 { 00634 Text->BoundingBox.X1 = Text->X + minx; 00635 Text->BoundingBox.Y1 = Text->Y + miny; 00636 Text->BoundingBox.X2 = Text->X + maxx; 00637 Text->BoundingBox.Y2 = Text->Y + maxy; 00638 RotateBoxLowLevel (&Text->BoundingBox, 00639 Text->X, Text->Y, Text->Direction); 00640 } 00641 00642 /* the bounding box covers the extent of influence 00643 * so it must include the clearance values too 00644 */ 00645 Text->BoundingBox.X1 -= PCB->Bloat; 00646 Text->BoundingBox.Y1 -= PCB->Bloat; 00647 Text->BoundingBox.X2 += PCB->Bloat; 00648 Text->BoundingBox.Y2 += PCB->Bloat; 00649 close_box(&Text->BoundingBox); 00650 } 00651 00655 bool 00656 IsDataEmpty (DataType *Data) 00657 { 00658 bool hasNoObjects; 00659 Cardinal i; 00660 00661 hasNoObjects = (Data->ViaN == 0); 00662 hasNoObjects &= (Data->ElementN == 0); 00663 for (i = 0; i < max_copper_layer + SILK_LAYER; i++) 00664 hasNoObjects = hasNoObjects && 00665 Data->Layer[i].LineN == 0 && 00666 Data->Layer[i].ArcN == 0 && 00667 Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0; 00668 return (hasNoObjects); 00669 } 00670 00671 int 00672 FlagIsDataEmpty (int parm) 00673 { 00674 int i = IsDataEmpty (PCB->Data); 00675 return parm ? !i : i; 00676 } 00677 00678 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */ 00679 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */ 00680 00681 bool 00682 IsLayerEmpty (LayerType *layer) 00683 { 00684 return (layer->LineN == 0 00685 && layer->TextN == 0 00686 && layer->PolygonN == 0 00687 && layer->ArcN == 0); 00688 } 00689 00690 bool 00691 IsLayerNumEmpty (int num) 00692 { 00693 return IsLayerEmpty (PCB->Data->Layer+num); 00694 } 00695 00696 bool 00697 IsLayerGroupEmpty (int num) 00698 { 00699 int i; 00700 for (i=0; i<PCB->LayerGroups.Number[num]; i++) 00701 if (!IsLayerNumEmpty (PCB->LayerGroups.Entries[num][i])) 00702 return false; 00703 return true; 00704 } 00705 00706 bool 00707 IsPasteEmpty (int side) 00708 { 00709 bool paste_empty = true; 00710 ALLPAD_LOOP (PCB->Data); 00711 { 00712 if (ON_SIDE (pad, side) && !TEST_FLAG (NOPASTEFLAG, pad) && pad->Mask > 0) 00713 { 00714 paste_empty = false; 00715 break; 00716 } 00717 } 00718 ENDALL_LOOP; 00719 return paste_empty; 00720 } 00721 00722 00723 typedef struct 00724 { 00725 int nplated; 00726 int nunplated; 00727 Cardinal group_from; 00728 Cardinal group_to; 00729 } HoleCountStruct; 00730 00731 static int 00732 hole_counting_callback (const BoxType * b, void *cl) 00733 { 00734 PinType *pin = (PinType *) b; 00735 HoleCountStruct *hcs = (HoleCountStruct *) cl; 00736 00737 if (hcs->group_from != 0 00738 || hcs->group_to != 0) 00739 { 00740 if (VIA_IS_BURIED (pin)) 00741 { 00742 if (hcs->group_from == GetLayerGroupNumberByNumber (pin->BuriedFrom) 00743 && hcs->group_to == GetLayerGroupNumberByNumber (pin->BuriedTo)) 00744 goto count; 00745 } 00746 return 1; 00747 } 00748 00749 count: 00750 if (TEST_FLAG (HOLEFLAG, pin)) 00751 hcs->nunplated++; 00752 else 00753 hcs->nplated++; 00754 return 1; 00755 } 00756 00763 void 00764 CountHoles (int *plated, int *unplated, const BoxType *within_area) 00765 { 00766 HoleCountStruct hcs = {0, 0, 0, 0}; 00767 00768 r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs); 00769 r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs); 00770 00771 if (plated != NULL) *plated = hcs.nplated; 00772 if (unplated != NULL) *unplated = hcs.nunplated; 00773 } 00774 00781 void 00782 CountHolesEx (int *plated, int *unplated, const BoxType *within_area, Cardinal group_from, Cardinal group_to) 00783 { 00784 HoleCountStruct hcs = {0, 0, group_from, group_to}; 00785 00786 r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs); 00787 r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs); 00788 00789 if (plated != NULL) *plated = hcs.nplated; 00790 if (unplated != NULL) *unplated = hcs.nunplated; 00791 } 00792 00798 BoxType * 00799 GetDataBoundingBox (DataType *Data) 00800 { 00801 static BoxType box; 00802 /* FIX ME: use r_search to do this much faster */ 00803 00804 /* preset identifiers with highest and lowest possible values */ 00805 box.X1 = box.Y1 = MAX_COORD; 00806 box.X2 = box.Y2 = -MAX_COORD; 00807 00808 /* now scan for the lowest/highest X and Y coordinate */ 00809 VIA_LOOP (Data); 00810 { 00811 box.X1 = MIN (box.X1, via->X - via->Thickness / 2); 00812 box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2); 00813 box.X2 = MAX (box.X2, via->X + via->Thickness / 2); 00814 box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2); 00815 } 00816 END_LOOP; 00817 ELEMENT_LOOP (Data); 00818 { 00819 box.X1 = MIN (box.X1, element->BoundingBox.X1); 00820 box.Y1 = MIN (box.Y1, element->BoundingBox.Y1); 00821 box.X2 = MAX (box.X2, element->BoundingBox.X2); 00822 box.Y2 = MAX (box.Y2, element->BoundingBox.Y2); 00823 { 00824 TextType *text = &NAMEONPCB_TEXT (element); 00825 box.X1 = MIN (box.X1, text->BoundingBox.X1); 00826 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1); 00827 box.X2 = MAX (box.X2, text->BoundingBox.X2); 00828 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2); 00829 }; 00830 } 00831 END_LOOP; 00832 ALLLINE_LOOP (Data); 00833 { 00834 box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2); 00835 box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2); 00836 box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2); 00837 box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2); 00838 box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2); 00839 box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2); 00840 box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2); 00841 box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2); 00842 } 00843 ENDALL_LOOP; 00844 ALLARC_LOOP (Data); 00845 { 00846 box.X1 = MIN (box.X1, arc->BoundingBox.X1); 00847 box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1); 00848 box.X2 = MAX (box.X2, arc->BoundingBox.X2); 00849 box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2); 00850 } 00851 ENDALL_LOOP; 00852 ALLTEXT_LOOP (Data); 00853 { 00854 box.X1 = MIN (box.X1, text->BoundingBox.X1); 00855 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1); 00856 box.X2 = MAX (box.X2, text->BoundingBox.X2); 00857 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2); 00858 } 00859 ENDALL_LOOP; 00860 ALLPOLYGON_LOOP (Data); 00861 { 00862 box.X1 = MIN (box.X1, polygon->BoundingBox.X1); 00863 box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1); 00864 box.X2 = MAX (box.X2, polygon->BoundingBox.X2); 00865 box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2); 00866 } 00867 ENDALL_LOOP; 00868 return (IsDataEmpty (Data) ? NULL : &box); 00869 } 00870 00877 void 00878 CenterDisplay (Coord X, Coord Y, bool warp_pointer) 00879 { 00880 Coord save_grid = PCB->Grid; 00881 00882 PCB->Grid = 1; 00883 00884 if (MoveCrosshairAbsolute (X, Y)) 00885 notify_crosshair_change (true); 00886 00887 if (warp_pointer) 00888 gui->set_crosshair (Crosshair.X, Crosshair.Y, 00889 HID_SC_CENTER_IN_VIEWPORT_AND_WARP_POINTER ); 00890 else 00891 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_CENTER_IN_VIEWPORT); 00892 00893 PCB->Grid = save_grid; 00894 } 00895 00903 void 00904 SetFontInfo (FontType *Ptr) 00905 { 00906 Cardinal i, j; 00907 SymbolType *symbol; 00908 LineType *line; 00909 Coord totalminy = MAX_COORD; 00910 00911 /* calculate cell with and height (is at least DEFAULT_CELLSIZE) 00912 * maximum cell width and height 00913 * minimum x and y position of all lines 00914 */ 00915 Ptr->MaxWidth = DEFAULT_CELLSIZE; 00916 Ptr->MaxHeight = DEFAULT_CELLSIZE; 00917 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++) 00918 { 00919 Coord minx, miny, maxx, maxy; 00920 00921 /* next one if the index isn't used or symbol is empty (SPACE) */ 00922 if (!symbol->Valid || !symbol->LineN) 00923 continue; 00924 00925 minx = miny = MAX_COORD; 00926 maxx = maxy = 0; 00927 for (line = symbol->Line, j = symbol->LineN; j; j--, line++) 00928 { 00929 minx = MIN (minx, line->Point1.X); 00930 miny = MIN (miny, line->Point1.Y); 00931 minx = MIN (minx, line->Point2.X); 00932 miny = MIN (miny, line->Point2.Y); 00933 maxx = MAX (maxx, line->Point1.X); 00934 maxy = MAX (maxy, line->Point1.Y); 00935 maxx = MAX (maxx, line->Point2.X); 00936 maxy = MAX (maxy, line->Point2.Y); 00937 } 00938 00939 /* move symbol to left edge */ 00940 for (line = symbol->Line, j = symbol->LineN; j; j--, line++) 00941 MOVE_LINE_LOWLEVEL (line, -minx, 0); 00942 00943 /* set symbol bounding box with a minimum cell size of (1,1) */ 00944 symbol->Width = maxx - minx + 1; 00945 symbol->Height = maxy + 1; 00946 00947 /* check total min/max */ 00948 Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width); 00949 Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height); 00950 totalminy = MIN (totalminy, miny); 00951 } 00952 00953 /* move coordinate system to the upper edge (lowest y on screen) */ 00954 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++) 00955 if (symbol->Valid) 00956 { 00957 symbol->Height -= totalminy; 00958 for (line = symbol->Line, j = symbol->LineN; j; j--, line++) 00959 MOVE_LINE_LOWLEVEL (line, 0, -totalminy); 00960 } 00961 00962 /* setup the box for the default symbol */ 00963 Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0; 00964 Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth; 00965 Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight; 00966 } 00967 00968 static Coord 00969 GetNum (char **s, const char *default_unit) 00970 { 00971 /* Read value */ 00972 Coord ret_val = GetValueEx (*s, NULL, NULL, NULL, default_unit); 00973 /* Advance pointer */ 00974 while(isalnum(**s) || **s == '.') 00975 (*s)++; 00976 return ret_val; 00977 } 00978 00986 char * 00987 make_route_string (RouteStyleType rs[], int n_styles) 00988 { 00989 GString *str = g_string_new (""); 00990 gint i; 00991 00992 for (i = 0; i < n_styles; ++i) 00993 { 00994 char *r_string = pcb_g_strdup_printf ("%s,%$mr,%$mr,%$mr,%$mr", rs[i].Name, 00995 rs[i].Thick, rs[i].Diameter, 00996 rs[i].Hole, rs[i].Keepaway); 00997 if (i > 0) 00998 g_string_append_c (str, ':'); 00999 g_string_append (str, r_string); 01000 g_free (r_string); 01001 } 01002 return g_string_free (str, FALSE); 01003 } 01004 01013 int 01014 ParseRouteString (char *s, RouteStyleType *routeStyle, const char *default_unit) 01015 { 01016 int i, style; 01017 char Name[256]; 01018 01019 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType)); 01020 for (style = 0; style < NUM_STYLES; style++, routeStyle++) 01021 { 01022 while (*s && isspace ((int) *s)) 01023 s++; 01024 for (i = 0; *s && *s != ','; i++) 01025 Name[i] = *s++; 01026 Name[i] = '\0'; 01027 routeStyle->Name = strdup (Name); 01028 if (!isdigit ((int) *++s)) 01029 goto error; 01030 routeStyle->Thick = GetNum (&s, default_unit); 01031 while (*s && isspace ((int) *s)) 01032 s++; 01033 if (*s++ != ',') 01034 goto error; 01035 while (*s && isspace ((int) *s)) 01036 s++; 01037 if (!isdigit ((int) *s)) 01038 goto error; 01039 routeStyle->Diameter = GetNum (&s, default_unit); 01040 while (*s && isspace ((int) *s)) 01041 s++; 01042 if (*s++ != ',') 01043 goto error; 01044 while (*s && isspace ((int) *s)) 01045 s++; 01046 if (!isdigit ((int) *s)) 01047 goto error; 01048 routeStyle->Hole = GetNum (&s, default_unit); 01049 /* for backwards-compatibility, we use a 10-mil default 01050 * for styles which omit the keepaway specification. */ 01051 if (*s != ',') 01052 routeStyle->Keepaway = MIL_TO_COORD(10); 01053 else 01054 { 01055 s++; 01056 while (*s && isspace ((int) *s)) 01057 s++; 01058 if (!isdigit ((int) *s)) 01059 goto error; 01060 routeStyle->Keepaway = GetNum (&s, default_unit); 01061 while (*s && isspace ((int) *s)) 01062 s++; 01063 } 01064 if (style < NUM_STYLES - 1) 01065 { 01066 while (*s && isspace ((int) *s)) 01067 s++; 01068 if (*s++ != ':') 01069 goto error; 01070 } 01071 } 01072 return (0); 01073 01074 error: 01075 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType)); 01076 return (1); 01077 } 01078 01086 int 01087 ParseGroupString (char *group_string, LayerGroupType *LayerGroup, int *LayerN) 01088 { 01089 char *s; 01090 int group, member, layer; 01091 bool c_set = false, /* flags for the two special layers to */ 01092 s_set = false; /* provide a default setting for old formats */ 01093 int groupnum[MAX_ALL_LAYER]; 01094 01095 *LayerN = 0; 01096 01097 /* Deterimine the maximum layer number */ 01098 for (s = group_string; s && *s; s++) 01099 { 01100 while (*s && isspace ((int) *s)) 01101 s++; 01102 01103 switch (*s) 01104 { 01105 case 'c': 01106 case 'C': 01107 case 't': 01108 case 'T': 01109 case 's': 01110 case 'S': 01111 case 'b': 01112 case 'B': 01113 break; 01114 01115 default: 01116 if (!isdigit ((int) *s)) 01117 goto error; 01118 *LayerN = MAX (*LayerN, atoi (s)); 01119 break; 01120 } 01121 01122 while (*++s && isdigit ((int) *s)); 01123 01124 /* ignore white spaces and check for separator */ 01125 while (*s && isspace ((int) *s)) 01126 s++; 01127 01128 if (*s == '\0') 01129 break; 01130 01131 if (*s != ':' && *s != ',') 01132 goto error; 01133 } 01134 01135 /* clear struct */ 01136 memset (LayerGroup, 0, sizeof (LayerGroupType)); 01137 01138 /* Clear assignments */ 01139 for (layer = 0; layer < MAX_ALL_LAYER; layer++) 01140 groupnum[layer] = -1; 01141 01142 /* loop over all groups */ 01143 for (s = group_string, group = 0; 01144 s && *s && group < *LayerN; 01145 group++) 01146 { 01147 while (*s && isspace ((int) *s)) 01148 s++; 01149 01150 /* loop over all group members */ 01151 for (member = 0; *s; s++) 01152 { 01153 /* ignore white spaces and get layernumber */ 01154 while (*s && isspace ((int) *s)) 01155 s++; 01156 switch (*s) 01157 { 01158 case 'c': 01159 case 'C': 01160 case 't': 01161 case 'T': 01162 layer = *LayerN + TOP_SILK_LAYER; 01163 c_set = true; 01164 break; 01165 01166 case 's': 01167 case 'S': 01168 case 'b': 01169 case 'B': 01170 layer = *LayerN + BOTTOM_SILK_LAYER; 01171 s_set = true; 01172 break; 01173 01174 default: 01175 layer = atoi (s) - 1; 01176 break; 01177 } 01178 if (layer > *LayerN + MAX (BOTTOM_SILK_LAYER, TOP_SILK_LAYER) || 01179 member >= *LayerN + 1) 01180 goto error; 01181 groupnum[layer] = group; 01182 LayerGroup->Entries[group][member++] = layer; 01183 while (*++s && isdigit ((int) *s)); 01184 01185 /* ignore white spaces and check for separator */ 01186 while (*s && isspace ((int) *s)) 01187 s++; 01188 if (!*s || *s == ':') 01189 break; 01190 } 01191 LayerGroup->Number[group] = member; 01192 if (*s == ':') 01193 s++; 01194 } 01195 01196 /* If no explicit solder or component layer group was found in the layer 01197 * group string, make group 0 the bottom side, and group 1 the top side. 01198 * This is done by assigning the relevant silkscreen layers to those groups. 01199 */ 01200 if (!s_set) 01201 LayerGroup->Entries[0][LayerGroup->Number[0]++] = *LayerN + BOTTOM_SILK_LAYER; 01202 if (!c_set) 01203 LayerGroup->Entries[1][LayerGroup->Number[1]++] = *LayerN + TOP_SILK_LAYER; 01204 01205 /* Assign a unique layer group to each layer that was not explicitly 01206 * assigned a particular group by its presence in the layer group string. 01207 */ 01208 for (layer = 0; layer < *LayerN && group < *LayerN; layer++) 01209 if (groupnum[layer] == -1) 01210 { 01211 LayerGroup->Entries[group][0] = layer; 01212 LayerGroup->Number[group] = 1; 01213 group++; 01214 } 01215 return (0); 01216 01217 /* reset structure on error */ 01218 error: 01219 memset (LayerGroup, 0, sizeof (LayerGroupType)); 01220 return (1); 01221 } 01222 01226 void 01227 AssignDefaultLayerTypes() 01228 { 01229 int num_found; 01230 Cardinal outline_layer = -1; 01231 01232 /* 01233 * There can be only one outline layer. During parsing guess_layertype() 01234 * applied well known cases already, but as this function operates on a 01235 * single layer only, it might end up with more than one hit for the whole 01236 * file. Especially after loading an older layout without saved flags. 01237 */ 01238 num_found = 0; 01239 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE) 01240 outline_layer = n; 01241 num_found++; 01242 END_LOOP; 01243 01244 if (num_found != 1) 01245 /* No or duplicate outline! Try to find a layer which is named exactly 01246 "outline". */ 01247 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE) 01248 if ( ! strcasecmp (layer->Name, "outline")) 01249 { 01250 outline_layer = n; 01251 num_found = 1; 01252 break; 01253 } 01254 END_LOOP; 01255 01256 if (num_found != 1) 01257 /* Next, try to find a layer which is named exactly "route". */ 01258 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE) 01259 if ( ! strcasecmp (layer->Name, "route")) 01260 { 01261 outline_layer = n; 01262 num_found = 1; 01263 break; 01264 } 01265 END_LOOP; 01266 01267 if (num_found != 1) 01268 /* As last resort, take the first layer claiming to be outline. */ 01269 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE) 01270 outline_layer = n; 01271 num_found = 1; 01272 break; 01273 END_LOOP; 01274 01275 /* Make sure our found outline layer is the only one. */ 01276 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE) 01277 if (n == outline_layer) 01278 layer->Type = LT_OUTLINE; 01279 else 01280 layer->Type = LT_ROUTE; /* best guess */ 01281 END_LOOP; 01282 } 01283 01284 extern void pcb_main_uninit(void); 01285 01289 void 01290 QuitApplication (void) 01291 { 01292 /* 01293 * save data if necessary. It not needed, then don't trigger EmergencySave 01294 * via our atexit() registering of EmergencySave(). We presumeably wanted to 01295 * exit here and thus it is not an emergency. 01296 */ 01297 if (PCB->Changed && Settings.SaveInTMP) 01298 EmergencySave (); 01299 else 01300 DisableEmergencySave (); 01301 01302 if (gui->do_exit == NULL) 01303 { 01304 pcb_main_uninit (); 01305 exit (0); 01306 } 01307 else 01308 gui->do_exit (gui); 01309 } 01310 01318 char * 01319 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter) 01320 { 01321 static DynamicStringType command; 01322 char *p; 01323 01324 if (Settings.verbose) 01325 { 01326 printf ("EvaluateFilename:\n"); 01327 printf ("\tTemplate: \033[33m%s\033[0m\n", Template); 01328 printf ("\tPath: \033[33m%s\033[0m\n", Path); 01329 printf ("\tFilename: \033[33m%s\033[0m\n", Filename); 01330 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter); 01331 } 01332 01333 DSClearString (&command); 01334 01335 for (p = Template; p && *p; p++) 01336 { 01337 /* copy character or add string to command */ 01338 if (*p == '%' 01339 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a')) 01340 switch (*(++p)) 01341 { 01342 case 'a': 01343 DSAddString (&command, Parameter); 01344 break; 01345 case 'f': 01346 DSAddString (&command, Filename); 01347 break; 01348 case 'p': 01349 DSAddString (&command, Path); 01350 break; 01351 } 01352 else 01353 DSAddCharacter (&command, *p); 01354 } 01355 DSAddCharacter (&command, '\0'); 01356 if (Settings.verbose) 01357 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data); 01358 01359 return strdup (command.Data); 01360 } 01361 01368 char * 01369 ExpandFilename (char *Dirname, char *Filename) 01370 { 01371 static DynamicStringType answer; 01372 char *command; 01373 FILE *pipe; 01374 int c; 01375 01376 /* allocate memory for commandline and build it */ 01377 DSClearString (&answer); 01378 if (Dirname) 01379 { 01380 command = (char *)calloc (strlen (Filename) + strlen (Dirname) + 7, 01381 sizeof (char)); 01382 sprintf (command, "echo %s/%s", Dirname, Filename); 01383 } 01384 else 01385 { 01386 command = (char *)calloc (strlen (Filename) + 6, sizeof (char)); 01387 sprintf (command, "echo %s", Filename); 01388 } 01389 01390 /* execute it with shell */ 01391 if ((pipe = popen (command, "r")) != NULL) 01392 { 01393 /* discard all but the first returned line */ 01394 for (;;) 01395 { 01396 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r') 01397 break; 01398 else 01399 DSAddCharacter (&answer, c); 01400 } 01401 01402 free (command); 01403 return (pclose (pipe) ? NULL : answer.Data); 01404 } 01405 01406 /* couldn't be expanded by the shell */ 01407 PopenErrorMessage (command); 01408 free (command); 01409 return (NULL); 01410 } 01411 01412 01416 int 01417 GetLayerNumber (DataType *Data, LayerType *Layer) 01418 { 01419 int i; 01420 01421 for (i = 0; i < MAX_ALL_LAYER; i++) 01422 if (Layer == &Data->Layer[i]) 01423 break; 01424 return (i); 01425 } 01426 01430 static void 01431 PushOnTopOfLayerStack (int NewTop) 01432 { 01433 int i; 01434 01435 /* ignore silk and other extra layers */ 01436 if (NewTop < max_copper_layer) 01437 { 01438 /* first find position of passed one */ 01439 for (i = 0; i < max_copper_layer; i++) 01440 if (LayerStack[i] == NewTop) 01441 break; 01442 01443 /* bring this element to the top of the stack */ 01444 for (; i; i--) 01445 LayerStack[i] = LayerStack[i - 1]; 01446 LayerStack[0] = NewTop; 01447 } 01448 } 01449 01450 01456 int 01457 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder) 01458 { 01459 int group, i, changed = 1; /* at least the current layer changes */ 01460 01461 /* Warning: these special case values must agree with what gui-top-window.c 01462 | thinks the are. 01463 */ 01464 01465 if (Settings.verbose) 01466 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n", 01467 Layer, On, ChangeStackOrder); 01468 01469 /* decrement 'i' to keep stack in order of layergroup */ 01470 group = GetLayerGroupNumberByNumber (Layer); 01471 for (i = PCB->LayerGroups.Number[group]; i;) 01472 { 01473 int layer = PCB->LayerGroups.Entries[group][--i]; 01474 01475 /* don't count the passed member of the group */ 01476 if (layer != Layer && layer < max_copper_layer) 01477 { 01478 PCB->Data->Layer[layer].On = On; 01479 01480 /* push layer on top of stack if switched on */ 01481 if (On && ChangeStackOrder) 01482 PushOnTopOfLayerStack (layer); 01483 changed++; 01484 } 01485 } 01486 01487 /* change at least the passed layer */ 01488 PCB->Data->Layer[Layer].On = On; 01489 if (On && ChangeStackOrder) 01490 PushOnTopOfLayerStack (Layer); 01491 01492 /* update control panel and exit */ 01493 hid_action ("LayersChanged"); 01494 return (changed); 01495 } 01496 01501 void 01502 LayerStringToLayerStack (char *s) 01503 { 01504 static int listed_layers = 0; 01505 int l = strlen (s); 01506 char **args; 01507 int i, argn, lno; 01508 int prev_sep = 1; 01509 01510 s = strdup (s); 01511 args = (char **) malloc (l * sizeof (char *)); 01512 argn = 0; 01513 01514 for (i=0; i<l; i++) 01515 { 01516 switch (s[i]) 01517 { 01518 case ' ': 01519 case '\t': 01520 case ',': 01521 case ';': 01522 case ':': 01523 prev_sep = 1; 01524 s[i] = '\0'; 01525 break; 01526 default: 01527 if (prev_sep) 01528 args[argn++] = s+i; 01529 prev_sep = 0; 01530 break; 01531 } 01532 } 01533 01534 for (i = 0; i < max_copper_layer + SILK_LAYER; i++) 01535 { 01536 if (i < max_copper_layer) 01537 LayerStack[i] = i; 01538 PCB->Data->Layer[i].On = false; 01539 } 01540 PCB->ElementOn = false; 01541 PCB->InvisibleObjectsOn = false; 01542 PCB->PinOn = false; 01543 PCB->ViaOn = false; 01544 PCB->RatOn = false; 01545 CLEAR_FLAG (SHOWMASKFLAG, PCB); 01546 Settings.ShowBottomSide = 0; 01547 01548 for (i=argn-1; i>=0; i--) 01549 { 01550 if (strcasecmp (args[i], "rats") == 0) 01551 PCB->RatOn = true; 01552 else if (strcasecmp (args[i], "invisible") == 0) 01553 PCB->InvisibleObjectsOn = true; 01554 else if (strcasecmp (args[i], "pins") == 0) 01555 PCB->PinOn = true; 01556 else if (strcasecmp (args[i], "vias") == 0) 01557 PCB->ViaOn = true; 01558 else if (strcasecmp (args[i], "elements") == 0 01559 || strcasecmp (args[i], "silk") == 0) 01560 PCB->ElementOn = true; 01561 else if (strcasecmp (args[i], "mask") == 0) 01562 SET_FLAG (SHOWMASKFLAG, PCB); 01563 else if (strcasecmp (args[i], "solderside") == 0) 01564 Settings.ShowBottomSide = 1; 01565 else if (isdigit ((int) args[i][0])) 01566 { 01567 lno = atoi (args[i]); 01568 ChangeGroupVisibility (lno, true, true); 01569 } 01570 else 01571 { 01572 int found = 0; 01573 for (lno = 0; lno < max_copper_layer; lno++) 01574 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0) 01575 { 01576 ChangeGroupVisibility (lno, true, true); 01577 found = 1; 01578 break; 01579 } 01580 if (!found) 01581 { 01582 fprintf(stderr, _("Warning: layer \"%s\" not known\n"), args[i]); 01583 if (!listed_layers) 01584 { 01585 fprintf (stderr, _("Named layers in this board are:\n")); 01586 listed_layers = 1; 01587 for (lno=0; lno < max_copper_layer; lno ++) 01588 fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name); 01589 fprintf(stderr, _("Also: component, solder, rats, invisible, " 01590 "pins, vias, elements or silk, mask, solderside.\n")); 01591 } 01592 } 01593 } 01594 } 01595 } 01596 01600 int 01601 GetLayerGroupNumberByPointer (LayerType *Layer) 01602 { 01603 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer))); 01604 } 01605 01609 int 01610 GetLayerGroupNumberByNumber (Cardinal Layer) 01611 { 01612 int group, entry; 01613 01614 for (group = 0; group < max_group; group++) 01615 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++) 01616 if (PCB->LayerGroups.Entries[group][entry] == Layer) 01617 return (group); 01618 01619 /* since every layer belongs to a group it is safe to return 01620 * the value without boundary checking 01621 */ 01622 return (group); 01623 } 01624 01629 int 01630 GetLayerGroupNumberBySide (int side) 01631 { 01632 /* Find the relavant board side layer group by determining the 01633 * layer group associated with the relevant side's silk-screen 01634 */ 01635 return GetLayerGroupNumberByNumber ( 01636 side == TOP_SIDE ? top_silk_layer : bottom_silk_layer); 01637 } 01638 01644 BoxType * 01645 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3) 01646 { 01647 switch (Type) 01648 { 01649 case LINE_TYPE: 01650 case ARC_TYPE: 01651 case TEXT_TYPE: 01652 case POLYGON_TYPE: 01653 case PAD_TYPE: 01654 case PIN_TYPE: 01655 case ELEMENTNAME_TYPE: 01656 return (BoxType *)Ptr2; 01657 case VIA_TYPE: 01658 case ELEMENT_TYPE: 01659 return (BoxType *)Ptr1; 01660 case POLYGONPOINT_TYPE: 01661 case LINEPOINT_TYPE: 01662 return (BoxType *)Ptr3; 01663 default: 01664 Message (_("Request for bounding box of unsupported type %d\n"), Type); 01665 return (BoxType *)Ptr2; 01666 } 01667 } 01668 01672 void 01673 SetArcBoundingBox (ArcType *Arc) 01674 { 01675 double ca1, ca2, sa1, sa2; 01676 double minx, maxx, miny, maxy; 01677 Angle ang1, ang2; 01678 Coord width; 01679 01680 /* first put angles into standard form: 01681 * ang1 < ang2, both angles between 0 and 720 */ 01682 Arc->Delta = CLAMP (Arc->Delta, -360, 360); 01683 01684 if (Arc->Delta > 0) 01685 { 01686 ang1 = NormalizeAngle (Arc->StartAngle); 01687 ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta); 01688 } 01689 else 01690 { 01691 ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta); 01692 ang2 = NormalizeAngle (Arc->StartAngle); 01693 } 01694 if (ang1 > ang2) 01695 ang2 += 360; 01696 /* Make sure full circles aren't treated as zero-length arcs */ 01697 if (Arc->Delta == 360 || Arc->Delta == -360) 01698 ang2 = ang1 + 360; 01699 01700 /* calculate sines, cosines */ 01701 sa1 = sin (M180 * ang1); 01702 ca1 = cos (M180 * ang1); 01703 sa2 = sin (M180 * ang2); 01704 ca2 = cos (M180 * ang2); 01705 01706 minx = MIN (ca1, ca2); 01707 maxx = MAX (ca1, ca2); 01708 miny = MIN (sa1, sa2); 01709 maxy = MAX (sa1, sa2); 01710 01711 /* Check for extreme angles */ 01712 if ((ang1 <= 0 && ang2 >= 0) || (ang1 <= 360 && ang2 >= 360)) maxx = 1; 01713 if ((ang1 <= 90 && ang2 >= 90) || (ang1 <= 450 && ang2 >= 450)) maxy = 1; 01714 if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540)) minx = -1; 01715 if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630)) miny = -1; 01716 01717 /* Finally, calcate bounds, converting sane geometry into pcb geometry */ 01718 Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx; 01719 Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx; 01720 Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny; 01721 Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy; 01722 01723 width = (Arc->Thickness + Arc->Clearance) / 2; 01724 01725 /* Adjust for our discrete polygon approximation */ 01726 width = (double)width * MAX (POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5; 01727 01728 Arc->BoundingBox.X1 -= width; 01729 Arc->BoundingBox.X2 += width; 01730 Arc->BoundingBox.Y1 -= width; 01731 Arc->BoundingBox.Y2 += width; 01732 close_box(&Arc->BoundingBox); 01733 01734 /* Update the arc end-points */ 01735 Arc->Point1.X = Arc->X - (double)Arc->Width * ca1; 01736 Arc->Point1.Y = Arc->Y + (double)Arc->Height * sa1; 01737 Arc->Point2.X = Arc->X - (double)Arc->Width * ca2; 01738 Arc->Point2.Y = Arc->Y + (double)Arc->Height * sa2; 01739 } 01740 01744 void 01745 ResetStackAndVisibility (void) 01746 { 01747 int top_group; 01748 Cardinal i; 01749 01750 for (i = 0; i < max_copper_layer + SILK_LAYER; i++) 01751 { 01752 if (i < max_copper_layer) 01753 LayerStack[i] = i; 01754 PCB->Data->Layer[i].On = true; 01755 } 01756 PCB->ElementOn = true; 01757 PCB->InvisibleObjectsOn = true; 01758 PCB->PinOn = true; 01759 PCB->ViaOn = true; 01760 PCB->RatOn = true; 01761 01762 /* Bring the component group to the front and make it active. */ 01763 top_group = GetLayerGroupNumberBySide (TOP_SIDE); 01764 ChangeGroupVisibility (PCB->LayerGroups.Entries[top_group][0], 1, 1); 01765 } 01766 01770 void 01771 SaveStackAndVisibility (void) 01772 { 01773 Cardinal i; 01774 static bool run = false; 01775 01776 if (run == false) 01777 { 01778 SavedStack.cnt = 0; 01779 run = true; 01780 } 01781 01782 if (SavedStack.cnt != 0) 01783 { 01784 fprintf (stderr, 01785 "SaveStackAndVisibility() layerstack was already saved and not" 01786 "yet restored. cnt = %d\n", SavedStack.cnt); 01787 } 01788 01789 for (i = 0; i < max_copper_layer + SILK_LAYER; i++) 01790 { 01791 if (i < max_copper_layer) 01792 SavedStack.LayerStack[i] = LayerStack[i]; 01793 SavedStack.LayerOn[i] = PCB->Data->Layer[i].On; 01794 } 01795 SavedStack.ElementOn = PCB->ElementOn; 01796 SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn; 01797 SavedStack.PinOn = PCB->PinOn; 01798 SavedStack.ViaOn = PCB->ViaOn; 01799 SavedStack.RatOn = PCB->RatOn; 01800 SavedStack.cnt++; 01801 } 01802 01806 void 01807 RestoreStackAndVisibility (void) 01808 { 01809 Cardinal i; 01810 01811 if (SavedStack.cnt == 0) 01812 { 01813 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not" 01814 " been saved. cnt = %d\n", SavedStack.cnt); 01815 return; 01816 } 01817 else if (SavedStack.cnt != 1) 01818 { 01819 fprintf (stderr, "RestoreStackAndVisibility() layerstack save count is" 01820 " wrong. cnt = %d\n", SavedStack.cnt); 01821 } 01822 01823 for (i = 0; i < max_copper_layer + SILK_LAYER; i++) 01824 { 01825 if (i < max_copper_layer) 01826 LayerStack[i] = SavedStack.LayerStack[i]; 01827 PCB->Data->Layer[i].On = SavedStack.LayerOn[i]; 01828 } 01829 PCB->ElementOn = SavedStack.ElementOn; 01830 PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn; 01831 PCB->PinOn = SavedStack.PinOn; 01832 PCB->ViaOn = SavedStack.ViaOn; 01833 PCB->RatOn = SavedStack.RatOn; 01834 01835 SavedStack.cnt--; 01836 } 01837 01844 char * 01845 GetWorkingDirectory (char *path) 01846 { 01847 #ifdef HAVE_GETCWD 01848 return getcwd (path, MAXPATHLEN); 01849 #else 01850 /* seems that some BSD releases lack of a prototype for getwd() */ 01851 return getwd (path); 01852 #endif 01853 01854 } 01855 01861 void 01862 CreateQuotedString (DynamicStringType *DS, char *S) 01863 { 01864 DSClearString (DS); 01865 DSAddCharacter (DS, '"'); 01866 while (*S) 01867 { 01868 if (*S == '"' || *S == '\\') 01869 DSAddCharacter (DS, '\\'); 01870 DSAddCharacter (DS, *S++); 01871 } 01872 DSAddCharacter (DS, '"'); 01873 } 01874 01875 BoxType * 01876 GetArcEnds (ArcType *Arc) 01877 { 01878 static BoxType box; 01879 box.X1 = Arc->X - Arc->Width * cos (Arc->StartAngle * M180); 01880 box.Y1 = Arc->Y + Arc->Height * sin (Arc->StartAngle * M180); 01881 box.X2 = Arc->X - Arc->Width * cos ((Arc->StartAngle + Arc->Delta) * M180); 01882 box.Y2 = Arc->Y + Arc->Height * sin ((Arc->StartAngle + Arc->Delta) * M180); 01883 return &box; 01884 } 01885 01886 01890 void 01891 ChangeArcAngles (LayerType *Layer, ArcType *a, 01892 Angle new_sa, Angle new_da) 01893 { 01894 if (new_da >= 360) 01895 { 01896 new_da = 360; 01897 new_sa = 0; 01898 } 01899 RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a); 01900 r_delete_entry (Layer->arc_tree, (BoxType *) a); 01901 AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a); 01902 a->StartAngle = new_sa; 01903 a->Delta = new_da; 01904 SetArcBoundingBox (a); 01905 r_insert_entry (Layer->arc_tree, (BoxType *) a, 0); 01906 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a); 01907 } 01908 01909 static char * 01910 BumpName (char *Name) 01911 { 01912 int num; 01913 char c, *start; 01914 static char temp[256]; 01915 01916 start = Name; 01917 /* seek end of string */ 01918 while (*Name != 0) 01919 Name++; 01920 /* back up to potential number */ 01921 for (Name--; isdigit ((int) *Name); Name--); 01922 Name++; 01923 if (*Name) 01924 num = atoi (Name) + 1; 01925 else 01926 num = 1; 01927 c = *Name; 01928 *Name = 0; 01929 sprintf (temp, "%s%d", start, num); 01930 /* if this is not our string, put back the blown character */ 01931 if (start != temp) 01932 *Name = c; 01933 return (temp); 01934 } 01935 01941 char * 01942 UniqueElementName (DataType *Data, char *Name) 01943 { 01944 bool unique = true; 01945 /* null strings are ok */ 01946 if (!Name || !*Name) 01947 return (Name); 01948 01949 for (;;) 01950 { 01951 ELEMENT_LOOP (Data); 01952 { 01953 if (NAMEONPCB_NAME (element) && 01954 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0) 01955 { 01956 Name = BumpName (Name); 01957 unique = false; 01958 break; 01959 } 01960 } 01961 END_LOOP; 01962 if (unique) 01963 return (Name); 01964 unique = true; 01965 } 01966 } 01967 01968 static void 01969 GetGridLockCoordinates (int type, void *ptr1, 01970 void *ptr2, void *ptr3, Coord * x, 01971 Coord * y) 01972 { 01973 switch (type) 01974 { 01975 case VIA_TYPE: 01976 *x = ((PinType *) ptr2)->X; 01977 *y = ((PinType *) ptr2)->Y; 01978 break; 01979 case LINE_TYPE: 01980 *x = ((LineType *) ptr2)->Point1.X; 01981 *y = ((LineType *) ptr2)->Point1.Y; 01982 break; 01983 case TEXT_TYPE: 01984 case ELEMENTNAME_TYPE: 01985 *x = ((TextType *) ptr2)->X; 01986 *y = ((TextType *) ptr2)->Y; 01987 break; 01988 case ELEMENT_TYPE: 01989 *x = ((ElementType *) ptr2)->MarkX; 01990 *y = ((ElementType *) ptr2)->MarkY; 01991 break; 01992 case POLYGON_TYPE: 01993 *x = ((PolygonType *) ptr2)->Points[0].X; 01994 *y = ((PolygonType *) ptr2)->Points[0].Y; 01995 break; 01996 01997 case LINEPOINT_TYPE: 01998 case POLYGONPOINT_TYPE: 01999 *x = ((PointType *) ptr3)->X; 02000 *y = ((PointType *) ptr3)->Y; 02001 break; 02002 case ARC_TYPE: 02003 { 02004 BoxType *box; 02005 02006 box = GetArcEnds ((ArcType *) ptr2); 02007 *x = box->X1; 02008 *y = box->Y1; 02009 break; 02010 } 02011 } 02012 } 02013 02014 void 02015 AttachForCopy (Coord PlaceX, Coord PlaceY) 02016 { 02017 Coord mx = 0, my = 0; 02018 02019 Crosshair.AttachedObject.RubberbandN = 0; 02020 if (! TEST_FLAG (SNAPPINFLAG, PCB)) 02021 { 02022 /* dither the grab point so that the mark, center, etc 02023 * will end up on a grid coordinate 02024 */ 02025 GetGridLockCoordinates (Crosshair.AttachedObject.Type, 02026 Crosshair.AttachedObject.Ptr1, 02027 Crosshair.AttachedObject.Ptr2, 02028 Crosshair.AttachedObject.Ptr3, &mx, &my); 02029 mx = GridFit (mx, PCB->Grid, PCB->GridOffsetX) - mx; 02030 my = GridFit (my, PCB->Grid, PCB->GridOffsetY) - my; 02031 } 02032 Crosshair.AttachedObject.X = PlaceX - mx; 02033 Crosshair.AttachedObject.Y = PlaceY - my; 02034 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB)) 02035 SetLocalRef (PlaceX - mx, PlaceY - my, true); 02036 Crosshair.AttachedObject.State = STATE_SECOND; 02037 02038 /* get boundingbox of object and set cursor range */ 02039 crosshair_update_range(); 02040 02041 /* get all attached objects if necessary */ 02042 if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB)) 02043 LookupRubberbandLines (Crosshair.AttachedObject.Type, 02044 Crosshair.AttachedObject.Ptr1, 02045 Crosshair.AttachedObject.Ptr2, 02046 Crosshair.AttachedObject.Ptr3); 02047 if (Settings.Mode != COPY_MODE && 02048 (Crosshair.AttachedObject.Type == ELEMENT_TYPE || 02049 Crosshair.AttachedObject.Type == VIA_TYPE || 02050 Crosshair.AttachedObject.Type == LINE_TYPE || 02051 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)) 02052 LookupRatLines (Crosshair.AttachedObject.Type, 02053 Crosshair.AttachedObject.Ptr1, 02054 Crosshair.AttachedObject.Ptr2, 02055 Crosshair.AttachedObject.Ptr3); 02056 } 02057 02061 int 02062 FileExists (const char *name) 02063 { 02064 FILE *f; 02065 f = fopen (name, "r"); 02066 if (f) 02067 { 02068 fclose (f); 02069 return 1; 02070 } 02071 return 0; 02072 } 02073 02074 char * 02075 Concat (const char *first, ...) 02076 { 02077 char *rv; 02078 int len; 02079 va_list a; 02080 02081 len = strlen (first); 02082 rv = (char *) malloc (len + 1); 02083 strcpy (rv, first); 02084 02085 va_start (a, first); 02086 while (1) 02087 { 02088 const char *s = va_arg (a, const char *); 02089 if (!s) 02090 break; 02091 len += strlen (s); 02092 rv = (char *) realloc (rv, len + 1); 02093 strcat (rv, s); 02094 } 02095 va_end (a); 02096 return rv; 02097 } 02098 02099 int 02100 mem_any_set (unsigned char *ptr, int bytes) 02101 { 02102 while (bytes--) 02103 if (*ptr++) 02104 return 1; 02105 return 0; 02106 } 02107 02111 FlagType 02112 MakeFlags (unsigned int flags) 02113 { 02114 FlagType rv; 02115 memset (&rv, 0, sizeof (rv)); 02116 rv.f = flags; 02117 return rv; 02118 } 02119 02124 FlagType 02125 OldFlags (unsigned int flags) 02126 { 02127 FlagType rv; 02128 int i, f; 02129 memset (&rv, 0, sizeof (rv)); 02130 /* If we move flag bits around, this is where we map old bits to them. */ 02131 rv.f = flags & 0xffff; 02132 f = 0x10000; 02133 for (i = 0; i < 8; i++) 02134 { 02135 /* use the closest thing to the old thermal style */ 02136 if (flags & f) 02137 rv.t[i / 2] |= (1 << (4 * (i % 2))); 02138 f <<= 1; 02139 } 02140 return rv; 02141 } 02142 02143 FlagType 02144 AddFlags (FlagType flag, unsigned int flags) 02145 { 02146 flag.f |= flags; 02147 return flag; 02148 } 02149 02150 FlagType 02151 MaskFlags (FlagType flag, unsigned int flags) 02152 { 02153 flag.f &= ~flags; 02154 return flag; 02155 } 02156 02157 /* Layer Group Functions. */ 02158 02163 int 02164 MoveLayerToGroup (int layer, int group) 02165 { 02166 int prev, i, j; 02167 02168 if (layer < 0 || layer > max_copper_layer + 1) 02169 return -1; 02170 prev = GetLayerGroupNumberByNumber (layer); 02171 if ((layer == bottom_silk_layer 02172 && group == GetLayerGroupNumberByNumber (top_silk_layer)) 02173 || (layer == top_silk_layer 02174 && group == GetLayerGroupNumberByNumber (bottom_silk_layer)) 02175 || (group < 0 || group >= max_group) || (prev == group)) 02176 return prev; 02177 02178 /* Remove layer from prev group */ 02179 for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++) 02180 if (PCB->LayerGroups.Entries[prev][i] != layer) 02181 PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i]; 02182 PCB->LayerGroups.Number[prev]--; 02183 02184 /* Add layer to new group. */ 02185 i = PCB->LayerGroups.Number[group]++; 02186 PCB->LayerGroups.Entries[group][i] = layer; 02187 02188 return group; 02189 } 02190 02194 char * 02195 LayerGroupsToString (LayerGroupType *lg) 02196 { 02197 #if MAX_ALL_LAYER < 9999 02198 /* Allows for layer numbers 0..9999 */ 02199 static char buf[(MAX_ALL_LAYER) * 5 + 1]; 02200 #endif 02201 char *cp = buf; 02202 char sep = 0; 02203 int group, entry; 02204 for (group = 0; group < max_group; group++) 02205 if (PCB->LayerGroups.Number[group]) 02206 { 02207 if (sep) 02208 *cp++ = ':'; 02209 sep = 1; 02210 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++) 02211 { 02212 int layer = PCB->LayerGroups.Entries[group][entry]; 02213 if (layer == top_silk_layer) 02214 { 02215 *cp++ = 'c'; 02216 } 02217 else if (layer == bottom_silk_layer) 02218 { 02219 *cp++ = 's'; 02220 } 02221 else 02222 { 02223 sprintf (cp, "%d", layer + 1); 02224 while (*++cp) 02225 ; 02226 } 02227 if (entry != PCB->LayerGroups.Number[group] - 1) 02228 *cp++ = ','; 02229 } 02230 } 02231 *cp++ = 0; 02232 return buf; 02233 } 02234 02235 int 02236 GetMaxBottomLayer () 02237 { 02238 int l = 0; 02239 int group, i; 02240 02241 group = GetLayerGroupNumberBySide (BOTTOM_SIDE); 02242 for (i = 0; i < PCB->LayerGroups.Number[group]; i++) 02243 { 02244 if (PCB->LayerGroups.Entries[group][i] < max_copper_layer) 02245 l = max (l, PCB->LayerGroups.Entries[group][i]); 02246 } 02247 return l; 02248 } 02249 02250 02258 void 02259 SanitizeBuriedVia (PinType *via) 02260 { 02261 /* we expect that via is burried/blind */ 02262 02263 if (via->BuriedFrom > via->BuriedTo) 02264 { 02265 int tmp = via->BuriedFrom; 02266 via->BuriedFrom = via->BuriedTo; 02267 via->BuriedTo = tmp; 02268 } 02269 02270 /* check, if via was extended to full stack (after first layer removal)*/ 02271 /* convert it in TH via in such case */ 02272 if (via->BuriedTo == GetMaxBottomLayer () 02273 && via->BuriedFrom == 0) 02274 via->BuriedTo = 0; 02275 } 02276 02277 #if 0 02278 bool 02279 IsLayerMoveSafe (int old_index, int new_index) 02280 { 02281 VIA_LOOP (PCB->Data); 02282 { 02283 if (VIA_IS_BURIED (via)) 02284 { 02285 /* moving from below to inside */ 02286 if ((old_index < via->BuriedFrom) 02287 && (new_index < via->BuriedTo)) 02288 return false; 02289 02290 /* moving from above to inside */ 02291 if ((old_index > via->BuriedTo) 02292 && (new_index > via->BuriedFrom)) 02293 return false; 02294 } 02295 } 02296 END_LOOP; 02297 02298 return true; 02299 } 02300 02301 #endif 02302 02308 void 02309 ChangeBuriedViasAfterLayerMove (int old_index, int new_index) 02310 { 02311 VIA_LOOP (PCB->Data); 02312 { 02313 if (VIA_IS_BURIED (via)) 02314 { 02315 /* do nothing, if both layers are below the via */ 02316 if ((old_index < via->BuriedFrom) 02317 && (new_index < via->BuriedFrom)) 02318 continue; 02319 02320 /* do nothing, if both layers are above the via */ 02321 if ((old_index > via->BuriedTo) 02322 && (new_index > via->BuriedTo)) 02323 continue; 02324 02325 /* moving via top layer - via top follows layer */ 02326 if (old_index == via->BuriedFrom) 02327 { 02328 AddObjectToSetViaLayersUndoList (via, via, via); 02329 via->BuriedFrom = new_index; 02330 } 02331 02332 /* moving via bottom layer - via bottom follows layer */ 02333 else if (old_index == via->BuriedTo) 02334 { 02335 AddObjectToSetViaLayersUndoList (via, via, via); 02336 via->BuriedTo = new_index; 02337 } 02338 02339 /* moving layer covered by via inside the via (except top and bottom) - do nothing */ 02340 else if (VIA_ON_LAYER (via, old_index) 02341 && VIA_ON_LAYER (via, new_index)) 02342 continue; 02343 02344 /* moving from inside to below - shrink via DANGEROUS op*/ 02345 else if (VIA_ON_LAYER (via, old_index) 02346 && (new_index < via->BuriedFrom)) 02347 { 02348 AddObjectToSetViaLayersUndoList (via, via, via); 02349 via->BuriedFrom++; 02350 } 02351 02352 /* moving from inside to above - shrink via DANGEROUS op*/ 02353 else if (VIA_ON_LAYER (via, old_index) 02354 && (new_index > via->BuriedTo)) 02355 { 02356 AddObjectToSetViaLayersUndoList (via, via, via); 02357 via->BuriedTo--; 02358 } 02359 02360 /* moving from above to below - shift via up */ 02361 else if ((old_index > via->BuriedTo) 02362 && (new_index < via->BuriedFrom)) 02363 { 02364 AddObjectToSetViaLayersUndoList (via, via, via); 02365 via->BuriedFrom++; 02366 via->BuriedTo++; 02367 } 02368 02369 /* moving from below to above - shift via down */ 02370 else if ((old_index < via->BuriedFrom) 02371 && (new_index >= via->BuriedTo)) 02372 { 02373 AddObjectToSetViaLayersUndoList (via, via, via); 02374 via->BuriedFrom--; 02375 via->BuriedTo--; 02376 } 02377 02378 /* moving from below to inside - extend via down DANGEROUS op*/ 02379 else if ((old_index < via->BuriedFrom) 02380 && (new_index < via->BuriedTo)) 02381 { 02382 AddObjectToSetViaLayersUndoList (via, via, via); 02383 via->BuriedFrom--; 02384 } 02385 02386 /* moving from above to inside - extend via up DANGEROUS op*/ 02387 else if ((old_index > via->BuriedTo) 02388 && (new_index > via->BuriedFrom)) 02389 { 02390 AddObjectToSetViaLayersUndoList ( via, via, via); 02391 via->BuriedTo++; 02392 } 02393 else 02394 { 02395 Message (_("Layer move: failed via update: %d -> %d, via: %d:%d\n"), old_index, new_index, via->BuriedFrom, via->BuriedTo); 02396 continue; 02397 } 02398 02399 SanitizeBuriedVia (via); 02400 } 02401 } 02402 END_LOOP; 02403 RemoveDegradedVias (); 02404 } 02405 02410 void 02411 ChangeBuriedViasAfterLayerCreate (int index) 02412 { 02413 VIA_LOOP (PCB->Data); 02414 { 02415 if (VIA_IS_BURIED (via)) 02416 { 02417 if (index <= via->BuriedFrom 02418 || index <= via->BuriedTo) 02419 AddObjectToSetViaLayersUndoList (via, via, via); 02420 02421 if (index <= via->BuriedFrom) 02422 via->BuriedFrom++; 02423 if (index <= via->BuriedTo) 02424 via->BuriedTo++; 02425 } 02426 } 02427 END_LOOP; 02428 } 02429 02434 void 02435 ChangeBuriedViasAfterLayerDelete (int index) 02436 { 02437 VIA_LOOP (PCB->Data); 02438 { 02439 if (VIA_IS_BURIED (via)) 02440 { 02441 if (index < via->BuriedFrom 02442 || index <= via->BuriedTo) 02443 AddObjectToSetViaLayersUndoList (via, via, via); 02444 02445 if (index < via->BuriedFrom) 02446 via->BuriedFrom--; 02447 if (index <= via->BuriedTo) 02448 via->BuriedTo--; 02449 02450 SanitizeBuriedVia (via); 02451 } 02452 } 02453 END_LOOP; 02454 RemoveDegradedVias (); 02455 } 02456 02461 bool 02462 ViaIsOnLayerGroup (PinType *via, int group) 02463 { 02464 Cardinal layer; 02465 02466 if (!VIA_IS_BURIED (via)) 02467 return true; 02468 02469 for (layer = via->BuriedFrom; layer <= via->BuriedTo; layer++) 02470 { 02471 if (GetLayerGroupNumberByNumber (layer) == group) 02472 return true; 02473 } 02474 02475 return false; 02476 } 02477 02482 bool 02483 ViaIsOnAnyVisibleLayer (PinType *via) 02484 { 02485 Cardinal layer; 02486 02487 if (!VIA_IS_BURIED (via)) 02488 return true; 02489 02490 for (layer = via->BuriedFrom; layer <= via->BuriedTo; layer ++) 02491 { 02492 if (PCB->Data->Layer[layer].On) 02493 return true; 02494 } 02495 02496 return false; 02497 } 02498 02499 02500 char * 02501 pcb_author (void) 02502 { 02503 #ifdef HAVE_GETPWUID 02504 static struct passwd *pwentry; 02505 static char *fab_author = 0; 02506 02507 if (!fab_author) 02508 { 02509 if (Settings.FabAuthor && Settings.FabAuthor[0]) 02510 fab_author = Settings.FabAuthor; 02511 else 02512 { 02513 int len; 02514 char *comma, *gecos; 02515 02516 /* ID the user. */ 02517 pwentry = getpwuid (getuid ()); 02518 gecos = pwentry->pw_gecos; 02519 comma = strchr (gecos, ','); 02520 if (comma) 02521 len = comma - gecos; 02522 else 02523 len = strlen (gecos); 02524 fab_author = (char *)malloc (len + 1); 02525 if (!fab_author) 02526 { 02527 perror ("pcb: out of memory.\n"); 02528 exit (-1); 02529 } 02530 memcpy (fab_author, gecos, len); 02531 fab_author[len] = 0; 02532 } 02533 } 02534 return fab_author; 02535 #else 02536 return "Unknown"; 02537 #endif 02538 } 02539 02540 02545 char * 02546 AttributeGetFromList (AttributeListType *list, char *name) 02547 { 02548 int i; 02549 for (i=0; i<list->Number; i++) 02550 if (strcmp (name, list->List[i].name) == 0) 02551 return list->List[i].value; 02552 return NULL; 02553 } 02554 02563 int 02564 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace) 02565 { 02566 int i; 02567 02568 /* If we're allowed to replace an existing attribute, see if we 02569 can. */ 02570 if (replace) 02571 { 02572 for (i=0; i<list->Number; i++) 02573 if (strcmp (name, list->List[i].name) == 0) 02574 { 02575 free (list->List[i].value); 02576 list->List[i].value = STRDUP (value); 02577 return 1; 02578 } 02579 } 02580 02581 /* At this point, we're going to need to add a new attribute to the 02582 list. See if there's room. */ 02583 if (list->Number >= list->Max) 02584 { 02585 list->Max += 10; 02586 list->List = (AttributeType *) realloc (list->List, 02587 list->Max * sizeof (AttributeType)); 02588 } 02589 02590 /* Now add the new attribute. */ 02591 i = list->Number; 02592 list->List[i].name = STRDUP (name); 02593 list->List[i].value = STRDUP (value); 02594 list->Number ++; 02595 return 0; 02596 } 02597 02601 void 02602 AttributeRemoveFromList(AttributeListType *list, char *name) 02603 { 02604 int i, j; 02605 for (i=0; i<list->Number; i++) 02606 if (strcmp (name, list->List[i].name) == 0) 02607 { 02608 free (list->List[i].name); 02609 free (list->List[i].value); 02610 for (j=i; j<list->Number-1; j++) 02611 list->List[j] = list->List[j+1]; 02612 list->Number --; 02613 } 02614 } 02615 02622 const char * 02623 c_dtostr (double d) 02624 { 02625 static char buf[100]; 02626 int i, f; 02627 char *bufp = buf; 02628 02629 if (d < 0) 02630 { 02631 *bufp++ = '-'; 02632 d = -d; 02633 } 02634 d += 0.0000005; /* rounding */ 02635 i = floor (d); 02636 d -= i; 02637 sprintf (bufp, "%d", i); 02638 bufp += strlen (bufp); 02639 *bufp++ = '.'; 02640 02641 f = floor (d * 1000000.0); 02642 sprintf (bufp, "%06d", f); 02643 return buf; 02644 } 02645 02646 void 02647 r_delete_element (DataType * data, ElementType * element) 02648 { 02649 r_delete_entry (data->element_tree, (BoxType *) element); 02650 PIN_LOOP (element); 02651 { 02652 r_delete_entry (data->pin_tree, (BoxType *) pin); 02653 } 02654 END_LOOP; 02655 PAD_LOOP (element); 02656 { 02657 r_delete_entry (data->pad_tree, (BoxType *) pad); 02658 } 02659 END_LOOP; 02660 ELEMENTTEXT_LOOP (element); 02661 { 02662 r_delete_entry (data->name_tree[n], (BoxType *) text); 02663 } 02664 END_LOOP; 02665 } 02666 02667 02674 char * 02675 GetInfoString (void) 02676 { 02677 HID **hids; 02678 int i; 02679 static DynamicStringType info; 02680 static int first_time = 1; 02681 02682 #define TAB " " 02683 02684 if (first_time) 02685 { 02686 first_time = 0; 02687 DSAddString (&info, 02688 _("This is PCB, an interactive\n" 02689 "printed circuit board editor\n" 02690 "version ")); 02691 DSAddString (&info, 02692 VERSION "\n\n" 02693 "Compiled on " __DATE__ " at " __TIME__ "\n\n" 02694 "by harry eaton\n\n" 02695 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n" 02696 "Copyright (C) harry eaton 1998-2007\n" 02697 "Copyright (C) C. Scott Ananian 2001\n" 02698 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n" 02699 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n"); 02700 DSAddString (&info, 02701 _("It is licensed under the terms of the GNU\n" 02702 "General Public License version 2\n" 02703 "See the LICENSE file for more information\n\n" 02704 "For more information see:\n")); 02705 DSAddString (&info, _("PCB homepage: ")); 02706 DSAddString (&info, "http://pcb.geda-project.org\n"); 02707 DSAddString (&info, _("gEDA homepage: ")); 02708 DSAddString (&info, "http://www.geda-project.org\n"); 02709 DSAddString (&info, _("gEDA Wiki: ")); 02710 DSAddString (&info, "http://wiki.geda-project.org\n"); 02711 02712 DSAddString (&info, _("\n----- Compile Time Options -----\n")); 02713 hids = hid_enumerate (); 02714 DSAddString (&info, _("GUI:\n")); 02715 for (i = 0; hids[i]; i++) 02716 { 02717 if (hids[i]->gui) 02718 { 02719 DSAddString (&info, TAB); 02720 DSAddString (&info, hids[i]->name); 02721 DSAddString (&info, " : "); 02722 DSAddString (&info, hids[i]->description); 02723 DSAddString (&info, "\n"); 02724 } 02725 } 02726 02727 DSAddString (&info, _("Exporters:\n")); 02728 for (i = 0; hids[i]; i++) 02729 { 02730 if (hids[i]->exporter) 02731 { 02732 DSAddString (&info, TAB); 02733 DSAddString (&info, hids[i]->name); 02734 DSAddString (&info, " : "); 02735 DSAddString (&info, hids[i]->description); 02736 DSAddString (&info, "\n"); 02737 } 02738 } 02739 02740 DSAddString (&info, _("Printers:\n")); 02741 for (i = 0; hids[i]; i++) 02742 { 02743 if (hids[i]->printer) 02744 { 02745 DSAddString (&info, TAB); 02746 DSAddString (&info, hids[i]->name); 02747 DSAddString (&info, " : "); 02748 DSAddString (&info, hids[i]->description); 02749 DSAddString (&info, "\n"); 02750 } 02751 } 02752 } 02753 #undef TAB 02754 02755 return info.Data; 02756 } 02757 02758 #ifdef MKDIR_IS_PCBMKDIR 02759 #error "Don't know how to create a directory on this system." 02760 #endif 02761 02766 int 02767 pcb_mkdir (const char *path, int mode) 02768 { 02769 return MKDIR (path, mode); 02770 } 02771 02783 int 02784 ElementOrientation (ElementType *e) 02785 { 02786 Coord pin1x, pin1y, pin2x, pin2y, dx, dy; 02787 bool found_pin1 = 0; 02788 bool found_pin2 = 0; 02789 02790 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */ 02791 pin1x = 0; 02792 pin1y = 0; 02793 pin2x = 0; 02794 pin2y = 0; 02795 02796 PIN_LOOP (e); 02797 { 02798 if (NSTRCMP (pin->Number, "1") == 0) 02799 { 02800 pin1x = pin->X; 02801 pin1y = pin->Y; 02802 found_pin1 = 1; 02803 } 02804 else if (NSTRCMP (pin->Number, "2") == 0) 02805 { 02806 pin2x = pin->X; 02807 pin2y = pin->Y; 02808 found_pin2 = 1; 02809 } 02810 } 02811 END_LOOP; 02812 02813 PAD_LOOP (e); 02814 { 02815 if (NSTRCMP (pad->Number, "1") == 0) 02816 { 02817 pin1x = (pad->Point1.X + pad->Point2.X) / 2; 02818 pin1y = (pad->Point1.Y + pad->Point2.Y) / 2; 02819 found_pin1 = 1; 02820 } 02821 else if (NSTRCMP (pad->Number, "2") == 0) 02822 { 02823 pin2x = (pad->Point1.X + pad->Point2.X) / 2; 02824 pin2y = (pad->Point1.Y + pad->Point2.Y) / 2; 02825 found_pin2 = 1; 02826 } 02827 } 02828 END_LOOP; 02829 02830 if (found_pin1 && found_pin2) 02831 { 02832 dx = pin2x - pin1x; 02833 dy = pin2y - pin1y; 02834 } 02835 else if (found_pin1 && (pin1x || pin1y)) 02836 { 02837 dx = pin1x; 02838 dy = pin1y; 02839 } 02840 else if (found_pin2 && (pin2x || pin2y)) 02841 { 02842 dx = pin2x; 02843 dy = pin2y; 02844 } 02845 else 02846 return 0; 02847 02848 if (abs(dx) > abs(dy)) 02849 return dx > 0 ? 0 : 2; 02850 return dy > 0 ? 3 : 1; 02851 } 02852 02853 int 02854 ActionListRotations(int argc, char **argv, Coord x, Coord y) 02855 { 02856 ELEMENT_LOOP (PCB->Data); 02857 { 02858 printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element)); 02859 } 02860 END_LOOP; 02861 02862 return 0; 02863 } 02864 02865 HID_Action misc_action_list[] = { 02866 {"ListRotations", 0, ActionListRotations, 02867 0,0}, 02868 }; 02869 02870 REGISTER_ACTIONS (misc_action_list)