moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kig / filters / cabri-filter.cc
blob0477081e712beef644c6a8acf847cc3d10a9d0aa
1 /**
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
18 USA
19 **/
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"
44 #include <qcolor.h>
45 #include <qfile.h>
46 #include <qregexp.h>
48 #include <kdebug.h>
49 #include <klocale.h>
51 /**
52 * a line:
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
60 * Parents
61 * Color:
62 * R -> red
63 * O -> purple
64 * Y -> yellow
65 * P -> dark purple
66 * V -> dark blue
67 * Bl -> blue
68 * lBl -> bright blue
69 * G -> bright green
70 * dG -> dark green
71 * Br -> brown
72 * dBr -> beige
73 * lGr -> light grey
74 * Gr -> grey
75 * dGr -> dark grey
76 * B -> black
77 * FillType:
78 * W -> not filled ( white ? )
79 * all values of the Color item are valid here too..
80 * Thickness:
81 * t -> thin
82 * tT -> medium
83 * T -> Thick
84 * Dots:
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.
90 * SpecialAppearance:
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..
96 * Visible:
97 * V means visible, I means invisible
98 * Fixed:
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.
101 * Parents:
102 * The numbers of the objects this object depends on
103 * Constants:
104 * Constants whose meaning depends on the type of object. E.g. for
105 * a point, this means first x, then y component.
108 struct CabriObject
110 uint id;
111 QCString type;
112 uint numberOfParents;
113 QColor color;
114 QColor fillColor;
115 int thick;
116 int lineSegLength;
117 int lineSegSplit;
118 int specialAppearanceSwitch;
119 bool visible;
120 bool fixed;
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 )
143 QString ret;
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 );
149 return ret;
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;
172 return Qt::black;
175 bool KigFilterCabri::readObject( QFile& f, CabriObject& myobj )
177 // there are 4 lines per object in the file, so we read them all
178 // four now.
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..
185 s = readLine( f );
187 QRegExp firstlinere( "^([^:]+): ([^,]+), ([^,]+), CN:([^,]*), VN:(.*)$" );
188 if ( ! firstlinere.exactMatch( line1 ) )
189 KIG_FILTER_PARSE_ERROR;
191 bool ok;
192 QString tmp;
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;
267 // kdDebug()
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;
287 return true;
290 KigDocument* KigFilterCabri::load( const QString& file )
292 QFile f( file );
293 if ( ! f.open( IO_ReadOnly ) )
295 fileNotFound( file );
296 return 0;
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." ) );
310 return 0;
312 else
313 KIG_FILTER_PARSE_ERROR;
316 // next we have:
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)
324 s = readLine( f );
326 // all Cabri files seem to at least have these center and axes...
327 if ( f.atEnd() )
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() )
340 CabriObject obj;
341 // we do one object each iteration..
342 if ( !readObject( f, obj ) )
343 return 0;
345 // reading linestyle
346 Qt::PenStyle ls = Qt::SolidLine;
347 if ( ( obj.lineSegLength > 1 ) && ( obj.lineSegLength < 6 ) &&
348 ( obj.lineSegSplit > 1 ) && ( obj.lineSegSplit <= 10 ) )
349 ls = Qt::DotLine;
350 else if ( ( obj.lineSegLength >= 6 ) && ( obj.lineSegSplit > 10 ) )
351 ls = Qt::DashLine;
352 int ps = 0;
354 args.clear();
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;
361 oc = 0;
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 )
369 case 0:
371 obj.thick -= 1;
372 break;
374 case 2:
376 obj.thick += 1;
377 break;
379 case 3:
381 obj.thick += 1;
382 ps = 1;
383 break;
385 case 4:
387 obj.thick += 2;
388 ps = 4;
389 break;
392 // different sizes for points..
393 obj.thick *= 2;
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" ||
415 obj.type == "Vec" )
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();
435 else assert( t );
436 oc = new ObjectTypeCalcer( t, args );
438 else if ( obj.type == "Pt/" )
440 // different sizes for points..
441 obj.thick *= 2;
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 )
479 ObjectCalcer* c =
480 new ObjectTypeCalcer( SegmentABType::instance(), args );
481 c->calc( *ret );
482 args.clear();
483 args.push_back( c );
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 )
500 ObjectCalcer* c =
501 new ObjectTypeCalcer( SegmentABType::instance(), args );
502 c->calc( *ret );
503 args.clear();
504 args.push_back( c );
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 );
549 else
551 notSupported( file, i18n( "This Cabri file contains a \"%1\" object, "
552 "which Kig does not currently support." ).arg( obj.type ) );
553 return 0;
556 if ( oc == 0 ) KIG_FILTER_PARSE_ERROR;
558 oc->calc( *ret );
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 );
564 oc = 0;
567 ret->addObjects( holders );
568 ret->setGrid( false );
569 ret->setAxes( false );
570 return ret;
573 KigFilterCabri* KigFilterCabri::instance()
575 static KigFilterCabri t;
576 return &t;