~ubuntu-branches/ubuntu/trusty/3depict/trusty-proposed

« back to all changes in this revision

Viewing changes to src/backend/state.cpp

  • Committer: Package Import Robot
  • Author(s): D Haley
  • Date: 2013-07-20 18:31:32 UTC
  • mfrom: (1.1.6)
  • Revision ID: package-import@ubuntu.com-20130720183132-5ak932y2x6pwo4fs
Tags: 0.0.14-1
* Update to upstream, 0.0.14
* Enable mathgl2.x configure option
* Modify build-depends, libmgl-dev >= 2.1.32

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      state.cpp - user session state handler
 
3
 *      Copyright (C) 2013, D Haley 
 
4
 
 
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 3 of the License, or
 
8
 *      (at your option) any later version.
 
9
 
 
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.
 
14
 
 
15
 *      You should have received a copy of the GNU General Public License
 
16
 *      along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
*/
 
18
 
 
19
#include "state.h"
 
20
 
 
21
#include "common/translation.h"
 
22
#include "common/xmlHelper.h"
 
23
#include "common/stringFuncs.h"
 
24
 
 
25
const unsigned int MAX_UNDO_SIZE=10;
 
26
 
 
27
#include <unistd.h>
 
28
 
 
29
 
 
30
AnalysisState::AnalysisState()
 
31
{
 
32
        modifyLevel=STATE_MODIFIED_NONE;
 
33
        useRelativePathsForSave=false;
 
34
        activeCamera=0;
 
35
}
 
36
 
 
37
 
 
38
void AnalysisState::operator=(const AnalysisState &oth)
 
39
{
 
40
        clear();
 
41
 
 
42
        activeTree=oth.activeTree;
 
43
        
 
44
        stashedTrees=oth.stashedTrees;
 
45
 
 
46
        effects.resize(oth.effects.size());
 
47
        for(size_t ui=0;ui<effects.size();ui++)
 
48
                effects[ui]= oth.effects[ui]->clone();
 
49
 
 
50
        savedCameras.resize(oth.savedCameras.size());
 
51
        for(size_t ui=0;ui<savedCameras.size();ui++)
 
52
                savedCameras[ui]= oth.savedCameras[ui]->clone();
 
53
 
 
54
 
 
55
        undoTrees=oth.undoTrees; 
 
56
        redoTrees=oth.redoTrees;
 
57
 
 
58
        fileName=oth.fileName;
 
59
        workingDir=oth.workingDir;
 
60
        useRelativePathsForSave=oth.useRelativePathsForSave;
 
61
 
 
62
        rBack=oth.rBack;
 
63
        gBack=oth.gBack;
 
64
        bBack=oth.bBack;
 
65
 
 
66
        worldAxisMode=oth.worldAxisMode;
 
67
        activeCamera=oth.activeCamera;
 
68
 
 
69
        modifyLevel=oth.modifyLevel;
 
70
 
 
71
 
 
72
        redoFilterStack=oth.redoFilterStack;
 
73
        undoFilterStack=oth.undoFilterStack;
 
74
 
 
75
}
 
76
 
 
77
 
 
78
void AnalysisState::clear()
 
79
{
 
80
        activeTree.clear();
 
81
        
 
82
        stashedTrees.clear();
 
83
 
 
84
        clearCams();
 
85
 
 
86
        clearEffects();
 
87
 
 
88
        undoTrees.clear(); 
 
89
        redoTrees.clear();
 
90
 
 
91
        fileName.clear();
 
92
        workingDir.clear();
 
93
}
 
94
 
 
95
void AnalysisState::clearCams()
 
96
{
 
97
        for(size_t ui=0;ui<savedCameras.size();ui++)
 
98
                delete savedCameras[ui];
 
99
        savedCameras.clear();
 
100
}
 
101
 
 
102
void AnalysisState::clearEffects()
 
103
{
 
104
        for(size_t ui=0;ui<effects.size();ui++)
 
105
                delete effects[ui];
 
106
 
 
107
        effects.clear();
 
108
 
 
109
}
 
110
bool AnalysisState::save(const char *cpFilename, std::map<string,string> &fileMapping,
 
111
                bool writePackage) const
 
112
{
 
113
        //Open file for output
 
114
        std::ofstream f(cpFilename);
 
115
 
 
116
        if(!f)
 
117
                return false;
 
118
        
 
119
        //Write header, also use local language if available
 
120
        const char *headerMessage = NTRANS("This file is a \"state\" file for the 3Depict program, and stores information about a particular analysis session. This file should be a valid \"XML\" file");
 
121
 
 
122
        f << "<!--" <<  headerMessage;
 
123
        if(TRANS(headerMessage) != headerMessage) 
 
124
                f << endl << TRANS(headerMessage); 
 
125
        
 
126
        f << "-->" <<endl;
 
127
 
 
128
 
 
129
 
 
130
        //Write state open tag 
 
131
        f<< "<threeDepictstate>" << endl;
 
132
        f<<tabs(1)<< "<writer version=\"" << PROGRAM_VERSION << "\"/>" << endl;
 
133
        //write general settings
 
134
        //---------------
 
135
        f << tabs(1) << "<backcolour r=\"" << rBack << "\" g=\"" << 
 
136
                 gBack  << "\" b=\"" << bBack << "\"/>" <<  endl;
 
137
        
 
138
        f << tabs(1) << "<showaxis value=\"" << worldAxisMode << "\"/>"  << endl;
 
139
 
 
140
 
 
141
        if(useRelativePathsForSave)
 
142
        {
 
143
                //Save path information
 
144
                //Note: When writing packages, 
 
145
                //- we don't want to leak path names to remote systems 
 
146
                //and
 
147
                //- we cannot assume that directory structures are preserved between systems
 
148
                //
 
149
                //so don't keep working directory in this case.
 
150
                if(writePackage || workingDir.empty() )
 
151
                {
 
152
                        //Are we saving the sate as a package, if so
 
153
                        //make sure we let other 3depict loaders know
 
154
                        //that we want to use relative paths
 
155
                        f << tabs(1) << "<userelativepaths/>"<< endl;
 
156
                }
 
157
                else
 
158
                {
 
159
                        //Not saving a package, however we could be, 
 
160
                        //for example, be autosaving a load-from-package. 
 
161
                        //We want to keep relative paths, but
 
162
                        //want to be able to find files if something goes askew
 
163
                        f << tabs(1) << "<userelativepaths origworkdir=\"" << workingDir << "\"/>"<< endl;
 
164
                }
 
165
        }
 
166
 
 
167
        //---------------
 
168
 
 
169
 
 
170
        //Write filter tree
 
171
        //---------------
 
172
        if(!activeTree.saveXML(f,fileMapping,writePackage,useRelativePathsForSave))
 
173
                return  false;
 
174
        //---------------
 
175
 
 
176
        //Save all cameras.
 
177
        f <<tabs(1) <<  "<cameras>" << endl;
 
178
 
 
179
        //First camera is the "working" camera, which is unnamed
 
180
        f << tabs(2) << "<active value=\"" << activeCamera << "\"/>" << endl;
 
181
        
 
182
        for(unsigned int ui=0;ui<savedCameras.size();ui++)
 
183
        {
 
184
                //ask each camera to write its own state, tab indent 2
 
185
                savedCameras[ui]->writeState(f,STATE_FORMAT_XML,2);
 
186
        }
 
187
        f <<tabs(1) <<  "</cameras>" << endl;
 
188
        
 
189
        if(stashedTrees.size())
 
190
        {
 
191
                f << tabs(1) << "<stashedfilters>" << endl;
 
192
 
 
193
                for(unsigned int ui=0;ui<stashedTrees.size();ui++)
 
194
                {
 
195
                        f << tabs(2) << "<stash name=\"" << stashedTrees[ui].first
 
196
                                << "\">" << endl;
 
197
                        stashedTrees[ui].second.saveXML(f,fileMapping,
 
198
                                        writePackage,useRelativePathsForSave,3);
 
199
                        f << tabs(2) << "</stash>" << endl;
 
200
                }
 
201
 
 
202
 
 
203
 
 
204
 
 
205
                f << tabs(1) << "</stashedfilters>" << endl;
 
206
        }
 
207
 
 
208
        //Save any effects
 
209
        if(effects.size())
 
210
        {
 
211
                f <<tabs(1) <<  "<effects>" << endl;
 
212
                for(unsigned int ui=0;ui<effects.size();ui++)
 
213
                        effects[ui]->writeState(f,STATE_FORMAT_XML,1);
 
214
                f <<tabs(1) <<  "</effects>" << endl;
 
215
 
 
216
        }
 
217
 
 
218
 
 
219
 
 
220
        //Close XMl tag.        
 
221
        f<< "</threeDepictstate>" << endl;
 
222
 
 
223
        //Debug check to ensure we have written a valid xml file
 
224
        ASSERT(isValidXML(cpFilename));
 
225
 
 
226
        modifyLevel=STATE_MODIFIED_NONE;
 
227
 
 
228
        return true;
 
229
}
 
230
 
 
231
bool AnalysisState::load(const char *cpFilename, std::ostream &errStream, bool merge) 
 
232
{
 
233
        clear();
 
234
 
 
235
        //Load the state from an XML file
 
236
        
 
237
        //here we use libxml2's loading routines
 
238
        //http://xmlsoft.org/
 
239
        //Tutorial: http://xmlsoft.org/tutorial/xmltutorial.pdf
 
240
        xmlDocPtr doc;
 
241
        xmlParserCtxtPtr context;
 
242
 
 
243
        context =xmlNewParserCtxt();
 
244
 
 
245
 
 
246
        if(!context)
 
247
        {
 
248
                errStream << TRANS("Failed to allocate parser") << std::endl;
 
249
                return false;
 
250
        }
 
251
 
 
252
        //Open the XML file
 
253
        doc = xmlCtxtReadFile(context, cpFilename, NULL,0);
 
254
 
 
255
        if(!doc)
 
256
                return false;
 
257
        
 
258
        //release the context
 
259
        xmlFreeParserCtxt(context);
 
260
        
 
261
 
 
262
        //By default, lets not use relative paths
 
263
        if(!merge)
 
264
                useRelativePathsForSave=false;
 
265
 
 
266
        //Lets do some parsing goodness
 
267
        //ahh parsing - verbose and boring
 
268
        FilterTree newFilterTree;
 
269
        vector<Camera *> newCameraVec;
 
270
        vector<Effect *> newEffectVec;
 
271
        vector<pair<string,FilterTree > > newStashes;
 
272
 
 
273
        std::string stateDir=onlyDir(cpFilename);
 
274
        try
 
275
        {
 
276
                std::stack<xmlNodePtr>  nodeStack;
 
277
                //retrieve root node    
 
278
                xmlNodePtr nodePtr = xmlDocGetRootElement(doc);
 
279
 
 
280
                //Umm where is our root node guys?
 
281
                if(!nodePtr)
 
282
                {
 
283
                        errStream << TRANS("Unable to retrieve root node in input state file... Is this really a non-empty XML file?") <<  endl;
 
284
                        throw 1;
 
285
                }
 
286
                
 
287
                //This *should* be an threeDepict state file
 
288
                if(xmlStrcmp(nodePtr->name, (const xmlChar *)"threeDepictstate"))
 
289
                {
 
290
                        errStream << TRANS("Base state node missing. Is this really a state XML file??") << endl;
 
291
                        throw 1;
 
292
                }
 
293
                //push root tag 
 
294
                nodeStack.push(nodePtr);
 
295
                
 
296
                //Now in threeDepictstate tag
 
297
                nodePtr = nodePtr->xmlChildrenNode;
 
298
                xmlChar *xmlString;
 
299
                //check for version tag & number
 
300
                if(!XMLHelpFwdToElem(nodePtr,"writer"))
 
301
                {
 
302
                        xmlString=xmlGetProp(nodePtr, (const xmlChar *)"version"); 
 
303
 
 
304
                        if(xmlString)
 
305
                        {
 
306
                                string tmpVer;
 
307
                                
 
308
                                tmpVer =(char *)xmlString;
 
309
                                //Check to see if only contains 0-9 period and "-" characters (valid version number)
 
310
                                if(tmpVer.find_first_not_of("0123456789.-")== std::string::npos)
 
311
                                {
 
312
                                        //Check between the writer reported version, and the current program version
 
313
                                        vector<string> vecStrs;
 
314
                                        vecStrs.push_back(tmpVer);
 
315
                                        vecStrs.push_back(PROGRAM_VERSION);
 
316
 
 
317
                                        if(getMaxVerStr(vecStrs)!=PROGRAM_VERSION)
 
318
                                        {
 
319
                                                errStream << TRANS("State was created by a newer version of this program.. ")
 
320
                                                        << TRANS("file reading will continue, but may fail.") << endl ;
 
321
                                        }
 
322
                                }
 
323
                                else
 
324
                                {
 
325
                                        errStream<< TRANS("Warning, unparseable version number in state file. File reading will continue, but may fail") << endl;
 
326
                                }
 
327
                                xmlFree(xmlString);
 
328
                        }
 
329
                }
 
330
                else
 
331
                {
 
332
                        errStream<< TRANS("Unable to find the \"writer\" node") << endl;
 
333
                        throw 1;
 
334
                }
 
335
        
 
336
 
 
337
                //Get the background colour
 
338
                //====
 
339
                float rTmp,gTmp,bTmp;
 
340
                if(XMLHelpFwdToElem(nodePtr,"backcolour"))
 
341
                {
 
342
                        errStream<< TRANS("Unable to find the \"backcolour\" node.") << endl;
 
343
                        throw 1;
 
344
                }
 
345
 
 
346
                xmlString=xmlGetProp(nodePtr,(const xmlChar *)"r");
 
347
                if(!xmlString)
 
348
                {
 
349
                        errStream<< TRANS("\"backcolour\" node missing \"r\" value.") << endl;
 
350
                        throw 1;
 
351
                }
 
352
                if(stream_cast(rTmp,(char *)xmlString))
 
353
                {
 
354
                        errStream<< TRANS("Unable to interpret \"backColour\" node's \"r\" value.") << endl;
 
355
                        throw 1;
 
356
                }
 
357
 
 
358
                xmlFree(xmlString);
 
359
                xmlString=xmlGetProp(nodePtr,(const xmlChar *)"g");
 
360
                if(!xmlString)
 
361
                {
 
362
                        errStream<< TRANS("\"backcolour\" node missing \"g\" value.") << endl;
 
363
                        throw 1;
 
364
                }
 
365
 
 
366
                if(stream_cast(gTmp,(char *)xmlString))
 
367
                {
 
368
                        errStream<< TRANS("Unable to interpret \"backColour\" node's \"g\" value.") << endl;
 
369
                        throw 1;
 
370
                }
 
371
 
 
372
                xmlFree(xmlString);
 
373
                xmlString=xmlGetProp(nodePtr,(const xmlChar *)"b");
 
374
                if(!xmlString)
 
375
                {
 
376
                        errStream<< TRANS("\"backcolour\" node missing \"b\" value.") << endl;
 
377
                        throw 1;
 
378
                }
 
379
 
 
380
                if(stream_cast(bTmp,(char *)xmlString))
 
381
                {
 
382
                        errStream<< TRANS("Unable to interpret \"backColour\" node's \"b\" value.") << endl;
 
383
                        throw 1;
 
384
                }
 
385
 
 
386
                if(rTmp > 1.0 || gTmp>1.0 || bTmp > 1.0 || 
 
387
                        rTmp < 0.0 || gTmp < 0.0 || bTmp < 0.0)
 
388
                {
 
389
                        errStream<< TRANS("\"backcolour\"s rgb values must be in range [0,1]") << endl;
 
390
                        throw 1;
 
391
                }
 
392
                rBack=rTmp;
 
393
                gBack=gTmp;
 
394
                bBack=bTmp;
 
395
                
 
396
                xmlFree(xmlString);
 
397
 
 
398
                nodeStack.push(nodePtr);
 
399
 
 
400
 
 
401
                if(!XMLHelpFwdToElem(nodePtr,"userelativepaths"))
 
402
                {
 
403
                        useRelativePathsForSave=true;
 
404
 
 
405
                        //Try to load the original working directory, if possible
 
406
                        if(!XMLGetAttrib(nodePtr,workingDir,"origworkdir"))
 
407
                                workingDir.clear();
 
408
                }
 
409
                
 
410
                nodePtr=nodeStack.top();
 
411
 
 
412
                //====
 
413
                
 
414
                //Get the axis visibility
 
415
                if(!XMLGetNextElemAttrib(nodePtr,worldAxisMode,"showaxis","value"))
 
416
                {
 
417
                        errStream << TRANS("Unable to find or interpret \"showaxis\" node") << endl;
 
418
                        throw 1;
 
419
                }
 
420
 
 
421
                //find filtertree data
 
422
                if(XMLHelpFwdToElem(nodePtr,"filtertree"))
 
423
                {
 
424
                        errStream << TRANS("Unable to locate \"filtertree\" node.") << endl;
 
425
                        throw 1;
 
426
                }
 
427
 
 
428
                //Load the filter tree
 
429
                if(newFilterTree.loadXML(nodePtr,errStream,stateDir))
 
430
                        throw 1;
 
431
 
 
432
                //Read camera states, if present
 
433
                nodeStack.push(nodePtr);
 
434
                if(!XMLHelpFwdToElem(nodePtr,"cameras"))
 
435
                {
 
436
                        //Move to camera active tag 
 
437
                        nodePtr=nodePtr->xmlChildrenNode;
 
438
                        if(XMLHelpFwdToElem(nodePtr,"active"))
 
439
                        {
 
440
                                errStream << TRANS("Cameras section missing \"active\" node.") << endl;
 
441
                                throw 1;
 
442
                        }
 
443
 
 
444
                        //read ID of active cam
 
445
                        xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
 
446
                        if(!xmlString)
 
447
                        {
 
448
                                errStream<< TRANS("Unable to find property \"value\"  for \"cameras->active\" node.") << endl;
 
449
                                throw 1;
 
450
                        }
 
451
 
 
452
                        if(stream_cast(activeCamera,xmlString))
 
453
                        {
 
454
                                errStream<< TRANS("Unable to interpret property \"value\"  for \"cameras->active\" node.") << endl;
 
455
                                throw 1;
 
456
                        }
 
457
 
 
458
                
 
459
                        //Spin through the list of each camera  
 
460
                        while(!XMLHelpNextType(nodePtr,XML_ELEMENT_NODE))
 
461
                        {
 
462
                                std::string tmpStr;
 
463
                                tmpStr =(const char *)nodePtr->name;
 
464
                                Camera *thisCam;
 
465
                                thisCam=0;
 
466
                                
 
467
                                //work out the camera type
 
468
                                if(tmpStr == "persplookat")
 
469
                                {
 
470
                                        thisCam = new CameraLookAt;
 
471
                                        if(!thisCam->readState(nodePtr->xmlChildrenNode))
 
472
                                        {
 
473
                                                std::string s =TRANS("Failed to interpret camera state for camera : "); 
 
474
 
 
475
                                                errStream<< s <<  newCameraVec.size() << endl;
 
476
                                                throw 1;
 
477
                                        }
 
478
                                }
 
479
                                else
 
480
                                {
 
481
                                        errStream << TRANS("Unable to interpret the camera type for camera : ") << newCameraVec.size() <<  endl;
 
482
                                        throw 1;
 
483
                                }
 
484
 
 
485
                                ASSERT(thisCam);
 
486
                                newCameraVec.push_back(thisCam);        
 
487
                        }
 
488
 
 
489
                        //Enforce active cam value validity
 
490
                        if(newCameraVec.size() < activeCamera)
 
491
                                activeCamera=0;
 
492
 
 
493
                }
 
494
                
 
495
                nodePtr=nodeStack.top();
 
496
                nodeStack.pop();
 
497
                
 
498
                nodeStack.push(nodePtr);
 
499
                //Read stashes if present
 
500
                if(!XMLHelpFwdToElem(nodePtr,"stashedfilters"))
 
501
                {
 
502
                        nodeStack.push(nodePtr);
 
503
 
 
504
                        //Move to stashes 
 
505
                        nodePtr=nodePtr->xmlChildrenNode;
 
506
 
 
507
                        while(!XMLHelpFwdToElem(nodePtr,"stash"))
 
508
                        {
 
509
                                string stashName;
 
510
                                FilterTree newStashTree;
 
511
                                newStashTree.clear();
 
512
 
 
513
                                //read name of stash
 
514
                                xmlString=xmlGetProp(nodePtr,(const xmlChar *)"name");
 
515
                                if(!xmlString)
 
516
                                {
 
517
                                        errStream << TRANS("Unable to locate stash name for stash ") << newStashTree.size()+1 << endl;
 
518
                                        throw 1;
 
519
                                }
 
520
                                stashName=(char *)xmlString;
 
521
 
 
522
                                if(!stashName.size())
 
523
                                {
 
524
                                        errStream << TRANS("Empty stash name for stash ") << newStashTree.size()+1 << endl;
 
525
                                        throw 1;
 
526
                                }
 
527
 
 
528
                                xmlNodePtr tmpNode;
 
529
                                tmpNode=nodePtr->xmlChildrenNode;
 
530
                                
 
531
                                if(XMLHelpFwdToElem(tmpNode,"filtertree"))
 
532
                                {
 
533
                                        errStream << TRANS("No filter tree for stash:") << stashName << endl;
 
534
                                        throw 1;
 
535
                                }
 
536
 
 
537
                                if(newStashTree.loadXML(tmpNode,errStream,stateDir))
 
538
                                {
 
539
                                        errStream << TRANS("For stash ") << newStashTree.size()+1 << endl;
 
540
                                        throw 1;
 
541
                                }
 
542
                                
 
543
                                //if there were any valid elements loaded (could be empty, for exmapl)
 
544
                                if(newStashTree.size())
 
545
                                        newStashes.push_back(make_pair(stashName,newStashTree));
 
546
                        }
 
547
 
 
548
                        nodePtr=nodeStack.top();
 
549
                        nodeStack.pop();
 
550
                }
 
551
                nodePtr=nodeStack.top();
 
552
                nodeStack.pop();
 
553
        
 
554
                //Read effects, if present
 
555
                nodeStack.push(nodePtr);
 
556
 
 
557
                //Read effects if present
 
558
                if(!XMLHelpFwdToElem(nodePtr,"effects"))
 
559
                {
 
560
                        std::string tmpStr;
 
561
                        nodePtr=nodePtr->xmlChildrenNode;
 
562
                        while(!XMLHelpNextType(nodePtr,XML_ELEMENT_NODE))
 
563
                        {
 
564
                                tmpStr =(const char *)nodePtr->name;
 
565
 
 
566
                                Effect *e;
 
567
                                e = makeEffect(tmpStr);
 
568
                                if(!e)
 
569
                                {
 
570
                                        errStream << TRANS("Unrecognised effect :") << tmpStr << endl;
 
571
                                        throw 1;
 
572
                                }
 
573
 
 
574
                                //Check the effects are unique
 
575
                                for(unsigned int ui=0;ui<newEffectVec.size();ui++)
 
576
                                {
 
577
                                        if(newEffectVec[ui]->getType()== e->getType())
 
578
                                        {
 
579
                                                delete e;
 
580
                                                errStream << TRANS("Duplicate effect found") << tmpStr << TRANS(" cannot use.") << endl;
 
581
                                                throw 1;
 
582
                                        }
 
583
 
 
584
                                }
 
585
 
 
586
                                nodeStack.push(nodePtr);
 
587
                                //Parse the effect
 
588
                                if(!e->readState(nodePtr))
 
589
                                {
 
590
                                        errStream << TRANS("Error reading effect : ") << e->getName() << std::endl;
 
591
                                
 
592
                                        throw 1;
 
593
                                }
 
594
                                nodePtr=nodeStack.top();
 
595
                                nodeStack.pop();
 
596
 
 
597
 
 
598
                                newEffectVec.push_back(e);                              
 
599
                        }
 
600
                }
 
601
                nodePtr=nodeStack.top();
 
602
                nodeStack.pop();
 
603
 
 
604
 
 
605
 
 
606
                nodeStack.push(nodePtr);
 
607
        }
 
608
        catch (int)
 
609
        {
 
610
                //Code threw an error, just say "bad parse" and be done with it
 
611
                xmlFreeDoc(doc);
 
612
                return false;
 
613
        }
 
614
        xmlFreeDoc(doc);        
 
615
 
 
616
        //Check that stashes are uniquely named
 
617
        // do brute force search, as there are unlikely to be many stashes      
 
618
        for(unsigned int ui=0;ui<newStashes.size();ui++)
 
619
        {
 
620
                for(unsigned int uj=0;uj<newStashes.size();uj++)
 
621
                {
 
622
                        if(ui == uj)
 
623
                                continue;
 
624
 
 
625
                        //If these match, states not uniquely named,
 
626
                        //and thus statefile is invalid.
 
627
                        if(newStashes[ui].first == newStashes[uj].first)
 
628
                                return false;
 
629
 
 
630
                }
 
631
        }
 
632
 
 
633
        if(!merge)
 
634
        {
 
635
                //Erase any viscontrol data, seeing as we got this far  
 
636
                clear(); 
 
637
                //Now replace it with the new data
 
638
                activeTree.swap(newFilterTree);
 
639
                std::swap(stashedTrees,newStashes);
 
640
        }
 
641
        else
 
642
        {
 
643
                //If we are merging, then there is a chance
 
644
                //of a name-clash. We avoid this by trying to append -merge continuously
 
645
                for(unsigned int ui=0;ui<newStashes.size();ui++)
 
646
                {
 
647
                        //protect against overload (very unlikely)
 
648
                        unsigned int maxCount;
 
649
                        maxCount=100;
 
650
                        while(hasFirstInPairVec(stashedTrees,newStashes[ui]) && --maxCount)
 
651
                                newStashes[ui].first+=TRANS("-merge");
 
652
 
 
653
                        if(maxCount)
 
654
                                stashedTrees.push_back(newStashes[ui]);
 
655
                        else
 
656
                                errStream << TRANS(" Unable to merge stashes correctly. This is improbable, so please report this.") << endl;
 
657
                }
 
658
 
 
659
                activeTree.addFilterTree(newFilterTree,0);
 
660
                undoTrees.clear();
 
661
                redoTrees.clear();
 
662
        }
 
663
 
 
664
        activeTree.initFilterTree();
 
665
        
 
666
        //Wipe the existing cameras, and then put the new cameras in place
 
667
        if(!merge)
 
668
                savedCameras.clear();
 
669
        
 
670
        //Set a default camera as needed. We don't need to track its unique ID, as this is
 
671
        //"invisible" to the UI
 
672
        if(!savedCameras.size())
 
673
        {
 
674
                Camera *c=new CameraLookAt();
 
675
                savedCameras.push_back(c);
 
676
                activeCamera=0;
 
677
        }
 
678
 
 
679
        //spin through
 
680
        for(unsigned int ui=0;ui<newCameraVec.size();ui++)
 
681
        {
 
682
                if(merge)
 
683
                {
 
684
                        //Don't merge the default camera (which has no name)
 
685
                        if(newCameraVec[ui]->getUserString().empty())
 
686
                                continue;
 
687
 
 
688
                        //Keep trying new names appending "-merge" each time to obtain a new, and hopefully unique name
 
689
                        // Abort after many times
 
690
                        unsigned int maxCount;
 
691
                        maxCount=100;
 
692
                        while(camNameExists(newCameraVec[ui]->getUserString()) && --maxCount)
 
693
                        {
 
694
                                newCameraVec[ui]->setUserString(newCameraVec[ui]->getUserString()+"-merge");
 
695
                        }
 
696
 
 
697
                        //If we have any attempts left, then it worked
 
698
                        if(maxCount)
 
699
                                savedCameras.push_back(newCameraVec[ui]);
 
700
                }
 
701
                else
 
702
                {
 
703
                        //If there is no userstring, then its a  "default"
 
704
                        // camera (one that does not show up to the users,
 
705
                        // and cannot be erased from the scene)
 
706
                        // set it directly. Otherwise, its a user camera.
 
707
                        savedCameras.push_back(newCameraVec[ui]);
 
708
                        activeCamera=ui;
 
709
                }
 
710
 
 
711
        }
 
712
 
 
713
        fileName=cpFilename;
 
714
 
 
715
 
 
716
        if(workingDir.empty())
 
717
        {
 
718
                char *wd;
 
719
#if defined(__APPLE__)
 
720
                //Apple defines a special getcwd that just works
 
721
                wd = getcwd(NULL, 0);
 
722
#elif defined(WIN32) || defined(WIN64)
 
723
                //getcwd under POSIX is not clearly defined, it requires
 
724
                // an input number of bytes that are enough to hold the path,
 
725
                // however it does not define how one goes about obtaining the
 
726
                // number of bytes needed. 
 
727
                char *wdtemp = (char*)malloc(PATH_MAX*20);
 
728
                wd=getcwd(wdtemp,PATH_MAX*20);
 
729
                if(!wd)
 
730
                {
 
731
                        free(wdtemp);
 
732
                        return false;   
 
733
                }
 
734
 
 
735
#else
 
736
                //GNU extension, which just does it (tm).
 
737
                wd = get_current_dir_name();
 
738
#endif
 
739
                workingDir=wd;
 
740
                free(wd);
 
741
        }
 
742
 
 
743
        //If we are merging then the default state has been altered
 
744
        // if we are not merging, then it is overwritten
 
745
        if(merge)
 
746
                setModifyLevel(STATE_MODIFIED_DATA);
 
747
        else
 
748
                setModifyLevel(STATE_MODIFIED_NONE);
 
749
 
 
750
        //Perform sanitisation on results
 
751
        return true;
 
752
}
 
753
 
 
754
bool AnalysisState::camNameExists(const std::string &s) const
 
755
{
 
756
        for(size_t ui=0; ui<savedCameras.size(); ui++) 
 
757
        {
 
758
                if (savedCameras[ui]->getUserString() == s ) 
 
759
                        return true;
 
760
        }
 
761
        return false;
 
762
}
 
763
 
 
764
void AnalysisState::copyFilterTree(FilterTree &f) const
 
765
{
 
766
        f = activeTree;
 
767
}
 
768
 
 
769
int AnalysisState::getWorldAxisMode() const 
 
770
{
 
771
        return worldAxisMode;
 
772
}
 
773
 
 
774
void AnalysisState::copyCams(vector<Camera *> &cams) const 
 
775
{
 
776
        ASSERT(!cams.size());
 
777
 
 
778
        cams.resize(savedCameras.size());
 
779
        for(size_t ui=0;ui<savedCameras.size();ui++)
 
780
                cams[ui] = savedCameras[ui]->clone();
 
781
}
 
782
 
 
783
void AnalysisState::copyCamsByRef(vector<const Camera *> &camRef) const
 
784
{
 
785
        camRef.resize(savedCameras.size());
 
786
        for(size_t ui=0;ui<camRef.size();ui++)
 
787
                camRef[ui]=savedCameras[ui];
 
788
}
 
789
 
 
790
const Camera *AnalysisState::getCam(size_t offset) const
 
791
{
 
792
        return savedCameras[offset];
 
793
}
 
794
 
 
795
void AnalysisState::removeCam(size_t offset)
 
796
{
 
797
        ASSERT(offset < savedCameras.size());
 
798
        savedCameras.erase(savedCameras.begin()+offset);
 
799
        if(activeCamera >=savedCameras.size())
 
800
                activeCamera=0;
 
801
}
 
802
 
 
803
void AnalysisState::addCamByClone(const Camera *c)
 
804
{
 
805
        setModifyLevel(STATE_MODIFIED_ANCILLARY);
 
806
        savedCameras.push_back(c->clone());
 
807
}
 
808
 
 
809
bool AnalysisState::setCamProperty(size_t offset, unsigned int key, const std::string &str)
 
810
{
 
811
        if(offset == activeCamera)
 
812
                setModifyLevel(STATE_MODIFIED_VIEW);
 
813
        else
 
814
                setModifyLevel(STATE_MODIFIED_ANCILLARY);
 
815
        return savedCameras[offset]->setProperty(key,str);
 
816
}
 
817
 
 
818
bool AnalysisState::getUseRelPaths() const 
 
819
{
 
820
        return useRelativePathsForSave;
 
821
}
 
822
                
 
823
void AnalysisState::getBackgroundColour(float &r, float &g, float &b) const
 
824
{
 
825
        r=rBack;
 
826
        g=gBack;
 
827
        b=bBack;
 
828
}
 
829
 
 
830
void AnalysisState::copyEffects(vector<Effect *> &e) const
 
831
{
 
832
        e.clear();
 
833
        for(size_t ui=0;ui<effects.size();ui++)
 
834
                e[ui]=effects[ui]->clone();
 
835
}
 
836
 
 
837
 
 
838
void AnalysisState::setBackgroundColour(float &r, float &g, float &b)
 
839
{
 
840
        if(rBack != r || gBack!=g || bBack!=b)
 
841
                setModifyLevel(STATE_MODIFIED_VIEW);
 
842
        rBack=r;
 
843
        gBack=g;
 
844
        bBack=b;
 
845
 
 
846
}
 
847
 
 
848
void AnalysisState::setWorldAxisMode(unsigned int mode)
 
849
{
 
850
        if(mode)
 
851
                setModifyLevel(STATE_MODIFIED_VIEW);
 
852
        worldAxisMode=mode;
 
853
}
 
854
 
 
855
void AnalysisState::setCamerasByCopy(vector<Camera *> &c, unsigned int active)
 
856
{
 
857
        setModifyLevel(STATE_MODIFIED_DATA);
 
858
        clearCams();
 
859
 
 
860
        savedCameras.swap(c);
 
861
        activeCamera=active;
 
862
}
 
863
 
 
864
void AnalysisState::setCameraByClone(const Camera *c, unsigned int offset)
 
865
{
 
866
        ASSERT(offset < savedCameras.size()); 
 
867
        delete savedCameras[offset];
 
868
        savedCameras[offset]=c->clone(); 
 
869
 
 
870
        if(offset == activeCamera)
 
871
                setModifyLevel(STATE_MODIFIED_VIEW);
 
872
        else
 
873
                setModifyLevel(STATE_MODIFIED_ANCILLARY);
 
874
}
 
875
 
 
876
void AnalysisState::setEffectsByCopy(const vector<const Effect *> &e)
 
877
{
 
878
        setModifyLevel(STATE_MODIFIED_VIEW);
 
879
        clearEffects();
 
880
 
 
881
        effects.resize(e.size());
 
882
        for(size_t ui=0;ui<e.size();ui++)
 
883
                effects[ui] = e[ui]->clone();
 
884
 
 
885
}
 
886
 
 
887
 
 
888
void AnalysisState::setUseRelPaths(bool useRel)
 
889
{
 
890
        useRelativePathsForSave=useRel;
 
891
}
 
892
void AnalysisState::setWorkingDir(const std::string &work)
 
893
{
 
894
        if(work!=workingDir)
 
895
                setModifyLevel(STATE_MODIFIED_DATA);
 
896
 
 
897
        workingDir=work;
 
898
}
 
899
 
 
900
void AnalysisState::setFilterTreeByClone(const FilterTree &f)
 
901
{
 
902
        setModifyLevel(STATE_MODIFIED_DATA);
 
903
        activeTree=f;
 
904
}
 
905
 
 
906
void AnalysisState::setStashedTreesByClone(const vector<std::pair<string,FilterTree> > &s)
 
907
{
 
908
        setModifyLevel(STATE_MODIFIED_ANCILLARY);
 
909
        stashedTrees=s;
 
910
}
 
911
 
 
912
void AnalysisState::addStashedTree(const std::pair<string,FilterTree> &s)
 
913
{
 
914
        setModifyLevel(STATE_MODIFIED_ANCILLARY);
 
915
        stashedTrees.push_back(s);
 
916
}
 
917
 
 
918
void AnalysisState::copyStashedTrees(std::vector<std::pair<string,FilterTree > > &s) const
 
919
{
 
920
        s=stashedTrees;
 
921
}
 
922
 
 
923
void AnalysisState::copyStashedTree(size_t offset,std::pair<string,FilterTree> &s) const
 
924
{
 
925
        s.first=stashedTrees[offset].first;
 
926
        s.second=stashedTrees[offset].second;
 
927
}
 
928
 
 
929
 
 
930
void AnalysisState::pushUndoStack()
 
931
{
 
932
        if(undoFilterStack.size() > MAX_UNDO_SIZE)
 
933
                undoFilterStack.pop_front();
 
934
 
 
935
        undoFilterStack.push_back(activeTree);
 
936
        redoFilterStack.clear();
 
937
}
 
938
 
 
939
void AnalysisState::popUndoStack(bool restorePopped)
 
940
{
 
941
        ASSERT(undoFilterStack.size());
 
942
 
 
943
        //Save the current filters to the redo stack.
 
944
        // note that the copy constructor will generate a clone for us.
 
945
        redoFilterStack.push_back(activeTree);
 
946
 
 
947
        if(redoFilterStack.size() > MAX_UNDO_SIZE)
 
948
                redoFilterStack.pop_front();
 
949
 
 
950
        if(restorePopped)
 
951
        {
 
952
                //Swap the current filter cache out with the undo stack result
 
953
                std::swap(activeTree,undoFilterStack.back());
 
954
                
 
955
        }
 
956
 
 
957
        //Pop the undo stack
 
958
        undoFilterStack.pop_back();
 
959
 
 
960
        setModifyLevel(STATE_MODIFIED_DATA);
 
961
}
 
962
 
 
963
void AnalysisState::popRedoStack()
 
964
{
 
965
        ASSERT(undoFilterStack.size() <=MAX_UNDO_SIZE);
 
966
        undoFilterStack.push_back(activeTree);
 
967
 
 
968
        activeTree.clear();
 
969
        //Swap the current filter cache out with the redo stack result
 
970
        activeTree.swap(redoFilterStack.back());
 
971
        
 
972
        //Pop the redo stack
 
973
        redoFilterStack.pop_back();
 
974
 
 
975
        setModifyLevel(STATE_MODIFIED_DATA);
 
976
}
 
977
 
 
978
 
 
979
void AnalysisState::eraseStash(size_t offset)
 
980
{
 
981
        ASSERT(offset < stashedTrees.size());
 
982
        setModifyLevel(STATE_MODIFIED_ANCILLARY);
 
983
        stashedTrees.erase(stashedTrees.begin() + offset);
 
984
}
 
985