mirror of
git://projects.qi-hardware.com/fped.git
synced 2025-01-05 16:00:15 +02:00
Moved fped over to /trunk/eda
git-svn-id: http://svn.openmoko.org/trunk/eda/fped@5374 99fdad57-331a-0410-800a-d7fa5415bdb3
This commit is contained in:
commit
e455b37abb
113
Makefile
Normal file
113
Makefile
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#
|
||||||
|
# Makefile - Makefile of fped, the footprint editor
|
||||||
|
#
|
||||||
|
# Written 2009 by Werner Almesberger
|
||||||
|
# Copyright 2009 by Werner Almesberger
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
|
||||||
|
OBJS = fped.o expr.o coord.o obj.o inst.o util.o error.o \
|
||||||
|
unparse.o \
|
||||||
|
cpp.o lex.yy.o y.tab.o \
|
||||||
|
gui.o gui_util.o gui_style.o gui_inst.o gui_status.o gui_canvas.o \
|
||||||
|
gui_icons.o
|
||||||
|
|
||||||
|
XPMS = point.xpm vec.xpm frame.xpm \
|
||||||
|
line.xpm rect.xpm pad.xpm circ.xpm arc.xpm meas.xpm
|
||||||
|
|
||||||
|
CFLAGS_GTK = `pkg-config --cflags gtk+-2.0`
|
||||||
|
LIBS_GTK = `pkg-config --libs gtk+-2.0`
|
||||||
|
|
||||||
|
CFLAGS_WARN=-Wall -Wshadow -Wmissing-prototypes \
|
||||||
|
-Wmissing-declarations
|
||||||
|
CFLAGS=-g $(CFLAGS_GTK) -DCPP='"cpp"' $(CFLAGS_WARN)
|
||||||
|
SLOPPY=-Wno-unused -Wno-implicit-function-declaration -Wno-missing-prototypes \
|
||||||
|
-Wno-missing-declarations
|
||||||
|
LDLIBS = -lm -lfl $(LIBS_GTK)
|
||||||
|
YACC=bison -y
|
||||||
|
YYFLAGS=-v
|
||||||
|
|
||||||
|
# ----- Verbosity control -----------------------------------------------------
|
||||||
|
|
||||||
|
CPP := $(CPP) # make sure changing CC won't affect CPP
|
||||||
|
|
||||||
|
CC_normal := $(CC)
|
||||||
|
YACC_normal := $(YACC)
|
||||||
|
LEX_normal := $(LEX)
|
||||||
|
DEPEND_normal = \
|
||||||
|
$(CPP) $(CFLAGS) -MM -MG *.c >.depend || \
|
||||||
|
{ rm -f .depend; exit 1; }
|
||||||
|
|
||||||
|
CC_quiet = @echo " CC " $@ && $(CC_normal)
|
||||||
|
YACC_quiet = @echo " YACC " $@ && $(YACC_normal)
|
||||||
|
LEX_quiet = @echo " LEX " $@ && $(LEX_normal)
|
||||||
|
GEN_quiet = @echo " GENERATE " $@ &&
|
||||||
|
DEPEND_quiet = @echo " DEPENDENCIES" && $(DEPEND_normal)
|
||||||
|
|
||||||
|
ifeq ($(V),1)
|
||||||
|
CC = $(CC_normal)
|
||||||
|
LEX = $(LEX_normal)
|
||||||
|
YACC = $(YACC_normal)
|
||||||
|
GEN =
|
||||||
|
DEPEND = $(DEPEND_normal)
|
||||||
|
else
|
||||||
|
CC = $(CC_quiet)
|
||||||
|
LEX = $(LEX_quiet)
|
||||||
|
YACC = $(YACC_quiet)
|
||||||
|
GEN = $(GEN_quiet)
|
||||||
|
DEPEND = $(DEPEND_quiet)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# ----- Rules -----------------------------------------------------------------
|
||||||
|
|
||||||
|
.PHONY: all dep depend clean
|
||||||
|
|
||||||
|
.SUFFIXES: .fig .xpm
|
||||||
|
|
||||||
|
# generate 26x26 pixels icons, then drop the 1-pixel frame
|
||||||
|
|
||||||
|
# this adds a magenta border
|
||||||
|
# sed '/2 2 0 1 /{s//2 2 0 15 /;s/ 0 7 / 22 7 /;}' $< | \
|
||||||
|
|
||||||
|
.fig.xpm:
|
||||||
|
fig2dev -L xpm -Z 0.32 -S 4 $< | \
|
||||||
|
convert -crop 24x24+1+1 - - | \
|
||||||
|
sed s/xpm__/xpm_`basename $@ .xpm`/ >$@
|
||||||
|
|
||||||
|
all: fped
|
||||||
|
|
||||||
|
fped: $(OBJS)
|
||||||
|
$(CC) -o $@ $(OBJS) $(LDLIBS)
|
||||||
|
|
||||||
|
lex.yy.c: fpd.l y.tab.h
|
||||||
|
$(LEX) fpd.l
|
||||||
|
|
||||||
|
lex.yy.o: lex.yy.c y.tab.h
|
||||||
|
$(CC) -c $(CFLAGS) $(SLOPPY) lex.yy.c
|
||||||
|
|
||||||
|
y.tab.c y.tab.h: fpd.y
|
||||||
|
$(YACC) $(YYFLAGS) -d fpd.y
|
||||||
|
|
||||||
|
y.tab.o: y.tab.c
|
||||||
|
$(CC) -c $(CFLAGS) $(SLOPPY) y.tab.c
|
||||||
|
|
||||||
|
gui_icons.o: $(XPMS:%=icons/%)
|
||||||
|
|
||||||
|
# ----- Dependencies ----------------------------------------------------------
|
||||||
|
|
||||||
|
dep depend .depend: lex.yy.c y.tab.h y.tab.c
|
||||||
|
$(DEPEND)
|
||||||
|
|
||||||
|
ifeq (.depend,$(wildcard .depend))
|
||||||
|
include .depend
|
||||||
|
endif
|
||||||
|
|
||||||
|
# ----- Cleanup ---------------------------------------------------------------
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS) $(XPMS:%=icons/%)
|
||||||
|
rm -f lex.yy.c y.tab.c y.tab.h y.output .depend
|
339
README
Normal file
339
README
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
fped - Footprint editor
|
||||||
|
=======================
|
||||||
|
|
||||||
|
fped is an editor that allows the interactive creation of footprints of
|
||||||
|
electronic components. Footprint definitions are stored in a text format
|
||||||
|
that resembles a programming language.
|
||||||
|
|
||||||
|
The language is constrained such that anything that can be expressed in
|
||||||
|
the textual definition also has a straightforward equivalent operation
|
||||||
|
that can be performed through the GUI.
|
||||||
|
|
||||||
|
|
||||||
|
Motivation
|
||||||
|
----------
|
||||||
|
|
||||||
|
KiCad already includes a footprint ("module") editor, so why do we need
|
||||||
|
a new one ? The issue with footprint generation for KiCad is that the
|
||||||
|
built-in module editor is basically a drawing program that only captures
|
||||||
|
the result of the module author's interpretation of a footprint drawing,
|
||||||
|
but does not include the steps that led to this construction.
|
||||||
|
|
||||||
|
Furthermore, accurate measuring of dimensions in the drawing can only be
|
||||||
|
done manually in the module editor, which makes review difficult and
|
||||||
|
time-consuming.
|
||||||
|
|
||||||
|
In fped, the construction process is made explicit and each step can be
|
||||||
|
expressed in terms of the parameters that appear in the vendor's
|
||||||
|
drawing. Dimensions can be explicitly measured and the results can be
|
||||||
|
included in the graphical output generated by fped.
|
||||||
|
|
||||||
|
Directly using parameters and construction steps from the reference
|
||||||
|
drawing reduces the risk of mistakes. Visualizing the construction
|
||||||
|
process and verificative measurements helps efficient and accurate
|
||||||
|
review.
|
||||||
|
|
||||||
|
|
||||||
|
Footprint definition file format
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Footprint definitions are stored in text files. The program "fped" reads
|
||||||
|
and (soon) writes such files, visualizes their content, and provides a
|
||||||
|
graphical editor for them.
|
||||||
|
|
||||||
|
The syntax is unique and draws from elements of a variety of languages
|
||||||
|
commonly found on unix systems. One specialty is that there are no
|
||||||
|
reserved words - the language keywords appear only at the beginning of
|
||||||
|
a line and can thus be recognized as such without restricting their use
|
||||||
|
for identifiers. This reduces the risk of creating incompatibilities
|
||||||
|
with existing designs when introduction future language features.
|
||||||
|
|
||||||
|
fped uses the C preprocessor for comments, conditional compilation,
|
||||||
|
and - to a limited extent - also macros. Long lines can be split by
|
||||||
|
ending them with a backslash.
|
||||||
|
|
||||||
|
|
||||||
|
Geometry model
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The geometry model consists of frames, vectors, and objects. The shape of
|
||||||
|
objects is defined by a number of points. These points are produced by
|
||||||
|
concatenating vectors.
|
||||||
|
|
||||||
|
E.g., to draw a line from (1mm, 1mm) to (2mm, 2mm), one would make a
|
||||||
|
vector from the origin to (1mm, 1mm) and one either from the origin or
|
||||||
|
from the previous vector to (2mm, 2mm), and then make a line connecting
|
||||||
|
the two points.
|
||||||
|
|
||||||
|
|
||||||
|
Units
|
||||||
|
- - -
|
||||||
|
|
||||||
|
fped can calculate in mm and mil. Units are specified by following a
|
||||||
|
number with "mm" or "mil", separated by zero or more spaces or tabs.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
1mm
|
||||||
|
2 mil
|
||||||
|
|
||||||
|
Units can be mixed in calculations, e.g.,
|
||||||
|
|
||||||
|
set a = 1mm+20mil
|
||||||
|
set b = 10*1mm
|
||||||
|
|
||||||
|
All values used as dimensions must be either mm or mil.
|
||||||
|
|
||||||
|
|
||||||
|
Vectors
|
||||||
|
- - - -
|
||||||
|
|
||||||
|
Vectors can be anonymous or they can be named for future reference:
|
||||||
|
|
||||||
|
vec <base> ( <x-expr>, <y-expr> )
|
||||||
|
<identifier>: vec <base> ( <x-expr>, <y-expr> )
|
||||||
|
|
||||||
|
The base can be one of the following items:
|
||||||
|
|
||||||
|
- @ is the origin of the frame containing the vector
|
||||||
|
- . is the end of the previous vector in this frame
|
||||||
|
- <identifier> is the name of a previous vector in the same frame
|
||||||
|
|
||||||
|
The following example would draw the line described in the previous
|
||||||
|
section:
|
||||||
|
|
||||||
|
a: vec @(1mm, 1mm)
|
||||||
|
b: vec .(1mm, 1mm)
|
||||||
|
line a b
|
||||||
|
|
||||||
|
|
||||||
|
Silk screen objects
|
||||||
|
- - - - - - - - - -
|
||||||
|
|
||||||
|
The output of fped is a footprint definition that contains pads and silk
|
||||||
|
screen drawings (we may add more layers in the future). These items are
|
||||||
|
called "objects". Their geometry is defined through points obtained with
|
||||||
|
vectors.
|
||||||
|
|
||||||
|
A line connects two points:
|
||||||
|
|
||||||
|
line <point-a> <point-b> [<width>]
|
||||||
|
|
||||||
|
The points can be specified with @, ., and an identifier, just like
|
||||||
|
a vector base. The option width specifies the thickness of the silk
|
||||||
|
screen line. If omitted, a hard-coded default of 15 mil is used.
|
||||||
|
|
||||||
|
A rectangle has sides parallel to the x and y axis and is defined
|
||||||
|
by two diagonally opposite corners:
|
||||||
|
|
||||||
|
rect <point-a> <point-b> [<width>]
|
||||||
|
|
||||||
|
A circle is defined by its center and a point on the circle:
|
||||||
|
|
||||||
|
circ <center> <point> [<width>]
|
||||||
|
|
||||||
|
This example draws a unit circle:
|
||||||
|
|
||||||
|
vec @(1mm, 0mm)
|
||||||
|
circ @ .
|
||||||
|
|
||||||
|
An arc is like a circle, but the part of the circle drawn is determined
|
||||||
|
by two points. The first point determines the radius and the starting
|
||||||
|
angle. The second point only determines the end angle but its distance
|
||||||
|
from the center is ignored.
|
||||||
|
|
||||||
|
arc <center> <radius> <end> [<width>]
|
||||||
|
|
||||||
|
The arc is drawn in a counter-clockwise direction. The following example
|
||||||
|
draws an arc of the unit circle in the x > 0, y > 0 quadrant:
|
||||||
|
|
||||||
|
from: vec @(1mm, 0mm)
|
||||||
|
to: vec @(0mm, 1mm)
|
||||||
|
arc @ from to
|
||||||
|
|
||||||
|
|
||||||
|
Pads
|
||||||
|
- -
|
||||||
|
|
||||||
|
Pads are similar to rectangles, but they also have a name.
|
||||||
|
|
||||||
|
pad "<name>" <point-a> <point-b>
|
||||||
|
|
||||||
|
Variables can be expanded in a pad's name by prefixing their name with
|
||||||
|
a dollar sign. The ${name} syntax is also available.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
vec @(1mm, 1mm)
|
||||||
|
pad "1" @ .
|
||||||
|
|
||||||
|
|
||||||
|
Measurements
|
||||||
|
- - - - - -
|
||||||
|
|
||||||
|
Measurements show the distance between two points:
|
||||||
|
|
||||||
|
meas <point-a> <point-b> <offset>
|
||||||
|
|
||||||
|
The offset is the distance from the imaginary line connecting points A
|
||||||
|
and B the measurement line is draw:
|
||||||
|
|
||||||
|
- if the offset is 0mm, the line will connect A and B
|
||||||
|
- if the offset is positive, the line would be on the left-hand side when
|
||||||
|
traveling from A to B
|
||||||
|
- if the offset is negative , the line would be on the right-hand side when
|
||||||
|
traveling from A to B
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
a: vec @(-1mm, 1mm)
|
||||||
|
b: vec @(1mm, 1mm)
|
||||||
|
meas a b 0.2 mm
|
||||||
|
|
||||||
|
|
||||||
|
Frames
|
||||||
|
- - -
|
||||||
|
|
||||||
|
Frames are used to group things and to reuse them multiple times. Frames
|
||||||
|
must be defined before they can be used:
|
||||||
|
|
||||||
|
frame <name> {
|
||||||
|
... items ...
|
||||||
|
}
|
||||||
|
|
||||||
|
Once defined, a frame is placed at a given location with
|
||||||
|
|
||||||
|
frame <name> <point>
|
||||||
|
|
||||||
|
The frame definitions must precede all other items in a footprint
|
||||||
|
description. Frames cannot be defined inside other frames, but frames
|
||||||
|
can invoke each other recursively.
|
||||||
|
|
||||||
|
For example, this puts two unity squares, one centered at (0 mm, 0 mm),
|
||||||
|
the other at (2 mm, 0 mm):
|
||||||
|
|
||||||
|
frame unit_square {
|
||||||
|
a: vec @(-0.5mm, -0.5mm)
|
||||||
|
b: vec .(1mm, 1mm)
|
||||||
|
rect a b
|
||||||
|
}
|
||||||
|
|
||||||
|
frame unit_square @
|
||||||
|
vec @(2mm, 0mm)
|
||||||
|
frame unit_square .
|
||||||
|
|
||||||
|
|
||||||
|
Names and variables
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
fped uses several name spaces:
|
||||||
|
|
||||||
|
- frame names occupy one global name space
|
||||||
|
|
||||||
|
- vector names occupy name spaces delimited by the frame they're
|
||||||
|
contained in. A vector name is only visible inside the frame in which
|
||||||
|
it is defined.
|
||||||
|
|
||||||
|
- variable names occupy name spaces delimited by the frame they're
|
||||||
|
contained in. A variable lookup starts in the frame in which the
|
||||||
|
corresponding expression appears and propagates to outer frames
|
||||||
|
until the variable is found.
|
||||||
|
|
||||||
|
- pads occupy one global name space (this is currently not enforced)
|
||||||
|
|
||||||
|
Note that names cannot be redefined. E.g., this does not work:
|
||||||
|
|
||||||
|
set a = 1
|
||||||
|
set a = a+1
|
||||||
|
|
||||||
|
The names spaces of frames, vectors, variables, and pads are separate
|
||||||
|
from each other.
|
||||||
|
|
||||||
|
|
||||||
|
Simple variables
|
||||||
|
- - - - - - - -
|
||||||
|
|
||||||
|
A variable with a single value is defined with the following
|
||||||
|
assignment syntax:
|
||||||
|
|
||||||
|
set <identifier> = <expression>
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
set a = b+2
|
||||||
|
|
||||||
|
|
||||||
|
Loops
|
||||||
|
- - -
|
||||||
|
|
||||||
|
A loop is a variable with a range of values:
|
||||||
|
|
||||||
|
loop <identifier> = <from>, <to>
|
||||||
|
|
||||||
|
The variable assumes all the values i for <from> <= i <= <to>, in
|
||||||
|
increments of one. E.g.,
|
||||||
|
|
||||||
|
loop n = 1, 3
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
loop n = 1, 3
|
||||||
|
|
||||||
|
both assigns the values 1, 2, and 3 to the variable "n".
|
||||||
|
|
||||||
|
When a loop is executed, the objects contained in the body of the
|
||||||
|
enclosing frame are generated for each value of the variable. If
|
||||||
|
a frame contains multiple loops, all possible combinations of the
|
||||||
|
values are generated.
|
||||||
|
|
||||||
|
The following example draws three concentric circles around the
|
||||||
|
origin, with radii 1, 2, and 3:
|
||||||
|
|
||||||
|
loop x = 1, 3
|
||||||
|
vec @(x*1mm, 0mm)
|
||||||
|
circ @ .
|
||||||
|
|
||||||
|
|
||||||
|
Tables
|
||||||
|
- - -
|
||||||
|
|
||||||
|
Tables combine values for multiple variables. Like loops, they are
|
||||||
|
used to iteratively generate objects. A table begins with a row of
|
||||||
|
variable names, followed by one or more rows with values. Rows are
|
||||||
|
enclosed in curly braces and their elements are separated by commas.
|
||||||
|
|
||||||
|
table
|
||||||
|
{ <identifier>, ... }
|
||||||
|
{ <expression>, ... }
|
||||||
|
...
|
||||||
|
|
||||||
|
Like loops, tables are iterated to generate objects. The following
|
||||||
|
example is equivalent to the one in the previous section:
|
||||||
|
|
||||||
|
table
|
||||||
|
{ x }
|
||||||
|
{ 1mm }
|
||||||
|
{ 2mm }
|
||||||
|
{ 3mm }
|
||||||
|
vec @(x, 0mm)
|
||||||
|
circ @ .
|
||||||
|
|
||||||
|
Note that we can set the unit of the values directly in this case.
|
||||||
|
|
||||||
|
Iteration is performed over rows. All variables of the table are set
|
||||||
|
to the value in the respective row at the same time. For example, in
|
||||||
|
|
||||||
|
table
|
||||||
|
{ x, y }
|
||||||
|
{ 1, 2 }
|
||||||
|
{ 3, 4 }
|
||||||
|
|
||||||
|
(x, y) assume the values (1, 2) and (3, 4).
|
||||||
|
|
||||||
|
|
||||||
|
Expressions
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Expressions can contain numeric constants (in non-exponential notation),
|
||||||
|
variable names, the arithmetic operations +, -, *, /, and unary -.
|
||||||
|
Parentheses can be used to change precedence.
|
54
TODO
Normal file
54
TODO
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
Missing features:
|
||||||
|
- add row selection
|
||||||
|
- populate input area (still needed: mm/mil, rezoom)
|
||||||
|
- add vec editor (need to be able to edit name, x, and y)
|
||||||
|
- add obj editor
|
||||||
|
- add table/var/loop editor (missing: add col/row, add/del var/table/loop)
|
||||||
|
- add default unit (combine with grid unit selection ?)
|
||||||
|
- consider adding auto/mm/mil selection for each dimension
|
||||||
|
- add measurements (partly done. still needed: find out how to define
|
||||||
|
non-trivial endpoints, e.g., some vector in last iteration of loop)
|
||||||
|
- add KiCad output
|
||||||
|
- add postscript output
|
||||||
|
- add option to include/omit helper vecs and frames (display and postscript)
|
||||||
|
|
||||||
|
Error detection:
|
||||||
|
- eliminate duplicate instances
|
||||||
|
|
||||||
|
Style:
|
||||||
|
- make column of entry field greedily consume all unallocated space
|
||||||
|
|
||||||
|
Bugs:
|
||||||
|
- default silk width has no business being hard-coded in obj.c
|
||||||
|
|
||||||
|
Code cleanup:
|
||||||
|
- merge edit_unique with edit_name
|
||||||
|
- merge find_var_in_frame with similar mechanisms in expr.c and fpd.y
|
||||||
|
- add regression tests
|
||||||
|
|
||||||
|
Open decisions:
|
||||||
|
- decide on table presentation (merge frame and vars into single entity ?)
|
||||||
|
- Q: should loop be (start, last) or (start, iterations) ? or start ... last ?
|
||||||
|
- change vector circle color ? (also, highlight on hover ?)
|
||||||
|
- Q: allow reassignment of vector names ?
|
||||||
|
A1: no: would cause confusion in GUI (vectors could become orphaned)
|
||||||
|
A2: yes. but we don't change the linkage.
|
||||||
|
- Q: how do we handle stacks of objects ?
|
||||||
|
A: we don't but we make it easy to avoid them, by giving a good zoom,
|
||||||
|
flexible selection, and by disallowing stacks of identical objects in the
|
||||||
|
first place.
|
||||||
|
- Q: add frame arguments ? (e.g., .frame pad(pin_num_offset) ...)
|
||||||
|
we can already approximate this by introducing an intermediate table that
|
||||||
|
sets up the arguments (provided that we don't consider vectors as well)
|
||||||
|
- Q: should we make it a requirement to generate objects only once ?
|
||||||
|
A: almost certainly yes.
|
||||||
|
|
||||||
|
Future directions:
|
||||||
|
- future: consider using cairo instead of gdk
|
||||||
|
- live update of value when entering strings and expressions ?
|
||||||
|
- advanced: non-standard solder mask
|
||||||
|
- advanced: solder paste exceptions (subtractive, additive)
|
||||||
|
- advanced: silk line width
|
||||||
|
- future: when encountering an error after a change, we could try to find the
|
||||||
|
same element in the old instance, and select it
|
||||||
|
- future: consider editing off-canvas items in place
|
164
coord.c
Normal file
164
coord.c
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* coord.c - Coordinate representation and basic operations
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "coord.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- unit conversion --------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
double mm_to_mil(double mm, int exponent)
|
||||||
|
{
|
||||||
|
return mm*pow(MIL_IN_MM, -exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double mil_to_mm(double mil, int exponent)
|
||||||
|
{
|
||||||
|
return mil*pow(MIL_IN_MM, exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----- vector operations ------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
struct coord normalize(struct coord v, unit_type len)
|
||||||
|
{
|
||||||
|
double f;
|
||||||
|
|
||||||
|
f = len/hypot(v.x, v.y);
|
||||||
|
v.x *= f;
|
||||||
|
v.y *= f;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct coord rotate(struct coord v, double angle)
|
||||||
|
{
|
||||||
|
double rad = M_PI*angle/180.0;
|
||||||
|
struct coord res;
|
||||||
|
|
||||||
|
res.x = v.x*cos(rad)-v.y*sin(rad);
|
||||||
|
res.y = v.y*cos(rad)+v.x*sin(rad);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct coord add_vec(struct coord a, struct coord b)
|
||||||
|
{
|
||||||
|
a.x += b.x;
|
||||||
|
a.y += b.y;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct coord sub_vec(struct coord a, struct coord b)
|
||||||
|
{
|
||||||
|
a.x -= b.x;
|
||||||
|
a.y -= b.y;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct coord neg_vec(struct coord v)
|
||||||
|
{
|
||||||
|
v.x = -v.x;
|
||||||
|
v.y = -v.y;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- distance calculations --------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
unit_type dist_point(struct coord a, struct coord b)
|
||||||
|
{
|
||||||
|
return hypot(a.x-b.x, a.y-b.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unit_type dist_line_xy(unit_type px, unit_type py,
|
||||||
|
unit_type ax, unit_type ay, unit_type bx, unit_type by)
|
||||||
|
{
|
||||||
|
unit_type d_min, d;
|
||||||
|
double a, f;
|
||||||
|
|
||||||
|
d_min = hypot(ax-px, ay-py);
|
||||||
|
d = hypot(bx-px, by-py);
|
||||||
|
if (d < d_min)
|
||||||
|
d_min = d;
|
||||||
|
if (ax != bx || ay != by) {
|
||||||
|
/*
|
||||||
|
* We make a the line vector from point B and b the vector from
|
||||||
|
* B to point P. Then we calculate the projection of b on a.
|
||||||
|
*/
|
||||||
|
ax -= bx;
|
||||||
|
ay -= by;
|
||||||
|
bx = px-bx;
|
||||||
|
by = py-by;
|
||||||
|
a = hypot(ax, ay);
|
||||||
|
f = ((double) ax*bx+(double) ay*by)/a/a;
|
||||||
|
if (f >= 0 && f <= 1) {
|
||||||
|
bx -= f*ax;
|
||||||
|
by -= f*ay;
|
||||||
|
d = hypot(bx, by);
|
||||||
|
if (d < d_min)
|
||||||
|
d_min = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unit_type dist_line(struct coord p, struct coord a, struct coord b)
|
||||||
|
{
|
||||||
|
return dist_line_xy(p.x, p.y, a.x, a.y, b.x, b.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unit_type dist_rect(struct coord p, struct coord min, struct coord max)
|
||||||
|
{
|
||||||
|
unit_type d_min, d;
|
||||||
|
|
||||||
|
d_min = dist_line_xy(p.x, p.y, min.x, min.y, max.x, min.y);
|
||||||
|
d = dist_line_xy(p.x, p.y, min.x, min.y, min.x, max.y);
|
||||||
|
if (d < d_min)
|
||||||
|
d_min = d;
|
||||||
|
d = dist_line_xy(p.x, p.y, min.x, max.y, max.x, max.y);
|
||||||
|
if (d < d_min)
|
||||||
|
d_min = d;
|
||||||
|
d = dist_line_xy(p.x, p.y, max.x, min.y, max.x, max.y);
|
||||||
|
if (d < d_min)
|
||||||
|
d_min = d;
|
||||||
|
return d_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int inside_rect(struct coord p, struct coord min, struct coord max)
|
||||||
|
{
|
||||||
|
if (p.x < min.x || p.x > max.x)
|
||||||
|
return 0;
|
||||||
|
if (p.y < min.y || p.y > max.y)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unit_type dist_circle(struct coord p, struct coord c, unit_type r)
|
||||||
|
{
|
||||||
|
unit_type d;
|
||||||
|
|
||||||
|
d = hypot(p.x-c.x, p.y-c.y);
|
||||||
|
return fabs(d-r);
|
||||||
|
}
|
78
coord.h
Normal file
78
coord.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* coord.h - Coordinate representation and basic operations
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COORD_H
|
||||||
|
#define COORD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define MICRON_UNITS 10
|
||||||
|
#define MIL_UNITS (25.4*MICRON_UNITS)
|
||||||
|
#define MM_UNITS (1000.0*MICRON_UNITS)
|
||||||
|
#define KICAD_UNIT (10.0*MIL_UNITS)
|
||||||
|
|
||||||
|
#define MIL_IN_MM 0.0254
|
||||||
|
|
||||||
|
|
||||||
|
typedef int32_t unit_type;
|
||||||
|
|
||||||
|
|
||||||
|
#define UNIT_ERROR ((unit_type) 1 << (sizeof(unit_type)*8-1))
|
||||||
|
|
||||||
|
|
||||||
|
struct coord {
|
||||||
|
unit_type x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static inline unit_type mil_to_units(double mil)
|
||||||
|
{
|
||||||
|
return mil*MIL_UNITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline unit_type mm_to_units(double mm)
|
||||||
|
{
|
||||||
|
return mm*MM_UNITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline double units_to_mm(unit_type u)
|
||||||
|
{
|
||||||
|
return (double) u/MM_UNITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline double units_to_kicad(unit_type u)
|
||||||
|
{
|
||||||
|
return (double) u/KICAD_UNIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double mm_to_mil(double mm, int exponent);
|
||||||
|
double mil_to_mm(double mil, int exponent);
|
||||||
|
|
||||||
|
struct coord normalize(struct coord v, unit_type len);
|
||||||
|
struct coord rotate(struct coord v, double angle);
|
||||||
|
struct coord add_vec(struct coord a, struct coord b);
|
||||||
|
struct coord sub_vec(struct coord a, struct coord b);
|
||||||
|
struct coord neg_vec(struct coord v);
|
||||||
|
|
||||||
|
unit_type dist_point(struct coord a, struct coord b);
|
||||||
|
unit_type dist_line(struct coord p, struct coord a, struct coord b);
|
||||||
|
unit_type dist_rect(struct coord p, struct coord min, struct coord max);
|
||||||
|
int inside_rect(struct coord p, struct coord min, struct coord max);
|
||||||
|
unit_type dist_circle(struct coord p, struct coord c, unit_type r);
|
||||||
|
|
||||||
|
#endif /* !COORD_H */
|
217
cpp.c
Normal file
217
cpp.c
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* cpp.c - CPP subprocess
|
||||||
|
*
|
||||||
|
* Written 2002-2004, 2006, 2008 by Werner Almesberger
|
||||||
|
* Copyright 2002, 2003 California Institute of Technology
|
||||||
|
* Copyright 2004, 2006 Werner Almesberger
|
||||||
|
* Copyright 2008 by OpenMoko, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include "cpp.h"
|
||||||
|
|
||||||
|
|
||||||
|
const char *cpp_command = CPP;
|
||||||
|
|
||||||
|
static pid_t cpp_pid;
|
||||||
|
static int cpp_argc = 0;
|
||||||
|
static const char **cpp_argv = NULL;
|
||||||
|
static int real_stdin = -1;
|
||||||
|
|
||||||
|
|
||||||
|
void add_cpp_arg(const char *arg)
|
||||||
|
{
|
||||||
|
if (!cpp_argc)
|
||||||
|
cpp_argc = 1;
|
||||||
|
cpp_argv = realloc(cpp_argv,sizeof(const char *)*(cpp_argc+1));
|
||||||
|
if (!cpp_argv) {
|
||||||
|
perror("realloc");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (cpp_argc == 1)
|
||||||
|
cpp_argv[0] = cpp_command;
|
||||||
|
if (arg) {
|
||||||
|
arg = strdup(arg);
|
||||||
|
if (!arg) {
|
||||||
|
perror("strdup");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cpp_argv[cpp_argc++] = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void add_cpp_Wp(const char *arg)
|
||||||
|
{
|
||||||
|
char *tmp = strdup(arg);
|
||||||
|
char *curr,*end;
|
||||||
|
|
||||||
|
if (!tmp) {
|
||||||
|
perror("strdup");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
curr = tmp;
|
||||||
|
do {
|
||||||
|
end = strchr(curr,',');
|
||||||
|
if (end)
|
||||||
|
*end++ = 0;
|
||||||
|
add_cpp_arg(curr);
|
||||||
|
curr = end;
|
||||||
|
}
|
||||||
|
while (end);
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void kill_cpp(void)
|
||||||
|
{
|
||||||
|
if (cpp_pid)
|
||||||
|
(void) kill(cpp_pid,SIGTERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void run_cpp(const char *name,int fd,int close_fd)
|
||||||
|
{
|
||||||
|
char **arg;
|
||||||
|
int fds[2];
|
||||||
|
|
||||||
|
if (pipe(fds) < 0) {
|
||||||
|
perror("pipe");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (name)
|
||||||
|
add_cpp_arg(name);
|
||||||
|
add_cpp_arg(NULL);
|
||||||
|
cpp_pid = fork();
|
||||||
|
if (cpp_pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (!cpp_pid) {
|
||||||
|
if (close(fds[0]) < 0) {
|
||||||
|
perror("close");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (close_fd != -1 && close(close_fd) < 0) {
|
||||||
|
perror("close");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fd != -1 && dup2(fd,0) < 0) {
|
||||||
|
perror("dup2");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (dup2(fds[1],1) < 0) {
|
||||||
|
perror("dup2");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (execvp(cpp_command,(char **) cpp_argv) < 0) {
|
||||||
|
/* prototype is weird */
|
||||||
|
perror(cpp_command);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/* not reached */
|
||||||
|
}
|
||||||
|
if (close(fds[1]) < 0) {
|
||||||
|
perror("close");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
real_stdin = dup(0);
|
||||||
|
if (real_stdin < 0) {
|
||||||
|
perror("dup");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fd != -1 && close(fd) < 0) {
|
||||||
|
perror("close");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (dup2(fds[0],0) < 0) {
|
||||||
|
perror("dup2");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
for (arg = (char **) cpp_argv+1; *arg; arg++)
|
||||||
|
free(*arg);
|
||||||
|
free(cpp_argv);
|
||||||
|
cpp_argv = NULL;
|
||||||
|
cpp_argc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void run_cpp_on_file(const char *name)
|
||||||
|
{
|
||||||
|
run_cpp(name,name ? -1 : 0,-1);
|
||||||
|
atexit(kill_cpp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void run_cpp_on_string(const char *str)
|
||||||
|
{
|
||||||
|
int fds[2];
|
||||||
|
pid_t pid;
|
||||||
|
int left,wrote;
|
||||||
|
|
||||||
|
if (pipe(fds) < 0) {
|
||||||
|
perror("pipe");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
run_cpp(NULL,fds[0],fds[1]);
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (!pid) {
|
||||||
|
for (left = strlen(str); left; left -= wrote) {
|
||||||
|
wrote = write(fds[1],str,left);
|
||||||
|
if (wrote < 0)
|
||||||
|
break; /* die silently */
|
||||||
|
str += wrote;
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (close(fds[1]) < 0) {
|
||||||
|
perror("close");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
atexit(kill_cpp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void reap_cpp(void)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
cpp_pid = 0;
|
||||||
|
if (waitpid(cpp_pid,&status,0) < 0) {
|
||||||
|
perror("waitpid");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (!status) {
|
||||||
|
if (dup2(real_stdin,0) < 0) {
|
||||||
|
perror("dup2");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
exit(WEXITSTATUS(status));
|
||||||
|
if (WIFSIGNALED(status))
|
||||||
|
fprintf(stderr,"cpp terminated with signal %d\n",WTERMSIG(status));
|
||||||
|
else
|
||||||
|
fprintf(stderr,"cpp terminated with incomprehensible status %d\n",
|
||||||
|
status);
|
||||||
|
exit(1);
|
||||||
|
}
|
26
cpp.h
Normal file
26
cpp.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* cpp.h - CPP subprocess
|
||||||
|
*
|
||||||
|
* Written 2002, 2003, 2008 by Werner Almesberger
|
||||||
|
* Copyright 2002, 2003 Caltech Netlab FAST project
|
||||||
|
* Copyright 2008 by OpenMoko, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CPP_H
|
||||||
|
#define CPP_H
|
||||||
|
|
||||||
|
|
||||||
|
extern const char *cpp_command;
|
||||||
|
|
||||||
|
void add_cpp_arg(const char *arg);
|
||||||
|
void add_cpp_Wp(const char *arg);
|
||||||
|
void run_cpp_on_file(const char *name); /* NULL for stdin */
|
||||||
|
void run_cpp_on_string(const char *str);
|
||||||
|
void reap_cpp(void);
|
||||||
|
|
||||||
|
#endif /* CPP_H */
|
76
error.c
Normal file
76
error.c
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* error.c - Error reporting
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern char *yytext;
|
||||||
|
|
||||||
|
int lineno = 1;
|
||||||
|
void (*reporter)(const char *s) = report_to_stderr;
|
||||||
|
|
||||||
|
|
||||||
|
void yyerrorf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char *buf;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
n = vsnprintf(NULL, 0, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
buf = alloc_size(n+1);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(buf, n+1, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fail(buf);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void yyerror(const char *s)
|
||||||
|
{
|
||||||
|
yyerrorf("%s", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void report_parse_error(const char *s)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%d: %s near \"%s\" ", lineno, s, yytext);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void report_to_stderr(const char *s)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", s);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fail(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
s = stralloc_vprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
reporter(s);
|
||||||
|
free(s);
|
||||||
|
}
|
30
error.h
Normal file
30
error.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* error.h - Error reporting
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ERROR_H
|
||||||
|
#define ERROR_H
|
||||||
|
|
||||||
|
|
||||||
|
extern int lineno;
|
||||||
|
|
||||||
|
extern void (*reporter)(const char *s);
|
||||||
|
|
||||||
|
|
||||||
|
void yyerrorf(const char *fmt, ...);
|
||||||
|
void yyerror(const char *s);
|
||||||
|
|
||||||
|
void report_to_stderr(const char *s);
|
||||||
|
void report_parse_error(const char *s);
|
||||||
|
void fail(const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif /* !ERROR_H */
|
423
expr.c
Normal file
423
expr.c
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
/*
|
||||||
|
* expr.c - Expressions and values
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "obj.h"
|
||||||
|
#include "unparse.h"
|
||||||
|
#include "expr.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct num undef = { .type = nt_none };
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- error reporting --------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
void fail_expr(const struct expr *expr)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
s = unparse(expr);
|
||||||
|
fail("in \"%s\" at line %d", s, expr->lineno);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- unit conversion --------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
const char *str_unit(struct num n)
|
||||||
|
{
|
||||||
|
if (n.exponent == 0)
|
||||||
|
return "";
|
||||||
|
if (n.type == nt_mm) {
|
||||||
|
switch (n.exponent) {
|
||||||
|
case -2:
|
||||||
|
return "mm^-2";
|
||||||
|
case -1:
|
||||||
|
return "mm^-1";
|
||||||
|
case 1:
|
||||||
|
return "mm";
|
||||||
|
case 2:
|
||||||
|
return "mm^2";
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n.type == nt_mil) {
|
||||||
|
switch (n.exponent) {
|
||||||
|
case -2:
|
||||||
|
return "mil^(-2)";
|
||||||
|
case -1:
|
||||||
|
return "mil^(-1)";
|
||||||
|
case 1:
|
||||||
|
return "mil";
|
||||||
|
case 2:
|
||||||
|
return "mil^2";
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int to_unit(struct num *n)
|
||||||
|
{
|
||||||
|
if (!is_distance(*n)) {
|
||||||
|
fail("%s^%d is not a distance",
|
||||||
|
n->type == nt_mm ? "mm" : n->type == nt_mil ? "mil" : "?",
|
||||||
|
n->exponent);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
switch (n->type) {
|
||||||
|
case nt_mil:
|
||||||
|
n->n = mil_to_units(n->n);
|
||||||
|
break;
|
||||||
|
case nt_mm:
|
||||||
|
n->n = mm_to_units(n->n);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- primary expressions ----------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
struct num op_num(const struct expr *self, const struct frame *frame)
|
||||||
|
{
|
||||||
|
return self->u.num;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct num eval_var(const struct frame *frame, const char *name)
|
||||||
|
{
|
||||||
|
const struct table *table;
|
||||||
|
const struct loop *loop;
|
||||||
|
const struct value *value;
|
||||||
|
struct var *var;
|
||||||
|
struct num res;
|
||||||
|
|
||||||
|
for (table = frame->tables; table; table = table->next) {
|
||||||
|
value = table->curr_row->values;
|
||||||
|
for (var = table->vars; var; var = var->next) {
|
||||||
|
if (var->name == name) {
|
||||||
|
if (var->visited) {
|
||||||
|
fail("recursive evaluation through "
|
||||||
|
"\"%s\"", name);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
var->visited = 1;
|
||||||
|
res = eval_num(value->expr, frame);
|
||||||
|
var->visited = 0;
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
|
value = value->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (loop = frame->loops; loop; loop = loop->next)
|
||||||
|
if (loop->var.name == name) {
|
||||||
|
if (!loop->initialized) {
|
||||||
|
fail("uninitialized loop \"%s\"", name);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
return make_num(loop->curr_value);
|
||||||
|
}
|
||||||
|
if (frame->curr_parent)
|
||||||
|
return eval_var(frame->curr_parent, name);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct num op_var(const struct expr *self, const struct frame *frame)
|
||||||
|
{
|
||||||
|
struct num res;
|
||||||
|
|
||||||
|
res = eval_var(frame, self->u.var);
|
||||||
|
if (is_undef(res))
|
||||||
|
fail("undefined variable \"%s\"", self->u.var);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- arithmetic -------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static struct num compatible_sum(struct num *a, struct num *b)
|
||||||
|
{
|
||||||
|
struct num res;
|
||||||
|
|
||||||
|
if (a->type != b->type) {
|
||||||
|
if (a->type == nt_mil) {
|
||||||
|
a->type = nt_mm;
|
||||||
|
a->n = mil_to_mm(a->n, a->exponent);
|
||||||
|
}
|
||||||
|
if (b->type == nt_mil) {
|
||||||
|
b->type = nt_mm;
|
||||||
|
b->n = mil_to_mm(b->n, a->exponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a->exponent != b->exponent) {
|
||||||
|
fail("incompatible exponents (%d, %d)",
|
||||||
|
a->exponent, b->exponent);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
res.type = a->type;
|
||||||
|
res.exponent = a->exponent;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct num compatible_mult(struct num *a, struct num *b,
|
||||||
|
int exponent)
|
||||||
|
{
|
||||||
|
struct num res;
|
||||||
|
|
||||||
|
if (a->type != b->type) {
|
||||||
|
if (a->type == nt_mil) {
|
||||||
|
a->type = nt_mm;
|
||||||
|
a->n = mil_to_mm(a->n, a->exponent);
|
||||||
|
}
|
||||||
|
if (b->type == nt_mil) {
|
||||||
|
b->type = nt_mm;
|
||||||
|
b->n = mil_to_mm(b->n, b->exponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.type = a->type;
|
||||||
|
res.exponent = exponent;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct num op_minus(const struct expr *self, const struct frame *frame)
|
||||||
|
{
|
||||||
|
struct num res;
|
||||||
|
|
||||||
|
res = eval_num(self->u.op.a, frame);
|
||||||
|
if (!is_undef(res))
|
||||||
|
res.n = -res.n;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define BINARY \
|
||||||
|
struct num a, b, res; \
|
||||||
|
\
|
||||||
|
a = eval_num(self->u.op.a, frame); \
|
||||||
|
if (is_undef(a)) \
|
||||||
|
return undef; \
|
||||||
|
b = eval_num(self->u.op.b, frame); \
|
||||||
|
if (is_undef(b)) \
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
|
||||||
|
struct num op_add(const struct expr *self, const struct frame *frame)
|
||||||
|
{
|
||||||
|
BINARY;
|
||||||
|
res = compatible_sum(&a, &b);
|
||||||
|
if (is_undef(res))
|
||||||
|
return undef;
|
||||||
|
res.n = a.n+b.n;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct num op_sub(const struct expr *self, const struct frame *frame)
|
||||||
|
{
|
||||||
|
BINARY;
|
||||||
|
res = compatible_sum(&a, &b);
|
||||||
|
if (is_undef(res))
|
||||||
|
return undef;
|
||||||
|
res.n = a.n-b.n;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct num op_mult(const struct expr *self, const struct frame *frame)
|
||||||
|
{
|
||||||
|
BINARY;
|
||||||
|
res = compatible_mult(&a, &b, a.exponent+b.exponent);
|
||||||
|
res.n = a.n*b.n;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct num op_div(const struct expr *self, const struct frame *frame)
|
||||||
|
{
|
||||||
|
BINARY;
|
||||||
|
if (!b.n) {
|
||||||
|
fail("Division by zero");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
res = compatible_mult(&a, &b, a.exponent-b.exponent);
|
||||||
|
res.n = a.n/b.n;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- expression construction ------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
struct expr *new_op(op_type op)
|
||||||
|
{
|
||||||
|
struct expr *expr;
|
||||||
|
|
||||||
|
expr = alloc_type(struct expr);
|
||||||
|
expr->op = op;
|
||||||
|
expr->lineno = lineno;
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct expr *binary_op(op_type op, struct expr *a, struct expr *b)
|
||||||
|
{
|
||||||
|
struct expr *expr;
|
||||||
|
|
||||||
|
expr = new_op(op);
|
||||||
|
expr->u.op.a = a;
|
||||||
|
expr->u.op.b = b;
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *eval_str(const struct frame *frame, const struct expr *expr)
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct num eval_num(const struct expr *expr, const struct frame *frame)
|
||||||
|
{
|
||||||
|
return expr->op(expr, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- string expansion -------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
char *expand(const char *name, const struct frame *frame)
|
||||||
|
{
|
||||||
|
int len = strlen(name);
|
||||||
|
char *buf = alloc_size(len+1);
|
||||||
|
char num_buf[100]; /* enough :-) */
|
||||||
|
const char *s, *s0;
|
||||||
|
char *var;
|
||||||
|
const char *var_unique;
|
||||||
|
struct num value;
|
||||||
|
int i, value_len;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for (s = name; *s; s++) {
|
||||||
|
if (*s != '$') {
|
||||||
|
buf[i++] = *s;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
s0 = ++s;
|
||||||
|
if (*s != '{') {
|
||||||
|
while (is_id_char(*s, s == s0))
|
||||||
|
s++;
|
||||||
|
if (s == s0)
|
||||||
|
goto invalid;
|
||||||
|
var = strnalloc(s0, s-s0);
|
||||||
|
len -= s-s0+1;
|
||||||
|
s--;
|
||||||
|
} else {
|
||||||
|
s++;
|
||||||
|
while (*s != '}') {
|
||||||
|
if (!*s) {
|
||||||
|
fail("unfinished \"${...}\"");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!is_id_char(*s, s == s0+1))
|
||||||
|
goto invalid;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
var = strnalloc(s0+1, s-s0-1);
|
||||||
|
len -= s-s0+2;
|
||||||
|
}
|
||||||
|
if (!frame)
|
||||||
|
continue;
|
||||||
|
var_unique = unique(var);
|
||||||
|
free(var);
|
||||||
|
value = eval_var(frame, var_unique);
|
||||||
|
if (is_undef(value)) {
|
||||||
|
fail("undefined variable \"%s\"", var_unique);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
value_len = snprintf(num_buf, sizeof(num_buf), "%lg%s",
|
||||||
|
value.n, str_unit(value));
|
||||||
|
len += value_len;
|
||||||
|
buf = realloc(buf, len);
|
||||||
|
if (!buf)
|
||||||
|
abort();
|
||||||
|
strcpy(buf+i, num_buf);
|
||||||
|
i += value_len;
|
||||||
|
}
|
||||||
|
buf[i] = 0;
|
||||||
|
return buf;
|
||||||
|
|
||||||
|
invalid:
|
||||||
|
fail("invalid character in variable name");
|
||||||
|
fail:
|
||||||
|
free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- expression-only parser -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
void scan_expr(const char *s);
|
||||||
|
int yyparse(void);
|
||||||
|
|
||||||
|
struct expr *expr_result;
|
||||||
|
|
||||||
|
|
||||||
|
struct expr *parse_expr(const char *s)
|
||||||
|
{
|
||||||
|
scan_expr(s);
|
||||||
|
return yyparse() ? NULL : expr_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void vacate_op(struct expr *expr)
|
||||||
|
{
|
||||||
|
if (expr->op == &op_num || expr->op == &op_var)
|
||||||
|
return;
|
||||||
|
if (expr->op == &op_minus) {
|
||||||
|
free_expr(expr->u.op.a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (expr->op == &op_add || expr->op == &op_sub ||
|
||||||
|
expr->op == &op_mult || expr->op == &op_div) {
|
||||||
|
free_expr(expr->u.op.a);
|
||||||
|
free_expr(expr->u.op.b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void free_expr(struct expr *expr)
|
||||||
|
{
|
||||||
|
vacate_op(expr);
|
||||||
|
free(expr);
|
||||||
|
}
|
120
expr.h
Normal file
120
expr.h
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* expr.h - Expressions and values
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef EXPR_H
|
||||||
|
#define EXPR_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define UNDEF HUGE_VAL
|
||||||
|
|
||||||
|
|
||||||
|
struct frame;
|
||||||
|
struct expr;
|
||||||
|
|
||||||
|
enum num_type {
|
||||||
|
nt_none,
|
||||||
|
nt_mm,
|
||||||
|
nt_mil,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct num {
|
||||||
|
enum num_type type;
|
||||||
|
int exponent;
|
||||||
|
double n;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct num (*op_type)(const struct expr *self,
|
||||||
|
const struct frame *frame);
|
||||||
|
|
||||||
|
struct expr {
|
||||||
|
op_type op;
|
||||||
|
union {
|
||||||
|
struct num num;
|
||||||
|
const char *var;
|
||||||
|
struct {
|
||||||
|
struct expr *a;
|
||||||
|
struct expr *b;
|
||||||
|
} op;
|
||||||
|
} u;
|
||||||
|
int lineno;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern struct num undef;
|
||||||
|
|
||||||
|
|
||||||
|
#define is_undef(num) ((num).type == nt_none)
|
||||||
|
#define is_dimensionless(num) (!(num).exponent)
|
||||||
|
|
||||||
|
|
||||||
|
static inline int is_distance(struct num num)
|
||||||
|
{
|
||||||
|
return (num.type == nt_mm || num.type == nt_mil) && num.exponent == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fail_expr(const struct expr *expr);
|
||||||
|
|
||||||
|
const char *str_unit(struct num n);
|
||||||
|
|
||||||
|
|
||||||
|
static inline struct num make_num(double n)
|
||||||
|
{
|
||||||
|
struct num res;
|
||||||
|
|
||||||
|
res.type = nt_mm;
|
||||||
|
res.exponent = 0;
|
||||||
|
res.n = n;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline struct num make_mil(double mil)
|
||||||
|
{
|
||||||
|
struct num res;
|
||||||
|
|
||||||
|
res.type = nt_mil;
|
||||||
|
res.exponent = 1;
|
||||||
|
res.n = mil;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int to_unit(struct num *n);
|
||||||
|
|
||||||
|
struct num op_num(const struct expr *self, const struct frame *frame);
|
||||||
|
struct num op_var(const struct expr *self, const struct frame *frame);
|
||||||
|
|
||||||
|
struct num op_minus(const struct expr *self, const struct frame *frame);
|
||||||
|
|
||||||
|
struct num op_add(const struct expr *self, const struct frame *frame);
|
||||||
|
struct num op_sub(const struct expr *self, const struct frame *frame);
|
||||||
|
struct num op_mult(const struct expr *self, const struct frame *frame);
|
||||||
|
struct num op_div(const struct expr *self, const struct frame *frame);
|
||||||
|
|
||||||
|
struct expr *new_op(op_type op);
|
||||||
|
struct expr *binary_op(op_type op, struct expr *a, struct expr *b);
|
||||||
|
|
||||||
|
struct num eval_var(const struct frame *frame, const char *name);
|
||||||
|
char *eval_str(const struct frame *frame, const struct expr *expr);
|
||||||
|
struct num eval_num(const struct expr *expr, const struct frame *frame);
|
||||||
|
|
||||||
|
/* if frame == NULL, we only check the syntax without expanding */
|
||||||
|
char *expand(const char *name, const struct frame *frame);
|
||||||
|
|
||||||
|
struct expr *parse_expr(const char *s);
|
||||||
|
void free_expr(struct expr *expr);
|
||||||
|
|
||||||
|
#endif /* !EXPR_H */
|
135
fpd.l
Normal file
135
fpd.l
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
%{
|
||||||
|
/*
|
||||||
|
* fpd.l - FootPrint Definition language
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "coord.h"
|
||||||
|
#include "expr.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
#include "y.tab.h"
|
||||||
|
|
||||||
|
|
||||||
|
int start_token = START_FPD;
|
||||||
|
|
||||||
|
static int disable_keywords = 0;
|
||||||
|
static int is_table = 0;
|
||||||
|
|
||||||
|
|
||||||
|
void scan_empty(void)
|
||||||
|
{
|
||||||
|
yy_scan_string("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void scan_expr(const char *s)
|
||||||
|
{
|
||||||
|
start_token = START_EXPR;
|
||||||
|
yy_scan_string(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
|
||||||
|
/* keywords are only accepted at the beginning of a line */
|
||||||
|
%s NOKEYWORD
|
||||||
|
|
||||||
|
NUM [0-9]+\.?[0-9]*
|
||||||
|
SP [\t ]*
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
%{
|
||||||
|
/*
|
||||||
|
* Nice hack:
|
||||||
|
*
|
||||||
|
* http://www.gnu.org/software/bison/manual/bison.html#
|
||||||
|
* Multiple-start_002dsymbols
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (start_token) {
|
||||||
|
int tmp = start_token;
|
||||||
|
start_token = 0;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
||||||
|
|
||||||
|
<INITIAL>"set" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_SET; }
|
||||||
|
<INITIAL>"loop" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_LOOP; }
|
||||||
|
<INITIAL>"frame" { BEGIN(NOKEYWORD);
|
||||||
|
is_table = 0;
|
||||||
|
return TOK_FRAME; }
|
||||||
|
<INITIAL>"table" { BEGIN(NOKEYWORD);
|
||||||
|
is_table = 1;
|
||||||
|
return TOK_TABLE; }
|
||||||
|
<INITIAL>"vec" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_VEC; }
|
||||||
|
<INITIAL>"pad" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_PAD; }
|
||||||
|
<INITIAL>"rect" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_RECT; }
|
||||||
|
<INITIAL>"line" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_LINE; }
|
||||||
|
<INITIAL>"circ" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_CIRC; }
|
||||||
|
<INITIAL>"arc" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_ARC; }
|
||||||
|
<INITIAL>"meas" { BEGIN(NOKEYWORD);
|
||||||
|
return TOK_MEAS; }
|
||||||
|
|
||||||
|
[a-zA-Z_][a-zA-Z_0-9]*: { *strchr(yytext, ':') = 0;
|
||||||
|
yylval.id = unique(yytext);
|
||||||
|
return LABEL; }
|
||||||
|
[a-zA-Z_][a-zA-Z_0-9]* { yylval.id = unique(yytext);
|
||||||
|
return ID; }
|
||||||
|
|
||||||
|
{NUM}{SP}mm { yylval.num.type = nt_mm;
|
||||||
|
yylval.num.exponent = 1;
|
||||||
|
sscanf(yytext, "%lf", &yylval.num.n);
|
||||||
|
return NUMBER; }
|
||||||
|
{NUM}{SP}mil { yylval.num.type = nt_mil;
|
||||||
|
yylval.num.exponent = 1;
|
||||||
|
sscanf(yytext, "%lf", &yylval.num.n);
|
||||||
|
return NUMBER; }
|
||||||
|
{NUM} { yylval.num.type = nt_mm; /* mm or mil */
|
||||||
|
yylval.num.exponent = 0;
|
||||||
|
sscanf(yytext, "%lf", &yylval.num.n);
|
||||||
|
return NUMBER; }
|
||||||
|
|
||||||
|
\"(\\[^\n\t]|[^\\"\n\t])*\" { *strrchr(yytext, '"') = 0;
|
||||||
|
yylval.str = stralloc(yytext+1);
|
||||||
|
return STRING; }
|
||||||
|
|
||||||
|
{SP} ;
|
||||||
|
\n { if (!disable_keywords)
|
||||||
|
BEGIN(INITIAL);
|
||||||
|
lineno++; }
|
||||||
|
|
||||||
|
"{" { BEGIN(NOKEYWORD);
|
||||||
|
disable_keywords = is_table;
|
||||||
|
return '{'; }
|
||||||
|
"}" { disable_keywords = 0;
|
||||||
|
return '}'; }
|
||||||
|
|
||||||
|
^#\ [0-9]+\ \"[^"]*\"(\ [0-9]+)*\n {
|
||||||
|
if (!disable_keywords)
|
||||||
|
BEGIN(INITIAL);
|
||||||
|
lineno = strtol(yytext+2, NULL, 0); }
|
||||||
|
|
||||||
|
. return *yytext;
|
509
fpd.y
Normal file
509
fpd.y
Normal file
@ -0,0 +1,509 @@
|
|||||||
|
%{
|
||||||
|
/*
|
||||||
|
* fpd.y - FootPrint Definition language
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "expr.h"
|
||||||
|
#include "obj.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern struct expr *expr_result;
|
||||||
|
|
||||||
|
static struct frame *curr_frame;
|
||||||
|
static struct table *curr_table;
|
||||||
|
static struct row *curr_row;
|
||||||
|
|
||||||
|
static struct frame *last_frame = NULL;
|
||||||
|
static struct vec *last_vec = NULL;
|
||||||
|
|
||||||
|
static struct table **next_table;
|
||||||
|
static struct loop **next_loop;
|
||||||
|
static struct vec **next_vec;
|
||||||
|
static struct obj **next_obj;
|
||||||
|
|
||||||
|
static int n_vars, n_values;
|
||||||
|
|
||||||
|
|
||||||
|
static struct frame *find_frame(const char *name)
|
||||||
|
{
|
||||||
|
struct frame *f;
|
||||||
|
|
||||||
|
for (f = frames; f; f = f->next)
|
||||||
|
if (f->name == name)
|
||||||
|
return f;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct vec *find_vec(const char *name)
|
||||||
|
{
|
||||||
|
struct vec *v;
|
||||||
|
|
||||||
|
for (v = curr_frame->vecs; v; v = v->next)
|
||||||
|
if (v->name == name)
|
||||||
|
return v;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void set_frame(struct frame *frame)
|
||||||
|
{
|
||||||
|
curr_frame = frame;
|
||||||
|
next_table = &frame->tables;
|
||||||
|
next_loop = &frame->loops;
|
||||||
|
next_vec = &frame->vecs;
|
||||||
|
next_obj = &frame->objs;
|
||||||
|
last_vec = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void make_var(const char *id, struct expr *expr)
|
||||||
|
{
|
||||||
|
struct table *table;
|
||||||
|
|
||||||
|
table = zalloc_type(struct table);
|
||||||
|
table->vars = zalloc_type(struct var);
|
||||||
|
table->vars->name = id;
|
||||||
|
table->vars->frame = curr_frame;
|
||||||
|
table->rows = zalloc_type(struct row);
|
||||||
|
table->rows->values = zalloc_type(struct value);
|
||||||
|
table->rows->values->expr = expr;
|
||||||
|
table->rows->values->row = table->rows;
|
||||||
|
*next_table = table;
|
||||||
|
next_table = &table->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void make_loop(const char *id, struct expr *from, struct expr *to)
|
||||||
|
{
|
||||||
|
struct loop *loop;
|
||||||
|
|
||||||
|
loop = alloc_type(struct loop);
|
||||||
|
loop->var.name = id;
|
||||||
|
loop->var.next = NULL;
|
||||||
|
loop->var.frame = curr_frame;
|
||||||
|
loop->from.expr = from;
|
||||||
|
loop->from.row = NULL;
|
||||||
|
loop->from.next = NULL;
|
||||||
|
loop->to.expr = to;
|
||||||
|
loop->to.row = NULL;
|
||||||
|
loop->to.next = NULL;
|
||||||
|
loop->next = NULL;
|
||||||
|
loop->active = 0;
|
||||||
|
loop->initialized = 0;
|
||||||
|
*next_loop = loop;
|
||||||
|
next_loop = &loop->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct obj *new_obj(enum obj_type type)
|
||||||
|
{
|
||||||
|
struct obj *obj;
|
||||||
|
|
||||||
|
obj = alloc_type(struct obj);
|
||||||
|
obj->type = type;
|
||||||
|
obj->next = NULL;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
|
||||||
|
%union {
|
||||||
|
struct num num;
|
||||||
|
char *str;
|
||||||
|
const char *id;
|
||||||
|
struct expr *expr;
|
||||||
|
struct frame *frame;
|
||||||
|
struct table *table;
|
||||||
|
struct var *var;
|
||||||
|
struct row *row;
|
||||||
|
struct value *value;
|
||||||
|
struct vec *vec;
|
||||||
|
struct obj *obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
%token START_FPD START_EXPR
|
||||||
|
%token TOK_SET TOK_LOOP TOK_FRAME TOK_TABLE TOK_VEC
|
||||||
|
%token TOK_PAD TOK_RECT TOK_LINE TOK_CIRC TOK_ARC TOK_MEAS
|
||||||
|
|
||||||
|
%token <num> NUMBER
|
||||||
|
%token <str> STRING
|
||||||
|
%token <id> ID LABEL
|
||||||
|
|
||||||
|
%type <table> table
|
||||||
|
%type <var> vars var
|
||||||
|
%type <row> rows
|
||||||
|
%type <value> row value
|
||||||
|
%type <vec> vec base
|
||||||
|
%type <obj> obj
|
||||||
|
%type <expr> expr opt_expr add_expr mult_expr unary_expr primary_expr
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
all:
|
||||||
|
START_FPD fpd
|
||||||
|
| START_EXPR expr
|
||||||
|
{
|
||||||
|
expr_result = $2;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
fpd:
|
||||||
|
{
|
||||||
|
root_frame = zalloc_type(struct frame);
|
||||||
|
set_frame(root_frame);
|
||||||
|
}
|
||||||
|
frame_defs frame_items
|
||||||
|
{
|
||||||
|
root_frame->prev = last_frame;
|
||||||
|
if (last_frame)
|
||||||
|
last_frame->next = root_frame;
|
||||||
|
else
|
||||||
|
frames = root_frame;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
frame_defs:
|
||||||
|
| frame_defs frame_def
|
||||||
|
;
|
||||||
|
|
||||||
|
frame_def:
|
||||||
|
TOK_FRAME ID '{'
|
||||||
|
{
|
||||||
|
if (find_frame($2)) {
|
||||||
|
yyerrorf("duplicate frame \"%s\"", $2);
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
|
curr_frame = zalloc_type(struct frame);
|
||||||
|
curr_frame->name = $2;
|
||||||
|
set_frame(curr_frame);
|
||||||
|
curr_frame->prev = last_frame;
|
||||||
|
if (last_frame)
|
||||||
|
last_frame->next = curr_frame;
|
||||||
|
else
|
||||||
|
frames = curr_frame;
|
||||||
|
last_frame = curr_frame;
|
||||||
|
}
|
||||||
|
frame_items '}'
|
||||||
|
{
|
||||||
|
set_frame(root_frame);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
frame_items:
|
||||||
|
| frame_item frame_items
|
||||||
|
;
|
||||||
|
|
||||||
|
frame_item:
|
||||||
|
table
|
||||||
|
| TOK_SET ID '=' expr
|
||||||
|
{
|
||||||
|
make_var($2, $4);
|
||||||
|
}
|
||||||
|
| TOK_LOOP ID '=' expr ',' expr
|
||||||
|
{
|
||||||
|
make_loop($2, $4, $6);
|
||||||
|
}
|
||||||
|
| vec
|
||||||
|
| LABEL vec
|
||||||
|
{
|
||||||
|
if (find_vec($1)) {
|
||||||
|
yyerrorf("duplicate vector \"%s\"", $1);
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
|
$2->name = $1;
|
||||||
|
}
|
||||||
|
| obj
|
||||||
|
{
|
||||||
|
*next_obj = $1;
|
||||||
|
next_obj = &$1->next;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
table:
|
||||||
|
TOK_TABLE
|
||||||
|
{
|
||||||
|
$<table>$ = zalloc_type(struct table);
|
||||||
|
*next_table = $<table>$;
|
||||||
|
curr_table = $<table>$;
|
||||||
|
n_vars = 0;
|
||||||
|
}
|
||||||
|
'{' vars '}' rows
|
||||||
|
{
|
||||||
|
$$ = $<table>2;
|
||||||
|
$$->vars = $4;
|
||||||
|
$$->rows = $6;
|
||||||
|
$$->active = 0;
|
||||||
|
next_table = &$$->next;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
vars:
|
||||||
|
var
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| vars ',' var
|
||||||
|
{
|
||||||
|
struct var **walk;
|
||||||
|
|
||||||
|
$$ = $1;
|
||||||
|
for (walk = &$$; *walk; walk = &(*walk)->next);
|
||||||
|
*walk = $3;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
var:
|
||||||
|
ID
|
||||||
|
{
|
||||||
|
$$ = alloc_type(struct var);
|
||||||
|
$$->name = $1;
|
||||||
|
$$->frame = curr_frame;
|
||||||
|
$$->next = NULL;
|
||||||
|
n_vars++;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
rows:
|
||||||
|
{
|
||||||
|
$$ = NULL;
|
||||||
|
}
|
||||||
|
| '{'
|
||||||
|
{
|
||||||
|
$<row>$ = alloc_type(struct row);
|
||||||
|
$<row>$->table = curr_table;
|
||||||
|
curr_row = $<row>$;;
|
||||||
|
n_values = 0;
|
||||||
|
}
|
||||||
|
row '}'
|
||||||
|
{
|
||||||
|
if (n_vars != n_values) {
|
||||||
|
yyerrorf("table has %d variables but row has "
|
||||||
|
"%d values", n_vars, n_values);
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
|
$<row>2->values = $3;
|
||||||
|
}
|
||||||
|
rows
|
||||||
|
{
|
||||||
|
$$ = $<row>2;
|
||||||
|
$$->next = $6;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
row:
|
||||||
|
value
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| row ',' value
|
||||||
|
{
|
||||||
|
struct value **walk;
|
||||||
|
|
||||||
|
$$ = $1;
|
||||||
|
for (walk = &$$; *walk; walk = &(*walk)->next);
|
||||||
|
*walk = $3;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
value:
|
||||||
|
expr
|
||||||
|
{
|
||||||
|
$$ = alloc_type(struct value);
|
||||||
|
$$->expr = $1;
|
||||||
|
$$->row = curr_row;
|
||||||
|
$$->next = NULL;
|
||||||
|
n_values++;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
vec:
|
||||||
|
TOK_VEC base '(' expr ',' expr ')'
|
||||||
|
{
|
||||||
|
$$ = alloc_type(struct vec);
|
||||||
|
$$->name = NULL;
|
||||||
|
$$->base = $2;
|
||||||
|
if ($2)
|
||||||
|
$2->n_refs++;
|
||||||
|
$$->x = $4;
|
||||||
|
$$->y = $6;
|
||||||
|
$$->n_refs = 0;
|
||||||
|
$$->frame = curr_frame;
|
||||||
|
$$->next = NULL;
|
||||||
|
last_vec = $$;
|
||||||
|
*next_vec = $$;
|
||||||
|
next_vec = &$$->next;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
base:
|
||||||
|
'@'
|
||||||
|
{
|
||||||
|
$$ = NULL;
|
||||||
|
}
|
||||||
|
| '.'
|
||||||
|
{
|
||||||
|
$$ = last_vec;
|
||||||
|
if (!$$) {
|
||||||
|
yyerrorf(". without predecessor");
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| ID
|
||||||
|
{
|
||||||
|
$$ = find_vec($1);
|
||||||
|
if (!$$) {
|
||||||
|
yyerrorf("unknown vector \"%s\"", $1);
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
obj:
|
||||||
|
TOK_PAD STRING base base
|
||||||
|
{
|
||||||
|
$$ = new_obj(ot_pad);
|
||||||
|
$$->base = $3;
|
||||||
|
$$->u.pad.name = $2;
|
||||||
|
$$->u.pad.other = $4;
|
||||||
|
}
|
||||||
|
| TOK_RECT base base opt_expr
|
||||||
|
{
|
||||||
|
$$ = new_obj(ot_rect);
|
||||||
|
$$->base = $2;
|
||||||
|
$$->u.rect.other = $3;
|
||||||
|
$$->u.rect.width = $4;
|
||||||
|
}
|
||||||
|
| TOK_LINE base base opt_expr
|
||||||
|
{
|
||||||
|
$$ = new_obj(ot_line);
|
||||||
|
$$->base = $2;
|
||||||
|
$$->u.line.other = $3;
|
||||||
|
$$->u.line.width = $4;
|
||||||
|
}
|
||||||
|
| TOK_CIRC base base opt_expr
|
||||||
|
{
|
||||||
|
$$ = new_obj(ot_arc);
|
||||||
|
$$->base = $2;
|
||||||
|
$$->u.arc.start = $3;
|
||||||
|
$$->u.arc.end = $3;
|
||||||
|
$$->u.arc.width = $4;
|
||||||
|
}
|
||||||
|
| TOK_ARC base base base opt_expr
|
||||||
|
{
|
||||||
|
$$ = new_obj(ot_arc);
|
||||||
|
$$->base = $2;
|
||||||
|
$$->u.arc.start = $3;
|
||||||
|
$$->u.arc.end = $4;
|
||||||
|
$$->u.arc.width = $5;
|
||||||
|
}
|
||||||
|
| TOK_MEAS base base expr
|
||||||
|
{
|
||||||
|
$$ = new_obj(ot_meas);
|
||||||
|
$$->base = $2;
|
||||||
|
$$->u.meas.other = $3;
|
||||||
|
$$->u.meas.offset = $4;
|
||||||
|
}
|
||||||
|
| TOK_FRAME ID base
|
||||||
|
{
|
||||||
|
$$ = new_obj(ot_frame);
|
||||||
|
$$->base = $3;
|
||||||
|
$$->u.frame = find_frame($2);
|
||||||
|
if (!$$->u.frame) {
|
||||||
|
yyerrorf("unknown frame \"%s\"", $2);
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
opt_expr:
|
||||||
|
{
|
||||||
|
$$ = NULL;
|
||||||
|
}
|
||||||
|
| expr
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
expr:
|
||||||
|
add_expr
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
add_expr:
|
||||||
|
mult_expr
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| add_expr '+' mult_expr
|
||||||
|
{
|
||||||
|
$$ = binary_op(op_add, $1, $3);
|
||||||
|
}
|
||||||
|
| add_expr '-' mult_expr
|
||||||
|
{
|
||||||
|
$$ = binary_op(op_sub, $1, $3);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
mult_expr:
|
||||||
|
unary_expr
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| mult_expr '*' unary_expr
|
||||||
|
{
|
||||||
|
$$ = binary_op(op_mult, $1, $3);
|
||||||
|
}
|
||||||
|
| mult_expr '/' unary_expr
|
||||||
|
{
|
||||||
|
$$ = binary_op(op_div, $1, $3);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
unary_expr:
|
||||||
|
primary_expr
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| '-' primary_expr
|
||||||
|
{
|
||||||
|
$$ = binary_op(op_minus, $2, NULL);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
primary_expr:
|
||||||
|
NUMBER
|
||||||
|
{
|
||||||
|
$$ = new_op(op_num);
|
||||||
|
$$->u.num = $1;
|
||||||
|
}
|
||||||
|
| ID
|
||||||
|
{
|
||||||
|
$$ = new_op(op_var);
|
||||||
|
$$->u.var = $1;
|
||||||
|
}
|
||||||
|
| '(' expr ')'
|
||||||
|
{
|
||||||
|
$$ = $2;
|
||||||
|
}
|
||||||
|
;
|
57
fped.c
Normal file
57
fped.c
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* fped.c - Footprint editor, main function
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "cpp.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "obj.h"
|
||||||
|
#include "inst.h"
|
||||||
|
#include "gui.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern void scan_empty(void);
|
||||||
|
extern int yyparse(void);
|
||||||
|
|
||||||
|
|
||||||
|
static void load_file(const char *name)
|
||||||
|
{
|
||||||
|
reporter = report_parse_error;
|
||||||
|
run_cpp_on_file(name);
|
||||||
|
(void) yyparse();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = gui_init(&argc, &argv);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
if (argc == 1) {
|
||||||
|
scan_empty();
|
||||||
|
(void) yyparse();
|
||||||
|
} else {
|
||||||
|
load_file(argv[1]);
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
}
|
||||||
|
reporter = report_to_stderr;
|
||||||
|
if (!instantiate())
|
||||||
|
return 1;
|
||||||
|
// inst_debug();
|
||||||
|
error = gui_main(argc, argv);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
return 0;
|
||||||
|
}
|
588
gui.c
Normal file
588
gui.c
Normal file
@ -0,0 +1,588 @@
|
|||||||
|
/*
|
||||||
|
* gui.c - Editor GUI core
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "inst.h"
|
||||||
|
#include "obj.h"
|
||||||
|
#include "unparse.h"
|
||||||
|
#include "gui_util.h"
|
||||||
|
#include "gui_style.h"
|
||||||
|
#include "gui_status.h"
|
||||||
|
#include "gui_canvas.h"
|
||||||
|
#include "gui_icons.h"
|
||||||
|
#include "gui.h"
|
||||||
|
|
||||||
|
|
||||||
|
GtkWidget *root;
|
||||||
|
|
||||||
|
static GtkWidget *frames_box;
|
||||||
|
static GtkWidget *vars_box;
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- menu bar ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void make_menu_bar(GtkWidget *vbox)
|
||||||
|
{
|
||||||
|
GtkWidget *bar;
|
||||||
|
GtkWidget *file_menu, *file, *quit;
|
||||||
|
|
||||||
|
bar = gtk_menu_bar_new();
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), bar, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
file_menu = gtk_menu_new();
|
||||||
|
|
||||||
|
file = gtk_menu_item_new_with_label("File");
|
||||||
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), file_menu);
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(bar), file);
|
||||||
|
|
||||||
|
quit = gtk_menu_item_new_with_label("Quit");
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit);
|
||||||
|
|
||||||
|
g_signal_connect(G_OBJECT(quit), "activate",
|
||||||
|
G_CALLBACK(gtk_main_quit), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- variable list ----------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void add_sep(GtkWidget *box, int size)
|
||||||
|
{
|
||||||
|
GtkWidget *sep;
|
||||||
|
GdkColor black = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
sep = gtk_drawing_area_new();
|
||||||
|
gtk_box_pack_start(GTK_BOX(box), sep, FALSE, TRUE, size);
|
||||||
|
gtk_widget_modify_bg(sep, GTK_STATE_NORMAL, &black);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- variable name editor ---------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static int find_var_in_frame(const struct frame *frame, const char *name)
|
||||||
|
{
|
||||||
|
const struct table *table;
|
||||||
|
const struct loop *loop;
|
||||||
|
const struct var *var;
|
||||||
|
|
||||||
|
for (table = frame->tables; table; table = table->next)
|
||||||
|
for (var = table->vars; var; var = var->next)
|
||||||
|
if (!strcmp(var->name, name))
|
||||||
|
return 1;
|
||||||
|
for (loop = frame->loops; loop; loop = loop->next)
|
||||||
|
if (!strcmp(loop->var.name, name))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int validate_var_name(const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
struct var *var = ctx;
|
||||||
|
|
||||||
|
if (!is_id(s))
|
||||||
|
return 0;
|
||||||
|
return !find_var_in_frame(var->frame, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void unselect_var(void *data)
|
||||||
|
{
|
||||||
|
struct var *var = data;
|
||||||
|
|
||||||
|
label_in_box_bg(var->widget, COLOR_VAR_PASSIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void edit_var(struct var *var)
|
||||||
|
{
|
||||||
|
inst_select_outside(var, unselect_var);
|
||||||
|
label_in_box_bg(var->widget, COLOR_VAR_EDITING);
|
||||||
|
status_set_name(var->name);
|
||||||
|
edit_unique(&var->name, validate_var_name, var);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- value editor ------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
static void unselect_value(void *data)
|
||||||
|
{
|
||||||
|
struct value *value = data;
|
||||||
|
|
||||||
|
label_in_box_bg(value->widget,
|
||||||
|
value->row && value->row->table && value->row->table->curr_row ==
|
||||||
|
value->row ? COLOR_CHOICE_SELECTED : COLOR_EXPR_PASSIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void edit_value(struct value *value)
|
||||||
|
{
|
||||||
|
inst_select_outside(value, unselect_value);
|
||||||
|
label_in_box_bg(value->widget, COLOR_EXPR_EDITING);
|
||||||
|
edit_expr(&value->expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- assignments ------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean assignment_var_select_event(GtkWidget *widget,
|
||||||
|
GdkEventButton *event, gpointer data)
|
||||||
|
{
|
||||||
|
edit_var(data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean assignment_value_select_event(GtkWidget *widget,
|
||||||
|
GdkEventButton *event, gpointer data)
|
||||||
|
{
|
||||||
|
edit_value(data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void build_assignment(GtkWidget *vbox, struct frame *frame,
|
||||||
|
struct table *table)
|
||||||
|
{
|
||||||
|
GtkWidget *hbox, *field;
|
||||||
|
char *expr;
|
||||||
|
|
||||||
|
if (!table->vars || table->vars->next)
|
||||||
|
return;
|
||||||
|
if (!table->rows || table->rows->next)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hbox = gtk_hbox_new(FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
field = label_in_box_new(table->vars->name);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
|
||||||
|
label_in_box_bg(field, COLOR_VAR_PASSIVE);
|
||||||
|
table->vars->widget = field;
|
||||||
|
g_signal_connect(G_OBJECT(box_of_label(field)),
|
||||||
|
"button_press_event",
|
||||||
|
G_CALLBACK(assignment_var_select_event), table->vars);
|
||||||
|
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" = "),
|
||||||
|
FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
expr = unparse(table->rows->values->expr);
|
||||||
|
field = label_in_box_new(expr);
|
||||||
|
free(expr);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
|
||||||
|
label_in_box_bg(field, COLOR_EXPR_PASSIVE);
|
||||||
|
table->rows->values->widget = field;
|
||||||
|
g_signal_connect(G_OBJECT(box_of_label(field)),
|
||||||
|
"button_press_event",
|
||||||
|
G_CALLBACK(assignment_value_select_event), table->rows->values);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- tables ------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean table_var_select_event(GtkWidget *widget,
|
||||||
|
GdkEventButton *event, gpointer data)
|
||||||
|
{
|
||||||
|
edit_var(data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean table_value_select_event(GtkWidget *widget,
|
||||||
|
GdkEventButton *event, gpointer data)
|
||||||
|
{
|
||||||
|
edit_value(data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void build_table(GtkWidget *vbox, struct frame *frame,
|
||||||
|
struct table *table)
|
||||||
|
{
|
||||||
|
GtkWidget *tab, *field;
|
||||||
|
GtkWidget *evbox;
|
||||||
|
struct var *var;
|
||||||
|
struct row *row;
|
||||||
|
struct value *value;
|
||||||
|
int n_vars = 0, n_rows = 0;
|
||||||
|
char *expr;
|
||||||
|
GdkColor col;
|
||||||
|
|
||||||
|
for (var = table->vars; var; var = var->next)
|
||||||
|
n_vars++;
|
||||||
|
for (row = table->rows; row; row = row->next)
|
||||||
|
n_rows++;
|
||||||
|
|
||||||
|
if (n_vars == 1 && n_rows == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
evbox = gtk_event_box_new();
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), evbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
tab = gtk_table_new(n_rows+1, n_vars, FALSE);
|
||||||
|
gtk_container_add(GTK_CONTAINER(evbox), tab);
|
||||||
|
col = get_color(COLOR_VAR_TABLE_SEP);
|
||||||
|
gtk_widget_modify_bg(GTK_WIDGET(evbox),
|
||||||
|
GTK_STATE_NORMAL, &col);
|
||||||
|
|
||||||
|
gtk_table_set_row_spacings(GTK_TABLE(tab), 1);
|
||||||
|
gtk_table_set_col_spacings(GTK_TABLE(tab), 1);
|
||||||
|
|
||||||
|
n_vars = 0;
|
||||||
|
for (var = table->vars; var; var = var->next) {
|
||||||
|
field = label_in_box_new(var->name);
|
||||||
|
gtk_table_attach_defaults(GTK_TABLE(tab), box_of_label(field),
|
||||||
|
n_vars, n_vars+1, 0, 1);
|
||||||
|
label_in_box_bg(field, COLOR_VAR_PASSIVE);
|
||||||
|
g_signal_connect(G_OBJECT(box_of_label(field)),
|
||||||
|
"button_press_event",
|
||||||
|
G_CALLBACK(table_var_select_event), var);
|
||||||
|
var->widget = field;
|
||||||
|
n_vars++;
|
||||||
|
}
|
||||||
|
n_rows = 0;
|
||||||
|
for (row = table->rows; row; row = row->next) {
|
||||||
|
n_vars = 0;
|
||||||
|
for (value = row->values; value; value = value->next) {
|
||||||
|
expr = unparse(value->expr);
|
||||||
|
field = label_in_box_new(expr);
|
||||||
|
free(expr);
|
||||||
|
gtk_table_attach_defaults(GTK_TABLE(tab),
|
||||||
|
box_of_label(field),
|
||||||
|
n_vars, n_vars+1,
|
||||||
|
n_rows+1, n_rows+2);
|
||||||
|
label_in_box_bg(field, table->active == n_rows ?
|
||||||
|
COLOR_ROW_SELECTED : COLOR_ROW_UNSELECTED);
|
||||||
|
g_signal_connect(G_OBJECT(box_of_label(field)),
|
||||||
|
"button_press_event",
|
||||||
|
G_CALLBACK(table_value_select_event), value);
|
||||||
|
value->widget = field;
|
||||||
|
n_vars++;
|
||||||
|
}
|
||||||
|
n_rows++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- loops ------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean loop_var_select_event(GtkWidget *widget,
|
||||||
|
GdkEventButton *event, gpointer data)
|
||||||
|
{
|
||||||
|
struct loop *loop = data;
|
||||||
|
|
||||||
|
edit_var(&loop->var);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean loop_from_select_event(GtkWidget *widget,
|
||||||
|
GdkEventButton *event, gpointer data)
|
||||||
|
{
|
||||||
|
struct loop *loop = data;
|
||||||
|
|
||||||
|
edit_value(&loop->from);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean loop_to_select_event(GtkWidget *widget,
|
||||||
|
GdkEventButton *event, gpointer data)
|
||||||
|
{
|
||||||
|
struct loop *loop = data;
|
||||||
|
|
||||||
|
edit_value(&loop->to);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void build_loop(GtkWidget *vbox, struct frame *frame,
|
||||||
|
struct loop *loop)
|
||||||
|
{
|
||||||
|
GtkWidget *hbox, *field;
|
||||||
|
char *expr;
|
||||||
|
|
||||||
|
hbox = gtk_hbox_new(FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
field = label_in_box_new(loop->var.name);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
|
||||||
|
label_in_box_bg(field, COLOR_VAR_PASSIVE);
|
||||||
|
g_signal_connect(G_OBJECT(box_of_label(field)),
|
||||||
|
"button_press_event",
|
||||||
|
G_CALLBACK(loop_var_select_event), loop);
|
||||||
|
loop->var.widget = field;
|
||||||
|
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" = "),
|
||||||
|
FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
expr = unparse(loop->from.expr);
|
||||||
|
field = label_in_box_new(expr);
|
||||||
|
free(expr);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
|
||||||
|
label_in_box_bg(field, COLOR_EXPR_PASSIVE);
|
||||||
|
g_signal_connect(G_OBJECT(box_of_label(field)),
|
||||||
|
"button_press_event",
|
||||||
|
G_CALLBACK(loop_from_select_event), loop);
|
||||||
|
loop->from.widget = field;
|
||||||
|
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" ... "),
|
||||||
|
FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
expr = unparse(loop->to.expr);
|
||||||
|
field = label_in_box_new(expr);
|
||||||
|
free(expr);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
|
||||||
|
label_in_box_bg(field, COLOR_EXPR_PASSIVE);
|
||||||
|
g_signal_connect(G_OBJECT(box_of_label(field)),
|
||||||
|
"button_press_event",
|
||||||
|
G_CALLBACK(loop_to_select_event), loop);
|
||||||
|
loop->to.widget = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- the list of variables, tables, and loops -------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void build_vars(GtkWidget *vbox, struct frame *frame)
|
||||||
|
{
|
||||||
|
struct table *table;
|
||||||
|
struct loop *loop;
|
||||||
|
|
||||||
|
destroy_all_children(GTK_CONTAINER(vbox));
|
||||||
|
for (table = frame->tables; table; table = table->next) {
|
||||||
|
add_sep(vbox, 3);
|
||||||
|
build_assignment(vbox, frame, table);
|
||||||
|
build_table(vbox, frame, table);
|
||||||
|
}
|
||||||
|
for (loop = frame->loops; loop; loop = loop->next) {
|
||||||
|
add_sep(vbox, 3);
|
||||||
|
build_loop(vbox, frame, loop);
|
||||||
|
}
|
||||||
|
gtk_widget_show_all(vbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- frame list -------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static int validate_frame_name(const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
struct frame *f;
|
||||||
|
|
||||||
|
if (!is_id(s))
|
||||||
|
return 0;
|
||||||
|
for (f = frames; f; f = f->next)
|
||||||
|
if (f->name && !strcmp(f->name, s))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void unselect_frame(void *data)
|
||||||
|
{
|
||||||
|
struct frame *frame= data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "unselect" means in this context that the selection has moved
|
||||||
|
* elsewhere. However, this does not necessarily change the frame.
|
||||||
|
* (And, in fact, since we rebuild the frame list anyway, the color
|
||||||
|
* change here doesn't matter if selecting a different frame.)
|
||||||
|
* So we revert from "editing" to "selected".
|
||||||
|
*/
|
||||||
|
label_in_box_bg(frame->label, COLOR_FRAME_SELECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void edit_frame(struct frame *frame)
|
||||||
|
{
|
||||||
|
inst_select_outside(frame, unselect_frame);
|
||||||
|
label_in_box_bg(frame->label, COLOR_FRAME_EDITING);
|
||||||
|
status_set_name(frame->name);
|
||||||
|
edit_unique(&frame->name, validate_frame_name, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void select_frame(struct frame *frame)
|
||||||
|
{
|
||||||
|
if (active_frame) {
|
||||||
|
label_in_box_bg(active_frame->label, COLOR_FRAME_UNSELECTED);
|
||||||
|
inst_deselect();
|
||||||
|
}
|
||||||
|
active_frame = frame;
|
||||||
|
change_world();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean frame_select_event(GtkWidget *widget, GdkEventButton *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
if (active_frame != data)
|
||||||
|
select_frame(data);
|
||||||
|
else {
|
||||||
|
if (active_frame->name)
|
||||||
|
edit_frame(data);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void build_frames(GtkWidget *vbox)
|
||||||
|
{
|
||||||
|
struct frame *f;
|
||||||
|
GtkWidget *label;
|
||||||
|
|
||||||
|
destroy_all_children(GTK_CONTAINER(vbox));
|
||||||
|
for (f = root_frame; f; f = f->prev) {
|
||||||
|
label = label_in_box_new(f->name ? f->name : "(root)");
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), box_of_label(label),
|
||||||
|
FALSE, TRUE, 0);
|
||||||
|
gtk_misc_set_padding(GTK_MISC(label), 2, 2);
|
||||||
|
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||||
|
|
||||||
|
label_in_box_bg(label, active_frame == f ?
|
||||||
|
COLOR_FRAME_SELECTED : COLOR_FRAME_UNSELECTED);
|
||||||
|
|
||||||
|
g_signal_connect(G_OBJECT(box_of_label(label)),
|
||||||
|
"button_press_event", G_CALLBACK(frame_select_event), f);
|
||||||
|
f->label = label;
|
||||||
|
}
|
||||||
|
gtk_widget_show_all(vbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- central screen area ----------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void make_center_area(GtkWidget *vbox)
|
||||||
|
{
|
||||||
|
GtkWidget *hbox, *vars, *paned;
|
||||||
|
GtkWidget *frame_list, *icons;
|
||||||
|
|
||||||
|
hbox = gtk_hbox_new(FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
|
||||||
|
|
||||||
|
/* Frame list */
|
||||||
|
|
||||||
|
frame_list = gtk_scrolled_window_new(NULL, NULL);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), frame_list, FALSE, TRUE, 0);
|
||||||
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(frame_list),
|
||||||
|
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||||
|
|
||||||
|
frames_box = gtk_vbox_new(FALSE, 0);
|
||||||
|
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(frame_list),
|
||||||
|
frames_box);
|
||||||
|
|
||||||
|
build_frames(frames_box);
|
||||||
|
|
||||||
|
add_sep(hbox, 2);
|
||||||
|
|
||||||
|
paned = gtk_hpaned_new();
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), paned, TRUE, TRUE, 0);
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
|
||||||
|
vars = gtk_scrolled_window_new(NULL, NULL);
|
||||||
|
gtk_paned_add1(GTK_PANED(paned), vars);
|
||||||
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vars),
|
||||||
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||||
|
gtk_widget_set_size_request(vars, 150, 100);
|
||||||
|
vars_box = gtk_vbox_new(FALSE, 0);
|
||||||
|
|
||||||
|
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(vars),
|
||||||
|
vars_box);
|
||||||
|
|
||||||
|
/* Canvas */
|
||||||
|
|
||||||
|
gtk_paned_add2(GTK_PANED(paned), make_canvas());
|
||||||
|
|
||||||
|
/* Icon bar */
|
||||||
|
|
||||||
|
icons = gui_setup_icons(root->window);
|
||||||
|
gtk_box_pack_end(GTK_BOX(hbox), icons, FALSE, FALSE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- GUI construction -------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
void change_world(void)
|
||||||
|
{
|
||||||
|
status_begin_reporting();
|
||||||
|
instantiate();
|
||||||
|
label_in_box_bg(active_frame->label, COLOR_FRAME_SELECTED);
|
||||||
|
build_frames(frames_box);
|
||||||
|
build_vars(vars_box, active_frame);
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void make_screen(GtkWidget *window)
|
||||||
|
{
|
||||||
|
GtkWidget *vbox;
|
||||||
|
|
||||||
|
vbox = gtk_vbox_new(FALSE, 0);
|
||||||
|
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||||||
|
|
||||||
|
make_menu_bar(vbox);
|
||||||
|
make_center_area(vbox);
|
||||||
|
make_status_area(vbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int gui_init(int *argc, char ***argv)
|
||||||
|
{
|
||||||
|
gtk_init(argc, argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int gui_main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
root = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||||
|
gtk_window_set_position(GTK_WINDOW(root), GTK_WIN_POS_CENTER);
|
||||||
|
gtk_window_set_default_size(GTK_WINDOW(root), 600, 400);
|
||||||
|
gtk_window_set_title(GTK_WINDOW(root), "fped");
|
||||||
|
|
||||||
|
/* get root->window */
|
||||||
|
gtk_widget_show_all(root);
|
||||||
|
|
||||||
|
g_signal_connect_swapped(G_OBJECT(root), "destroy",
|
||||||
|
G_CALLBACK(gtk_main_quit), NULL);
|
||||||
|
|
||||||
|
make_screen(root);
|
||||||
|
build_vars(vars_box, root_frame);
|
||||||
|
|
||||||
|
gtk_widget_show_all(root);
|
||||||
|
|
||||||
|
gui_setup_style(root->window);
|
||||||
|
init_canvas();
|
||||||
|
edit_nothing();
|
||||||
|
select_frame(root_frame);
|
||||||
|
|
||||||
|
gtk_main();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
29
gui.h
Normal file
29
gui.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* gui.h - Editor GUI core
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUI_H
|
||||||
|
#define GUI_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
|
||||||
|
extern GtkWidget *root;
|
||||||
|
|
||||||
|
|
||||||
|
/* update everything after a model change */
|
||||||
|
void change_world(void);
|
||||||
|
|
||||||
|
int gui_init(int *argc, char ***argv);
|
||||||
|
int gui_main(int argc, char **argv);
|
||||||
|
|
||||||
|
#endif /* !GUI_H */
|
327
gui_canvas.c
Normal file
327
gui_canvas.c
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
* gui_canvas.c - GUI, canvas
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "obj.h"
|
||||||
|
#include "inst.h"
|
||||||
|
#include "gui_inst.h"
|
||||||
|
#include "gui_style.h"
|
||||||
|
#include "gui_status.h"
|
||||||
|
#include "gui.h"
|
||||||
|
#include "gui_canvas.h"
|
||||||
|
|
||||||
|
|
||||||
|
static struct draw_ctx ctx;
|
||||||
|
static struct coord curr_pos;
|
||||||
|
static struct coord user_origin = { 0, 0 };
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- status display ---------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void update_zoom(void)
|
||||||
|
{
|
||||||
|
status_set_zoom("x%d", ctx.scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void update_pos(struct coord pos)
|
||||||
|
{
|
||||||
|
status_set_sys_pos("X %5.2lf Y %5.2lf mm",
|
||||||
|
units_to_mm(pos.x), units_to_mm(pos.y));
|
||||||
|
status_set_user_pos("x %5.2lf y %5.2lf mm",
|
||||||
|
units_to_mm(pos.x-user_origin.x), units_to_mm(pos.y-user_origin.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- coordinate system ------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void center(void)
|
||||||
|
{
|
||||||
|
struct bbox bbox;
|
||||||
|
|
||||||
|
bbox = inst_get_bbox();
|
||||||
|
ctx.center.x = (bbox.min.x+bbox.max.x)/2;
|
||||||
|
ctx.center.y = (bbox.min.y+bbox.max.y)/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void auto_scale(void)
|
||||||
|
{
|
||||||
|
struct bbox bbox;
|
||||||
|
unit_type h, w;
|
||||||
|
int sx, sy;
|
||||||
|
float aw, ah;
|
||||||
|
|
||||||
|
bbox = inst_get_bbox();
|
||||||
|
aw = ctx.widget->allocation.width;
|
||||||
|
ah = ctx.widget->allocation.height;
|
||||||
|
h = bbox.max.x-bbox.min.x;
|
||||||
|
w = bbox.max.y-bbox.min.y;
|
||||||
|
aw -= 2*CANVAS_CLEARANCE;
|
||||||
|
ah -= 2*CANVAS_CLEARANCE;
|
||||||
|
if (aw < 1)
|
||||||
|
aw = 1;
|
||||||
|
if (ah < 1)
|
||||||
|
ah = 1;
|
||||||
|
sx = ceil(h/aw);
|
||||||
|
sy = ceil(w/ah);
|
||||||
|
ctx.scale = sx > sy ? sx : sy > 0 ? sy : 1;
|
||||||
|
|
||||||
|
update_zoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- drawing ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
void redraw(void)
|
||||||
|
{
|
||||||
|
float aw, ah;
|
||||||
|
|
||||||
|
aw = ctx.widget->allocation.width;
|
||||||
|
ah = ctx.widget->allocation.height;
|
||||||
|
gdk_draw_rectangle(ctx.widget->window, gc_bg, TRUE, 0, 0, aw, ah);
|
||||||
|
|
||||||
|
inst_draw(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- drag -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void drag_left(struct coord pos)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void drag_middle(struct coord pos)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
struct coord pos = canvas_to_coord(&ctx, event->x, event->y);
|
||||||
|
|
||||||
|
curr_pos.x = event->x;
|
||||||
|
curr_pos.y = event->y;
|
||||||
|
if (event->state & GDK_BUTTON1_MASK)
|
||||||
|
drag_left(pos);
|
||||||
|
if (event->state & GDK_BUTTON2_MASK)
|
||||||
|
drag_middle(pos);
|
||||||
|
update_pos(pos);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- button press and release ------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
struct coord pos = canvas_to_coord(&ctx, event->x, event->y);
|
||||||
|
const struct inst *prev;
|
||||||
|
|
||||||
|
switch (event->button) {
|
||||||
|
case 1:
|
||||||
|
prev = selected_inst;
|
||||||
|
inst_deselect();
|
||||||
|
inst_select(&ctx, pos);
|
||||||
|
if (prev != selected_inst)
|
||||||
|
redraw();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ctx.center = pos;
|
||||||
|
redraw();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- zoom control ------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
static void zoom_in(struct coord pos)
|
||||||
|
{
|
||||||
|
if (ctx.scale < 2)
|
||||||
|
return;
|
||||||
|
ctx.scale /= 2;
|
||||||
|
ctx.center.x = (ctx.center.x+pos.x)/2;
|
||||||
|
ctx.center.y = (ctx.center.y+pos.y)/2;
|
||||||
|
update_zoom();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void zoom_out(struct coord pos)
|
||||||
|
{
|
||||||
|
struct bbox bbox;
|
||||||
|
|
||||||
|
bbox = inst_get_bbox();
|
||||||
|
bbox.min = translate(&ctx, bbox.min);
|
||||||
|
bbox.max = translate(&ctx, bbox.max);
|
||||||
|
if (bbox.min.x >= 0 && bbox.max.y >= 0 &&
|
||||||
|
bbox.max.x < ctx.widget->allocation.width &&
|
||||||
|
bbox.min.y < ctx.widget->allocation.height)
|
||||||
|
return;
|
||||||
|
ctx.scale *= 2;
|
||||||
|
ctx.center.x = 2*ctx.center.x-pos.x;
|
||||||
|
ctx.center.y = 2*ctx.center.y-pos.y;
|
||||||
|
update_zoom();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
struct coord pos = canvas_to_coord(&ctx, event->x, event->y);
|
||||||
|
|
||||||
|
switch (event->direction) {
|
||||||
|
case GDK_SCROLL_UP:
|
||||||
|
zoom_in(pos);
|
||||||
|
break;
|
||||||
|
case GDK_SCROLL_DOWN:
|
||||||
|
zoom_out(pos);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* ignore */;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- keys -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
struct coord pos = canvas_to_coord(&ctx, curr_pos.x, curr_pos.y);
|
||||||
|
|
||||||
|
switch (event->keyval) {
|
||||||
|
case ' ':
|
||||||
|
user_origin = pos;
|
||||||
|
update_pos(pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- expose event ------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
static int first = 1;
|
||||||
|
if (first) {
|
||||||
|
init_canvas();
|
||||||
|
first = 0;
|
||||||
|
}
|
||||||
|
redraw();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- enter/leave ------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean enter_notify_event(GtkWidget *widget, GdkEventCrossing *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
gtk_widget_grab_focus(widget);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean leave_notify_event(GtkWidget *widget, GdkEventCrossing *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- canvas setup ------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that we call init_canvas twice: first to make sure we'll make it safely
|
||||||
|
* through select_frame, and the second time to set the geometry for the actual
|
||||||
|
* screen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void init_canvas(void)
|
||||||
|
{
|
||||||
|
center();
|
||||||
|
auto_scale();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GtkWidget *make_canvas(void)
|
||||||
|
{
|
||||||
|
GtkWidget *canvas;
|
||||||
|
GdkColor black = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
/* Canvas */
|
||||||
|
|
||||||
|
canvas = gtk_drawing_area_new();
|
||||||
|
gtk_widget_modify_bg(canvas, GTK_STATE_NORMAL, &black);
|
||||||
|
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "motion_notify_event",
|
||||||
|
G_CALLBACK(motion_notify_event), NULL);
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "button_press_event",
|
||||||
|
G_CALLBACK(button_press_event), NULL);
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "button_release_event",
|
||||||
|
G_CALLBACK(button_release_event), NULL);
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "scroll_event",
|
||||||
|
G_CALLBACK(scroll_event), NULL);
|
||||||
|
|
||||||
|
GTK_WIDGET_SET_FLAGS(canvas, GTK_CAN_FOCUS);
|
||||||
|
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "key_press_event",
|
||||||
|
G_CALLBACK(key_press_event), NULL);
|
||||||
|
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "expose_event",
|
||||||
|
G_CALLBACK(expose_event), NULL);
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "enter_notify_event",
|
||||||
|
G_CALLBACK(enter_notify_event), NULL);
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "leave_notify_event",
|
||||||
|
G_CALLBACK(leave_notify_event), NULL);
|
||||||
|
|
||||||
|
gtk_widget_set_events(canvas,
|
||||||
|
GDK_EXPOSE | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
|
||||||
|
GDK_KEY_PRESS_MASK |
|
||||||
|
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
|
||||||
|
GDK_SCROLL |
|
||||||
|
GDK_POINTER_MOTION_MASK);
|
||||||
|
|
||||||
|
ctx.widget = canvas;
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
}
|
25
gui_canvas.h
Normal file
25
gui_canvas.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* gui_canvas.h - GUI, canvas
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUI_CANVAS_H
|
||||||
|
#define GUI_CANVAS_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
|
||||||
|
void redraw(void);
|
||||||
|
|
||||||
|
GtkWidget *make_canvas(void);
|
||||||
|
void init_canvas(void);
|
||||||
|
|
||||||
|
#endif /* !GUI_CANVAS_H */
|
87
gui_icons.c
Normal file
87
gui_icons.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* gui_icons.c - GUI, icon bar
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "gui_util.h"
|
||||||
|
#include "gui_icons.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "icons/arc.xpm"
|
||||||
|
#include "icons/circ.xpm"
|
||||||
|
#include "icons/frame.xpm"
|
||||||
|
#include "icons/line.xpm"
|
||||||
|
#include "icons/meas.xpm"
|
||||||
|
#include "icons/pad.xpm"
|
||||||
|
#include "icons/point.xpm"
|
||||||
|
#include "icons/rect.xpm"
|
||||||
|
#include "icons/vec.xpm"
|
||||||
|
|
||||||
|
|
||||||
|
static GtkToolItem *icon_button(GtkWidget *bar, GdkDrawable *drawable,
|
||||||
|
char **xpm, GtkToolItem *last)
|
||||||
|
{
|
||||||
|
GdkPixmap *pixmap;
|
||||||
|
GtkWidget *image;
|
||||||
|
GtkToolItem *item;
|
||||||
|
|
||||||
|
pixmap = gdk_pixmap_create_from_xpm_d(drawable, NULL, NULL, xpm);
|
||||||
|
image = gtk_image_new_from_pixmap(pixmap, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gtk_radio_tool_button_new_from_widget is *huge*. we try to do things in a
|
||||||
|
* more compact way.
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
if (last)
|
||||||
|
item = gtk_radio_tool_button_new_from_widget(
|
||||||
|
GTK_RADIO_TOOL_BUTTON(last));
|
||||||
|
else
|
||||||
|
item = gtk_radio_tool_button_new(NULL);
|
||||||
|
gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), image);
|
||||||
|
#else
|
||||||
|
item = gtk_tool_item_new();
|
||||||
|
gtk_container_add(GTK_CONTAINER(item), image);
|
||||||
|
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(item), 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GtkWidget *gui_setup_icons(GdkDrawable *drawable)
|
||||||
|
{
|
||||||
|
GtkWidget *bar;
|
||||||
|
GtkToolItem *last;
|
||||||
|
|
||||||
|
bar = gtk_toolbar_new();
|
||||||
|
gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_ICONS);
|
||||||
|
gtk_toolbar_set_orientation(GTK_TOOLBAR(bar),
|
||||||
|
GTK_ORIENTATION_VERTICAL);
|
||||||
|
//gtk_container_set_border_width(GTK_CONTAINER(bar), 5);
|
||||||
|
|
||||||
|
last = icon_button(bar, drawable, xpm_point, NULL);
|
||||||
|
last = icon_button(bar, drawable, xpm_vec, last);
|
||||||
|
last = icon_button(bar, drawable, xpm_frame, last);
|
||||||
|
last = icon_button(bar, drawable, xpm_pad, last);
|
||||||
|
last = icon_button(bar, drawable, xpm_line, last);
|
||||||
|
last = icon_button(bar, drawable, xpm_rect, last);
|
||||||
|
last = icon_button(bar, drawable, xpm_circ, last);
|
||||||
|
last = icon_button(bar, drawable, xpm_arc, last);
|
||||||
|
last = icon_button(bar, drawable, xpm_meas, last);
|
||||||
|
|
||||||
|
return bar;
|
||||||
|
}
|
22
gui_icons.h
Normal file
22
gui_icons.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* gui_icons.h - GUI, icon bar
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUI_ICONS_H
|
||||||
|
#define GUI_ICONS_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
|
||||||
|
GtkWidget *gui_setup_icons(GdkDrawable *drawable);
|
||||||
|
|
||||||
|
#endif /* !GUI_ICONS_H */
|
421
gui_inst.c
Normal file
421
gui_inst.c
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
/*
|
||||||
|
* gui_inst.c - GUI, instance functions
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "inst.h"
|
||||||
|
#include "gui.h"
|
||||||
|
#include "gui_util.h"
|
||||||
|
#include "gui_style.h"
|
||||||
|
#include "gui_inst.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define DA GDK_DRAWABLE(ctx->widget->window)
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- coordinate translation -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
struct coord translate(const struct draw_ctx *ctx, struct coord pos)
|
||||||
|
{
|
||||||
|
pos.x -= ctx->center.x;
|
||||||
|
pos.y -= ctx->center.y;
|
||||||
|
pos.x /= ctx->scale;
|
||||||
|
pos.y /= ctx->scale;
|
||||||
|
pos.y = -pos.y;
|
||||||
|
pos.x += ctx->widget->allocation.width/2;
|
||||||
|
pos.y += ctx->widget->allocation.height/2;
|
||||||
|
//fprintf(stderr, "%d %d\n", (int) pos.x, (int) pos.y);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct coord canvas_to_coord(const struct draw_ctx *ctx, int x, int y)
|
||||||
|
{
|
||||||
|
struct coord pos;
|
||||||
|
|
||||||
|
x -= ctx->widget->allocation.width/2;
|
||||||
|
y -= ctx->widget->allocation.height/2;
|
||||||
|
y = -y;
|
||||||
|
pos.x = x*ctx->scale+ctx->center.x;
|
||||||
|
pos.y = y*ctx->scale+ctx->center.y;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- drawing primitives ------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_arc(struct draw_ctx *ctx, GdkGC *gc, int fill,
|
||||||
|
int x, int y, int r, double a1, double a2)
|
||||||
|
{
|
||||||
|
if (a1 == a2)
|
||||||
|
a2 = a1+360;
|
||||||
|
gdk_draw_arc(DA, gc, fill, x-r, y-r, 2*r, 2*r, a1*64, (a2-a1)*64);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_circle(struct draw_ctx *ctx, GdkGC *gc, int fill,
|
||||||
|
int x, int y, int r)
|
||||||
|
{
|
||||||
|
draw_arc(ctx, gc, fill, x, y, r, 0, 360);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_eye(struct draw_ctx *ctx, GdkGC *gc, struct coord center,
|
||||||
|
int r1, int r2)
|
||||||
|
{
|
||||||
|
draw_circle(ctx, gc, TRUE, center.x, center.y, r1);
|
||||||
|
draw_circle(ctx, gc, FALSE, center.x, center.y, r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_POINTS 10
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_poly(struct draw_ctx *ctx, GdkGC *gc, int fill,
|
||||||
|
const struct coord *points, int n_points)
|
||||||
|
{
|
||||||
|
GdkPoint gp[MAX_POINTS];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (n_points > MAX_POINTS)
|
||||||
|
abort();
|
||||||
|
for (i = 0; i != n_points; i++) {
|
||||||
|
gp[i].x = points[i].x;
|
||||||
|
gp[i].y = points[i].y;
|
||||||
|
}
|
||||||
|
if (fill)
|
||||||
|
gdk_draw_polygon(DA, gc, fill, gp, n_points);
|
||||||
|
else {
|
||||||
|
gdk_draw_line(DA, gc, gp[0].x, gp[0].y, gp[1].x, gp[1].y);
|
||||||
|
gdk_draw_line(DA, gc, gp[1].x, gp[1].y, gp[2].x, gp[2].y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_arrow(struct draw_ctx *ctx, GdkGC *gc, int fill,
|
||||||
|
struct coord from, struct coord to, int len, double angle)
|
||||||
|
{
|
||||||
|
struct coord p[3];
|
||||||
|
struct coord side;
|
||||||
|
|
||||||
|
side = normalize(sub_vec(to, from), len);
|
||||||
|
p[0] = add_vec(to, rotate(side, 180-angle));
|
||||||
|
p[1] = to;
|
||||||
|
p[2] = add_vec(to, rotate(side, 180+angle));
|
||||||
|
draw_poly(ctx, gc, fill, p, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static enum mode get_mode(struct inst *self)
|
||||||
|
{
|
||||||
|
if (selected_inst == self)
|
||||||
|
return mode_selected;
|
||||||
|
if (self->active)
|
||||||
|
return self->in_path ? mode_active_in_path : mode_active;
|
||||||
|
return self->in_path ? mode_inactive_in_path : mode_inactive;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- vec --------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
unit_type gui_dist_vec(struct inst *self, struct coord pos, unit_type scale)
|
||||||
|
{
|
||||||
|
unit_type d;
|
||||||
|
|
||||||
|
d = dist_point(pos, self->u.rect.end)/scale;
|
||||||
|
return d > VEC_EYE_R ? -1 : d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The circles at a vector's tip enjoy the highest selection priority. However,
|
||||||
|
* users will probably also expected a click on a nicely exposed stem to work.
|
||||||
|
* So we give it second look after having exhausted all other options.
|
||||||
|
*/
|
||||||
|
|
||||||
|
unit_type gui_dist_vec_fallback(struct inst *self, struct coord pos,
|
||||||
|
unit_type scale)
|
||||||
|
{
|
||||||
|
unit_type d;
|
||||||
|
|
||||||
|
d = dist_line(pos, self->base, self->u.rect.end)/scale;
|
||||||
|
return d > SELECT_R ? -1 : d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gui_draw_vec(struct inst *self, struct draw_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct coord from = translate(ctx, self->base);
|
||||||
|
struct coord to = translate(ctx, self->u.rect.end);
|
||||||
|
GdkGC *gc;
|
||||||
|
|
||||||
|
gc = gc_vec[get_mode(self)];
|
||||||
|
draw_arrow(ctx, gc, TRUE, from, to, VEC_ARROW_LEN, VEC_ARROW_ANGLE);
|
||||||
|
gdk_draw_line(DA, gc, from.x, from.y, to.x, to.y);
|
||||||
|
draw_circle(ctx, gc, FALSE, to.x, to.y, VEC_EYE_R);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- line -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
unit_type gui_dist_line(struct inst *self, struct coord pos, unit_type scale)
|
||||||
|
{
|
||||||
|
unit_type r, d;
|
||||||
|
|
||||||
|
r = self->u.rect.width/scale/2;
|
||||||
|
if (r < SELECT_R)
|
||||||
|
r = SELECT_R;
|
||||||
|
d = dist_line(pos, self->bbox.min, self->bbox.max)/scale;
|
||||||
|
return d > r ? -1 : d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gui_draw_line(struct inst *self, struct draw_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct coord min = translate(ctx, self->base);
|
||||||
|
struct coord max = translate(ctx, self->u.rect.end);
|
||||||
|
GdkGC *gc;
|
||||||
|
|
||||||
|
gc = gc_obj[get_mode(self)];
|
||||||
|
set_width(gc, self->u.rect.width/ctx->scale);
|
||||||
|
gdk_draw_line(DA, gc, min.x, min.y, max.x, max.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- rect -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
unit_type gui_dist_rect(struct inst *self, struct coord pos, unit_type scale)
|
||||||
|
{
|
||||||
|
unit_type r, d;
|
||||||
|
|
||||||
|
r = self->u.rect.width/scale/2;
|
||||||
|
if (r < SELECT_R)
|
||||||
|
r = SELECT_R;
|
||||||
|
d = dist_rect(pos, self->bbox.min, self->bbox.max)/scale;
|
||||||
|
return d > r ? -1 : d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gui_draw_rect(struct inst *self, struct draw_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct coord min = translate(ctx, self->bbox.min);
|
||||||
|
struct coord max = translate(ctx, self->bbox.max);
|
||||||
|
GdkGC *gc;
|
||||||
|
|
||||||
|
gc = gc_obj[get_mode(self)];
|
||||||
|
set_width(gc, self->u.rect.width/ctx->scale);
|
||||||
|
gdk_draw_rectangle(DA, gc, FALSE,
|
||||||
|
min.x, max.y, max.x-min.x, min.y-max.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- pad --------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
unit_type gui_dist_pad(struct inst *self, struct coord pos, unit_type scale)
|
||||||
|
{
|
||||||
|
unit_type d;
|
||||||
|
|
||||||
|
if (inside_rect(pos, self->bbox.min, self->bbox.max))
|
||||||
|
return SELECT_R;
|
||||||
|
d = dist_rect(pos, self->bbox.min, self->bbox.max)/scale;
|
||||||
|
return d > SELECT_R ? -1 : d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gui_draw_pad(struct inst *self, struct draw_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct coord min = translate(ctx, self->bbox.min);
|
||||||
|
struct coord max = translate(ctx, self->bbox.max);
|
||||||
|
GdkGC *gc;
|
||||||
|
struct coord c;
|
||||||
|
unit_type h, w;
|
||||||
|
|
||||||
|
gc = gc_pad[get_mode(self)];
|
||||||
|
gdk_draw_rectangle(DA, gc, TRUE,
|
||||||
|
min.x, max.y, max.x-min.x, min.y-max.y);
|
||||||
|
|
||||||
|
gc = gc_ptext[get_mode(self)];
|
||||||
|
c = add_vec(min, max);
|
||||||
|
h = min.y-max.y;
|
||||||
|
w = max.x-min.x;
|
||||||
|
render_text(DA, gc, c.x/2, c.y/2, w <= h ? 0 : 90, self->u.name,
|
||||||
|
PAD_FONT, 0.5, 0.5,
|
||||||
|
w-2*PAD_BORDER, h-2*PAD_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- arc --------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static struct coord rotate_r(struct coord center, unit_type r, double angle)
|
||||||
|
{
|
||||||
|
struct coord res;
|
||||||
|
|
||||||
|
angle = angle/180.0*M_PI;
|
||||||
|
res.x = center.x+r*cos(angle);
|
||||||
|
res.y = center.y+r*sin(angle);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale)
|
||||||
|
{
|
||||||
|
struct coord c = self->base;
|
||||||
|
struct coord p;
|
||||||
|
unit_type r, d_min, d;
|
||||||
|
double angle, a2;
|
||||||
|
|
||||||
|
r = self->u.arc.width/scale/2;
|
||||||
|
if (r < SELECT_R)
|
||||||
|
r = SELECT_R;
|
||||||
|
|
||||||
|
/* check endpoints */
|
||||||
|
|
||||||
|
p = rotate_r(c, self->u.arc.r, self->u.arc.a1);
|
||||||
|
d_min = hypot(pos.x-p.x, pos.y-p.y);
|
||||||
|
|
||||||
|
p = rotate_r(c, self->u.arc.r, self->u.arc.a2);
|
||||||
|
d = hypot(pos.x-p.x, pos.y-p.y);
|
||||||
|
if (d < d_min)
|
||||||
|
d_min = d;
|
||||||
|
|
||||||
|
if (d_min/scale <= r)
|
||||||
|
return d;
|
||||||
|
|
||||||
|
/* distance from the circle containing the arc */
|
||||||
|
|
||||||
|
d = dist_circle(pos, c, self->u.arc.r)/scale;
|
||||||
|
if (d > r)
|
||||||
|
return -1;
|
||||||
|
if (self->u.arc.a1 == self->u.arc.a2)
|
||||||
|
return d;
|
||||||
|
|
||||||
|
/* see if we're close to the part that's actually drawn */
|
||||||
|
|
||||||
|
angle = atan2(pos.y-c.y, pos.x-c.x)/M_PI*180.0;
|
||||||
|
if (angle < 0)
|
||||||
|
angle += 180;
|
||||||
|
a2 = self->u.arc.a2;
|
||||||
|
if (a2 < self->u.arc.a1)
|
||||||
|
a2 += 180;
|
||||||
|
return angle >= self->u.arc.a1 && angle <= a2 ? d : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gui_draw_arc(struct inst *self, struct draw_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct coord center = translate(ctx, self->base);
|
||||||
|
GdkGC *gc;
|
||||||
|
|
||||||
|
gc = gc_obj[get_mode(self)];
|
||||||
|
set_width(gc, self->u.arc.width/ctx->scale);
|
||||||
|
draw_arc(ctx, gc, FALSE, center.x, center.y,
|
||||||
|
self->u.arc.r/ctx->scale, self->u.arc.a1, self->u.arc.a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- meas -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static struct coord offset_vec(const struct inst *self)
|
||||||
|
{
|
||||||
|
struct coord a, b, res;
|
||||||
|
double f;
|
||||||
|
|
||||||
|
a = self->base;
|
||||||
|
b = self->u.meas.end;
|
||||||
|
res.x = a.y-b.y;
|
||||||
|
res.y = b.x-a.x;
|
||||||
|
if (res.x == 0 && res.y == 0)
|
||||||
|
return res;
|
||||||
|
f = self->u.meas.offset/hypot(res.x, res.y);
|
||||||
|
res.x *= f;
|
||||||
|
res.y *= f;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale)
|
||||||
|
{
|
||||||
|
struct coord a, b, off;
|
||||||
|
unit_type d;
|
||||||
|
|
||||||
|
off = offset_vec(self);
|
||||||
|
a = add_vec(self->base, off);
|
||||||
|
b = add_vec(self->u.meas.end, off);
|
||||||
|
d = dist_line(pos, a, b)/scale;
|
||||||
|
return d > SELECT_R ? -1 : d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gui_draw_meas(struct inst *self, struct draw_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct coord a0, b0, a1, b1, off, c, d;
|
||||||
|
GdkGC *gc;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
off = offset_vec(self);
|
||||||
|
a0 = translate(ctx, self->base);
|
||||||
|
b0 = translate(ctx, self->u.meas.end);
|
||||||
|
a1 = translate(ctx, add_vec(self->base, off));
|
||||||
|
b1 = translate(ctx, add_vec(self->u.meas.end, off));
|
||||||
|
gc = gc_meas[get_mode(self)];
|
||||||
|
gdk_draw_line(DA, gc, a0.x, a0.y, a1.x, a1.y);
|
||||||
|
gdk_draw_line(DA, gc, b0.x, b0.y, b1.x, b1.y);
|
||||||
|
gdk_draw_line(DA, gc, a1.x, a1.y, b1.x, b1.y);
|
||||||
|
draw_arrow(ctx, gc, FALSE, a1, b1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
|
||||||
|
draw_arrow(ctx, gc, FALSE, b1, a1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
|
||||||
|
|
||||||
|
c = add_vec(a1, b1);
|
||||||
|
d = sub_vec(b0, a0);
|
||||||
|
s = stralloc_printf("%lgmm",
|
||||||
|
units_to_mm(dist_point(self->base, self->u.meas.end)));
|
||||||
|
render_text(DA, gc, c.x/2, c.y/2, -atan2(d.y, d.x)/M_PI*180, s,
|
||||||
|
MEAS_FONT, 0.5, -MEAS_BASELINE_OFFSET,
|
||||||
|
dist_point(a1, b1)-1.5*MEAS_ARROW_LEN, 0);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- frame ------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
void gui_draw_frame(struct inst *self, struct draw_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct coord center = translate(ctx, self->base);
|
||||||
|
struct coord corner = { self->bbox.min.x, self->bbox.max.y };
|
||||||
|
GdkGC *gc;
|
||||||
|
|
||||||
|
gc = gc_frame[get_mode(self)];
|
||||||
|
draw_eye(ctx, gc, center, FRAME_EYE_R1, FRAME_EYE_R2);
|
||||||
|
if (!self->u.frame.ref->name)
|
||||||
|
return;
|
||||||
|
corner = translate(ctx, corner);
|
||||||
|
corner.x -= FRAME_CLEARANCE;
|
||||||
|
corner.y -= FRAME_CLEARANCE;
|
||||||
|
gdk_draw_line(DA, gc, corner.x, corner.y,
|
||||||
|
corner.x+FRAME_SHORT_X, corner.y);
|
||||||
|
gdk_draw_line(DA, gc, corner.x, corner.y,
|
||||||
|
corner.x, corner.y+FRAME_SHORT_Y);
|
||||||
|
render_text(DA, gc, corner.x, corner.y, 0, self->u.frame.ref->name,
|
||||||
|
FRAME_FONT, 0, -FRAME_BASELINE_OFFSET, 0, 0);
|
||||||
|
}
|
50
gui_inst.h
Normal file
50
gui_inst.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* gui_inst.h - GUI, instance functions
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUI_INST_H
|
||||||
|
#define GUI_INST_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "coord.h"
|
||||||
|
#include "inst.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct draw_ctx {
|
||||||
|
GtkWidget *widget;
|
||||||
|
int scale;
|
||||||
|
struct coord center;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct coord translate(const struct draw_ctx *ctx, struct coord pos);
|
||||||
|
struct coord canvas_to_coord(const struct draw_ctx *ctx, int x, int y);
|
||||||
|
|
||||||
|
unit_type gui_dist_vec(struct inst *self, struct coord pos, unit_type scale);
|
||||||
|
unit_type gui_dist_vec_fallback(struct inst *self, struct coord pos,
|
||||||
|
unit_type scale);
|
||||||
|
unit_type gui_dist_line(struct inst *self, struct coord pos, unit_type scale);
|
||||||
|
unit_type gui_dist_rect(struct inst *self, struct coord pos, unit_type scale);
|
||||||
|
unit_type gui_dist_pad(struct inst *self, struct coord pos, unit_type scale);
|
||||||
|
unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale);
|
||||||
|
unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale);
|
||||||
|
|
||||||
|
void gui_draw_vec(struct inst *self, struct draw_ctx *ctx);
|
||||||
|
void gui_draw_line(struct inst *self, struct draw_ctx *ctx);
|
||||||
|
void gui_draw_rect(struct inst *self, struct draw_ctx *ctx);
|
||||||
|
void gui_draw_pad(struct inst *self, struct draw_ctx *ctx);
|
||||||
|
void gui_draw_arc(struct inst *self, struct draw_ctx *ctx);
|
||||||
|
void gui_draw_meas(struct inst *self, struct draw_ctx *ctx);
|
||||||
|
void gui_draw_frame(struct inst *self, struct draw_ctx *ctx);
|
||||||
|
|
||||||
|
#endif /* !GUI_INST_H */
|
476
gui_status.c
Normal file
476
gui_status.c
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
/*
|
||||||
|
* gui_status.c - GUI, status area
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "coord.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "unparse.h"
|
||||||
|
#include "gui_util.h"
|
||||||
|
#include "gui_style.h"
|
||||||
|
#include "gui.h"
|
||||||
|
#include "gui_status.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct edit_ops {
|
||||||
|
int (*changed)(GtkWidget *widget, const char *s, void *ctx);
|
||||||
|
int (*activate)(GtkWidget *widget, const char *s, void *ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct edit_ops *edit_ops = NULL;
|
||||||
|
static void *edit_ctx;
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- setter functions -------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static GtkWidget *status_name, *status_entry;
|
||||||
|
static GtkWidget *status_x, *status_y;
|
||||||
|
static GtkWidget *status_r, *status_angle;
|
||||||
|
static GtkWidget *status_sys_pos, *status_user_pos;
|
||||||
|
static GtkWidget *status_zoom, *status_grid;
|
||||||
|
static GtkWidget *status_msg;
|
||||||
|
|
||||||
|
|
||||||
|
static void set_label(GtkWidget *label, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
s = stralloc_vprintf(fmt, ap);
|
||||||
|
gtk_label_set_text(GTK_LABEL(label), s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define SETTER(name) \
|
||||||
|
void status_set_##name(const char *fmt, ...) \
|
||||||
|
{ \
|
||||||
|
va_list ap; \
|
||||||
|
\
|
||||||
|
va_start(ap, fmt); \
|
||||||
|
set_label(status_##name, fmt, ap); \
|
||||||
|
va_end(ap); \
|
||||||
|
}
|
||||||
|
|
||||||
|
SETTER(name)
|
||||||
|
SETTER(x)
|
||||||
|
SETTER(y)
|
||||||
|
SETTER(r)
|
||||||
|
SETTER(angle)
|
||||||
|
SETTER(sys_pos)
|
||||||
|
SETTER(user_pos)
|
||||||
|
SETTER(zoom)
|
||||||
|
SETTER(grid)
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- complex status updates -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
void status_set_xy(struct coord coord)
|
||||||
|
{
|
||||||
|
status_set_x("x = %5.2f mm", units_to_mm(coord.x));
|
||||||
|
status_set_y("y = %5.2f mm", units_to_mm(coord.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void entry_color(const char *color)
|
||||||
|
{
|
||||||
|
GdkColor col;
|
||||||
|
|
||||||
|
col = get_color(color);
|
||||||
|
gtk_widget_modify_base(GTK_WIDGET(status_entry),
|
||||||
|
GTK_STATE_NORMAL, &col);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- identifier fields ------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
struct edit_unique_ctx {
|
||||||
|
const char **s;
|
||||||
|
int (*validate)(const char *s, void *ctx);
|
||||||
|
void *ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int unique_changed(GtkWidget *widget, const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
const struct edit_unique_ctx *unique_ctx = ctx;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
if (!strcmp(s, *unique_ctx->s)) {
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ok = !unique_ctx->validate || unique_ctx->validate(s, unique_ctx->ctx);
|
||||||
|
entry_color(ok ? COLOR_EDIT_GOOD : COLOR_EDIT_BAD);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int unique_activate(GtkWidget *widget, const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
const struct edit_unique_ctx *unique_ctx = ctx;
|
||||||
|
|
||||||
|
if (strcmp(s, *unique_ctx->s) &&
|
||||||
|
unique_ctx->validate && !unique_ctx->validate(s, unique_ctx->ctx))
|
||||||
|
return 0;
|
||||||
|
*unique_ctx->s = unique(s);
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct edit_ops edit_ops_unique = {
|
||||||
|
.changed = unique_changed,
|
||||||
|
.activate = unique_activate,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void edit_unique(const char **s, int (*validate)(const char *s, void *ctx),
|
||||||
|
void *ctx)
|
||||||
|
{
|
||||||
|
static struct edit_unique_ctx unique_ctx;
|
||||||
|
|
||||||
|
unique_ctx.s = s;
|
||||||
|
unique_ctx.validate = validate;
|
||||||
|
unique_ctx.ctx = ctx;
|
||||||
|
edit_ops = &edit_ops_unique;
|
||||||
|
edit_ctx = &unique_ctx;
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(status_entry), *s);
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
gtk_widget_show(status_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- identifier fields with NULL --------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static int unique_null_changed(GtkWidget *widget, const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
const struct edit_unique_ctx *unique_ctx = ctx;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
if (!strcmp(s, *unique_ctx->s ? *unique_ctx->s : "")) {
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ok = !*s;
|
||||||
|
if (!ok)
|
||||||
|
ok = !unique_ctx->validate ||
|
||||||
|
unique_ctx->validate(s, unique_ctx->ctx);
|
||||||
|
entry_color(ok ? COLOR_EDIT_GOOD : COLOR_EDIT_BAD);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int unique_null_activate(GtkWidget *widget, const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
const struct edit_unique_ctx *unique_ctx = ctx;
|
||||||
|
|
||||||
|
if (!*s)
|
||||||
|
*unique_ctx->s = NULL;
|
||||||
|
else {
|
||||||
|
if (unique_ctx->validate &&
|
||||||
|
!unique_ctx->validate(s, unique_ctx->ctx))
|
||||||
|
return 0;
|
||||||
|
*unique_ctx->s = unique(s);
|
||||||
|
}
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct edit_ops edit_ops_null_unique = {
|
||||||
|
.changed = unique_null_changed,
|
||||||
|
.activate = unique_null_activate,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void edit_unique_null(const char **s,
|
||||||
|
int (*validate)(const char *s, void *ctx), void *ctx)
|
||||||
|
{
|
||||||
|
static struct edit_unique_ctx unique_ctx;
|
||||||
|
|
||||||
|
unique_ctx.s = s;
|
||||||
|
unique_ctx.validate = validate;
|
||||||
|
unique_ctx.ctx = ctx;
|
||||||
|
edit_ops = &edit_ops_null_unique;
|
||||||
|
edit_ctx = &unique_ctx;
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(status_entry), *s ? *s : "");
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
gtk_widget_show(status_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- string fields ----------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
struct edit_name_ctx {
|
||||||
|
char **s;
|
||||||
|
int (*validate)(const char *s, void *ctx);
|
||||||
|
void *ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int name_changed(GtkWidget *widget, const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
const struct edit_name_ctx *name_ctx = ctx;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
if (!strcmp(s, *name_ctx->s)) {
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ok = !name_ctx->validate || name_ctx->validate(s, name_ctx->ctx);
|
||||||
|
entry_color(ok ? COLOR_EDIT_GOOD : COLOR_EDIT_BAD);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int name_activate(GtkWidget *widget, const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
const struct edit_name_ctx *name_ctx = ctx;
|
||||||
|
|
||||||
|
if (name_ctx->validate && !name_ctx->validate(s, name_ctx->ctx))
|
||||||
|
return 0;
|
||||||
|
free(*name_ctx->s);
|
||||||
|
*name_ctx->s = stralloc(s);
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct edit_ops edit_ops_name = {
|
||||||
|
.changed = name_changed,
|
||||||
|
.activate = name_activate,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void edit_name(char **s, int (*validate)(const char *s, void *ctx), void *ctx)
|
||||||
|
{
|
||||||
|
static struct edit_name_ctx name_ctx;
|
||||||
|
|
||||||
|
name_ctx.s = s;
|
||||||
|
name_ctx.validate = validate;
|
||||||
|
name_ctx.ctx = ctx;
|
||||||
|
edit_ops = &edit_ops_name;
|
||||||
|
edit_ctx = &name_ctx;
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(status_entry), *s);
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
gtk_widget_show(status_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- expression fields ------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static struct expr *try_parse_expr(const char *s)
|
||||||
|
{
|
||||||
|
status_begin_reporting();
|
||||||
|
return parse_expr(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int expr_changed(GtkWidget *widget, const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
struct expr *expr;
|
||||||
|
|
||||||
|
expr = try_parse_expr(s);
|
||||||
|
if (!expr) {
|
||||||
|
entry_color(COLOR_EDIT_BAD);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
entry_color(COLOR_EDIT_GOOD);
|
||||||
|
free_expr(expr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int expr_activate(GtkWidget *widget, const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
struct expr **anchor = ctx;
|
||||||
|
struct expr *expr;
|
||||||
|
|
||||||
|
expr = try_parse_expr(s);
|
||||||
|
if (!expr)
|
||||||
|
return 0;
|
||||||
|
free_expr(*anchor);
|
||||||
|
*anchor = expr;
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct edit_ops edit_ops_expr = {
|
||||||
|
.changed = expr_changed,
|
||||||
|
.activate = expr_activate,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void edit_expr(struct expr **expr)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
edit_ops = &edit_ops_expr;
|
||||||
|
edit_ctx = expr;
|
||||||
|
s = unparse(*expr);
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(status_entry), s);
|
||||||
|
free(s);
|
||||||
|
entry_color(COLOR_EDIT_ASIS);
|
||||||
|
gtk_widget_show(status_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- text entry -------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean changed(GtkWidget *widget, GdkEventMotion *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
if (edit_ops && edit_ops->changed)
|
||||||
|
edit_ops->changed(widget,
|
||||||
|
gtk_entry_get_text(GTK_ENTRY(widget)), edit_ctx);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean activate(GtkWidget *widget, GdkEventMotion *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
if (edit_ops && edit_ops->activate)
|
||||||
|
if (edit_ops->activate(widget,
|
||||||
|
gtk_entry_get_text(GTK_ENTRY(widget)), edit_ctx)) {
|
||||||
|
inst_deselect();
|
||||||
|
change_world();
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void edit_nothing(void)
|
||||||
|
{
|
||||||
|
edit_ops = NULL;
|
||||||
|
gtk_widget_hide(status_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- status reports ---------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static gint context_id;
|
||||||
|
static int have_msg = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static void clear_status_msg(void)
|
||||||
|
{
|
||||||
|
if (have_msg) {
|
||||||
|
gtk_statusbar_pop(GTK_STATUSBAR(status_msg), context_id);
|
||||||
|
have_msg = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void report_to_gui(const char *s)
|
||||||
|
{
|
||||||
|
if (!have_msg)
|
||||||
|
gtk_statusbar_push(GTK_STATUSBAR(status_msg), context_id, s);
|
||||||
|
have_msg = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void status_begin_reporting(void)
|
||||||
|
{
|
||||||
|
clear_status_msg();
|
||||||
|
reporter = report_to_gui;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- setup ------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static GtkWidget *add_label(GtkWidget *tab, int col, int row)
|
||||||
|
{
|
||||||
|
GtkWidget *label;
|
||||||
|
|
||||||
|
label = label_in_box_new(NULL);
|
||||||
|
gtk_table_attach_defaults(GTK_TABLE(tab), box_of_label(label),
|
||||||
|
col, col+1, row, row+1);
|
||||||
|
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
|
||||||
|
gtk_label_set_selectable(GTK_LABEL(label), TRUE);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void make_status_area(GtkWidget *vbox)
|
||||||
|
{
|
||||||
|
GtkWidget *tab, *hbox;
|
||||||
|
|
||||||
|
tab = gtk_table_new(5, 2, FALSE);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), tab, FALSE, TRUE, 0);
|
||||||
|
|
||||||
|
/* name and input */
|
||||||
|
|
||||||
|
status_name = add_label(tab, 0, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @@@ this should make the entry consume all available space. alas, it
|
||||||
|
* doesn't work like that :-(
|
||||||
|
*/
|
||||||
|
hbox = gtk_hbox_new(TRUE, 0);
|
||||||
|
gtk_table_attach_defaults(GTK_TABLE(tab), hbox,
|
||||||
|
0, 1, 1, 2);
|
||||||
|
|
||||||
|
status_entry = gtk_entry_new();
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), status_entry, TRUE, TRUE, 0);
|
||||||
|
|
||||||
|
gtk_entry_set_has_frame(GTK_ENTRY(status_entry), FALSE);
|
||||||
|
|
||||||
|
g_signal_connect(G_OBJECT(status_entry), "changed",
|
||||||
|
G_CALLBACK(changed), status_entry);
|
||||||
|
g_signal_connect(G_OBJECT(status_entry), "activate",
|
||||||
|
G_CALLBACK(activate), status_entry);
|
||||||
|
|
||||||
|
/* x / y */
|
||||||
|
|
||||||
|
status_x = add_label(tab, 1, 0);
|
||||||
|
status_y = add_label(tab, 1, 1);
|
||||||
|
|
||||||
|
/* r / angle */
|
||||||
|
|
||||||
|
status_r = add_label(tab, 2, 0);
|
||||||
|
status_angle = add_label(tab, 2, 1);
|
||||||
|
|
||||||
|
/* sys / user pos */
|
||||||
|
|
||||||
|
status_sys_pos = add_label(tab, 3, 0);
|
||||||
|
status_user_pos = add_label(tab, 3, 1);
|
||||||
|
|
||||||
|
/* zoom / grid */
|
||||||
|
|
||||||
|
status_zoom = add_label(tab, 4, 0);
|
||||||
|
status_grid = add_label(tab, 4, 1);
|
||||||
|
|
||||||
|
/* message bar */
|
||||||
|
|
||||||
|
status_msg = gtk_statusbar_new();
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), status_msg, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(status_msg),
|
||||||
|
"messages");
|
||||||
|
}
|
47
gui_status.h
Normal file
47
gui_status.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* gui_status.h - GUI, status area
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUI_STATUS_H
|
||||||
|
#define GUI_STATUS_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "coord.h"
|
||||||
|
#include "expr.h"
|
||||||
|
|
||||||
|
|
||||||
|
void edit_unique(const char **s, int (*validate)(const char *s, void *ctx),
|
||||||
|
void *ctx);
|
||||||
|
void edit_unique_null(const char **s, int (*validate)(const char *s, void *ctx),
|
||||||
|
void *ctx);
|
||||||
|
void edit_name(char **s, int (*validate)(const char *s, void *ctx), void *ctx);
|
||||||
|
void edit_expr(struct expr **expr);
|
||||||
|
void edit_nothing(void);
|
||||||
|
|
||||||
|
void status_set_name(const char *fmt, ...);
|
||||||
|
void status_set_x(const char *fmt, ...);
|
||||||
|
void status_set_y(const char *fmt, ...);
|
||||||
|
void status_set_r(const char *fmt, ...);
|
||||||
|
void status_set_angle(const char *fmt, ...);
|
||||||
|
void status_set_sys_pos(const char *fmt, ...);
|
||||||
|
void status_set_user_pos(const char *fmt, ...);
|
||||||
|
void status_set_zoom(const char *fmt, ...);
|
||||||
|
void status_set_grid(const char *fmt, ...);
|
||||||
|
|
||||||
|
void status_set_xy(struct coord coord);
|
||||||
|
|
||||||
|
void status_begin_reporting(void);
|
||||||
|
|
||||||
|
void make_status_area(GtkWidget *vbox) ;
|
||||||
|
|
||||||
|
#endif /* !GUI_STATUS_H */
|
69
gui_style.c
Normal file
69
gui_style.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* gui_style.c - GUI, style definitions
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "inst.h"
|
||||||
|
#include "gui_util.h"
|
||||||
|
#include "gui.h"
|
||||||
|
#include "gui_style.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define INVALID "#00ffff"
|
||||||
|
|
||||||
|
|
||||||
|
GdkGC *gc_bg;
|
||||||
|
GdkGC *gc_vec[mode_n];
|
||||||
|
GdkGC *gc_obj[mode_n];
|
||||||
|
GdkGC *gc_pad[mode_n];
|
||||||
|
GdkGC *gc_ptext[mode_n];
|
||||||
|
GdkGC *gc_meas[mode_n];
|
||||||
|
GdkGC *gc_frame[mode_n];
|
||||||
|
|
||||||
|
|
||||||
|
static GdkGC *gc(const char *spec, int width)
|
||||||
|
{
|
||||||
|
GdkGCValues gc_values = {
|
||||||
|
.background = get_color("black"),
|
||||||
|
.foreground = get_color(spec),
|
||||||
|
.line_width = width,
|
||||||
|
};
|
||||||
|
|
||||||
|
return gdk_gc_new_with_values(root->window, &gc_values,
|
||||||
|
GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_LINE_WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void style(GdkGC *gcs[mode_n],
|
||||||
|
const char *in, const char *in_path, const char *act, const char *act_path,
|
||||||
|
const char *sel)
|
||||||
|
{
|
||||||
|
gcs[mode_inactive] = gc(in, 1);
|
||||||
|
gcs[mode_inactive_in_path] = gc(in_path, 1);
|
||||||
|
gcs[mode_active] = gc(act, 1);
|
||||||
|
gcs[mode_active_in_path] = gc(act_path, 1);
|
||||||
|
gcs[mode_selected] = gc(sel, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gui_setup_style(GdkDrawable *drawable)
|
||||||
|
{
|
||||||
|
gc_bg = gc("#000000", 0);
|
||||||
|
/* inactive in+path active act+path selected */
|
||||||
|
style(gc_vec, "#202000", "#404020", "#909040", "#c0c080", "#ffff80");
|
||||||
|
style(gc_obj, "#006060", INVALID, "#00ffff", INVALID, "#ffff80");
|
||||||
|
style(gc_pad, "#400000", INVALID, "#ff0000", INVALID, "#ffff80");
|
||||||
|
style(gc_ptext, "#404040", INVALID, "#ffffff", INVALID, "#ffffff");
|
||||||
|
style(gc_meas, "#280040", INVALID, "#ff00ff", INVALID, "#ffff80");
|
||||||
|
style(gc_frame, "#004000", "#205020", "#00ff00", INVALID, INVALID);
|
||||||
|
}
|
91
gui_style.h
Normal file
91
gui_style.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* gui_style.h - GUI, style definitions
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUI_STYLE_H
|
||||||
|
#define GUI_STYLE_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "inst.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- screen distances, etc. -------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
#define CANVAS_CLEARANCE 10
|
||||||
|
|
||||||
|
#define VEC_ARROW_LEN 10
|
||||||
|
#define VEC_ARROW_ANGLE 20
|
||||||
|
#define VEC_EYE_R 5
|
||||||
|
|
||||||
|
#define PAD_FONT "Sans Bold 24"
|
||||||
|
#define PAD_BORDER 2
|
||||||
|
|
||||||
|
#define MEAS_FONT "Sans 8"
|
||||||
|
#define MEAS_BASELINE_OFFSET 0.1
|
||||||
|
#define MEAS_ARROW_LEN 9
|
||||||
|
#define MEAS_ARROW_ANGLE 30
|
||||||
|
|
||||||
|
#define FRAME_FONT "Sans 8"
|
||||||
|
#define FRAME_BASELINE_OFFSET 0.1
|
||||||
|
#define FRAME_SHORT_X 100
|
||||||
|
#define FRAME_SHORT_Y 20
|
||||||
|
#define FRAME_CLEARANCE 5
|
||||||
|
#define FRAME_EYE_R1 3
|
||||||
|
#define FRAME_EYE_R2 5
|
||||||
|
|
||||||
|
#define SELECT_R 6 /* pixels within which we select */
|
||||||
|
|
||||||
|
#define MIN_FONT_SCALE 0.20 /* don't scale fonts below this */
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- assorted colors --------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
#define COLOR_EDIT_ASIS "#ffffff"
|
||||||
|
#define COLOR_EDIT_GOOD "#a0ffa0"
|
||||||
|
#define COLOR_EDIT_BAD "#ffa0a0"
|
||||||
|
|
||||||
|
#define COLOR_EDITING "#ff00ff"
|
||||||
|
|
||||||
|
#define COLOR_FRAME_UNSELECTED "#c0c0c0"
|
||||||
|
#define COLOR_FRAME_SELECTED "#fff0a0"
|
||||||
|
#define COLOR_FRAME_EDITING COLOR_EDITING
|
||||||
|
|
||||||
|
#define COLOR_VAR_PASSIVE COLOR_FRAME_UNSELECTED
|
||||||
|
#define COLOR_VAR_EDITING COLOR_EDITING
|
||||||
|
#define COLOR_EXPR_PASSIVE "#f0f0ff"
|
||||||
|
#define COLOR_EXPR_EDITING COLOR_EDITING
|
||||||
|
#define COLOR_CHOICE_UNSELECTED COLOR_EXPR_PASSIVE
|
||||||
|
#define COLOR_CHOICE_SELECTED "#9090ff"
|
||||||
|
#define COLOR_ROW_UNSELECTED COLOR_CHOICE_UNSELECTED
|
||||||
|
#define COLOR_ROW_SELECTED COLOR_CHOICE_SELECTED
|
||||||
|
|
||||||
|
#define COLOR_VAR_TABLE_SEP "black"
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- canvas drawing styles --------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
extern GdkGC *gc_bg;
|
||||||
|
extern GdkGC *gc_vec[mode_n];
|
||||||
|
extern GdkGC *gc_obj[mode_n];
|
||||||
|
extern GdkGC *gc_pad[mode_n];
|
||||||
|
extern GdkGC *gc_ptext[mode_n];
|
||||||
|
extern GdkGC *gc_meas[mode_n];
|
||||||
|
extern GdkGC *gc_frame[mode_n];
|
||||||
|
|
||||||
|
|
||||||
|
void gui_setup_style(GdkDrawable *drawable);
|
||||||
|
|
||||||
|
#endif /* !GUI_STYLE_H */
|
167
gui_util.c
Normal file
167
gui_util.c
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* gui_util.c - GUI helper functions
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "gui_style.h"
|
||||||
|
#include "gui.h"
|
||||||
|
#include "gui_util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- look up a color --------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
GdkColor get_color(const char *spec)
|
||||||
|
{
|
||||||
|
GdkColormap *cmap;
|
||||||
|
GdkColor color;
|
||||||
|
|
||||||
|
cmap = gdk_drawable_get_colormap(root->window);
|
||||||
|
if (!gdk_color_parse(spec, &color))
|
||||||
|
abort();
|
||||||
|
if (!gdk_colormap_alloc_color(cmap, &color, FALSE, TRUE))
|
||||||
|
abort();
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- lines with a width ------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
void set_width(GdkGC *gc, int width)
|
||||||
|
{
|
||||||
|
gdk_gc_set_line_attributes(gc, width < 1 ? 1 : width,
|
||||||
|
GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- labels in a box --------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
GtkWidget *label_in_box_new(const char *s)
|
||||||
|
{
|
||||||
|
GtkWidget *evbox, *label;
|
||||||
|
|
||||||
|
evbox = gtk_event_box_new();
|
||||||
|
label = gtk_label_new(s);
|
||||||
|
gtk_misc_set_padding(GTK_MISC(label), 1, 1);
|
||||||
|
gtk_container_add(GTK_CONTAINER(evbox), label);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GtkWidget *box_of_label(GtkWidget *label)
|
||||||
|
{
|
||||||
|
return gtk_widget_get_ancestor(label, GTK_TYPE_EVENT_BOX);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void label_in_box_bg(GtkWidget *label, const char *color)
|
||||||
|
{
|
||||||
|
GtkWidget *box;
|
||||||
|
GdkColor col = get_color(color);
|
||||||
|
|
||||||
|
box = box_of_label(label);
|
||||||
|
gtk_widget_modify_bg(box, GTK_STATE_NORMAL, &col);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- render a text string ---------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
void render_text(GdkDrawable *da, GdkGC *gc, int x, int y, double angle,
|
||||||
|
const char *s, const char *font, double xalign, double yalign,
|
||||||
|
int xmax, int ymax)
|
||||||
|
{
|
||||||
|
GdkScreen *screen;
|
||||||
|
PangoRenderer *renderer;
|
||||||
|
PangoContext *context;
|
||||||
|
PangoLayout *layout;
|
||||||
|
PangoFontDescription *desc;
|
||||||
|
int width, height;
|
||||||
|
PangoMatrix m = PANGO_MATRIX_INIT;
|
||||||
|
double f_min, f;
|
||||||
|
|
||||||
|
/* set up the renderer */
|
||||||
|
|
||||||
|
screen = gdk_drawable_get_screen(da);
|
||||||
|
renderer = gdk_pango_renderer_get_default(screen);
|
||||||
|
gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER(renderer), da);
|
||||||
|
gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER(renderer), gc);
|
||||||
|
|
||||||
|
/* start preparing the layout */
|
||||||
|
|
||||||
|
context = gdk_pango_context_get_for_screen(screen);
|
||||||
|
layout = pango_layout_new(context);
|
||||||
|
pango_layout_set_text(layout, s, -1);
|
||||||
|
|
||||||
|
/* apply the font */
|
||||||
|
|
||||||
|
desc = pango_font_description_from_string(font);
|
||||||
|
pango_layout_set_font_description(layout, desc);
|
||||||
|
pango_font_description_free(desc);
|
||||||
|
|
||||||
|
/* align and position the text */
|
||||||
|
|
||||||
|
pango_layout_get_size(layout, &width, &height);
|
||||||
|
f_min = 1.0;
|
||||||
|
if (xmax) {
|
||||||
|
f = xmax/((double) width/PANGO_SCALE);
|
||||||
|
if (f < f_min)
|
||||||
|
f_min = f;
|
||||||
|
}
|
||||||
|
if (ymax) {
|
||||||
|
f = ymax/((double) height/PANGO_SCALE);
|
||||||
|
if (f < f_min)
|
||||||
|
f_min = f;
|
||||||
|
}
|
||||||
|
if (f_min < MIN_FONT_SCALE)
|
||||||
|
f_min = MIN_FONT_SCALE;
|
||||||
|
pango_matrix_translate(&m, x, y);
|
||||||
|
pango_matrix_rotate(&m, angle);
|
||||||
|
pango_matrix_translate(&m,
|
||||||
|
-xalign*f_min*width/PANGO_SCALE,
|
||||||
|
(yalign-1)*f_min*height/PANGO_SCALE);
|
||||||
|
pango_matrix_scale(&m, f_min, f_min);
|
||||||
|
|
||||||
|
pango_context_set_matrix(context, &m);
|
||||||
|
pango_layout_context_changed(layout);
|
||||||
|
pango_renderer_draw_layout(renderer, layout, 0, 0);
|
||||||
|
|
||||||
|
/* clean up renderer */
|
||||||
|
|
||||||
|
gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER(renderer), NULL);
|
||||||
|
gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER(renderer), NULL);
|
||||||
|
|
||||||
|
/* free objects */
|
||||||
|
|
||||||
|
g_object_unref(layout);
|
||||||
|
g_object_unref(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- kill the content of a container ----------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void destroy_callback(GtkWidget *widget, gpointer data)
|
||||||
|
{
|
||||||
|
gtk_widget_destroy(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void destroy_all_children(GtkContainer *container)
|
||||||
|
{
|
||||||
|
gtk_container_foreach(container, destroy_callback, NULL);
|
||||||
|
}
|
34
gui_util.h
Normal file
34
gui_util.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* gui_util.h - GUI helper functions
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUI_UTIL_H
|
||||||
|
#define GUI_UTIL_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
|
||||||
|
GdkColor get_color(const char *spec);
|
||||||
|
|
||||||
|
void set_width(GdkGC *gc, int width);
|
||||||
|
|
||||||
|
GtkWidget *label_in_box_new(const char *s);
|
||||||
|
GtkWidget *box_of_label(GtkWidget *label);
|
||||||
|
void label_in_box_bg(GtkWidget *box, const char *color);
|
||||||
|
|
||||||
|
void render_text(GdkDrawable *da, GdkGC *gc, int x, int y, double angle,
|
||||||
|
const char *s, const char *font, double xalign, double yalign,
|
||||||
|
int xmax, int ymax);
|
||||||
|
|
||||||
|
void destroy_all_children(GtkContainer *container);
|
||||||
|
|
||||||
|
#endif /* !GUI_UTIL_H */
|
13
icons/arc.fig
Normal file
13
icons/arc.fig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
5 1 0 10 3 7 50 -1 -1 0.000 0 1 1 0 4005.000 4395.000 5550 4500 5100 3300 3900 2850
|
||||||
|
0 0 10.00 450.00 600.00
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
12
icons/circ.fig
Normal file
12
icons/circ.fig
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
1 3 0 10 3 7 50 -1 -1 0.000 1 0.0000 4800 3600 900 900 4800 3600 5700 3600
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
14
icons/frame.fig
Normal file
14
icons/frame.fig
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
||||||
|
2 1 0 10 12 7 50 -1 -1 0.000 0 0 -1 0 0 3
|
||||||
|
3900 4275 3900 3675 5700 3675
|
||||||
|
4 0 12 50 -1 22 42 0.0000 4 135 450 3750 3525 FRAME\001
|
13
icons/line.fig
Normal file
13
icons/line.fig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
||||||
|
2 1 0 10 3 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||||
|
3900 4200 5700 3000
|
19
icons/meas.fig
Normal file
19
icons/meas.fig
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
||||||
|
2 1 0 10 21 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||||
|
3900 3300 3900 3900
|
||||||
|
2 1 0 10 21 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||||
|
5700 3300 5700 3900
|
||||||
|
2 1 0 10 21 7 50 -1 -1 0.000 0 0 -1 1 1 2
|
||||||
|
0 0 10.00 450.00 450.00
|
||||||
|
0 0 10.00 450.00 450.00
|
||||||
|
3900 3600 5700 3600
|
15
icons/pad.fig
Normal file
15
icons/pad.fig
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
||||||
|
2 2 0 0 0 4 50 -1 20 0.000 0 0 -1 0 0 5
|
||||||
|
4200 2700 5400 2700 5400 4500 4200 4500 4200 2700
|
||||||
|
2 1 0 15 7 7 45 -1 -1 0.000 1 1 -1 0 0 3
|
||||||
|
4875 4050 4875 3150 4650 3375
|
14
icons/point.fig
Normal file
14
icons/point.fig
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
||||||
|
2 3 0 0 0 7 50 -1 10 0.000 0 0 -1 0 0 8
|
||||||
|
3900 4350 4050 4500 5250 3300 5325 3675 5700 2700 4725 3075
|
||||||
|
5100 3150 3900 4350
|
13
icons/rect.fig
Normal file
13
icons/rect.fig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
||||||
|
2 2 0 10 3 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3900 3000 5700 3000 5700 4200 3900 4200 3900 3000
|
11
icons/template.fig
Normal file
11
icons/template.fig
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
17
icons/vec.fig
Normal file
17
icons/vec.fig
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#FIG 3.2 Produced by xfig version 3.2.5a
|
||||||
|
Landscape
|
||||||
|
Center
|
||||||
|
Inches
|
||||||
|
A4
|
||||||
|
100.00
|
||||||
|
Single
|
||||||
|
-2
|
||||||
|
1200 2
|
||||||
|
0 32 #c0c000
|
||||||
|
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||||
|
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
|
||||||
|
2 1 0 10 32 7 50 -1 -1 0.000 0 0 -1 1 0 2
|
||||||
|
1 1 10.00 300.00 300.00
|
||||||
|
3900 4275 5700 3075
|
||||||
|
2 1 0 10 32 7 50 -1 -1 0.000 1 1 -1 0 0 3
|
||||||
|
4050 3150 4575 3525 4425 2925
|
665
inst.c
Normal file
665
inst.c
Normal file
@ -0,0 +1,665 @@
|
|||||||
|
/*
|
||||||
|
* inst.c - Instance structures
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "coord.h"
|
||||||
|
#include "expr.h"
|
||||||
|
#include "obj.h"
|
||||||
|
#include "gui_status.h"
|
||||||
|
#include "gui_inst.h"
|
||||||
|
#include "inst.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct inst_ops {
|
||||||
|
void (*debug)(struct inst *self);
|
||||||
|
void (*save)(FILE *file, struct inst *self);
|
||||||
|
void (*draw)(struct inst *self, struct draw_ctx *ctx);
|
||||||
|
unit_type (*distance)(struct inst *self, struct coord pos,
|
||||||
|
unit_type scale);
|
||||||
|
void (*select)(struct inst *self);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum inst_prio {
|
||||||
|
ip_frame, /* frames have their own selection */
|
||||||
|
ip_pad, /* pads also accept clicks inside */
|
||||||
|
ip_circ, /* circles don't overlap easily */
|
||||||
|
ip_arc, /* arc are like circles, just shorter */
|
||||||
|
ip_rect, /* rectangles have plenty of sides */
|
||||||
|
ip_meas, /* mesurements are like lines but set a bit apart */
|
||||||
|
ip_line, /* lines are easly overlapped by other things */
|
||||||
|
ip_vec, /* vectors only have the end point */
|
||||||
|
ip_n, /* number of priorities */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define FOR_INST_PRIOS_UP(prio) \
|
||||||
|
for (prio = 0; prio != ip_n; prio++)
|
||||||
|
|
||||||
|
#define FOR_INST_PRIOS_DOWN(prio) \
|
||||||
|
for (prio = ip_n-1; prio != (enum inst_prio) -1; prio--)
|
||||||
|
|
||||||
|
#define FOR_INSTS_UP(prio, inst) \
|
||||||
|
FOR_INST_PRIOS_UP(prio) \
|
||||||
|
for (inst = insts[prio]; inst; inst = inst->next)
|
||||||
|
|
||||||
|
#define FOR_INSTS_DOWN(prio, inst) \
|
||||||
|
FOR_INST_PRIOS_DOWN(prio) \
|
||||||
|
for (inst = insts[prio]; inst; inst = inst->next)
|
||||||
|
|
||||||
|
|
||||||
|
struct inst *curr_frame = NULL;
|
||||||
|
struct inst *selected_inst = NULL;
|
||||||
|
|
||||||
|
static struct inst *insts[ip_n], **next_inst[ip_n];
|
||||||
|
static struct inst *prev_insts[ip_n];
|
||||||
|
|
||||||
|
static unsigned long active_set = 0;
|
||||||
|
|
||||||
|
|
||||||
|
#define IS_ACTIVE ((active_set & 1))
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- selection of items not on the canvas ------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
static void *selected_outside = NULL;
|
||||||
|
static void (*outside_deselect)(void *item);
|
||||||
|
|
||||||
|
|
||||||
|
static void deselect_outside(void)
|
||||||
|
{
|
||||||
|
if (selected_outside && outside_deselect)
|
||||||
|
outside_deselect(selected_outside);
|
||||||
|
selected_outside = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_select_outside(void *item, void (*deselect)(void *item))
|
||||||
|
{
|
||||||
|
if (item == selected_outside)
|
||||||
|
return;
|
||||||
|
deselect_outside();
|
||||||
|
inst_deselect();
|
||||||
|
selected_outside = item;
|
||||||
|
outside_deselect = deselect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- selection --------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static struct inst_ops vec_ops;
|
||||||
|
static struct inst_ops frame_ops;
|
||||||
|
|
||||||
|
|
||||||
|
static void set_path(int on)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
return;
|
||||||
|
if (inst->ops != &vec_ops && inst->ops != &frame_ops)
|
||||||
|
return;
|
||||||
|
/* @@@ wrong */
|
||||||
|
for (inst = selected_inst; inst; inst = inst->outer) {
|
||||||
|
if (inst->ops != &vec_ops && inst->ops != &frame_ops)
|
||||||
|
break;
|
||||||
|
inst->in_path = on;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int inst_select(const struct draw_ctx *ctx, struct coord pos)
|
||||||
|
{
|
||||||
|
enum inst_prio prio;
|
||||||
|
struct inst *inst;
|
||||||
|
int best_dist, dist;
|
||||||
|
|
||||||
|
deselect_outside();
|
||||||
|
edit_nothing();
|
||||||
|
selected_inst = NULL;
|
||||||
|
FOR_INST_PRIOS_DOWN(prio) {
|
||||||
|
for (inst = insts[prio]; inst; inst = inst->next) {
|
||||||
|
if (!inst->active || !inst->ops->distance)
|
||||||
|
continue;
|
||||||
|
dist = inst->ops->distance(inst, pos, ctx->scale);
|
||||||
|
if (dist >= 0 && (!selected_inst || best_dist > dist)) {
|
||||||
|
selected_inst = inst;
|
||||||
|
best_dist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selected_inst)
|
||||||
|
goto selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* give vectors a second chance */
|
||||||
|
|
||||||
|
for (inst = insts[ip_vec]; inst; inst = inst->next) {
|
||||||
|
if (!inst->active)
|
||||||
|
continue;
|
||||||
|
dist = gui_dist_vec_fallback(inst, pos, ctx->scale);
|
||||||
|
if (dist >= 0 && (!selected_inst || best_dist > dist)) {
|
||||||
|
selected_inst = inst;
|
||||||
|
best_dist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selected_inst)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
selected:
|
||||||
|
set_path(1);
|
||||||
|
if (selected_inst->ops->select)
|
||||||
|
selected_inst->ops->select(selected_inst);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_deselect(void)
|
||||||
|
{
|
||||||
|
if (selected_inst)
|
||||||
|
set_path(0);
|
||||||
|
deselect_outside();
|
||||||
|
status_set_name("");
|
||||||
|
status_set_x("");
|
||||||
|
status_set_y("");
|
||||||
|
status_set_r("");
|
||||||
|
status_set_angle("");
|
||||||
|
selected_inst = NULL;
|
||||||
|
edit_nothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void rect_status(struct coord a, struct coord b, unit_type width)
|
||||||
|
{
|
||||||
|
struct coord d = sub_vec(b, a);
|
||||||
|
double angle;
|
||||||
|
|
||||||
|
status_set_xy(d);
|
||||||
|
if (!d.x && !d.y)
|
||||||
|
status_set_angle("a = 0 deg");
|
||||||
|
else {
|
||||||
|
angle = atan2(d.y, d.x)/M_PI*180.0;
|
||||||
|
if (angle < 0)
|
||||||
|
angle += 360;
|
||||||
|
status_set_angle("a = %3.1f deg", angle);
|
||||||
|
}
|
||||||
|
status_set_r("r = %5.2f mm", hypot(units_to_mm(d.x), units_to_mm(d.y)));
|
||||||
|
if (width != -1)
|
||||||
|
status_set_name("width = %5.2f mm", units_to_mm(width));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- helper functions for instance creation ---------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void update_bbox(struct bbox *bbox, struct coord coord)
|
||||||
|
{
|
||||||
|
if (bbox->min.x > coord.x)
|
||||||
|
bbox->min.x = coord.x;
|
||||||
|
if (bbox->max.x < coord.x)
|
||||||
|
bbox->max.x = coord.x;
|
||||||
|
if (bbox->min.y > coord.y)
|
||||||
|
bbox->min.y = coord.y;
|
||||||
|
if (bbox->max.y < coord.y)
|
||||||
|
bbox->max.y = coord.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void propagate_bbox(const struct inst *inst)
|
||||||
|
{
|
||||||
|
update_bbox(&curr_frame->bbox, inst->bbox.min);
|
||||||
|
update_bbox(&curr_frame->bbox, inst->bbox.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct inst *add_inst(const struct inst_ops *ops, enum inst_prio prio,
|
||||||
|
struct coord base)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
inst = alloc_type(struct inst);
|
||||||
|
inst->ops = ops;
|
||||||
|
inst->base = inst->bbox.min = inst->bbox.max = base;
|
||||||
|
inst->outer = curr_frame;
|
||||||
|
inst->active = IS_ACTIVE;
|
||||||
|
inst->in_path = 0;
|
||||||
|
inst->next = NULL;
|
||||||
|
*next_inst[prio] = inst;
|
||||||
|
next_inst[prio] = &inst->next;
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- vec --------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void vec_op_debug(struct inst *self)
|
||||||
|
{
|
||||||
|
printf("vec %lg, %lg -> %lg, %lg\n",
|
||||||
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
||||||
|
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int validate_vec_name(const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
struct vec *vec = ctx;
|
||||||
|
const struct vec *walk;
|
||||||
|
|
||||||
|
if (!is_id(s))
|
||||||
|
return 0;
|
||||||
|
for (walk = vec->frame->vecs; walk; walk = walk->next)
|
||||||
|
if (walk->name && !strcmp(walk->name, s))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void vec_op_select(struct inst *self)
|
||||||
|
{
|
||||||
|
status_set_name(self->vec->name ? self->vec->name : "");
|
||||||
|
rect_status(self->base, self->u.rect.end, -1);
|
||||||
|
edit_unique_null(&self->vec->name, validate_vec_name, self->vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct inst_ops vec_ops = {
|
||||||
|
.debug = vec_op_debug,
|
||||||
|
.draw = gui_draw_vec,
|
||||||
|
.distance = gui_dist_vec,
|
||||||
|
.select = vec_op_select,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int inst_vec(struct vec *vec, struct coord base)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
inst = add_inst(&vec_ops, ip_vec, base);
|
||||||
|
inst->vec = vec;
|
||||||
|
inst->u.rect.end = vec->pos;
|
||||||
|
update_bbox(&inst->bbox, vec->pos);
|
||||||
|
propagate_bbox(inst);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- line -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void line_op_debug(struct inst *self)
|
||||||
|
{
|
||||||
|
printf("line %lg, %lg / %lg, %lg\n",
|
||||||
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
||||||
|
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void line_op_select(struct inst *self)
|
||||||
|
{
|
||||||
|
rect_status(self->bbox.min, self->bbox.max, self->u.rect.width);
|
||||||
|
edit_expr(&self->obj->u.line.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct inst_ops line_ops = {
|
||||||
|
.debug = line_op_debug,
|
||||||
|
.draw = gui_draw_line,
|
||||||
|
.distance = gui_dist_line,
|
||||||
|
.select = line_op_select,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
inst = add_inst(&line_ops, ip_line, a);
|
||||||
|
inst->obj = obj;
|
||||||
|
inst->u.rect.end = b;
|
||||||
|
inst->u.rect.width = width;
|
||||||
|
update_bbox(&inst->bbox, b);
|
||||||
|
propagate_bbox(inst);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- rect -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void rect_op_debug(struct inst *self)
|
||||||
|
{
|
||||||
|
printf("rect %lg, %lg / %lg, %lg\n",
|
||||||
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
||||||
|
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void rect_op_select(struct inst *self)
|
||||||
|
{
|
||||||
|
rect_status(self->bbox.min, self->bbox.max, self->u.rect.width);
|
||||||
|
edit_expr(&self->obj->u.rect.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct inst_ops rect_ops = {
|
||||||
|
.debug = rect_op_debug,
|
||||||
|
.draw = gui_draw_rect,
|
||||||
|
.distance = gui_dist_rect,
|
||||||
|
.select = rect_op_select,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
inst = add_inst(&rect_ops, ip_rect, a);
|
||||||
|
inst->obj = obj;
|
||||||
|
inst->u.rect.end = b;
|
||||||
|
inst->u.rect.width = width;
|
||||||
|
update_bbox(&inst->bbox, b);
|
||||||
|
propagate_bbox(inst);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- pad --------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void pad_op_debug(struct inst *self)
|
||||||
|
{
|
||||||
|
printf("pad \"%s\" %lg, %lg / %lg, %lg\n", self->u.name,
|
||||||
|
units_to_mm(self->bbox.min.x), units_to_mm(self->bbox.min.y),
|
||||||
|
units_to_mm(self->bbox.max.x), units_to_mm(self->bbox.max.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int validate_pad_name(const char *s, void *ctx)
|
||||||
|
{
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
tmp = expand(s, NULL);
|
||||||
|
if (!tmp)
|
||||||
|
return 0;
|
||||||
|
free(tmp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void pad_op_select(struct inst *self)
|
||||||
|
{
|
||||||
|
status_set_name(self->u.name);
|
||||||
|
rect_status(self->bbox.min, self->bbox.max, -1);
|
||||||
|
edit_name(&self->obj->u.pad.name, validate_pad_name, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct inst_ops pad_ops = {
|
||||||
|
.debug = pad_op_debug,
|
||||||
|
.draw = gui_draw_pad,
|
||||||
|
.distance = gui_dist_pad,
|
||||||
|
.select = pad_op_select,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
inst = add_inst(&pad_ops, ip_pad, a);
|
||||||
|
inst->obj = obj;
|
||||||
|
inst->u.name = stralloc(name);
|
||||||
|
update_bbox(&inst->bbox, b);
|
||||||
|
propagate_bbox(inst);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- arc --------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void arc_op_debug(struct inst *self)
|
||||||
|
{
|
||||||
|
printf("arc %lg, %lg radius %lg %lg ... %lg\n",
|
||||||
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
||||||
|
units_to_mm(self->u.arc.r), self->u.arc.a1, self->u.arc.a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void arc_op_select(struct inst *self)
|
||||||
|
{
|
||||||
|
status_set_xy(self->base);
|
||||||
|
status_set_angle("a = %3.1f deg",
|
||||||
|
self->u.arc.a1 == self->u.arc.a2 ? 360 :
|
||||||
|
self->u.arc.a2-self->u.arc.a1);
|
||||||
|
status_set_r("r = %5.2f mm", units_to_mm(self->u.arc.r));
|
||||||
|
status_set_name("width = %5.2f mm", units_to_mm(self->u.arc.width));
|
||||||
|
edit_expr(&self->obj->u.arc.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct inst_ops arc_ops = {
|
||||||
|
.debug = arc_op_debug,
|
||||||
|
.draw = gui_draw_arc,
|
||||||
|
.distance = gui_dist_arc,
|
||||||
|
.select = arc_op_select,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int inst_arc(struct obj *obj, struct coord center, struct coord start,
|
||||||
|
struct coord end, unit_type width)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
double r, a1, a2;
|
||||||
|
|
||||||
|
inst = add_inst(&arc_ops, ip_arc, center);
|
||||||
|
inst->obj = obj;
|
||||||
|
r = hypot(start.x-center.x, start.y-center.y);
|
||||||
|
a1 = atan2(start.y-center.y, start.x-center.x)/M_PI*180.0;
|
||||||
|
a2 = atan2(end.y-center.y, end.x-center.x)/M_PI*180.0;
|
||||||
|
if (a1 < 0)
|
||||||
|
a1 += 360.0;
|
||||||
|
if (a2 < 0)
|
||||||
|
a2 += 360.0;
|
||||||
|
inst->u.arc.r = r;
|
||||||
|
inst->u.arc.a1 = a1;
|
||||||
|
inst->u.arc.a2 = a2;
|
||||||
|
inst->u.arc.width = width;
|
||||||
|
inst->bbox.min.x = center.x-r;
|
||||||
|
inst->bbox.max.x = center.x+r;
|
||||||
|
inst->bbox.min.y = center.x-r;
|
||||||
|
inst->bbox.max.y = center.x+r;
|
||||||
|
propagate_bbox(inst);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- measurement ------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void meas_op_debug(struct inst *self)
|
||||||
|
{
|
||||||
|
printf("meas %lg, %lg / %lg, %lg offset %lg\n",
|
||||||
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
||||||
|
units_to_mm(self->u.meas.end.x), units_to_mm(self->u.meas.end.y),
|
||||||
|
units_to_mm(self->u.meas.offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void meas_op_select(struct inst *self)
|
||||||
|
{
|
||||||
|
rect_status(self->bbox.min, self->bbox.max, -1);
|
||||||
|
status_set_name("offset = %5.2f mm", units_to_mm(self->u.meas.offset));
|
||||||
|
edit_expr(&self->obj->u.meas.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct inst_ops meas_ops = {
|
||||||
|
.debug = meas_op_debug,
|
||||||
|
.draw = gui_draw_meas,
|
||||||
|
.distance = gui_dist_meas,
|
||||||
|
.select = meas_op_select,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int inst_meas(struct obj *obj, struct coord from, struct coord to,
|
||||||
|
unit_type offset)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
inst = add_inst(&meas_ops, ip_meas, from);
|
||||||
|
inst->obj = obj;
|
||||||
|
inst->u.meas.end = to;
|
||||||
|
inst->u.meas.offset = offset;
|
||||||
|
/* @@@ our bbox is actually a bit more complex than this */
|
||||||
|
update_bbox(&inst->bbox, to);
|
||||||
|
propagate_bbox(inst);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- active instance --------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
void inst_begin_active(int active)
|
||||||
|
{
|
||||||
|
active_set = (active_set << 1) | active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_end_active(void)
|
||||||
|
{
|
||||||
|
active_set >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- frame ------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static void frame_op_debug(struct inst *self)
|
||||||
|
{
|
||||||
|
printf("frame %s @ %lg, %lg\n",
|
||||||
|
self->u.frame.ref->name ? self->u.frame.ref->name : "(root)",
|
||||||
|
units_to_mm(self->base.x), units_to_mm(self->base.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct inst_ops frame_ops = {
|
||||||
|
.debug = frame_op_debug,
|
||||||
|
.draw = gui_draw_frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void inst_begin_frame(const struct frame *frame, struct coord base, int active)
|
||||||
|
{
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
inst = add_inst(&frame_ops, ip_frame, base);
|
||||||
|
inst->u.frame.ref = frame;
|
||||||
|
inst->active = active;
|
||||||
|
curr_frame = inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_end_frame(const struct frame *frame)
|
||||||
|
{
|
||||||
|
struct inst *inst = curr_frame;
|
||||||
|
|
||||||
|
curr_frame = curr_frame->outer;
|
||||||
|
if (curr_frame)
|
||||||
|
propagate_bbox(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- misc. ------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
struct bbox inst_get_bbox(void)
|
||||||
|
{
|
||||||
|
return insts[ip_frame]->bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void inst_free(struct inst *list[ip_n])
|
||||||
|
{
|
||||||
|
enum inst_prio prio;
|
||||||
|
struct inst *next;
|
||||||
|
|
||||||
|
FOR_INST_PRIOS_UP(prio)
|
||||||
|
while (list[prio]) {
|
||||||
|
next = list[prio]->next;
|
||||||
|
free(list[prio]);
|
||||||
|
list[prio] = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_start(void)
|
||||||
|
{
|
||||||
|
enum inst_prio prio;
|
||||||
|
|
||||||
|
FOR_INST_PRIOS_UP(prio) {
|
||||||
|
prev_insts[prio] = insts[prio];
|
||||||
|
insts[prio] = NULL;
|
||||||
|
next_inst[prio] = &insts[prio];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_commit(void)
|
||||||
|
{
|
||||||
|
inst_free(prev_insts);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_revert(void)
|
||||||
|
{
|
||||||
|
enum inst_prio prio;
|
||||||
|
|
||||||
|
inst_free(insts);
|
||||||
|
FOR_INST_PRIOS_UP(prio)
|
||||||
|
insts[prio] = prev_insts[prio];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_draw(struct draw_ctx *ctx)
|
||||||
|
{
|
||||||
|
enum inst_prio prio;
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
FOR_INSTS_UP(prio, inst)
|
||||||
|
if (!inst->active && inst->ops->draw)
|
||||||
|
inst->ops->draw(inst, ctx);
|
||||||
|
FOR_INSTS_UP(prio, inst)
|
||||||
|
if (prio != ip_frame && inst->active &&
|
||||||
|
inst != selected_inst && inst->ops->draw)
|
||||||
|
inst->ops->draw(inst, ctx);
|
||||||
|
for (inst = insts[ip_frame]; inst; inst = inst->next)
|
||||||
|
if (inst->active && inst != selected_inst && inst->ops->draw)
|
||||||
|
inst->ops->draw(inst, ctx);
|
||||||
|
if (selected_inst && selected_inst->ops->draw)
|
||||||
|
selected_inst->ops->draw(selected_inst, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void inst_debug(void)
|
||||||
|
{
|
||||||
|
enum inst_prio prio;
|
||||||
|
struct inst *inst;
|
||||||
|
|
||||||
|
FOR_INSTS_UP(prio, inst)
|
||||||
|
inst->ops->debug(inst);
|
||||||
|
}
|
104
inst.h
Normal file
104
inst.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* inst.h - Instance structures
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef INST_H
|
||||||
|
#define INST_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "coord.h"
|
||||||
|
#include "obj.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum mode {
|
||||||
|
mode_inactive, /* on inactive frame */
|
||||||
|
mode_inactive_in_path, /* inactive but is in path to selected */
|
||||||
|
mode_active, /* on active frame */
|
||||||
|
mode_active_in_path, /* active and is in path to selected */
|
||||||
|
mode_selected, /* item is selected */
|
||||||
|
mode_n /* number of modes */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bbox {
|
||||||
|
struct coord min;
|
||||||
|
struct coord max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct inst_ops;
|
||||||
|
struct draw_ctx;
|
||||||
|
|
||||||
|
struct inst {
|
||||||
|
const struct inst_ops *ops;
|
||||||
|
struct coord base;
|
||||||
|
// struct inst *base_inst; /* frame or vector leading to this item */
|
||||||
|
struct bbox bbox;
|
||||||
|
struct vec *vec; /* undefined if not vector */
|
||||||
|
struct obj *obj; /* undefined if not object */
|
||||||
|
struct inst *outer; /* frame containing this item */
|
||||||
|
int active;
|
||||||
|
int in_path;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
const struct frame *ref;
|
||||||
|
} frame;
|
||||||
|
const char *name;
|
||||||
|
struct {
|
||||||
|
struct coord end;
|
||||||
|
unit_type width;
|
||||||
|
} rect;
|
||||||
|
struct {
|
||||||
|
unit_type r;
|
||||||
|
double a1, a2;
|
||||||
|
unit_type width;
|
||||||
|
} arc;
|
||||||
|
struct {
|
||||||
|
struct coord end;
|
||||||
|
double offset;
|
||||||
|
} meas;
|
||||||
|
} u;
|
||||||
|
struct inst *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern struct inst *selected_inst;
|
||||||
|
|
||||||
|
|
||||||
|
void inst_select_outside(void *item, void (*deselect)(void *item));
|
||||||
|
int inst_select(const struct draw_ctx *ctx, struct coord pos);
|
||||||
|
void inst_deselect(void);
|
||||||
|
|
||||||
|
int inst_vec(struct vec *vec, struct coord base);
|
||||||
|
int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width);
|
||||||
|
int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width);
|
||||||
|
int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b);
|
||||||
|
int inst_arc(struct obj *obj, struct coord center, struct coord start,
|
||||||
|
struct coord stop, unit_type width);
|
||||||
|
int inst_meas(struct obj *obj, struct coord from, struct coord to,
|
||||||
|
unit_type offset);
|
||||||
|
|
||||||
|
void inst_begin_active(int active);
|
||||||
|
void inst_end_active(void);
|
||||||
|
|
||||||
|
void inst_begin_frame(const struct frame *frame, struct coord base, int active);
|
||||||
|
void inst_end_frame(const struct frame *frame);
|
||||||
|
|
||||||
|
struct bbox inst_get_bbox(void);
|
||||||
|
|
||||||
|
void inst_start(void);
|
||||||
|
void inst_commit(void);
|
||||||
|
void inst_revert(void);
|
||||||
|
|
||||||
|
void inst_draw(struct draw_ctx *ctx);
|
||||||
|
void inst_debug(void);
|
||||||
|
|
||||||
|
#endif /* !INST_H */
|
270
obj.c
Normal file
270
obj.c
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* obj.c - Object definition model
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "expr.h"
|
||||||
|
#include "inst.h"
|
||||||
|
#include "obj.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define DEFAULT_SILK_WIDTH make_mil(15) /* @@@ */
|
||||||
|
|
||||||
|
#define MAX_ITERATIONS 1000 /* abort "loop"s at this limit */
|
||||||
|
|
||||||
|
|
||||||
|
struct frame *frames = NULL;
|
||||||
|
struct frame *root_frame = NULL;
|
||||||
|
struct frame *active_frame = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
static int generate_frame(struct frame *frame, struct coord base,
|
||||||
|
const struct frame *parent, int active);
|
||||||
|
|
||||||
|
|
||||||
|
static struct num eval_unit(const struct expr *expr, const struct frame *frame)
|
||||||
|
{
|
||||||
|
struct num d;
|
||||||
|
|
||||||
|
d = eval_num(expr, frame);
|
||||||
|
if (!is_undef(d) && to_unit(&d))
|
||||||
|
return d;
|
||||||
|
fail_expr(expr);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct num eval_unit_default(const struct expr *expr,
|
||||||
|
const struct frame *frame, struct num def)
|
||||||
|
{
|
||||||
|
if (expr)
|
||||||
|
return eval_unit(expr, frame);
|
||||||
|
to_unit(&def);
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int generate_vecs(struct frame *frame, struct coord base)
|
||||||
|
{
|
||||||
|
struct coord vec_base;
|
||||||
|
struct vec *vec;
|
||||||
|
struct num x, y;
|
||||||
|
|
||||||
|
for (vec = frame->vecs; vec; vec = vec->next) {
|
||||||
|
x = eval_unit(vec->x, frame);
|
||||||
|
if (is_undef(x))
|
||||||
|
return 0;
|
||||||
|
y = eval_unit(vec->y, frame);
|
||||||
|
if (is_undef(y))
|
||||||
|
return 0;
|
||||||
|
vec_base = vec->base ? vec->base->pos : base;
|
||||||
|
vec->pos = vec_base;
|
||||||
|
vec->pos.x += x.n;
|
||||||
|
vec->pos.y += y.n;
|
||||||
|
if (!inst_vec(vec, vec_base))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int generate_objs(struct frame *frame, struct coord base, int active)
|
||||||
|
{
|
||||||
|
struct obj *obj;
|
||||||
|
char *name;
|
||||||
|
int ok;
|
||||||
|
struct num width, offset;
|
||||||
|
|
||||||
|
for (obj = frame->objs; obj; obj = obj->next)
|
||||||
|
switch (obj->type) {
|
||||||
|
case ot_frame:
|
||||||
|
if (!generate_frame(obj->u.frame,
|
||||||
|
obj->base ? obj->base->pos : base, frame, active))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case ot_line:
|
||||||
|
width = eval_unit_default(obj->u.line.width, frame,
|
||||||
|
DEFAULT_SILK_WIDTH);
|
||||||
|
if (is_undef(width))
|
||||||
|
return 0;
|
||||||
|
if (!inst_line(obj, obj->base ? obj->base->pos : base,
|
||||||
|
obj->u.line.other ? obj->u.line.other->pos : base,
|
||||||
|
width.n))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case ot_rect:
|
||||||
|
width = eval_unit_default(obj->u.rect.width, frame,
|
||||||
|
DEFAULT_SILK_WIDTH);
|
||||||
|
if (is_undef(width))
|
||||||
|
return 0;
|
||||||
|
if (!inst_rect(obj, obj->base ? obj->base->pos : base,
|
||||||
|
obj->u.rect.other ? obj->u.rect.other->pos : base,
|
||||||
|
width.n))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case ot_pad:
|
||||||
|
name = expand(obj->u.pad.name, frame);
|
||||||
|
if (!name)
|
||||||
|
return 0;
|
||||||
|
ok = inst_pad(obj, name,
|
||||||
|
obj->base ? obj->base->pos : base,
|
||||||
|
obj->u.pad.other ? obj->u.pad.other->pos : base);
|
||||||
|
free(name);
|
||||||
|
if (!ok)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case ot_arc:
|
||||||
|
width = eval_unit_default(obj->u.arc.width, frame,
|
||||||
|
DEFAULT_SILK_WIDTH);
|
||||||
|
if (is_undef(width))
|
||||||
|
return 0;
|
||||||
|
if (!inst_arc(obj, obj->base ? obj->base->pos : base,
|
||||||
|
obj->u.arc.start ? obj->u.arc.start->pos : base,
|
||||||
|
obj->u.arc.end ? obj->u.arc.end->pos : base,
|
||||||
|
width.n))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case ot_meas:
|
||||||
|
offset = eval_unit(obj->u.meas.offset, frame);
|
||||||
|
if (is_undef(offset))
|
||||||
|
return 0;
|
||||||
|
if (!inst_meas(obj, obj->base ? obj->base->pos : base,
|
||||||
|
obj->u.meas.other ? obj->u.meas.other->pos : base,
|
||||||
|
offset.n))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int generate_items(struct frame *frame, struct coord base, int active)
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
inst_begin_active(active && frame == active_frame);
|
||||||
|
ok = generate_vecs(frame, base) && generate_objs(frame, base, active);
|
||||||
|
inst_end_active();
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int run_loops(struct frame *frame, struct loop *loop,
|
||||||
|
struct coord base, int active)
|
||||||
|
{
|
||||||
|
struct num from, to;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (!loop)
|
||||||
|
return generate_items(frame, base, active);
|
||||||
|
from = eval_num(loop->from.expr, frame);
|
||||||
|
if (is_undef(from)) {
|
||||||
|
fail_expr(loop->from.expr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!is_dimensionless(from)) {
|
||||||
|
fail("incompatible type for start value");
|
||||||
|
fail_expr(loop->from.expr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to = eval_num(loop->to.expr, frame);
|
||||||
|
if (is_undef(to)) {
|
||||||
|
fail_expr(loop->to.expr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!is_dimensionless(to)) {
|
||||||
|
fail("incompatible type for end value");
|
||||||
|
fail_expr(loop->to.expr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!loop->initialized);
|
||||||
|
loop->curr_value = from.n;
|
||||||
|
loop->initialized = 1;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
for (; loop->curr_value <= to.n; loop->curr_value += 1) {
|
||||||
|
if (n >= MAX_ITERATIONS) {
|
||||||
|
fail("%s: too many iterations (%d)", loop->var.name,
|
||||||
|
MAX_ITERATIONS);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!run_loops(frame, loop->next, base,
|
||||||
|
active && loop->active == n))
|
||||||
|
goto fail;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
loop->initialized = 0;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
loop->initialized = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int iterate_tables(struct frame *frame, struct table *table,
|
||||||
|
struct coord base, int active)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (!table)
|
||||||
|
return run_loops(frame, frame->loops, base, active);
|
||||||
|
n = 0;
|
||||||
|
for (table->curr_row = table->rows; table->curr_row;
|
||||||
|
table->curr_row = table->curr_row->next) {
|
||||||
|
if (!iterate_tables(frame, table->next, base,
|
||||||
|
active && table->active == n))
|
||||||
|
return 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int generate_frame(struct frame *frame, struct coord base,
|
||||||
|
const struct frame *parent, int active)
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We ensure during construction that frames can never recurse.
|
||||||
|
*/
|
||||||
|
inst_begin_frame(frame, base, active && frame == active_frame);
|
||||||
|
frame->curr_parent = parent;
|
||||||
|
ok = iterate_tables(frame, frame->tables, base, active);
|
||||||
|
inst_end_frame(frame);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int instantiate(void)
|
||||||
|
{
|
||||||
|
struct coord zero = { 0, 0 };
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
inst_start();
|
||||||
|
ok = generate_frame(root_frame, zero, NULL, 1);
|
||||||
|
if (ok)
|
||||||
|
inst_commit();
|
||||||
|
else
|
||||||
|
inst_revert();
|
||||||
|
return ok;
|
||||||
|
}
|
167
obj.h
Normal file
167
obj.h
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* obj.h - Object definition model
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef OBJ_H
|
||||||
|
#define OBJ_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "expr.h"
|
||||||
|
#include "coord.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct var {
|
||||||
|
const char *name;
|
||||||
|
struct var *next;
|
||||||
|
|
||||||
|
/* back reference */
|
||||||
|
struct frame *frame;
|
||||||
|
|
||||||
|
/* for the GUI */
|
||||||
|
GtkWidget *widget;
|
||||||
|
|
||||||
|
/* for evaluation */
|
||||||
|
int visited;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct value {
|
||||||
|
struct expr *expr;
|
||||||
|
struct value *next;
|
||||||
|
|
||||||
|
/* back reference */
|
||||||
|
struct row *row;
|
||||||
|
|
||||||
|
/* for the GUI */
|
||||||
|
GtkWidget *widget;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct row {
|
||||||
|
struct value *values;
|
||||||
|
struct row *next;
|
||||||
|
|
||||||
|
/* back reference */
|
||||||
|
struct table *table;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct table {
|
||||||
|
struct var *vars;
|
||||||
|
struct row *rows;
|
||||||
|
struct table *next;
|
||||||
|
|
||||||
|
/* used during generation and when editing */
|
||||||
|
struct row *curr_row;
|
||||||
|
|
||||||
|
/* GUI use */
|
||||||
|
int active; /* n-th row is active, 0 based */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct loop {
|
||||||
|
struct var var;
|
||||||
|
struct value from;
|
||||||
|
struct value to;
|
||||||
|
struct loop *next;
|
||||||
|
|
||||||
|
/* used during generation */
|
||||||
|
double curr_value;
|
||||||
|
|
||||||
|
/* GUI use */
|
||||||
|
int active; /* n-th iteration is active, 0 based */
|
||||||
|
|
||||||
|
/* for evaluation */
|
||||||
|
int initialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vec {
|
||||||
|
const char *name; /* NULL if anonymous */
|
||||||
|
struct expr *x;
|
||||||
|
struct expr *y;
|
||||||
|
struct vec *base; /* NULL if frame */
|
||||||
|
int n_refs;
|
||||||
|
struct vec *next;
|
||||||
|
|
||||||
|
/* used during generation */
|
||||||
|
struct coord pos;
|
||||||
|
|
||||||
|
/* used when editing */
|
||||||
|
struct frame *frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct frame {
|
||||||
|
const char *name; /* NULL if top-level */
|
||||||
|
struct table *tables;
|
||||||
|
struct loop *loops;
|
||||||
|
struct vec *vecs;
|
||||||
|
struct obj *objs;
|
||||||
|
struct frame *next;
|
||||||
|
struct frame *prev; /* for the list of frames in the GUI */
|
||||||
|
|
||||||
|
/* used during generation */
|
||||||
|
const struct frame *curr_parent;
|
||||||
|
|
||||||
|
/* for the GUI */
|
||||||
|
GtkWidget *label;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum obj_type {
|
||||||
|
ot_frame,
|
||||||
|
ot_rect,
|
||||||
|
ot_pad,
|
||||||
|
ot_line,
|
||||||
|
ot_arc,
|
||||||
|
ot_meas,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rect {
|
||||||
|
struct vec *other; /* NULL if frame origin */
|
||||||
|
struct expr *width;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pad {
|
||||||
|
char *name;
|
||||||
|
struct vec *other; /* NULL if frame origin */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arc {
|
||||||
|
struct vec *start; /* NULL if frame origin */
|
||||||
|
struct vec *end; /* NULL if this is a circle */
|
||||||
|
struct expr *width;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct meas {
|
||||||
|
struct vec *other; /* NULL if frame origin */
|
||||||
|
struct expr *offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct obj {
|
||||||
|
enum obj_type type;
|
||||||
|
union {
|
||||||
|
struct frame *frame;
|
||||||
|
struct rect rect;
|
||||||
|
struct rect line;
|
||||||
|
struct pad pad;
|
||||||
|
struct arc arc;
|
||||||
|
struct meas meas;
|
||||||
|
} u;
|
||||||
|
struct vec *base;
|
||||||
|
struct obj *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern struct frame *frames;
|
||||||
|
extern struct frame *root_frame;
|
||||||
|
extern struct frame *active_frame;
|
||||||
|
|
||||||
|
|
||||||
|
int instantiate(void);
|
||||||
|
|
||||||
|
#endif /* !OBJ_H */
|
70
qfn.fpd
Normal file
70
qfn.fpd
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Example of a QFN package (and general construction site to experiment with
|
||||||
|
* fped features during development)
|
||||||
|
*
|
||||||
|
* Everything you see here is likely to change sooner or later.
|
||||||
|
*
|
||||||
|
* http://www.nxp.com/acrobat/packages/footprint/SOT616-1_fp_reflow.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
frame pad_up {
|
||||||
|
c: vec @(-D/2, 0mm)
|
||||||
|
vec .(0mm, C)
|
||||||
|
meas c . 0.2mm
|
||||||
|
vec c(D, C)
|
||||||
|
set pad = n+1
|
||||||
|
pad "$pad" c .
|
||||||
|
}
|
||||||
|
|
||||||
|
frame pads {
|
||||||
|
loop n = 0, N/4-1
|
||||||
|
|
||||||
|
vec @(P*(n-(N/4-1)/2), -Ay/2)
|
||||||
|
frame pad_up .
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set N = 24
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that this table is not a great example because it contains lots of
|
||||||
|
* fields we don't really need for iterations. But it's useful for driving
|
||||||
|
* the GUI to extremes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
table
|
||||||
|
{ P, Ax, Ay, Bx, By, C, D, SLx, SLy, SPx_tot, SPy_tot, SPx, SPy, Gx, Gy, Hx, Hy }
|
||||||
|
{ 0.5mm, 5mm, 5mm, 3.2mm, 3.2mm, 0.9mm, 0.24mm, 2.1mm, 2.1mm, 1.2mm,
|
||||||
|
1.2mm, 0.45mm, 0.45mm, 4.3mm, 4.3mm, 5.25mm, 5.25mm }
|
||||||
|
|
||||||
|
h_x0y0: vec @(-Hx/2, -Hy/2)
|
||||||
|
h_x1y1: vec .(Hx, Hy)
|
||||||
|
rect h_x0y0 h_x1y1 8mil
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we can't draw the package outline on the silk screen for it would print
|
||||||
|
* over the pads.
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
g_x0y0: vec @(-Gx/2, -Gy/2)
|
||||||
|
g_x1y1: vec .(Gx, Gy)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
frame pads @
|
||||||
|
|
||||||
|
// ARC, just for testing
|
||||||
|
|
||||||
|
c: vec @(-1mm, 1mm)
|
||||||
|
r: vec c(0mm, 0.5mm)
|
||||||
|
e: vec c(-0.5mm, 0mm)
|
||||||
|
arc c r e 2mil
|
||||||
|
|
||||||
|
r2: vec c(0mm, 0.8mm)
|
||||||
|
circ c r2 5mil
|
||||||
|
|
||||||
|
/*
|
||||||
|
x1 = 1+2*3
|
||||||
|
x2 = (1+2)*3
|
||||||
|
x3 = 1-(2+3)
|
||||||
|
*/
|
111
unparse.c
Normal file
111
unparse.c
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* unparse.c - Dump an expression tree into a string
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is crazily inefficient but who cares :-)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "expr.h"
|
||||||
|
#include "unparse.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum prec {
|
||||||
|
prec_add,
|
||||||
|
prec_mult,
|
||||||
|
prec_unary,
|
||||||
|
prec_primary,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int precedence(op_type op)
|
||||||
|
{
|
||||||
|
if (op == op_add || op == op_sub)
|
||||||
|
return prec_add;
|
||||||
|
if (op == op_mult || op == op_div)
|
||||||
|
return prec_mult;
|
||||||
|
if (op == op_minus)
|
||||||
|
return prec_unary;
|
||||||
|
if (op == op_num || op == op_var)
|
||||||
|
return prec_primary;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *merge3(char *a, const char *op, char *b)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
buf = alloc_size(strlen(op)+strlen(a)+strlen(b)+1);
|
||||||
|
sprintf(buf, "%s%s%s", a, op, b);
|
||||||
|
free(a);
|
||||||
|
free(b);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *merge2(const char *op, char *a)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
buf = alloc_size(strlen(op)+strlen(a)+1);
|
||||||
|
sprintf(buf, "%s%s", op, a);
|
||||||
|
free(a);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *unparse_op(const struct expr *expr, enum prec prec)
|
||||||
|
{
|
||||||
|
char tmp[100];
|
||||||
|
char *buf, *temp;
|
||||||
|
|
||||||
|
|
||||||
|
if (prec > precedence(expr->op)) {
|
||||||
|
temp = unparse_op(expr, prec_add);
|
||||||
|
buf = alloc_size(strlen(temp)+3);
|
||||||
|
sprintf(buf, "(%s)", temp);
|
||||||
|
free(temp);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
if (expr->op == op_num) {
|
||||||
|
snprintf(tmp, sizeof(tmp), "%lg%s",
|
||||||
|
expr->u.num.n, str_unit(expr->u.num));
|
||||||
|
return stralloc(tmp);
|
||||||
|
}
|
||||||
|
if (expr->op == op_var)
|
||||||
|
return stralloc(expr->u.var);
|
||||||
|
if (expr->op == op_minus)
|
||||||
|
return merge2("-", unparse_op(expr->u.op.a, prec_add));
|
||||||
|
if (expr->op == op_add)
|
||||||
|
return merge3(unparse_op(expr->u.op.a, prec_add), "+",
|
||||||
|
unparse_op(expr->u.op.b, prec_add));
|
||||||
|
if (expr->op == op_sub)
|
||||||
|
return merge3(unparse_op(expr->u.op.a, prec_add), "-",
|
||||||
|
unparse_op(expr->u.op.b, prec_mult));
|
||||||
|
if (expr->op == op_mult)
|
||||||
|
return merge3(unparse_op(expr->u.op.a, prec_mult), "*",
|
||||||
|
unparse_op(expr->u.op.b, prec_mult));
|
||||||
|
if (expr->op == op_div)
|
||||||
|
return merge3(unparse_op(expr->u.op.a, prec_mult), "/",
|
||||||
|
unparse_op(expr->u.op.b, prec_primary));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *unparse(const struct expr *expr)
|
||||||
|
{
|
||||||
|
return expr ? unparse_op(expr, prec_add) : stralloc("");
|
||||||
|
}
|
22
unparse.h
Normal file
22
unparse.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* unparse.h - Dump an expression tree into a string
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef UNPARSE_H
|
||||||
|
#define UNPARSE_H
|
||||||
|
|
||||||
|
#include "expr.h"
|
||||||
|
|
||||||
|
|
||||||
|
char *unparse(const struct expr *expr);
|
||||||
|
|
||||||
|
#endif /* !UNPARSE_H */
|
100
util.c
Normal file
100
util.c
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* util.c - Common utility functions
|
||||||
|
*
|
||||||
|
* Written 2009 by Werner Almesberger
|
||||||
|
* Copyright 2009 by Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- printf buffer allocation ------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
char *stralloc_vprintf(const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
va_list aq;
|
||||||
|
char *buf;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
va_copy(aq, ap);
|
||||||
|
n = snprintf(NULL, 0, fmt, aq);
|
||||||
|
va_end(aq);
|
||||||
|
buf = alloc_size(n+1);
|
||||||
|
vsnprintf(buf, n+1, fmt, ap);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *stralloc_printf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
s = stralloc_vprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- identifier syntax check ------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
int is_id_char(char c, int first)
|
||||||
|
{
|
||||||
|
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
|
||||||
|
return 1;
|
||||||
|
if (first)
|
||||||
|
return 0;
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int is_id(const char *s)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
if (!*s)
|
||||||
|
return 0;
|
||||||
|
for (p = s; *p; p++)
|
||||||
|
if (!is_id_char(*p, s == p))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- unique identifiers ------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
static struct unique {
|
||||||
|
const char *s;
|
||||||
|
struct unique *next;
|
||||||
|
} *uniques = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/* @@@ consider using rb trees */
|
||||||
|
|
||||||
|
const char *unique(const char *s)
|
||||||
|
{
|
||||||
|
struct unique **walk;
|
||||||
|
|
||||||
|
for (walk = &uniques; *walk; walk = &(*walk)->next)
|
||||||
|
if (!strcmp(s, (*walk)->s))
|
||||||
|
return (*walk)->s;
|
||||||
|
*walk = alloc_type(struct unique);
|
||||||
|
(*walk)->s = stralloc(s);
|
||||||
|
(*walk)->next = NULL;
|
||||||
|
return (*walk)->s;
|
||||||
|
}
|
58
util.h
Normal file
58
util.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* util.h - Common utility functions
|
||||||
|
*
|
||||||
|
* Written 2006, 2009 by Werner Almesberger
|
||||||
|
* Copyright 2006, 2009 Werner Almesberger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef UTIL_H
|
||||||
|
#define UTIL_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define alloc_size(s) \
|
||||||
|
({ void *alloc_size_tmp = malloc(s); \
|
||||||
|
if (!alloc_size_tmp) \
|
||||||
|
abort(); \
|
||||||
|
alloc_size_tmp; })
|
||||||
|
|
||||||
|
#define alloc_type(t) ((t *) alloc_size(sizeof(t)))
|
||||||
|
|
||||||
|
#define zalloc_type(t) \
|
||||||
|
({ t *zalloc_type_tmp = alloc_type(t); \
|
||||||
|
memset(zalloc_type_tmp, 0, sizeof(t)); \
|
||||||
|
zalloc_type_tmp; })
|
||||||
|
|
||||||
|
#define stralloc(s) \
|
||||||
|
({ char *stralloc_tmp = strdup(s); \
|
||||||
|
if (!stralloc_tmp) \
|
||||||
|
abort(); \
|
||||||
|
stralloc_tmp; })
|
||||||
|
|
||||||
|
#define strnalloc(s, n) \
|
||||||
|
({ char *strnalloc_tmp = alloc_size((n)+1); \
|
||||||
|
if (!strnalloc_tmp) \
|
||||||
|
abort(); \
|
||||||
|
strncpy(strnalloc_tmp, (s), (n)); \
|
||||||
|
strnalloc_tmp[n] = 0; \
|
||||||
|
strnalloc_tmp; })
|
||||||
|
|
||||||
|
|
||||||
|
char *stralloc_vprintf(const char *fmt, va_list ap);
|
||||||
|
char *stralloc_printf(const char *fmt, ...);
|
||||||
|
|
||||||
|
int is_id_char(char c, int first);
|
||||||
|
int is_id(const char *s);
|
||||||
|
|
||||||
|
const char *unique(const char *s);
|
||||||
|
|
||||||
|
#endif /* !UTIL_H */
|
Loading…
Reference in New Issue
Block a user