2 This file is part of Kig, a KDE program for Interactive Geometry...
3 Copyright (C) 2002 Dominique Devriese <devriese@kde.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 #include "cabri-filter.h"
23 #include "../kig/kig_document.h"
24 #include "../kig/kig_part.h"
25 #include "../misc/coordinate.h"
26 #include "../objects/arc_type.h"
27 #include "../objects/bogus_imp.h"
28 #include "../objects/circle_type.h"
29 #include "../objects/conic_types.h"
30 #include "../objects/curve_imp.h"
31 #include "../objects/line_imp.h"
32 #include "../objects/line_type.h"
33 #include "../objects/object_calcer.h"
34 #include "../objects/object_drawer.h"
35 #include "../objects/object_factory.h"
36 #include "../objects/object_holder.h"
37 #include "../objects/other_imp.h"
38 #include "../objects/other_type.h"
39 #include "../objects/point_type.h"
40 #include "../objects/polygon_type.h"
41 #include "../objects/transform_types.h"
42 #include "../objects/vector_type.h"
53 * "Nr": Type, Unknown, "CN:"NumberOfParents, "VN:"Unknown
54 * Color, FillType, Thickness, "DS":Dots, "GT":SpecialAppearance, Visible, Fixed
55 * ["Const": Parents] ["Val": Constants]
57 * Nr: Simple sequential numbering of the objects in a file.
58 * Type: seen so far: Pt, Axes, Line, Cir
59 * NumberOfParents: The number of parents that will be specified in
78 * W -> not filled ( white ? )
79 * all values of the Color item are valid here too..
85 * a specification for how a line should be drawn ( with many
86 * dots, with short lines, it's a pretty generic format: the first
87 * number signifies the number of sequential dots to draw first, and
88 * the next is the sum of this first number with the number of
89 * spaces to leave before starting a new segment.
91 * a number indicating some way of specially drawing an object. This
92 * can be modified using the "Modify Appearance" button in
93 * Cabri. For example, for a segment, the number indicates the
94 * amount of ticks to put on the segment, to indicate
95 * correspondances between segments..
97 * V means visible, I means invisible
99 * St means fix this object ( if you move one of its parents, it
100 * won't move ), nSt ( the default ) means don't fix this object.
102 * The numbers of the objects this object depends on
104 * Constants whose meaning depends on the type of object. E.g. for
105 * a point, this means first x, then y component.
112 uint numberOfParents
;
118 int specialAppearanceSwitch
;
121 std::vector
<int> parents
;
122 std::vector
<double> data
;
125 KigFilterCabri::KigFilterCabri()
129 KigFilterCabri::~KigFilterCabri()
133 bool KigFilterCabri::supportMime( const QString
& mime
)
135 // ugly hack to avoid duplicate extension ( XFig and Cabri files
136 // have the same .fig extension ).
137 return ( mime
== "image/x-xfig" ) ||
138 ( mime
== "application/x-cabri" );
141 static QString
readLine( QFile
& file
)
144 file
.readLine( ret
, 10000L );
145 if ( ret
[ret
.length() - 1] == '\n' )
146 ret
.truncate( ret
.length() - 1 );
147 if ( ret
[ret
.length() - 1] == '\r' )
148 ret
.truncate( ret
.length() - 1 );
152 static QColor
translatecolor( const QString
& s
)
154 if ( s
== "R" ) return Qt::red
;
155 if ( s
== "O" ) return Qt::magenta
;
156 if ( s
== "Y" ) return Qt::yellow
;
157 if ( s
== "P" ) return Qt::darkMagenta
;
158 if ( s
== "V" ) return Qt::darkBlue
;
159 if ( s
== "Bl" ) return Qt::blue
;
160 if ( s
== "lBl" ) return Qt::cyan
; // TODO: bright blue
161 if ( s
== "G" ) return Qt::green
;
162 if ( s
== "dG" ) return Qt::darkGreen
;
163 if ( s
== "Br" ) return QColor( 165, 42, 42 );
164 if ( s
== "dBr" ) return QColor( 128, 128, 0 );
165 if ( s
== "lGr" ) return Qt::lightGray
;
166 if ( s
== "Gr" ) return Qt::gray
;
167 if ( s
== "dGr" ) return Qt::darkGray
;
168 if ( s
== "B" ) return Qt::black
;
169 if ( s
== "W" ) return Qt::white
;
171 kdDebug() << k_funcinfo
<< "unknown color: " << s
<< endl
;
175 bool KigFilterCabri::readObject( QFile
& f
, CabriObject
& myobj
)
177 // there are 4 lines per object in the file, so we read them all
179 QString line1
, line2
, line3
, s
;
180 QString file
= f
.name();
181 line1
= readLine( f
);
182 line2
= readLine( f
);
183 line3
= readLine( f
);
184 // ignore line 4, it is empty..
187 QRegExp
firstlinere( "^([^:]+): ([^,]+), ([^,]+), CN:([^,]*), VN:(.*)$" );
188 if ( ! firstlinere
.exactMatch( line1
) )
189 KIG_FILTER_PARSE_ERROR
;
194 tmp
= firstlinere
.cap( 1 );
195 myobj
.id
= tmp
.toInt( &ok
);
196 if ( !ok
) KIG_FILTER_PARSE_ERROR
;
198 tmp
= firstlinere
.cap( 2 );
199 myobj
.type
= tmp
.latin1();
201 tmp
= firstlinere
.cap( 3 );
202 // i have no idea what this number means..
204 tmp
= firstlinere
.cap( 4 );
205 myobj
.numberOfParents
= tmp
.toInt( &ok
);
206 if ( ! ok
) KIG_FILTER_PARSE_ERROR
;
208 tmp
= firstlinere
.cap( 5 );
209 // i have no idea what this number means..
211 QRegExp
secondlinere( "^([^,]+), ([^,]+), ([^,]+), DS:([^ ]+) ([^,]+), GT:([^,]+), ([^,]+), (.*)$" );
212 if ( ! secondlinere
.exactMatch( line2
) )
213 KIG_FILTER_PARSE_ERROR
;
215 tmp
= secondlinere
.cap( 1 );
216 myobj
.color
= translatecolor( tmp
);
217 // if ( ! color.isValid() ) KIG_FILTER_PARSE_ERROR;
219 tmp
= secondlinere
.cap( 2 );
220 myobj
.fillColor
= translatecolor( tmp
);
221 // if ( ! fillcolor.isValid() ) KIG_FILTER_PARSE_ERROR;
223 tmp
= secondlinere
.cap( 3 );
224 myobj
.thick
= tmp
== "t" ? 1 : tmp
== "tT" ? 2 : 3;
226 tmp
= secondlinere
.cap( 4 );
227 myobj
.lineSegLength
= tmp
.toInt( &ok
);
228 if ( ! ok
) KIG_FILTER_PARSE_ERROR
;
230 tmp
= secondlinere
.cap( 5 );
231 myobj
.lineSegSplit
= tmp
.toInt( &ok
);
232 if ( ! ok
) KIG_FILTER_PARSE_ERROR
;
234 tmp
= secondlinere
.cap( 6 );
235 myobj
.specialAppearanceSwitch
= tmp
.toInt( &ok
);
236 if ( ! ok
) KIG_FILTER_PARSE_ERROR
;
238 tmp
= secondlinere
.cap( 7 );
239 myobj
.visible
= tmp
== "V";
241 tmp
= secondlinere
.cap( 8 );
242 myobj
.fixed
= tmp
== "St";
244 QRegExp
thirdlinere( "^(Const: ([^,]*),? ?)?(Val: (.*))?$" );
245 if ( ! thirdlinere
.exactMatch( line3
) )
246 KIG_FILTER_PARSE_ERROR
;
248 tmp
= thirdlinere
.cap( 2 );
249 QStringList parentsids
= QStringList::split( ' ', tmp
);
250 for ( QStringList::iterator i
= parentsids
.begin();
251 i
!= parentsids
.end(); ++i
)
253 myobj
.parents
.push_back( ( *i
).toInt( &ok
) );
254 if ( ! ok
) KIG_FILTER_PARSE_ERROR
;
256 if ( myobj
.parents
.size() != myobj
.numberOfParents
)
257 KIG_FILTER_PARSE_ERROR
;
259 tmp
= thirdlinere
.cap( 4 );
260 QStringList valIds
= QStringList::split( ' ', tmp
);
261 for ( QStringList::iterator i
= valIds
.begin();
262 i
!= valIds
.end(); ++i
)
264 myobj
.data
.push_back( ( *i
).toDouble( &ok
) );
265 if ( ! ok
) KIG_FILTER_PARSE_ERROR
;
268 // << k_funcinfo << endl
269 // << "id = " << myobj.id << endl
270 // << "type = " << myobj.type << endl
271 // << "numberOfParents = " << myobj.numberOfParents << endl
272 // << "color = " << myobj.color.name() << endl
273 // << "fillcolor = " << myobj.fillColor.name() << endl
274 // << "thick = " << myobj.thick << endl
275 // << "lineseglength = " << myobj.lineSegLength << endl
276 // << "linesegsplit = " << myobj.lineSegSplit << endl
277 // << "specialAppearanceSwitch = " << myobj.specialAppearanceSwitch << endl
278 // << "visible = " << visible << endl
279 // << "fixed = " << myobj.fixed << endl
280 // << "parents =" << endl;
281 // for ( std::vector<int>::iterator i = myobj.parents.begin(); i != myobj.parents.end(); ++i )
282 // kdDebug() << " " << *i << endl;
283 // kdDebug() << "vals = " << endl;
284 // for ( std::vector<double>::iterator i = myobj.data.begin(); i != myobj.data.end(); ++i )
285 // kdDebug() << " " << *i << endl;
290 KigDocument
* KigFilterCabri::load( const QString
& file
)
293 if ( ! f
.open( IO_ReadOnly
) )
295 fileNotFound( file
);
299 KigDocument
* ret
= new KigDocument();
301 QString s
= readLine( f
);
302 QString a
= s
.left( 21 );
303 QString b
= s
.mid( 21 );
304 if( a
!= "FIGURE CabriII vers. " ||
305 ( b
!= "DOS 1.0" && b
!= "MS-Windows 1.0" ) )
307 if ( s
.left( 5 ) == "#FIG " )
309 notSupported( file
, i18n( "This is a XFig file, not a Cabri figure." ) );
313 KIG_FILTER_PARSE_ERROR
;
317 // line 2: empty line
318 // line 3: window dimensions -> we don't need/use that...
319 // line 4: empty line
320 // line 5 through 8: center point
321 // line 9 through 12: axes
322 // so we skip 11 lines...
323 for( int i
= 0; i
!= 11; ++i
)
326 // all Cabri files seem to at least have these center and axes...
328 KIG_FILTER_PARSE_ERROR
;
330 std::vector
<ObjectHolder
*> holders
;
331 std::vector
<ObjectCalcer
*> calcers
;
333 const ObjectFactory
* fact
= ObjectFactory::instance();
335 std::vector
<ObjectCalcer
*> args
;
336 ObjectCalcer
* oc
= 0;
338 while ( ! f
.atEnd() )
341 // we do one object each iteration..
342 if ( !readObject( f
, obj
) )
346 Qt::PenStyle ls
= Qt::SolidLine
;
347 if ( ( obj
.lineSegLength
> 1 ) && ( obj
.lineSegLength
< 6 ) &&
348 ( obj
.lineSegSplit
> 1 ) && ( obj
.lineSegSplit
<= 10 ) )
350 else if ( ( obj
.lineSegLength
>= 6 ) && ( obj
.lineSegSplit
> 10 ) )
355 for ( std::vector
<int>::iterator i
= obj
.parents
.begin();
356 i
!= obj
.parents
.end(); ++i
)
357 args
.push_back( calcers
[*i
-3] );
359 // two fake objects at the start ( origin and axes.. )
360 if ( obj
.id
!= calcers
.size() + 3 ) KIG_FILTER_PARSE_ERROR
;
362 if ( obj
.type
== "Pt" )
364 if ( ! args
.empty() ) KIG_FILTER_PARSE_ERROR
;
365 if ( obj
.data
.size() != 2 ) KIG_FILTER_PARSE_ERROR
;
367 switch ( obj
.specialAppearanceSwitch
)
392 // different sizes for points..
395 oc
= fact
->fixedPointCalcer( Coordinate( obj
.data
[0], obj
.data
[1] ) );
397 else if ( obj
.type
== "Cir" )
399 if ( args
.size() == 1 )
401 if ( obj
.data
.size() != 1 ) KIG_FILTER_PARSE_ERROR
;
402 ObjectConstCalcer
* radc
=
403 new ObjectConstCalcer( new DoubleImp( obj
.data
[0] ) );
404 args
.push_back( radc
);
405 oc
= new ObjectTypeCalcer( CircleBPRType::instance(), args
);
407 else if ( args
.size() == 2 )
409 if ( ! obj
.data
.empty() ) KIG_FILTER_PARSE_ERROR
;
410 oc
= new ObjectTypeCalcer( CircleBCPType::instance(), args
);
412 else KIG_FILTER_PARSE_ERROR
;
414 else if ( obj
.type
== "Line" || obj
.type
== "Ray" || obj
.type
== "Seg" ||
417 if ( args
.size() == 1 )
419 if ( obj
.data
.size() != 2 ) KIG_FILTER_PARSE_ERROR
;
420 Coordinate
vect( obj
.data
[0], obj
.data
[1] );
421 ObjectConstCalcer
* vectorcalcer
=
422 new ObjectConstCalcer( new VectorImp( Coordinate( 0, 0 ), vect
) );
423 args
.push_back( vectorcalcer
);
424 ObjectTypeCalcer
* secondpoint
=
425 new ObjectTypeCalcer( TranslatedType::instance(), args
);
426 secondpoint
->calc( *ret
);
427 args
[1] = secondpoint
;
429 if ( args
.size() != 2 ) KIG_FILTER_PARSE_ERROR
;
430 const ObjectType
* t
= 0;
431 if ( obj
.type
== "Line" ) t
= LineABType::instance();
432 else if ( obj
.type
== "Ray" ) t
= RayABType::instance();
433 else if ( obj
.type
== "Seg" ) t
= SegmentABType::instance();
434 else if ( obj
.type
== "Vec" ) t
= VectorType::instance();
436 oc
= new ObjectTypeCalcer( t
, args
);
438 else if ( obj
.type
== "Pt/" )
440 // different sizes for points..
442 if ( args
.size() != 1 || obj
.data
.size() != 2 )
443 KIG_FILTER_PARSE_ERROR
;
444 ObjectCalcer
* parent
= args
[0];
445 if ( !parent
->imp()->inherits( CurveImp::stype() ) )
446 KIG_FILTER_PARSE_ERROR
;
447 const CurveImp
* curve
= static_cast<const CurveImp
*>( parent
->imp() );
448 Coordinate pt
= Coordinate( obj
.data
[0], obj
.data
[1] );
449 double param
= curve
->getParam( pt
, *ret
);
450 args
.push_back( new ObjectConstCalcer( new DoubleImp( param
) ) );
451 oc
= new ObjectTypeCalcer( ConstrainedPointType::instance(), args
);
453 else if ( obj
.type
== "Perp" || obj
.type
== "Par" )
455 if ( args
.size() != 2 || obj
.data
.size() != 0 )
456 KIG_FILTER_PARSE_ERROR
;
457 const ObjectType
* t
= 0;
458 if ( obj
.type
== "Perp" ) t
= LinePerpendLPType::instance();
459 else if ( obj
.type
== "Par" ) t
= LineParallelLPType::instance();
460 else assert( false );
461 oc
= new ObjectTypeCalcer( t
, args
);
463 else if ( obj
.type
== "Arc" )
465 if ( args
.size() != 3 || ! obj
.data
.empty() )
466 KIG_FILTER_PARSE_ERROR
;
467 oc
= new ObjectTypeCalcer( ArcBTPType::instance(), args
);
469 else if ( obj
.type
== "Con" )
471 if ( args
.size() != 5 || !obj
.data
.empty() )
472 KIG_FILTER_PARSE_ERROR
;
473 oc
= new ObjectTypeCalcer( ConicB5PType::instance(), args
);
475 else if ( obj
.type
== "Mid" )
477 if ( args
.size() == 2 )
480 new ObjectTypeCalcer( SegmentABType::instance(), args
);
485 // midpoint -> this can be the midpoint of a segment, two
486 // points, or a vector...
487 if ( args
.size() != 1 || !obj
.data
.empty() )
488 KIG_FILTER_PARSE_ERROR
;
489 ObjectCalcer
* parent
= args
[0];
490 if ( parent
->imp()->inherits( SegmentImp::stype() ) )
491 oc
= fact
->propertyObjectCalcer( parent
, "mid-point" ) ;
492 else if ( parent
->imp()->inherits( VectorImp::stype() ) )
493 oc
= fact
->propertyObjectCalcer( parent
, "vect-mid-point" );
494 else KIG_FILTER_PARSE_ERROR
;
496 else if ( obj
.type
== "PBiss" )
498 if ( args
.size() == 2 )
501 new ObjectTypeCalcer( SegmentABType::instance(), args
);
506 if ( args
.size() != 1 || !obj
.data
.empty() )
507 KIG_FILTER_PARSE_ERROR
;
508 ObjectCalcer
* parent
= args
[0];
509 ObjectCalcer
* midpoint
= 0;
510 if ( parent
->imp()->inherits( SegmentImp::stype() ) )
511 midpoint
= fact
->propertyObjectCalcer( parent
, "mid-point" ) ;
512 // else if ( parent->imp()->inherits( VectorImp::stype() ) )
513 // midpoint = fact->propertyObjectCalcer( parent, "vect-mid-point" );
514 else KIG_FILTER_PARSE_ERROR
;
515 midpoint
->calc( *ret
);
516 args
.push_back( midpoint
);
517 oc
= new ObjectTypeCalcer( LinePerpendLPType::instance(), args
);
519 else if ( obj
.type
== "Pol" )
521 if ( args
.size() < 3 || !obj
.data
.empty() )
522 KIG_FILTER_PARSE_ERROR
;
523 oc
= new ObjectTypeCalcer( PolygonBNPType::instance(), args
);
525 else if ( obj
.type
== "Locus" )
527 if ( args
.size() != 2 || !obj
.data
.empty() )
528 KIG_FILTER_PARSE_ERROR
;
529 oc
= fact
->locusCalcer( args
[0], args
[1] );
531 else if ( obj
.type
== "Refl" )
533 if ( args
.size() != 2 || !obj
.data
.empty() )
534 KIG_FILTER_PARSE_ERROR
;
535 oc
= new ObjectTypeCalcer( LineReflectionType::instance(), args
);
537 else if ( obj
.type
== "Sym" )
539 if ( args
.size() != 2 || !obj
.data
.empty() )
540 KIG_FILTER_PARSE_ERROR
;
541 oc
= new ObjectTypeCalcer( PointReflectionType::instance(), args
);
543 else if ( obj
.type
== "Tran" )
545 if ( args
.size() != 2 || !obj
.data
.empty() )
546 KIG_FILTER_PARSE_ERROR
;
547 oc
= new ObjectTypeCalcer( TranslatedType::instance(), args
);
551 notSupported( file
, i18n( "This Cabri file contains a \"%1\" object, "
552 "which Kig does not currently support." ).arg( obj
.type
) );
556 if ( oc
== 0 ) KIG_FILTER_PARSE_ERROR
;
559 calcers
.push_back( oc
);
560 ObjectDrawer
* d
= new ObjectDrawer( obj
.color
, obj
.thick
, obj
.visible
, ls
, ps
);
561 ObjectHolder
* oh
= new ObjectHolder( oc
, d
);
562 holders
.push_back( oh
);
567 ret
->addObjects( holders
);
568 ret
->setGrid( false );
569 ret
->setAxes( false );
573 KigFilterCabri
* KigFilterCabri::instance()
575 static KigFilterCabri t
;