]>
Commit | Line | Data |
---|---|---|
cecfc5e7 VZ |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: No names yet. | |
3 | // Purpose: Contrib. demo | |
4 | // Author: Aleksandras Gluchovas | |
5 | // Modified by: | |
6 | // Created: 22/09/98 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) Aleskandars Gluchovas | |
9 | // Licence: wxWindows licence | |
10 | ///////////////////////////////////////////////////////////////////////////// | |
11 | ||
12 | // For compilers that support precompilation, includes "wx/wx.h". | |
13 | #include "wx/wxprec.h" | |
14 | ||
15 | #ifdef __BORLANDC__ | |
16 | #pragma hdrstop | |
17 | #endif | |
18 | ||
19 | #ifndef WX_PRECOMP | |
20 | #include "wx/wx.h" | |
21 | #endif | |
22 | ||
23 | #include "docripper.h" | |
24 | ||
4888e623 GT |
25 | #if wxUSE_IOSTREAMH |
26 | #include <iostream.h> | |
27 | #else | |
28 | #include <iostream> | |
29 | #endif | |
cecfc5e7 VZ |
30 | |
31 | // script templates | |
32 | ||
33 | // ***** currently only HTML versions of variouse templates available ***** // | |
34 | ||
35 | static const char* HTM_TopTempl = | |
36 | ||
37 | "<html><body bgcolor=#FFFFFF>\n\ | |
38 | \n\n<!------ Automatically Generated by \"wxDocRipper\"------->\n\n\n\ | |
39 | <p><h2>$(NAME)</h2><p>\n\ | |
40 | <ul>\n\ | |
41 | $(REFLIST)\ | |
42 | </ul><p>\n\n\ | |
43 | "; | |
44 | ||
45 | static const char* HTM_ContentIdxTempl = | |
46 | ||
47 | "\ | |
48 | <a name=\"r$(ID)_$(NAME)\">\n\ | |
49 | <p><hr>\n\ | |
50 | <h2><p>$(NAME)<p></h2>\ | |
51 | <ul>\n\ | |
52 | $(REFLIST)\ | |
53 | </ul><p>\n\n\ | |
54 | "; | |
55 | ||
56 | static const char* HTM_SuperContentTempl = | |
57 | ||
58 | "\ | |
59 | <a name=\"r$(ID)_$(NAME)\">\n\ | |
60 | <p><hr>\n\ | |
61 | <p><h2>$(NAME)<p></h2>\ | |
62 | $(BODY)\n\ | |
63 | "; | |
64 | ||
65 | static const char* HTM_SubContentTempl = | |
66 | ||
67 | "\ | |
68 | <a name=\"r$(ID)_$(NAME)\">\n\ | |
69 | <p><hr>\n\ | |
70 | <p><h3>$(NAME)<p></h3>\ | |
71 | $(BODY)\n\ | |
72 | "; | |
73 | ||
74 | static const char* HTM_OutLineTempl = | |
75 | ||
76 | "\ | |
77 | <p>\n\ | |
78 | <b><font color=\"#FF0000\">$(NAME)</font></b><p>\n\ | |
79 | "; | |
80 | ||
81 | static const char* HTM_OutLine1Templ = | |
82 | ||
83 | "\ | |
84 | <p>\n\ | |
85 | <b><i><font color=\"#101010\">$(NAME)</font></i></b>\n\ | |
86 | <ul>\n\ | |
87 | $(REFLIST)\ | |
88 | </ul>\n\n\ | |
89 | "; | |
90 | ||
91 | static const char* HTM_RefTempl = | |
92 | ||
93 | "\ | |
94 | <li><a href=\"#r$(ID)_$(NAME)\">$(NAME)</A>\n\ | |
95 | "; | |
96 | ||
97 | static const char* HTM_DeadRefTempl = | |
98 | ||
99 | "\ | |
100 | <li></b>$(NAME)\n\ | |
101 | "; | |
102 | ||
103 | /***** Implementation for class RipperDocGen *****/ | |
104 | ||
105 | RipperDocGen::RipperDocGen() | |
106 | ||
107 | : mTopTempl ( HTM_TopTempl ), | |
108 | mContentIdxTempl ( HTM_ContentIdxTempl ), | |
109 | mSuperContentTempl( HTM_SuperContentTempl ), | |
110 | mSubContentTempl ( HTM_SubContentTempl ), | |
111 | mOutLineTempl ( HTM_OutLineTempl ), | |
112 | mOutLine1Templ ( HTM_OutLine1Templ ), | |
113 | ||
114 | mRefTempl ( HTM_RefTempl ), | |
115 | mDeadRefTempl ( HTM_DeadRefTempl ), | |
116 | ||
117 | mpCurClassSect(0) | |
118 | { // topIndex is not referenced | |
119 | mpTopIdx = new ScriptSection( "Source Code Contents" , "", &mTopTempl , 0 ); | |
120 | mpClassIdx = new ScriptSection( "Classes Reference" , "", &mContentIdxTempl, &mRefTempl ); | |
121 | mpEnumIdx = new ScriptSection( "Enumerations Reference" , "", &mContentIdxTempl, &mRefTempl ); | |
122 | mpTypeDefIdx = new ScriptSection( "Type Definitions Reference" , "", &mContentIdxTempl, &mRefTempl ); | |
123 | mpMacroIdx = new ScriptSection( "Macros Reference" , "", &mContentIdxTempl, &mRefTempl ); | |
124 | mpGlobalVarsIdx = new ScriptSection( "Global Variables Reference" , "", &mContentIdxTempl, &mRefTempl ); | |
125 | mpGlobalFuncIdx = new ScriptSection( "Global Functions Reference", "", &mContentIdxTempl, &mRefTempl ); | |
126 | mpConstIdx = new ScriptSection( "Constants Reference" , "", &mContentIdxTempl, &mRefTempl ); | |
127 | ||
128 | // assemble top index | |
129 | mpTopIdx->AddSection( mpClassIdx , 1 ); | |
130 | mpTopIdx->AddSection( mpEnumIdx , 1 ); | |
131 | mpTopIdx->AddSection( mpTypeDefIdx , 1 ); | |
132 | mpTopIdx->AddSection( mpMacroIdx , 1 ); | |
133 | mpTopIdx->AddSection( mpGlobalVarsIdx, 1 ); | |
134 | mpTopIdx->AddSection( mpGlobalFuncIdx, 1 ); | |
135 | mpTopIdx->AddSection( mpConstIdx , 1 ); | |
136 | ||
137 | // register reserved variables for index and description templates | |
138 | ScriptSection::RegisterTemplate( mTopTempl ); | |
139 | ScriptSection::RegisterTemplate( mContentIdxTempl ); | |
140 | ScriptSection::RegisterTemplate( mSuperContentTempl ); | |
141 | ScriptSection::RegisterTemplate( mSubContentTempl ); | |
142 | ScriptSection::RegisterTemplate( mOutLineTempl ); | |
143 | ScriptSection::RegisterTemplate( mOutLine1Templ ); | |
144 | ScriptSection::RegisterTemplate( mRefTempl ); | |
145 | ScriptSection::RegisterTemplate( mDeadRefTempl ); | |
146 | ||
147 | // create the top-most (interfile) context | |
148 | mpFileBinderCtx = new spFile(); | |
149 | ||
150 | // the default script is HTML | |
151 | mTags = get_HTML_markup_tags(); | |
152 | ||
153 | mpParser = 0; // no default parser! | |
154 | } | |
155 | ||
156 | void RipperDocGen::Init( SourceParserBase* pParser ) | |
157 | { | |
158 | mpParser = pParser; | |
159 | } | |
160 | ||
161 | RipperDocGen::~RipperDocGen() | |
162 | { | |
163 | delete mpFileBinderCtx; | |
164 | } | |
165 | ||
166 | void RipperDocGen::AppendComments( spContext& fromContext, string& str ) | |
167 | { | |
168 | if ( !fromContext.HasComments() ) return; | |
169 | ||
170 | size_t start = str.length(); | |
171 | ||
172 | str += mTags[TAG_BOLD].end; | |
173 | str += mTags[TAG_PARAGRAPH].start; | |
174 | ||
175 | MCommentListT& lst = fromContext.GetCommentList(); | |
176 | ||
177 | for( size_t i = 0; i != lst.size(); ++i ) | |
178 | { | |
179 | ||
180 | if ( i != 0 ) | |
181 | ||
182 | if ( lst[i]->StartsParagraph() ) | |
183 | { | |
184 | str += mTags[TAG_PARAGRAPH].start; | |
4888e623 | 185 | |
cecfc5e7 VZ |
186 | } |
187 | ||
188 | str += lst[i]->mText; | |
189 | } | |
190 | ||
191 | // remove new lines, and insert paragraph breaks | |
4888e623 | 192 | |
cecfc5e7 VZ |
193 | // if empty lines found |
194 | ||
195 | size_t len = str.length(); | |
196 | ||
197 | for( size_t n = start; n != len; ++n ) | |
198 | ||
199 | if ( str[n] == 10 || | |
200 | str[n] == 13 ) | |
4888e623 | 201 | |
cecfc5e7 | 202 | { |
4888e623 | 203 | |
cecfc5e7 | 204 | if ( n + 2 < len ) |
4888e623 | 205 | |
cecfc5e7 | 206 | { |
4888e623 | 207 | |
cecfc5e7 | 208 | if ( ( str[n] == 13 && str[n+1] == 10 && // FIXME:: quick-hack |
4888e623 | 209 | |
cecfc5e7 | 210 | str[n+2] == 13 ) || |
4888e623 | 211 | |
cecfc5e7 | 212 | ( str[n] == 10 && str[n+1] == 10 ) |
4888e623 | 213 | |
cecfc5e7 | 214 | ) |
4888e623 | 215 | |
cecfc5e7 | 216 | { |
4888e623 | 217 | |
cecfc5e7 | 218 | str.insert( n + 1, "<p>" ); // FIXME:: quick-hack |
4888e623 | 219 | |
cecfc5e7 | 220 | len += 3; |
4888e623 | 221 | |
cecfc5e7 | 222 | } |
4888e623 | 223 | |
cecfc5e7 VZ |
224 | } |
225 | ||
4888e623 GT |
226 | |
227 | ||
cecfc5e7 | 228 | str[n] = ' '; |
4888e623 | 229 | |
cecfc5e7 VZ |
230 | } |
231 | ||
4888e623 | 232 | |
cecfc5e7 VZ |
233 | str += mTags[TAG_PARAGRAPH].end; |
234 | } | |
235 | ||
236 | void RipperDocGen::AppendMulitilineStr( string& st, string& mlStr ) | |
237 | { | |
238 | st = mTags[TAG_FIXED_FONT].start; | |
239 | st += mlStr; | |
240 | st += mTags[TAG_FIXED_FONT].end; | |
241 | } | |
242 | ||
243 | void RipperDocGen::AppendHighlightedSource( string& st, string source ) | |
244 | { | |
245 | // FIXME:: below should not be fixed :) | |
246 | char buf[1024*32]; | |
247 | ||
248 | // DBG::: | |
a5a0dd06 | 249 | // ASSERT( source.length() + 1 < sizeof(buf) ); |
cecfc5e7 VZ |
250 | |
251 | strcpy( buf, source.c_str() ); | |
252 | ||
253 | // highlight things | |
254 | mSrcPainter.Init(); | |
255 | mSrcPainter.ProcessSource( buf, strlen(buf) ); | |
256 | mSrcPainter.GetResultString( st, mTags ); | |
257 | } | |
258 | ||
259 | bool RipperDocGen::CheckIfUncommented( spContext& ctx, ScriptSection& toSect ) | |
260 | { | |
261 | if ( ctx.HasComments() ) return 0; | |
262 | ||
263 | toSect.AddReference( | |
264 | new ScriptSection( GetScopedName( ctx ), "", 0, &mDeadRefTempl ) | |
265 | ); | |
266 | ||
267 | return 1; | |
268 | } | |
269 | ||
270 | ScriptTemplate* RipperDocGen::GetRefTemplFor( spContext& ctx ) | |
271 | { | |
272 | if ( ctx.HasComments() ) | |
273 | ||
274 | return &mRefTempl; | |
275 | else | |
276 | return &mDeadRefTempl; | |
277 | } | |
278 | ||
279 | string RipperDocGen::GetScopedName( spContext& ofCtx ) | |
280 | { | |
281 | if ( ofCtx.IsInFile() ) return ofCtx.GetName(); | |
282 | else | |
283 | return ofCtx.GetOutterContext()->GetName() + | |
284 | "::" + ofCtx.GetName(); | |
285 | } | |
286 | ||
287 | void RipperDocGen::AddToCurrentClass( ScriptSection* pSection, spContext& ctx, | |
288 | const char* subSectionName ) | |
289 | { | |
290 | string sName; | |
291 | ||
292 | if ( ctx.mVisibility == SP_VIS_PROTECTED ) | |
293 | ||
294 | sName = "Protected members/"; | |
295 | else | |
296 | if ( ctx.mVisibility == SP_VIS_PRIVATE ) | |
297 | ||
298 | sName = "Private members/"; | |
299 | else | |
300 | sName = "Public members/"; | |
301 | ||
302 | sName += subSectionName; | |
303 | ||
304 | ScriptSection* pSect = mpCurClassSect->GetSubsection( sName.c_str() ); | |
305 | ||
306 | if ( CheckIfUncommented( ctx, *pSect ) ) | |
307 | { | |
308 | delete pSection; | |
309 | return; | |
310 | } | |
311 | ||
312 | pSect->AddReference( pSection ); | |
313 | ||
314 | mpCurClassSect->AddSection( pSection ); | |
315 | } | |
316 | ||
317 | void RipperDocGen::LinkSuperClassRefs() | |
318 | { | |
319 | MMemberListT clLst; | |
320 | ||
321 | // collect all classes in the context tree | |
322 | mpFileBinderCtx->GetContextList( clLst, SP_CTX_CLASS ); | |
323 | ||
324 | for( size_t i = 0; i != clLst.size(); ++i ) | |
325 | { | |
326 | spClass& cl = *((spClass*)clLst[i]); | |
327 | ||
328 | // FIXME:: why sometimes GetUserData() returns NULL? | |
329 | if ( !cl.GetUserData() ) | |
330 | continue; | |
331 | ||
332 | ScriptSection* pClSect = (ScriptSection*)cl.GetUserData(); | |
333 | ScriptSection* pSuperSect = pClSect->GetSubsection("Derived from"); | |
334 | ||
335 | for( size_t n = 0; n != cl.mSuperClassNames.size(); ++n ) | |
336 | { | |
337 | string& superClName = cl.mSuperClassNames[n]; | |
338 | ||
339 | spClass* pFound = NULL; | |
340 | ||
341 | string* name; | |
342 | ||
343 | for( size_t k = 0; k != clLst.size(); ++k ) | |
344 | { | |
345 | name = &clLst[k]->GetName(); | |
346 | ||
347 | if ( clLst[k]->GetName() == superClName ) | |
348 | { | |
349 | pFound = (spClass*)clLst[k]; | |
350 | break; | |
351 | } | |
352 | } | |
353 | ||
354 | if ( !pFound ) | |
355 | { | |
356 | ScriptSection* pNotFound = | |
357 | new ScriptSection( superClName, "", 0, &mDeadRefTempl ); | |
358 | ||
359 | pSuperSect->AddReference( pNotFound ); | |
360 | } | |
361 | else | |
362 | if ( pFound->GetUserData() ) | |
363 | ||
364 | pSuperSect->AddReference( | |
365 | (ScriptSection*)pFound->GetUserData() ); | |
366 | } | |
367 | } | |
368 | } | |
369 | ||
370 | void RipperDocGen::ProcessFile( const char* sourceFile ) | |
371 | { | |
29ee95e8 | 372 | wxSTD cout << "Processing file " << sourceFile << "..." << wxSTD endl; |
cecfc5e7 VZ |
373 | |
374 | spFile* pCtx = mpParser->ParseFile( sourceFile ); | |
375 | ||
376 | if ( pCtx == NULL ) | |
377 | { | |
29ee95e8 | 378 | wxSTD cout << "Cannot open file " << sourceFile << ", skipped..." << wxSTD endl; |
cecfc5e7 VZ |
379 | |
380 | return; | |
381 | } | |
382 | ||
383 | VisitAll( *pCtx, TRUE ); | |
384 | ||
385 | mpFileBinderCtx->AddMember( pCtx ); | |
386 | } | |
387 | ||
388 | // implementations of "visiting procedures" | |
389 | ||
390 | void RipperDocGen::VisitEnumeration( spEnumeration& en ) | |
391 | { | |
392 | // FOR NOW:: do not reference "nameless" enums | |
393 | if ( en.GetName() == "" ) return; | |
394 | ||
395 | if ( CheckIfUncommented( en, *mpEnumIdx ) ) | |
396 | return; | |
397 | ||
398 | string body; | |
399 | body += mTags[TAG_BOLD].start; | |
400 | ||
401 | AppendMulitilineStr( body, en.mEnumContent ); | |
402 | ||
403 | body += mTags[TAG_BOLD].end; | |
404 | ||
405 | string line; | |
406 | AppendHighlightedSource( line, body ); | |
407 | AppendComments( en, line ); | |
408 | ||
409 | mpEnumIdx->AddSection( | |
410 | new ScriptSection( en.GetName(), line, | |
411 | &mSubContentTempl, | |
412 | GetRefTemplFor( en ) ), 1 | |
413 | ); | |
414 | } | |
415 | ||
416 | void RipperDocGen::VisitTypeDef( spTypeDef& td ) | |
417 | { | |
418 | if ( CheckIfUncommented( td, *mpTypeDefIdx ) ) | |
419 | return; | |
420 | ||
421 | string body; | |
422 | body += mTags[TAG_BOLD].start; | |
423 | body += "typdef "; | |
424 | body += mTags[TAG_BOLD].end; | |
425 | ||
426 | AppendMulitilineStr( body, td.mOriginalType ); | |
427 | body += td.mOriginalType; | |
428 | body += ' '; | |
429 | ||
430 | body += mTags[TAG_BOLD].start; | |
431 | body += td.GetName(); | |
432 | body += mTags[TAG_BOLD].end; | |
433 | ||
434 | string line; | |
435 | AppendHighlightedSource( line, body ); | |
436 | AppendComments( td, line ); | |
437 | ||
438 | mpTypeDefIdx->AddSection( | |
439 | new ScriptSection( td.GetName(), line, | |
440 | &mSubContentTempl, | |
441 | GetRefTemplFor( td ) ), TRUE | |
442 | ); | |
443 | } | |
444 | ||
445 | void RipperDocGen::VisitPreprocessorLine( spPreprocessorLine& pd ) | |
446 | { | |
447 | if ( pd.mDefType != SP_PREP_DEF_REDEFINE_SYMBOL ) | |
4888e623 | 448 | |
cecfc5e7 | 449 | |
4888e623 | 450 | |
cecfc5e7 VZ |
451 | return; |
452 | ||
4888e623 | 453 | |
cecfc5e7 VZ |
454 | if ( CheckIfUncommented( pd, *mpMacroIdx ) ) |
455 | return; | |
456 | ||
457 | string body; | |
458 | body += mTags[TAG_FIXED_FONT].start; | |
459 | ||
460 | string coloredLine = pd.mLine; | |
461 | AppendHighlightedSource( coloredLine, pd.mLine ); | |
462 | ||
463 | AppendMulitilineStr( body, coloredLine ); | |
464 | ||
465 | body += mTags[TAG_FIXED_FONT].end; | |
466 | ||
467 | AppendComments( pd, body ); | |
468 | ||
469 | mpMacroIdx->AddSection( | |
470 | new ScriptSection( pd.GetName(), body, | |
471 | &mSubContentTempl, | |
472 | GetRefTemplFor( pd ) ), TRUE | |
473 | ); | |
474 | } | |
475 | ||
476 | void RipperDocGen::VisitClass( spClass& cl ) | |
477 | { | |
478 | // FOR NOW:: do not document nested classes - | |
479 | // nicier visiting method yet needed | |
480 | ||
481 | if ( cl.IsInClass() ) | |
482 | { | |
483 | SkipChildren(); // spVisitor's method | |
484 | return; | |
485 | } | |
486 | ||
487 | string body; | |
488 | AppendComments( cl, body ); | |
489 | ||
490 | mpCurClassSect = | |
491 | new ScriptSection( cl.GetName(), body, &mSuperContentTempl, &mRefTempl ); | |
492 | ||
493 | // set up reference in the class context, pointing back | |
494 | // to the section where this class is represented | |
495 | cl.SetUserData( mpCurClassSect ); | |
496 | ||
497 | ScriptSection* pSuper = new ScriptSection( "Derived from" ,"", &mOutLine1Templ,0, 1 ); | |
498 | ||
499 | ScriptSection* pPublic = new ScriptSection( "Public members" ,"", &mOutLineTempl,0, 1 ); | |
500 | ScriptSection* pProtected = new ScriptSection( "Protected members" ,"", &mOutLineTempl,0, 1 ); | |
501 | ScriptSection* pPrivate = new ScriptSection( "Private members" ,"", &mOutLineTempl,0, 1 ); | |
502 | ||
503 | pPublic->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) ); | |
504 | pPublic->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) ); | |
505 | ||
506 | ||
507 | pProtected->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) ); | |
508 | pProtected->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) ); | |
509 | ||
510 | ||
511 | pPrivate->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) ); | |
512 | pPrivate->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) ); | |
513 | ||
514 | mpCurClassSect->AddSection( pSuper ); | |
515 | mpCurClassSect->AddSection( pPublic ); | |
516 | mpCurClassSect->AddSection( pProtected ); | |
517 | mpCurClassSect->AddSection( pPrivate ); | |
518 | ||
519 | mpClassIdx->AddSection( mpCurClassSect, TRUE ); | |
520 | } | |
521 | ||
522 | void RipperDocGen::VisitAttribute( spAttribute& attr ) | |
523 | { | |
524 | string body; | |
525 | body += mTags[TAG_BOLD].start; | |
526 | body += attr.mType; | |
527 | body += mTags[TAG_BOLD].end; | |
528 | ||
529 | body += mTags[TAG_ITALIC].start; | |
530 | body += ' '; | |
531 | body += attr.GetName(); | |
532 | body += mTags[TAG_ITALIC].end; | |
533 | ||
534 | string line; | |
535 | AppendHighlightedSource( line, body ); | |
536 | AppendComments( attr, line ); | |
537 | ||
538 | ScriptSection* pSection = | |
539 | new ScriptSection( GetScopedName( attr ), line, | |
540 | &mSubContentTempl, | |
541 | GetRefTemplFor( attr ) ); | |
542 | ||
543 | if ( attr.mIsConstant ) | |
544 | ||
545 | mpConstIdx->AddSection( pSection, TRUE ); | |
546 | ||
547 | else | |
548 | if ( !attr.IsInClass() ) | |
549 | { | |
550 | if ( CheckIfUncommented( attr, *mpGlobalVarsIdx ) ) | |
551 | return; | |
552 | ||
553 | mpGlobalVarsIdx->AddSection( pSection, TRUE ); | |
554 | } | |
555 | else | |
556 | ||
557 | AddToCurrentClass( pSection, attr, "Attributes" ); | |
558 | } | |
559 | ||
560 | void RipperDocGen::VisitOperation( spOperation& op ) | |
561 | { | |
562 | string body; | |
563 | ||
564 | AppendHighlightedSource( body, op.GetFullName(mTags) ); | |
565 | ||
566 | AppendComments( op, body ); | |
567 | ||
568 | ScriptSection* pSection = | |
569 | new ScriptSection( GetScopedName( op ), body, | |
570 | &mSubContentTempl, | |
571 | GetRefTemplFor( op ) ); | |
572 | ||
573 | if ( !op.IsInClass() ) | |
574 | { | |
575 | if ( CheckIfUncommented( op, *mpGlobalFuncIdx ) ) | |
576 | return; | |
577 | ||
578 | mpGlobalFuncIdx->AddSection( pSection, 1 ); | |
579 | } | |
580 | else | |
581 | AddToCurrentClass( pSection, op, "Operations" ); | |
582 | } | |
583 | ||
584 | bool RipperDocGen::OnSaveDocument( ScriptStream& stm ) | |
585 | { | |
586 | LinkSuperClassRefs(); | |
587 | ||
588 | // FOR NOW:: doesn't work yet | |
589 | //mpTopIdx->RemoveEmptySections(); | |
590 | ||
591 | return 1; // saving can proceed now | |
592 | } | |
593 |