Subversion Repositories DevTools

Rev

Rev 2090 | Rev 2094 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2088 ghuddy 1
using System;
2
using System.Drawing;
3
using System.Collections;
4
using System.ComponentModel;
5
using System.Windows.Forms;
6
using Word;
2092 ghuddy 7
using Microsoft.Office.Core;
2088 ghuddy 8
 
2092 ghuddy 9
 
2088 ghuddy 10
namespace EA_DocGen
11
{
12
 
13
 
14
   /// <summary>
15
   /// Summary description for createWordDoc.
16
   /// </summary>
17
   public class createWordDoc : System.Windows.Forms.Form
18
   {
19
      // Enterprise Architect Model related data
20
      private EA.Repository EA_Repository = null;
21
      private EA.Package EA_ParentPackage = null;
22
      private EA.Project EA_Project;
23
 
24
      private EA_Utilities EA_Utils = null;
25
 
26
      private bool oneShot_skipRootPackage = false;
27
 
28
      // MS-Office Word related data
29
      private Word.Application WordApp = null;
30
      private Word.Document WordDocument = null;
31
      private Word.Range WordRange;
32
      private object startLocation;
33
      private object endLocation;
34
 
35
      private int[] DocSection = null;
36
      private int   iDocSection = 0;
37
 
2092 ghuddy 38
      // Some style names used in EA_DocGen - most are expected to exist in the input template/doc
39
      // but the Requirement formatting styles are programmatcally generated if they are found to not
40
      // exist. The reason for this is that as of writing this add-in, they do not exist in the ERG
41
      // standard template.
42
      private const string styleName_ReqAppBody  = "Requirements Approved Body";
43
      private const string styleName_ReqAppHdr   = "Requirements Approved Heading";
44
      private const string styleName_ReqPropBody = "Requirements Proposed Body";
45
      private const string styleName_ReqPropHdr  = "Requirements Proposed Heading";
46
      private const string styleName_ReqRejBody  = "Requirements Rejected Body";
47
      private const string styleName_ReqRejHdr   = "Requirements Rejected Heading";
48
      private const string styleName_ReqName     = "Requirements Name";
49
      private const string styleName_Body1       = "Body 1";
50
      private const string styleName_Heading1    = "Heading 1";
51
      private const string styleName_Heading2    = "Heading 2";
52
      private const string styleName_Heading3    = "Heading 3";
53
      private const string styleName_Heading4    = "Heading 4";
54
      private const string styleName_Heading5    = "Heading 5";
55
      private const string styleName_Heading6    = "Heading 6";
56
      private const string styleName_Heading7    = "Heading 7";
57
      private const string styleName_Heading8    = "Heading 8";
58
      private const string styleName_Heading9    = "Heading 9";
59
      private const string styleName_NumPara1    = "NumPara 1";
60
      private const string styleName_NumPara2    = "NumPara 2";
61
      private const string styleName_NumPara3    = "NumPara 3";
62
      private const string styleName_NumPara4    = "NumPara 4";
63
      private const string styleName_NumPara5    = "NumPara 5";
64
      private const string styleName_NumPara6    = "NumPara 6";
65
      private const string styleName_NumPara7    = "NumPara 7";
66
      private const string styleName_NumPara8    = "NumPara 8";
67
      private const string styleName_NumPara9    = "NumPara 9";
68
      private const string styleName_TableText   = "Table Text";
69
      private const string styleName_RefListNum  = "Reference List Number";
70
      private const string styleName_RefListText = "Reference List Text";
71
      private const string styleName_Normal      = "Normal";
72
 
73
 
2088 ghuddy 74
      private System.Windows.Forms.TextBox textBox_template;
75
      private System.Windows.Forms.Button button_browse_template;
76
      private System.Windows.Forms.TextBox textBox_output_file;
77
      private System.Windows.Forms.Button button_browse_output_file;
78
      private System.Windows.Forms.Button button_cancel;
79
      private System.Windows.Forms.Button button_generate;
80
      private System.Windows.Forms.Button button_view_output;
81
      private System.Windows.Forms.Label label1;
82
      private System.Windows.Forms.Label label2;
83
      private System.Windows.Forms.TextBox textBox_progress;
84
      private System.Windows.Forms.Label label3;
85
      private System.Windows.Forms.CheckBox checkBox_WordVisibility;
86
      /// <summary>
87
      /// Required designer variable.
88
      /// </summary>
89
      private System.ComponentModel.Container components = null;
90
 
91
      public createWordDoc(EA.Repository theRepository, EA.Package theParentPackage, EA_Utilities theEA_Utils)
92
      {
93
         //
94
         // Required for Windows Form Designer support
95
         //
96
         InitializeComponent();
97
 
98
         EA_Repository    = theRepository;
99
         EA_ParentPackage = theParentPackage;
100
         EA_Project       = EA_Repository.GetProjectInterface();
101
 
102
         EA_Utils         = theEA_Utils;
103
 
104
         DocSection = new int[12];
105
         for (int i=0; i<DocSection.Length; i++)
106
         {
107
            DocSection[i] = 0;
108
         }
109
 
110
         WordApp = new Word.Application();
111
      }
112
 
113
      /// <summary>
114
      /// Clean up any resources being used.
115
      /// </summary>
116
      protected override void Dispose( bool disposing )
117
      {
118
         if( disposing )
119
         {
120
            if(components != null)
121
            {
122
               components.Dispose();
123
            }
124
         }
125
         base.Dispose( disposing );
126
      }
127
 
128
 
129
 
130
 
131
      #region Windows Form Designer generated code
132
      /// <summary>
133
      /// Required method for Designer support - do not modify
134
      /// the contents of this method with the code editor.
135
      /// </summary>
136
      private void InitializeComponent()
137
      {
138
         this.textBox_template = new System.Windows.Forms.TextBox();
139
         this.button_browse_template = new System.Windows.Forms.Button();
140
         this.textBox_output_file = new System.Windows.Forms.TextBox();
141
         this.button_browse_output_file = new System.Windows.Forms.Button();
142
         this.button_cancel = new System.Windows.Forms.Button();
143
         this.button_generate = new System.Windows.Forms.Button();
144
         this.button_view_output = new System.Windows.Forms.Button();
145
         this.label1 = new System.Windows.Forms.Label();
146
         this.label2 = new System.Windows.Forms.Label();
147
         this.textBox_progress = new System.Windows.Forms.TextBox();
148
         this.label3 = new System.Windows.Forms.Label();
149
         this.checkBox_WordVisibility = new System.Windows.Forms.CheckBox();
150
         this.SuspendLayout();
151
         // 
152
         // textBox_template
153
         // 
154
         this.textBox_template.Location = new System.Drawing.Point(16, 40);
155
         this.textBox_template.Name = "textBox_template";
156
         this.textBox_template.Size = new System.Drawing.Size(592, 22);
157
         this.textBox_template.TabIndex = 0;
158
         this.textBox_template.Text = "";
159
         this.textBox_template.TextChanged += new System.EventHandler(this.textBox_template_TextChanged);
160
         // 
161
         // button_browse_template
162
         // 
163
         this.button_browse_template.Location = new System.Drawing.Point(616, 40);
164
         this.button_browse_template.Name = "button_browse_template";
165
         this.button_browse_template.Size = new System.Drawing.Size(80, 23);
166
         this.button_browse_template.TabIndex = 1;
167
         this.button_browse_template.Text = "Browse";
168
         this.button_browse_template.Click += new System.EventHandler(this.button_browse_template_Click);
169
         // 
170
         // textBox_output_file
171
         // 
172
         this.textBox_output_file.Location = new System.Drawing.Point(16, 112);
173
         this.textBox_output_file.Name = "textBox_output_file";
174
         this.textBox_output_file.Size = new System.Drawing.Size(592, 22);
175
         this.textBox_output_file.TabIndex = 2;
176
         this.textBox_output_file.Text = "";
177
         this.textBox_output_file.TextChanged += new System.EventHandler(this.textBox_output_file_TextChanged);
178
         // 
179
         // button_browse_output_file
180
         // 
181
         this.button_browse_output_file.Location = new System.Drawing.Point(616, 112);
182
         this.button_browse_output_file.Name = "button_browse_output_file";
183
         this.button_browse_output_file.Size = new System.Drawing.Size(80, 24);
184
         this.button_browse_output_file.TabIndex = 3;
185
         this.button_browse_output_file.Text = "Browse";
186
         this.button_browse_output_file.Click += new System.EventHandler(this.button_browse_output_file_Click);
187
         // 
188
         // button_cancel
189
         // 
190
         this.button_cancel.Location = new System.Drawing.Point(616, 384);
191
         this.button_cancel.Name = "button_cancel";
192
         this.button_cancel.Size = new System.Drawing.Size(80, 23);
193
         this.button_cancel.TabIndex = 4;
194
         this.button_cancel.Text = "Close";
195
         this.button_cancel.Click += new System.EventHandler(this.button_cancel_Click);
196
         // 
197
         // button_generate
198
         // 
199
         this.button_generate.Location = new System.Drawing.Point(496, 384);
200
         this.button_generate.Name = "button_generate";
201
         this.button_generate.Size = new System.Drawing.Size(96, 24);
202
         this.button_generate.TabIndex = 5;
203
         this.button_generate.Text = "Generate";
204
         this.button_generate.Click += new System.EventHandler(this.button_generate_Click);
205
         // 
206
         // button_view_output
207
         // 
208
         this.button_view_output.Location = new System.Drawing.Point(376, 384);
209
         this.button_view_output.Name = "button_view_output";
210
         this.button_view_output.Size = new System.Drawing.Size(96, 23);
211
         this.button_view_output.TabIndex = 6;
212
         this.button_view_output.Text = "View Output";
213
         this.button_view_output.Click += new System.EventHandler(this.button_view_output_Click);
214
         // 
215
         // label1
216
         // 
217
         this.label1.Location = new System.Drawing.Point(16, 16);
218
         this.label1.Name = "label1";
219
         this.label1.Size = new System.Drawing.Size(224, 23);
220
         this.label1.TabIndex = 7;
221
         this.label1.Text = "Input Template or Document Name";
222
         // 
223
         // label2
224
         // 
225
         this.label2.Location = new System.Drawing.Point(16, 88);
226
         this.label2.Name = "label2";
227
         this.label2.Size = new System.Drawing.Size(280, 23);
228
         this.label2.TabIndex = 8;
229
         this.label2.Text = "Output Document Name";
230
         // 
231
         // textBox_progress
232
         // 
233
         this.textBox_progress.Location = new System.Drawing.Point(16, 184);
234
         this.textBox_progress.Multiline = true;
235
         this.textBox_progress.Name = "textBox_progress";
236
         this.textBox_progress.ReadOnly = true;
237
         this.textBox_progress.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
238
         this.textBox_progress.Size = new System.Drawing.Size(680, 168);
239
         this.textBox_progress.TabIndex = 9;
240
         this.textBox_progress.Text = "";
241
         // 
242
         // label3
243
         // 
244
         this.label3.Location = new System.Drawing.Point(16, 152);
245
         this.label3.Name = "label3";
246
         this.label3.TabIndex = 10;
247
         this.label3.Text = "Progress";
248
         // 
249
         // checkBox_WordVisibility
250
         // 
251
         this.checkBox_WordVisibility.Location = new System.Drawing.Point(16, 384);
252
         this.checkBox_WordVisibility.Name = "checkBox_WordVisibility";
253
         this.checkBox_WordVisibility.Size = new System.Drawing.Size(256, 24);
254
         this.checkBox_WordVisibility.TabIndex = 17;
255
         this.checkBox_WordVisibility.Text = "Show MS-WORD During Construction";
256
         // 
257
         // createWordDoc
258
         // 
259
         this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
260
         this.ClientSize = new System.Drawing.Size(712, 432);
261
         this.Controls.Add(this.checkBox_WordVisibility);
262
         this.Controls.Add(this.label3);
263
         this.Controls.Add(this.textBox_progress);
264
         this.Controls.Add(this.textBox_output_file);
265
         this.Controls.Add(this.textBox_template);
266
         this.Controls.Add(this.label2);
267
         this.Controls.Add(this.label1);
268
         this.Controls.Add(this.button_view_output);
269
         this.Controls.Add(this.button_generate);
270
         this.Controls.Add(this.button_cancel);
271
         this.Controls.Add(this.button_browse_output_file);
272
         this.Controls.Add(this.button_browse_template);
273
         this.Name = "createWordDoc";
274
         this.Text = "createWordDoc";
275
         this.Closing += new System.ComponentModel.CancelEventHandler(this.createWordDoc_Closing);
276
         this.ResumeLayout(false);
277
 
278
      }
279
      #endregion
280
 
281
 
282
      #region Message handlers
283
      private void button_view_output_Click(object sender, System.EventArgs e)
284
      {
285
         if (WordApp != null)
286
         {
287
            WordApp.Visible = true;
288
         }
289
      }
290
 
291
 
292
 
293
 
294
      private void button_generate_Click(object sender, System.EventArgs e)
295
      {
296
         if (this.textBox_template.Text.Length > 0)
297
         {
298
            if (this.textBox_output_file.Text.Length > 0)
299
            {
300
               createTheWordDoc();
301
            }
302
            else
303
            {
304
               MessageBox.Show("Error - must specify an output file");
305
            }
306
         }
307
         else
308
         {
309
            MessageBox.Show("Error - must specify an input template file");
310
         }
311
      }
312
 
313
      private void button_cancel_Click(object sender, System.EventArgs e)
314
      {
315
         this.Close();   
316
      }
317
 
318
      private void button_browse_template_Click(object sender, System.EventArgs e)
319
      {
320
         OpenFileDialog dialog = new OpenFileDialog();
321
         dialog.Filter = "Word Files (*.doc;*.dot)|*.doc;*.dot|All files(*.*)|*.*";
322
         dialog.FilterIndex = 1;
323
         dialog.ShowDialog();
324
         this.textBox_template.Text = dialog.FileName;
325
 
326
      }
327
 
328
      private void button_browse_output_file_Click(object sender, System.EventArgs e)
329
      {
330
         OpenFileDialog dialog = new OpenFileDialog();
331
         dialog.Filter = "Word Files (*.doc;*.dot)|*.doc;*.dot|All files(*.*)|*.*";
332
         dialog.FilterIndex = 1;
333
         dialog.CheckFileExists = false;
334
         dialog.ShowDialog();
335
         this.textBox_output_file.Text = dialog.FileName;
336
      }
337
 
338
      private void textBox_template_TextChanged(object sender, System.EventArgs e)
339
      {
340
 
341
      }
342
 
343
      private void textBox_output_file_TextChanged(object sender, System.EventArgs e)
344
      {
345
 
346
      }
347
 
348
      private void createWordDoc_Closing(object sender, System.ComponentModel.CancelEventArgs e)
349
      {
350
         if (WordApp != null)
351
         {
352
            // try-catch just in case user closed the application themselves whilst viewing the
353
            // generated output document
354
            try 
355
            {
356
               object nothing = Type.Missing;
357
               object notTrue = false;
358
 
359
               WordApp.Application.Quit( ref notTrue, ref nothing, ref nothing);
360
            }
361
            catch (Exception createWordDoc_Closing_exception)
362
            {
363
               // dummy statement
364
               String s = createWordDoc_Closing_exception.Message;
365
            }
366
         }
367
      }
368
 
369
      #endregion
370
 
371
 
372
      #region word document generation code
373
 
374
      /// <summary>
375
      /// This class is one derived from the EA_UtilitiesRecursionWorker base class so that
376
      /// an instance of it can be used as a parameter in the EA_Utilities.findAndProcessPackageElements
377
      /// method which recursively parses a given package. Thus, clients can parse EA model structure but
378
      /// specify their own functionality to be carried out on packages and elements found in the 
379
      /// parsing.
380
      /// This particular derived class is designed to collect a list of element names from the
381
      /// package, from elements that have a type that is acceptable.
382
      /// </summary>
383
      public class ElementAccumulator : EA_UtilitiesRecursionWorker
384
      {
385
         public ArrayList Elements = null;
386
 
387
         private ArrayList validElementTypes = null;
388
 
389
         public ElementAccumulator(ArrayList elementTypeList): base()
390
         {
391
            validElementTypes = elementTypeList;
392
            Elements = new ArrayList();
393
         }
394
 
395
         public override void processElement( EA.Element theElement )
396
         {
397
            if (validElementTypes.Contains( theElement.Type ))
398
            {
399
               if ( (theElement.Type == "Requirement") && (theElement.Status == "Rejected") )
400
                  return;
401
 
402
               Elements.Add( theElement );
403
            }
404
         }
405
      }
406
 
407
 
408
      private void trackDocSection(int recurse_level)
409
      {
410
         iDocSection = recurse_level;
411
         DocSection[iDocSection]++;     
412
         for (int i=iDocSection+1; i<DocSection.Length; i++)
413
            DocSection[i] = 0;
414
      }
415
 
416
 
417
      /// <summary>
418
      /// Displays progress of the document generation in the createWordDocument dialog
419
      /// </summary>
420
      /// <param name="prefix"></param>
421
      /// <param name="EA_string"></param>
422
      private void displayProgress(string prefix, string EA_string)
423
      {
424
         textBox_progress.AppendText( prefix + EA_string + "\r\n" );
425
 
426
         // Display to the output tab as well - as a longer term record of what happened
427
         string s = "";
428
         for (int i = 1; i<=iDocSection; i++)
429
         {
430
            if (i!=1)
431
               s = s + ".";
432
            s = s + DocSection[i].ToString();
433
         }
434
         while (s.Length < 30)
435
            s += " ";
436
 
437
         EA_Repository.WriteOutput( Main.GUI_OUTPUT_TAB_NAME, s + prefix + EA_string, 0);
438
      }
439
 
440
 
441
      /// <summary>
442
      /// This method takes the specified EA diagram element and puts the diagram image
443
      /// into the clipboard from where it can be pasted into the word document. After
444
      /// pasting it into the document, it sets the style and other aspects of the image.
445
      /// </summary>
446
      /// <param name="theDiagram"></param>
447
      private void appendAndSelectDiagramViaClipboard(EA.Diagram theDiagram)
448
      {
449
         // open the diagram in EA and copy it's image to the clipboard
450
         if (EA_Project.PutDiagramImageOnClipboard(theDiagram.DiagramGUID,0))
451
         {
452
            // create a range at the end of the document
453
            startLocation = WordDocument.Content.End;
454
            WordDocument.Content.InsertParagraphAfter();
455
            endLocation = WordDocument.Content.End;
456
            WordRange = WordDocument.Range(ref startLocation, ref endLocation);
457
            object direction = Word.WdCollapseDirection.wdCollapseEnd;
458
            WordRange.Collapse(ref direction);  // collapse prevents existing content being replaced
459
 
460
            // Get ready for the diagram paste and the formatting we want to do on it with the
461
            // selection object.
462
            SelectInsertionPointAtEndOfDocument();
463
 
464
            // Paste the diagram into the document
465
            WordRange.Paste();           
466
 
467
            // Set style of the diagram to "Normal" so that it can occupy space all the way to the left margin
2092 ghuddy 468
            object l_style = styleName_Normal;
2088 ghuddy 469
            endLocation = WordDocument.Content.End;
470
            WordRange = WordDocument.Range(ref startLocation, ref endLocation);
471
            WordRange.set_Style(ref l_style);
472
 
473
            // Center the diagram on the page
474
            object unit = Word.WdUnits.wdLine;
475
            object missing = Type.Missing;
476
            object count = 1;
477
            WordApp.Selection.MoveDown( ref unit, ref count, ref missing);
478
            WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
479
 
480
            // Add a caption
481
            WordDocument.Content.InsertParagraphAfter();
482
            SelectInsertionPointAtEndOfDocument();         
483
            object Label = "Figure";
484
            object Title = Type.Missing;
485
            object TitleAutoText = Type.Missing;
486
            object Position = Word.WdCaptionPosition.wdCaptionPositionAbove;
487
            object ExcludeLabel = 0;
488
            WordApp.Selection.InsertCaption( ref Label, ref Title, ref TitleAutoText, ref Position, ref ExcludeLabel);
489
            WordApp.Selection.TypeText( ": " + theDiagram.Name.ToString());
490
            WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
491
 
492
            // experimental code to create a bookmark for the figure caption
493
            //WordDocument.Paragraphs[ WordDocument.Paragraphs.Count ].Range.Select();
494
            //object lastParaRange = WordDocument.Paragraphs[ WordDocument.Paragraphs.Count ].Range;
495
            //string bookmarkName = theDiagram.Name;
496
            //bookmarkName = bookmarkName.Replace(' ', '_');
497
            //WordDocument.Bookmarks.Add( bookmarkName, ref lastParaRange );
498
 
499
            // Close the diagram in EA
500
            EA_Repository.CloseDiagram(theDiagram.DiagramID);
501
         }
502
      }
503
 
504
 
505
      /// <summary>
506
      /// Appends a specified text string to the word document, selects the new text, and applies
507
      /// the specified style formatting to it.
508
      /// </summary>
509
      /// <param name="wordText"></param>
510
      /// <param name="styleText"></param>
511
      /// <param name="continuation"></param>
512
      private void appendAndSelectText(string wordText, string styleText) 
513
      { 
514
         appendAndSelectText(wordText, styleText, false );
515
      }
516
 
517
      private void appendAndSelectText(string wordText, string styleText, bool continuation )
518
      {
519
         if (wordText.Length > 0)
520
         {
521
            object style = styleText;
522
            int i;
523
            startLocation = 0;
524
            endLocation = i = WordDocument.Content.End;
525
 
526
            WordRange = WordDocument.Range(ref startLocation, ref endLocation);
527
 
528
            if (!continuation)
529
               WordRange.InsertAfter( "\n" );
530
 
531
            WordRange.InsertAfter( wordText );
532
 
533
            // Make a range out of the pasted text
534
            startLocation = i;
535
            endLocation = WordDocument.Content.End;
536
            WordRange = WordDocument.Range(ref startLocation, ref endLocation);
537
 
538
            // and set the pasted text style
539
            WordRange.set_Style(ref style);
540
         }
541
      }
542
 
543
 
544
      /// <summary>
545
      /// Appends a specified text string to the word document, selects the new text, and applies
546
      /// a heading level style to it. 
547
      /// </summary>
548
      /// <param name="wordText"></param>
549
      /// <param name="level"></param>
550
      private void appendAndSelectHeadingText(string wordText, int level)
551
      {
552
         // Convert level to heading style
2092 ghuddy 553
         string styleText;
2088 ghuddy 554
         switch(level)
555
         {
2092 ghuddy 556
            case 1: styleText = styleName_Heading1; break;
557
            case 2: styleText = styleName_Heading2; break;
558
            case 3: styleText = styleName_Heading3; break;
559
            case 4: styleText = styleName_Heading4; break;
560
            case 5: styleText = styleName_Heading5; break;
561
            case 6: styleText = styleName_Heading6; break;
562
            case 7: styleText = styleName_Heading7; break;
563
            case 8: styleText = styleName_Heading8; break;
564
            case 9: styleText = styleName_Heading9; break;
565
            default: styleText = styleName_Heading9; break;
2088 ghuddy 566
         }
567
         // append the text as a heading
568
         appendAndSelectText(wordText, styleText);
569
      }
570
 
571
 
572
      /// <summary>
573
      /// Appends a specified text string to the word document, selects the new text, and applies
574
      /// a num para heading level style to it. 
575
      /// </summary>
576
      /// <param name="wordText"></param>
577
      /// <param name="level"></param>
578
      private void appendAndSelectNumParaText(string wordText, int level)
579
      {
580
         // Convert level to heading style
2092 ghuddy 581
         string styleText;
2088 ghuddy 582
         switch(level)
583
         {
2092 ghuddy 584
            case 1: styleText = styleName_NumPara1; break;
585
            case 2: styleText = styleName_NumPara2; break;
586
            case 3: styleText = styleName_NumPara3; break;
587
            case 4: styleText = styleName_NumPara4; break;
588
            case 5: styleText = styleName_NumPara5; break;
589
            case 6: styleText = styleName_NumPara6; break;
590
            case 7: styleText = styleName_NumPara7; break;
591
            case 8: styleText = styleName_NumPara8; break;
592
            case 9: styleText = styleName_NumPara9; break;
593
            default: styleText = styleName_NumPara9; break;
2088 ghuddy 594
         }
595
         // append the text as a heading
596
         appendAndSelectText(wordText, styleText);
597
      }
598
 
599
 
600
      private void SelectInsertionPointAtEndOfDocument()
601
      {
602
         object unit; 
603
         object extend;
604
 
605
         unit = Word.WdUnits.wdStory; 
606
         extend = Word.WdMovementType.wdMove;
607
         WordApp.Selection.EndKey(ref unit, ref extend);
608
      }
609
 
610
 
611
      /// <summary>
612
      /// This is an attempt to create a table similar in style to those made by the ERG doc template macros.
613
      /// It is not exaclty that same though, and further work could be done to align it more precisely.
614
      /// </summary>
615
      /// <param name="tableTitle"></param>
616
      /// <param name="numRows"></param>
617
      /// <param name="numCols"></param>
618
      /// <returns></returns>
619
      private int createTable(string tableTitle, int numRows, int numCols)
620
      {
621
         WordDocument.Content.InsertParagraphAfter();
622
         SelectInsertionPointAtEndOfDocument();
623
 
624
         object Label = "Table";
625
         object Title = Type.Missing;
626
         object TitleAutoText = Type.Missing;
627
         object Position = Word.WdCaptionPosition.wdCaptionPositionAbove;
628
         object ExcludeLabel = 0;
629
         WordApp.Selection.InsertCaption( ref Label, ref Title, ref TitleAutoText, ref Position, ref ExcludeLabel);
630
         WordApp.Selection.TypeText( ": " + tableTitle);
631
         WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
632
 
633
         startLocation = WordDocument.Content.End;
634
         WordDocument.Content.InsertParagraphAfter();
635
         endLocation = WordDocument.Content.End;
636
         WordRange = WordDocument.Range(ref startLocation, ref endLocation);
637
 
638
         object defaultTableBehaviour = Type.Missing;
639
         object autofitBehaviour = Type.Missing;
640
         Word.Table Table = WordDocument.Tables.Add( WordRange, numRows, numCols, ref defaultTableBehaviour, ref autofitBehaviour );
641
 
642
         Table.Rows[1].Shading.BackgroundPatternColor = Word.WdColor.wdColorGray10;
643
         Table.Borders.OutsideLineStyle = Word.WdLineStyle.wdLineStyleSingle;
644
         Table.Borders.InsideLineStyle = Word.WdLineStyle.wdLineStyleSingle;
645
 
646
         Table.Select();
2092 ghuddy 647
         object tableTextStyle = styleName_TableText;
2088 ghuddy 648
         Table.Range.set_Style( ref tableTextStyle );
649
 
650
         return WordDocument.Tables.Count;
651
      }
652
 
653
 
654
 
655
      /// <summary>
656
      /// This function process a package link element, parsing each linked package by the
657
      /// parse_package() recursive function.
658
      /// </summary>
659
      /// <param name="theElement"></param>
660
      /// <param name="recurse_level"></param>
661
      private void processPackageLink(EA.Element theElement, int recurse_level)
662
      {
663
         string [] EA_DocGenPkgLnk = null;
664
         string delimStr = "\r\n";
665
         char [] delim = delimStr.ToCharArray();
666
         EA_DocGenPkgLnk = theElement.Notes.ToString().Split(delim,100);
667
         string linkedName = "";
668
 
669
         foreach(string s in EA_DocGenPkgLnk)
670
         {
671
            if (s.Length > 0 && s != "\n" && s != "\r")
672
            {
673
               if (s == "skiproot")
674
               {
675
                  oneShot_skipRootPackage = true;
676
               }
677
               else if (s[0] == '{')
678
               {
679
                  EA.Package theFoundPackage = EA_Utils.EA_Finder.findPackageInRepositoryByGUID(s);
680
                  if (theFoundPackage != null)
681
                  {
682
                     parse_package(theFoundPackage, recurse_level);
683
                  }
684
                  else
685
                  {
686
                     MessageBox.Show("WARNING - Could not find linked package : " + linkedName );
687
                  }
688
               }
689
               else
690
               {
691
                  linkedName = s;
692
               }            
693
            }
694
         }
695
      }
696
 
697
 
698
      /// <summary>
699
      /// This function process a diagram link element, using the appendAndSelectDiagramViaClipboard()
700
      /// for each linked diagram.
701
      /// </summary>
702
      /// <param name="theElement"></param>
703
      private void processDiagramLink(EA.Element theElement)
704
      {
705
         string [] EA_DocGenDiagLnk = null;
706
         string delimStr = "\r\n";
707
         char [] delim = delimStr.ToCharArray();
708
         EA_DocGenDiagLnk = theElement.Notes.ToString().Split(delim,100);
709
         string linkedName = "";
710
 
711
         foreach(string s in EA_DocGenDiagLnk)
712
         {
713
            if (s.Length > 0 && s != "\n" && s != "\r" )
714
            {
715
               if (s[0] == '{')
716
               {
717
                  EA.Diagram theFoundDiagram = EA_Utils.EA_Finder.findDiagramInRepositoryByGUID(s);
718
                  if (theFoundDiagram != null)
719
                  {
720
                     displayProgress( "DIAGRAM: ", theFoundDiagram.Name.ToString() );
721
                     appendAndSelectDiagramViaClipboard( theFoundDiagram );
722
                  }
723
                  else 
724
                  {
725
                     MessageBox.Show("WARNING - Could not find linked diagram : " + linkedName );
726
                  }
727
               }
728
               else
729
               {
730
                  linkedName = s;
731
               }            
732
            }
733
         }         
734
      }
735
 
736
 
737
      /// <summary>
738
      /// This function process an element link element, parsing each linked element by the
739
      /// parse_element() recursive function.
740
      /// </summary>
741
      /// <param name="theElement"></param>
742
      /// <param name="recurse_level"></param>
743
      private void processElementLink(EA.Element theElement, int recurse_level )
744
      {
745
         string [] EA_DocGenEleLnk = null;
746
         string delimStr = "\r\n";
747
         char [] delim = delimStr.ToCharArray();
748
         EA_DocGenEleLnk = theElement.Notes.ToString().Split(delim,100);
749
         string linkedName = "";
750
 
751
         foreach(string s in EA_DocGenEleLnk)
752
         {
753
            if (s.Length > 0 && s != "\n" && s != "\r")
754
            {
755
               if ( s[0] == '{')
756
               {
757
                  EA.Element theFoundElement = EA_Utils.EA_Finder.findElementInRepositoryByGUID(s);
758
                  if (theFoundElement != null)
759
                  {
760
                     parse_element( theFoundElement, recurse_level );
761
                  }
762
                  else 
763
                  {
764
                     MessageBox.Show("WARNING - Could not find linked element : " + linkedName );
765
                  }
766
               }
767
               else
768
               {
769
                  linkedName = s;
770
               }            
771
            }
772
         }         
773
      }
774
 
775
 
776
      /// <summary>
777
      /// This method creates a table from the content of an EA_DocGenTable element in the model.
778
      /// The elements notes section contains the table parameters and cell content.
779
      /// See the EA_Utilities::AddTableElement() method for the layout of this content.
780
      /// </summary>
781
      /// <param name="theElement"></param>
782
      /// <param name="recurse_level"></param>
783
      private void processTableElement(EA.Element theElement, int recurse_level )
784
      {
785
         string [] EA_DocGenTable = null;
786
         string [] cells = null;
787
 
788
         string delimStr = "\r\n";
789
         char [] delim = delimStr.ToCharArray();
790
 
791
         string trimStr = " ";
792
         char [] trimChars = trimStr.ToCharArray();
793
 
794
         EA_DocGenTable = theElement.Notes.ToString().Split(delim,200);
795
 
796
         int columnCount = 0;
797
         int rowCount = 0;
798
         char seperator = ',';
799
         string tableTitle = "";
800
         bool gotSeperator = false;
801
         bool gotTitle = false;
802
 
803
         // Scan the notes content line by line looking for table parameters and counting
804
         // the number of table rows (which is not specified as an explicit table parameter).
805
         int s_ElementsConsumedByTableParams = 0;
806
         foreach(string s in EA_DocGenTable)
807
         {
808
            if (s.Length > 0 && s != "\n" && s != "\r" )
809
            {
810
               if (gotTitle == false && s.StartsWith("title"))
811
               {
812
                  s_ElementsConsumedByTableParams++;
813
                  tableTitle = EA_Utils.options.getOptionValue(s, tableTitle);
814
                  gotTitle = true;
815
                  if (tableTitle == "")
816
                  {
817
                     MessageBox.Show( "Table Element Serialisation Failed - Bad Title" );
818
                     break;
819
                  }
820
               }
821
 
822
               else if (columnCount == 0 && s.StartsWith("columns"))
823
               {
824
                  s_ElementsConsumedByTableParams++;
825
                  columnCount = EA_Utils.options.getOptionValue(s, columnCount);
826
                  if (columnCount == 0)
827
                  {
828
                     MessageBox.Show( "Table Element Serialisation Failed - bad column count" );
829
                     break;
830
                  }
831
               }
832
               else if (gotSeperator == false && s.StartsWith("seperator"))
833
               {
834
                  s_ElementsConsumedByTableParams++;
835
                  seperator = EA_Utils.options.getOptionValue(s, seperator);
836
                  gotSeperator = true;
837
               }
838
               else
839
               {
840
                  rowCount++;
841
               }
842
            }
843
         }
844
 
845
         if (gotTitle == true && gotSeperator == true && columnCount != 0)
846
         {
847
            if (rowCount < 2)
848
            {
849
               MessageBox.Show( "Table Element Serialisation Failed - Insufficient Rows" );
850
            }
851
            else
852
            {
853
               // create the table in the word doc
854
               int tableNum = createTable( tableTitle, rowCount, columnCount );
855
               Word.Table table = WordDocument.Tables[tableNum];
856
               object center = Word.WdParagraphAlignment.wdAlignParagraphCenter;
857
 
858
               // scan the element notes again to extract the cell content and add it to the
859
               // table we just created.
860
               int row = 1;
861
               int col = 1;
862
               foreach(string s in EA_DocGenTable)
863
               {
864
                  if (s.Length > 0 && s != "\n" && s != "\r" )
865
                  {
866
                     if (s_ElementsConsumedByTableParams > 0)
867
                     {
868
                        s_ElementsConsumedByTableParams--;
869
                     }
870
                     else
871
                     {
872
                        delimStr = seperator.ToString();
873
                        delim = delimStr.ToCharArray();
874
                        cells = s.Split(delim,columnCount);
875
 
876
                        if (cells.Length != columnCount)
877
                        {
878
                           MessageBox.Show( "Table Element Serialisation Failed - Bad Row" );
879
                           break;
880
                        }
881
                        else
882
                        {
883
                           // serialise table row
884
                           for(col=1; col<=columnCount; col++)
885
                           {
886
                              cells[col-1].TrimStart( trimChars );
887
                              cells[col-1].TrimEnd( trimChars );
888
 
889
                              table.Cell(row,col).Range.Text = cells[col-1];
890
 
891
                              // special handling for heading row
892
                              if (row == 1)
893
                              {
894
                                 table.Cell(row,col).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
895
                                 table.Cell(row,col).Range.Font.Bold = 1;
896
                              }
897
                           }
898
                           row++;
899
                        }                     
900
                     }
901
                  }
902
               }
903
            }
904
         }
905
         else
906
         {
907
            MessageBox.Show( "Table Element Serialisation Failed - Table Parameters Incomplete" );
908
         }
909
      }
910
 
911
 
912
      /// <summary>
913
      /// This function process a text element. It is a simple wrapper for the 
914
      /// appendAndSelectText()function.
915
      /// </summary>
916
      /// <param name="theElement"></param>
917
      /// <param name="recurse_level"></param>
918
      private void processTextElement(EA.Element theElement, int recurse_level )
919
      {
2092 ghuddy 920
         appendAndSelectText( theElement.Notes.ToString(), styleName_Body1 );
2088 ghuddy 921
      }
922
 
923
 
924
 
925
 
926
      /// <summary>
927
      /// This function processes elements as if they represented document references for
928
      /// the References section of a document.
929
      /// </summary>
930
      /// <param name="thePackage"></param>
931
      private void createReferencesSection(EA.Package thePackage)
932
      {
933
         foreach(EA.Element theElement in thePackage.Elements)
934
         {
935
            // Here we want to apply the special ERG template styles for each reference
936
            // item.
2092 ghuddy 937
            appendAndSelectText( theElement.Name.ToString(), styleName_RefListNum );
938
            appendAndSelectText( theElement.Notes.ToString(), styleName_RefListText );
2088 ghuddy 939
         }
940
      }
941
 
942
 
943
      private void createTerminologySection(EA.Package thePackage)
944
      {
945
         // Use local package elements as source of glossary, but if none are present, resort to using
946
         // the project glossary in the repository 
947
         int numItems = thePackage.Elements.Count;
948
         if (numItems == 0)
949
         {
950
            numItems = EA_Repository.Terms.Count;
951
         }
952
 
953
         if (numItems > 0)
954
         {
955
            // Create a table
956
            int tableNum = createTable("Terminology", numItems+1, 2);
957
            Word.Table table = WordDocument.Tables[tableNum];
958
            object center = Word.WdParagraphAlignment.wdAlignParagraphCenter;
959
            table.Cell(1,1).Range.Text = "Term";
960
            table.Cell(1,1).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
961
            table.Cell(1,1).Range.Font.Bold = 1;
962
            table.Cell(1,2).Range.Text = "Definition";
963
            table.Cell(1,2).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
964
            table.Cell(1,2).Range.Font.Bold = 1;
965
            table.Columns[1].SetWidth(100, Word.WdRulerStyle.wdAdjustSameWidth );
966
            int row = 2;
967
 
968
            if (thePackage.Elements.Count > 0)
969
            {
970
               // The advantage of using the package content is that the elements come out alphabetically
971
               // sorted already (assuming user has not over-riden the sort order in the project browser).
972
               // Also, the glossary content can be exactly tailored to what the document requires.
973
               foreach(EA.Element theElement in thePackage.Elements)
974
               {
975
                  table.Cell(row,1).Range.Text = theElement.Name.ToString();
976
                  table.Cell(row,2).Range.Text = theElement.Notes.ToString();
977
                  row++;
978
               }
979
            }
980
            else // default to using the project glossary in the repository
981
            {
982
               // TODO 
983
               // 1. Alphabetically Sort the glossary terms before adding them to the table
984
               //    The Repository glossary seems to be provided in non-sorted order unfortunately.
985
 
986
               // inject the glossary into the table
987
               foreach(EA.Term theTerm in EA_Repository.Terms)
988
               {
989
                  table.Cell(row,1).Range.Text = theTerm.Term;
990
                  table.Cell(row,2).Range.Text = theTerm.Meaning;
991
                  row++;
992
               }
993
            }
994
         }
995
      }
996
 
997
 
998
      private bool generatePackageHeadingAndDescription( EA.Package thePackage, int recurse_level )
999
      {
1000
         // disarm the one-shot package skipping feature if it was armed, and exit the function.
1001
         if (oneShot_skipRootPackage == true)
1002
         {
1003
            oneShot_skipRootPackage = false;
1004
            return false;
1005
         }
1006
 
1007
         // The package name is a heading, and the package notes are paragraphs 
1008
         // directly under that heading
1009
         displayProgress( "PACKAGE: ", thePackage.Name.ToString() );
1010
 
1011
         appendAndSelectHeadingText( thePackage.Name.ToString(), recurse_level );
1012
 
1013
         // Special handling for package called "Terminology" 
1014
         if (thePackage.Name == "Terminology")
1015
         {
2092 ghuddy 1016
            appendAndSelectText( thePackage.Notes.ToString(), styleName_Body1 );
2088 ghuddy 1017
            createTerminologySection(thePackage);
1018
            return true;
1019
         }
1020
 
1021
         if (thePackage.Name == "References")
1022
         {
2092 ghuddy 1023
            appendAndSelectText( thePackage.Notes.ToString(), styleName_Body1 );
2088 ghuddy 1024
            createReferencesSection(thePackage);
1025
            return true;
1026
         }
1027
 
1028
         // use the package notes as body text and indicate to caller that package elements 
1029
         // have not been consumed
2092 ghuddy 1030
         appendAndSelectText( thePackage.Notes.ToString(), styleName_Body1 );      
2088 ghuddy 1031
         return false;
1032
      }
1033
 
1034
 
1035
      private void generatePackageDiagrams( EA.Package thePackage )
1036
      {
1037
         // default handling of diagrams
1038
         foreach(EA.Diagram theDiagram in thePackage.Diagrams)
1039
         {
1040
            displayProgress( "DIAGRAM: ", theDiagram.Name.ToString() );
1041
 
1042
            appendAndSelectDiagramViaClipboard( theDiagram );
1043
         }
1044
      }
1045
 
1046
 
1047
      private void generateElementContent( EA.Element theElement, int recurse_level )
1048
      {
1049
         recurse_level++;
1050
         if (recurse_level > 9)
1051
         {
1052
            throw new System.StackOverflowException("Max Recursion Depth (9) Exceeded" );
1053
         }
1054
         trackDocSection(recurse_level);
1055
 
1056
         if (EA_Utils.options.elementTypeFoundInEA_DocGen( theElement.Type ))
1057
         {
1058
            displayProgress( "ELEMENT: ", theElement.Name.ToString() );
1059
 
1060
            if ( theElement.Type.StartsWith("Requirement")
1061
               && EA_Utils.options.opt_DisplayRequirementsWithStatus == true )
1062
            {
1063
               generateRequirementText( theElement );
1064
            }
1065
            else
1066
            {
1067
               if (theElement.Type.StartsWith("Requirement")
1068
                  && EA_Utils.options.opt_DisplayRequirementElementsAsSections == false )
1069
               {
1070
                  string s;
1071
                  s = EA_Utils.options.opt_RequirementElementDisplayFormat;
1072
                  s = s.Replace(@"%s",@"{0}");
2092 ghuddy 1073
                  appendAndSelectText( String.Format( s, theElement.Name ), styleName_Body1);
2088 ghuddy 1074
                  WordRange.Font.Bold = 1;
1075
               }
1076
               else if ( EA_Utils.options.opt_ElementHeadingTransitionLevel > 0
1077
                  && EA_Utils.options.opt_ElementHeadingTransitionLevel <= recurse_level )
1078
                  appendAndSelectNumParaText( theElement.Name.ToString(), recurse_level );
1079
               else
1080
                  appendAndSelectHeadingText( theElement.Name.ToString(), recurse_level );
1081
 
2092 ghuddy 1082
               appendAndSelectText( theElement.Notes.ToString(), styleName_Body1 );
2088 ghuddy 1083
            }
1084
         }
1085
         else
1086
         {
1087
            displayProgress( "FILTERED: ", theElement.Type );
1088
 
1089
         }
1090
         // TODO
1091
         // We will probably have to put special code in here at some point for attributes
1092
         // of, and collections of things within elements. This will become apparent once
1093
         // someone tries to produce a design document where such things need to be included
1094
         // in the generated document. Lots of work needed here eventually.
1095
 
1096
      }
1097
 
1098
 
1099
      /// <summary>
1100
      /// This function was the only way I could figure out how to obtain a style object for
1101
      /// the style name given. Everything else I tried, didn't work, even code from Visual Basic
1102
      /// examples failed to work or even compile (once converted) in C#.
1103
      /// </summary>
1104
      /// <param name="styleText"></param>
1105
      /// <returns></returns>
1106
      private Word.Style getStyle(string styleText)
1107
      {
1108
         foreach(Word.Style aStyle in WordDocument.Styles)
1109
         {
1110
            //MessageBox.Show( aStyle.NameLocal );
1111
            if (aStyle.NameLocal.StartsWith(styleText))
1112
            {
1113
               return aStyle;
1114
            }
1115
         }
1116
         return null;
1117
      }
1118
 
1119
 
1120
      /// <summary>
1121
      /// This function searches for the old content in the document in order to remove it
1122
      /// prior to adding in the new content from the latest EA model data.
1123
      /// </summary>
1124
      private void removeExistingDocumentContent()
1125
      {
1126
         //WordApp.Visible = true;  // enable this when debugging this horrible function
1127
         Word.Selection aSelection;
2092 ghuddy 1128
         object findStyle = getStyle( styleName_Heading1 );
2088 ghuddy 1129
 
1130
         WordApp.Selection.WholeStory();
1131
         aSelection = WordApp.Selection;
1132
 
1133
         aSelection.Find.ClearFormatting();
1134
         aSelection.Find.set_Style( ref findStyle );
1135
         aSelection.Find.Text = "Introduction";
1136
         aSelection.Find.Replacement.Text = "";
1137
         aSelection.Find.Forward = true;
1138
         aSelection.Find.MatchCase = false;
1139
         aSelection.Find.MatchWholeWord = false;
1140
         aSelection.Find.MatchWildcards = false;
1141
         aSelection.Find.MatchSoundsLike = false;
1142
         aSelection.Find.MatchAllWordForms = false;
1143
 
1144
         object missingValue = Type.Missing;
1145
         if (aSelection.Find.Execute(ref missingValue, ref missingValue, 
1146
            ref missingValue, ref missingValue, ref missingValue, 
1147
            ref missingValue, ref missingValue, ref missingValue, 
1148
            ref missingValue, ref missingValue, ref missingValue, 
1149
            ref missingValue, ref missingValue, ref missingValue, 
1150
            ref missingValue)) 
1151
         { 
1152
            // found Introduction with Heading 1 style so we are now at the point in the 
1153
            // document where everything here on must be deleted in readiness for the newly
1154
            // generated content. Extend the range to the end of the document and cut out the
1155
            // old content.
1156
            Word.Range range = aSelection.Range;
1157
            range.End = WordDocument.Content.End;
1158
            range.Cut();
2092 ghuddy 1159
            // try and ensure that no heading style is left in place following the removal
1160
            object style = styleName_Body1;
1161
            range.set_Style(ref style);
2088 ghuddy 1162
         }
1163
      }
1164
 
1165
 
1166
      /// <summary>
1167
      /// Parses an element (because elements can contain sub-elements).
1168
      /// This is also the function that identifies the occurrences of the special
1169
      /// EA_DocGenXXX elements.
1170
      /// </summary>
1171
      /// <param name="theElement"></param>
1172
      /// <param name="recurse_level"></param>
1173
      private void parse_element(EA.Element theElement, int recurse_level)
1174
      {
1175
         // First look for and handle special elements that control the doc-gen process
1176
 
1177
         // The EA_DocGenPackageLink can contain a list of GUIDs in its notes section
1178
         // that point to model structure outside of the localised structure forming the
1179
         // document. This enables a document to grab content from other external models
1180
         // as long as they are loaded into the repository.
1181
         if (theElement.Name.StartsWith("EA_DocGenPackageLink"))
1182
         {
1183
            processPackageLink( theElement, recurse_level );
1184
         }
1185
 
1186
            // The EA_DocGenDiagramLink can contain a list of GUIDs in its notes section
1187
            // that point to diagram elements anywhere in the repository.
1188
         else if (theElement.Name.StartsWith("EA_DocGenDiagramLink"))
1189
         {
1190
            processDiagramLink( theElement );
1191
         }
1192
 
1193
            // The EA_DocGenDiagramLink can contain a list of GUIDs in its notes section
1194
            // that point to diagram elements anywhere in the repository.
1195
         else if (theElement.Name.StartsWith("EA_DocGenElementLink"))
1196
         {
1197
            processElementLink( theElement, recurse_level );
1198
         }
1199
 
1200
            // The EA_DocGenTable element can contain table content in its notes section
1201
            // and must be dealt with in a special way.
1202
         else if (theElement.Name.StartsWith("EA_DocGenTable"))
1203
         {
1204
            processTableElement( theElement, recurse_level );
1205
         }
1206
 
1207
            // The EA_DocGenText element can contain text content in its notes section
1208
            // that must be appended to the doc in Body 1 style within the current
1209
            // section.
1210
         else if (theElement.Name.StartsWith("EA_DocGenText"))
1211
         {
1212
            processTextElement( theElement, recurse_level );
1213
         }
1214
 
1215
         else if (theElement.Name.StartsWith("EA_DocGenRelationshipMatrix"))
1216
         {
1217
            processRelationshipMatrixElement( theElement, recurse_level );
1218
         }
1219
 
1220
         else
1221
         {
1222
            generateElementContent( theElement, recurse_level );
1223
         }      
1224
      }
1225
 
1226
 
1227
      /// <summary>
1228
      /// Parses the package and all of its content, extracting information as needed, and
1229
      /// adding it to the word document. This method is recursive, since recursion is the
1230
      /// easiest way to walk thru' the package levels in the model.
1231
      /// </summary>
1232
      /// <param name="parentPackage"></param>
1233
      /// <param name="recurse_level"></param>
1234
      private void parse_package(EA.Package thePackage, int recurse_level)
1235
      {
1236
         // track recursion level to control heading style formatting requirements
1237
         // The one-shot skiproot feature of the EA_DocGenPackageLink element handling has an effect
1238
         // here, as you can see. 
1239
         bool rootPackageWasSkipped = !oneShot_skipRootPackage;
1240
         if (oneShot_skipRootPackage == false)
1241
         {
1242
            recurse_level++;
1243
            trackDocSection(recurse_level);
1244
         }
1245
 
1246
         if (recurse_level > 9)
1247
         {
1248
            throw new System.StackOverflowException("Max Recursion Depth (9) Exceeded" );
1249
         }
1250
 
1251
         // generate package heading and description. This may, depending on the package name,
1252
         // consume the elements in the package. If it does not, then consume them (and any diagrams)
1253
         // locally here.
1254
         if (false == generatePackageHeadingAndDescription( thePackage, recurse_level ))
1255
         {
1256
            // consume diagrams
1257
            generatePackageDiagrams( thePackage );
1258
 
1259
            // consume elements - we have to use a special sorting class here because of peculiarties
1260
            // in the way EA holds the elements in the collections. 
1261
            EA_ElementSorter elementSorter = new EA_ElementSorter(thePackage);
1262
            EA.Element theElement = null;
1263
            int theElementsRelativeLevel = 0;
1264
            if (true == elementSorter.getFirst(ref theElement, ref theElementsRelativeLevel))
1265
            {
1266
               do
1267
               {
1268
                  int theElementsRecurseLevel = recurse_level + theElementsRelativeLevel;
1269
 
1270
                  parse_element( theElement, theElementsRecurseLevel );
1271
 
1272
               } while (true == elementSorter.getNext(ref theElement, ref theElementsRelativeLevel));
1273
            }
1274
         }
1275
 
1276
         // Scan through the packages within this package.
1277
         foreach(EA.Package lowerLevelPackage in thePackage.Packages)
1278
         {
1279
            // recurse
1280
            parse_package(lowerLevelPackage, recurse_level);
1281
         }
1282
 
1283
 
1284
         if (rootPackageWasSkipped == false)
1285
            recurse_level--;
1286
      }
1287
 
1288
 
1289
      /// <summary>
1290
      /// This is the overall generate document method. It creates a word document from 
1291
      /// an input template, sets various characteristics of the document, schedules the
1292
      /// EA parsing function, before finally saving the document to the output file name.
1293
      /// </summary>
1294
      private void createTheWordDoc()
1295
      {
2092 ghuddy 1296
         // caputre initial security settings
1297
         MsoAutomationSecurity  securityBefore = WordApp.AutomationSecurity;
1298
 
2088 ghuddy 1299
         try 
1300
         {
1301
            object template       = this.textBox_template.Text;
1302
            object newTemplate    = Type.Missing;
1303
            object outputFilename = this.textBox_output_file.Text;
1304
 
1305
            object docType = Type.Missing;
1306
            object visible = Type.Missing;
1307
            object nothing = Type.Missing;
1308
            object notTrue = false;
1309
 
1310
            EA_Repository.WriteOutput( Main.GUI_OUTPUT_TAB_NAME, "", 0);
1311
            EA_Repository.WriteOutput( Main.GUI_OUTPUT_TAB_NAME, 
1312
                                       String.Format( "Generating Document {0}", this.textBox_output_file.Text), 0 );
1313
 
2092 ghuddy 1314
            // tell word not to show itself unless user has checked the option to say otherwise
2088 ghuddy 1315
            WordApp.Visible = this.checkBox_WordVisibility.Checked;
2092 ghuddy 1316
 
1317
            // disable VB macros from running in the document
1318
            WordApp.AutomationSecurity = MsoAutomationSecurity.msoAutomationSecurityForceDisable;
1319
            WordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
1320
 
2088 ghuddy 1321
            // Create a document from the input template
1322
            WordDocument = WordApp.Documents.Add(ref template, ref newTemplate, ref docType, ref visible);
1323
 
1324
            // Remove all old content from the input document/template. Note that this assumes
1325
            // that content will begin at section 1 with the title "Introduction" and it will have
1326
            // "Heading 1" style.
1327
            removeExistingDocumentContent();
1328
 
1329
            // turn off grammar and spell checking
1330
            WordDocument.GrammarChecked = false;
1331
            WordDocument.SpellingChecked = false;
1332
 
2092 ghuddy 1333
            // If the input template does not contain the styles needed for requirement documents, 
1334
            // programmatically create them just in case.
1335
            createRequirementStylesIfNecessary();
1336
 
2088 ghuddy 1337
            // Parse EA_DocGen Document Model. The parent package is the document model itself
1338
            // and the packages within it are the top level packages only.
1339
            // Question: do we have to parse diagrams and elements at this top level? So far,
1340
            // this has been found to be unecessary, but you never know. For now, dont do it.
1341
            foreach(EA.Package thePackage in EA_ParentPackage.Packages)
1342
            {
1343
               parse_package(thePackage, 0);
1344
            }
1345
 
1346
 
1347
            // Select the entire document, and Update all fields, including Table of Contents
1348
            // and then collapse the selection in case user wants to view the document, because
1349
            // they wont want to see the inverse video of the selection.
1350
            WordApp.Selection.WholeStory();
1351
            WordApp.Selection.Fields.Update();
1352
            object direction = Word.WdCollapseDirection.wdCollapseEnd;
1353
            WordApp.Selection.Collapse(ref direction);
1354
 
1355
            // Save the document to the output file
1356
            WordDocument.SaveAs( ref outputFilename, 
1357
               ref nothing, ref nothing, ref nothing, ref nothing, 
1358
               ref nothing, ref nothing, ref nothing, ref nothing,
1359
               ref nothing, ref nothing, ref nothing, ref nothing,
1360
               ref nothing, ref nothing, ref nothing);
1361
 
1362
            // Tell user the process completed ok
1363
            textBox_progress.AppendText( "---Completed---" );
1364
            MessageBox.Show("Document Generation Complete");
1365
 
1366
 
1367
         }
1368
         catch (Exception createTheWordDoc_exception)
1369
         {
1370
            MessageBox.Show("Document Generation Failed\n\n" + createTheWordDoc_exception.Message);
1371
         }
2092 ghuddy 1372
 
1373
         // restore security settings
1374
         WordApp.AutomationSecurity = securityBefore;
2088 ghuddy 1375
      }
1376
 
1377
      /// <summary>
2092 ghuddy 1378
      /// Creates a wdStyleTypeParagraph based style
1379
      /// </summary>
1380
      /// <param name="newStyleName"></param>
1381
      /// <param name="followingStyleName"></param>
1382
      /// <param name="baseStyle"></param>
1383
      /// <returns></returns>
1384
      private Word.Style createParagrapghStyle(string newStyleName, 
1385
                                               string followingStyleName, 
1386
                                               Word.Style baseStyle)
1387
      {
1388
         Word.Style s = getStyle(newStyleName);
1389
         if (s == null)
1390
         {
1391
            object o_styleType = Word.WdStyleType.wdStyleTypeParagraph;
1392
            s = WordDocument.Styles.Add(newStyleName, ref o_styleType);
1393
 
1394
            object o_baseStyle = baseStyle.NameLocal;
1395
            object o_followingStyle = followingStyleName;
1396
            s.set_BaseStyle(ref o_baseStyle);
1397
            s.set_NextParagraphStyle(ref o_followingStyle);
1398
 
1399
            s.Font = baseStyle.Font;
1400
            s.ParagraphFormat = baseStyle.ParagraphFormat;
1401
            s.LanguageID = Word.WdLanguageID.wdEnglishAUS;
1402
            s.NoProofing = 0;
1403
            s.Frame.Delete();
1404
            s.NoSpaceBetweenParagraphsOfSameStyle = false;
1405
 
1406
         }
1407
         return s;
1408
      }
1409
 
1410
      /// <summary>
1411
      /// Creates a wdStyleTypeCharacter based style
1412
      /// </summary>
1413
      /// <param name="newStyleName"></param>
1414
      /// <param name="baseStyle"></param>
1415
      /// <returns></returns>
1416
      private Word.Style createCharacterStyle(string newStyleName, Word.Style baseStyle)
1417
      {
1418
         Word.Style s = getStyle(newStyleName);
1419
         if (s == null)
1420
         {
1421
            object o_styleType = Word.WdStyleType.wdStyleTypeCharacter;
1422
            s = WordDocument.Styles.Add(newStyleName, ref o_styleType);
1423
            object o_baseStyle = baseStyle.NameLocal;
1424
            s.set_BaseStyle(ref o_baseStyle);
1425
            s.Font = baseStyle.Font;
1426
            s.LanguageID = Word.WdLanguageID.wdEnglishAUS;
1427
            s.NoProofing = 0;
1428
         }
1429
         return s;
1430
      }
1431
 
1432
 
1433
      /// <summary>
1434
      /// Creates the styles needed for generating requirement documentation, if they do not
1435
      /// already exist.
1436
      /// </summary>
1437
      private void createRequirementStylesIfNecessary()
1438
      {
1439
         Word.Style s_Body1 = getStyle(styleName_Body1);
1440
         if (s_Body1 != null)
1441
         {
1442
            // APPROVED ///////////////////////////////////////////////////////////////////////////////////////////////
1443
            Word.Style s_ReqAppBody = createParagrapghStyle(styleName_ReqAppBody, styleName_ReqAppBody, s_Body1);
1444
            if (s_ReqAppBody != null)
1445
            {
1446
               // No changes needed - this is just a copy of Body 1
1447
            }
1448
 
1449
            Word.Style s_ReqAppHdr = null;
1450
            if (s_ReqAppBody != null)
1451
            {
1452
               s_ReqAppHdr = createParagrapghStyle(styleName_ReqAppHdr, styleName_ReqAppBody, s_Body1);
1453
               if (s_ReqAppHdr != null)
1454
               {
1455
                  // Indent:Hanging: 2.5cm, Space After: 3pt, Keep with next, Tabs: 16.5cm, Right
1456
                  s_ReqAppHdr.ParagraphFormat.FirstLineIndent = WordApp.CentimetersToPoints((float)-2.5);
1457
                  s_ReqAppHdr.ParagraphFormat.SpaceAfter = 3;
1458
                  s_ReqAppHdr.ParagraphFormat.KeepWithNext = (int)MsoTriState.msoTrue;
1459
                  s_ReqAppHdr.ParagraphFormat.TabStops.ClearAll();
1460
                  object alignment = Word.WdTabAlignment.wdAlignTabRight;
1461
                  object leader = Word.WdTabLeader.wdTabLeaderSpaces;
1462
                  s_ReqAppHdr.ParagraphFormat.TabStops.Add( WordApp.CentimetersToPoints((float)16.5), ref alignment, ref leader );
1463
               }
1464
            }
1465
 
1466
            // PROPOSED ///////////////////////////////////////////////////////////////////////////////////////////////
1467
            Word.Style s_ReqPropBody = null;
1468
            if (s_ReqAppBody != null)
1469
            {
1470
               s_ReqPropBody = createParagrapghStyle(styleName_ReqPropBody, styleName_ReqPropBody, s_ReqAppBody );
1471
               if (s_ReqPropBody != null)
1472
               {
1473
                  // Font: Italic
1474
                  s_ReqPropBody.Font.Italic = (int)MsoTriState.msoTrue;
1475
               }
1476
            }
1477
 
1478
            if (s_ReqAppHdr != null && s_ReqPropBody != null)
1479
            {
1480
               Word.Style s_ReqPropHdr = createParagrapghStyle(styleName_ReqPropHdr, styleName_ReqPropBody, s_ReqAppHdr );
1481
               if (s_ReqPropHdr != null)
1482
               {
1483
                  // Font: Italic
1484
                  s_ReqPropHdr.Font.Italic = (int)MsoTriState.msoTrue;
1485
               }
1486
            }
1487
 
1488
            // REJECTED ///////////////////////////////////////////////////////////////////////////////////////////////
1489
            Word.Style s_ReqRejBody = null;
1490
            if (s_ReqAppBody != null)
1491
            {
1492
               s_ReqRejBody = createParagrapghStyle(styleName_ReqRejBody, styleName_ReqRejBody, s_ReqAppBody );
1493
               if (s_ReqRejBody != null)
1494
               {
1495
                  // Font: Italic
1496
                  s_ReqRejBody.Font.StrikeThrough = (int)MsoTriState.msoTrue;
1497
               }
1498
            }
1499
 
1500
            if (s_ReqAppHdr != null && s_ReqRejBody != null)
1501
            {
1502
               Word.Style s_ReqRejHdr = createParagrapghStyle(styleName_ReqRejHdr, styleName_ReqRejBody, s_ReqAppHdr );
1503
               if (s_ReqRejHdr != null)
1504
               {
1505
                  // Font: Italic
1506
                  s_ReqRejHdr.Font.StrikeThrough = (int)MsoTriState.msoTrue;
1507
               }
1508
            }
1509
 
1510
            // REQUIREMENT NAME ///////////////////////////////////////////////////////////////////////////////////////////////
1511
            Word.Style s_DefParaFont = getStyle("Default Paragraph Font");
1512
            if (s_DefParaFont != null)
1513
            {
1514
               Word.Style s_ReqName = createCharacterStyle(styleName_ReqName, s_DefParaFont );
1515
               if (s_ReqName != null)
1516
               {
1517
                  s_ReqName.Font.Bold = (int)MsoTriState.msoTrue;
1518
               }
1519
            }
1520
         }
1521
      }
1522
 
1523
      /// <summary>
2088 ghuddy 1524
      /// Generates the text for requirements.  This uses custom styles to format the text 
1525
      /// rather than formatting it in code.
1526
      /// </summary>
1527
      private void generateRequirementText( EA.Element theElement )
1528
      {
1529
         string reqID = "";
1530
         string reqShortDesc = "";
1531
         string reqNotes = "";
1532
         string reqStatusText = "";
1533
         string reqHeadingStyle ="";
1534
         string reqParaStyle = "";
2092 ghuddy 1535
         object reqNameStyle = styleName_ReqName;
2088 ghuddy 1536
         Word.Range shortDescRange;
1537
 
1538
         // Set the style depending on the status
1539
         switch ( theElement.Status )
1540
         {
1541
            case "Proposed":
1542
               reqStatusText = "(Proposed)";
2092 ghuddy 1543
               reqHeadingStyle = styleName_ReqPropHdr;
1544
               reqParaStyle    = styleName_ReqPropBody;
2088 ghuddy 1545
               break;
1546
 
1547
            case "Rejected":
2092 ghuddy 1548
               reqHeadingStyle = styleName_ReqRejHdr;
1549
               reqParaStyle    = styleName_ReqRejBody;
2088 ghuddy 1550
               break;
1551
 
1552
            case "Approved":
1553
               reqStatusText = "(Phase " + theElement.Phase + ")";
2092 ghuddy 1554
               reqHeadingStyle = styleName_ReqAppHdr;
1555
               reqParaStyle    = styleName_ReqAppBody;
2088 ghuddy 1556
               break;
1557
 
1558
            default:
1559
               reqStatusText = "(" + theElement.Status + ")(Phase " + theElement.Phase + ")";
2092 ghuddy 1560
               reqHeadingStyle = styleName_ReqAppHdr;
1561
               reqParaStyle    = styleName_ReqAppBody;
2088 ghuddy 1562
               break;
1563
         }
1564
 
1565
         // Pull out the ID from the name
1566
         int pos = theElement.Name.IndexOf( " ", 0, theElement.Name.Length );
1567
         reqID = theElement.Name.Substring( 0, pos );
1568
 
1569
         // Pull out the short description from the rest of the name
1570
         reqShortDesc = theElement.Name.Substring( pos, theElement.Name.Length-pos );
1571
         reqShortDesc = reqShortDesc.Trim();
1572
 
1573
         // Pull out the notes
1574
         reqNotes = theElement.Notes.ToString();
1575
 
1576
         // Add the text
1577
         appendAndSelectText( reqID + '\t', reqHeadingStyle );
1578
         appendAndSelectText( reqShortDesc, reqHeadingStyle, true );
1579
         shortDescRange = WordRange;
1580
         appendAndSelectText( '\t' + reqStatusText, reqHeadingStyle, true );
1581
 
1582
         //reapply the name char style to the short desc.
1583
         shortDescRange.Start = shortDescRange.Start-1;
1584
         shortDescRange.End = WordRange.Start-1;
1585
         shortDescRange.set_Style( ref reqNameStyle );
1586
 
1587
         appendAndSelectText( reqNotes, reqParaStyle );
1588
 
1589
      }
1590
 
1591
 
2092 ghuddy 1592
      /// <summary>
1593
      /// Class to sort EA elements alphabetically, in a case insensitive way.
1594
      /// This is used by the processRelationshipMatrixElement method.
1595
      /// </summary>
2088 ghuddy 1596
      public class elementSortByName : IComparer
1597
      {
1598
         int IComparer.Compare( object x, object y)
1599
         {
1600
            if (x!=null & y!= null)
1601
               return (new CaseInsensitiveComparer()).Compare( ((EA.Element)x).Name, ((EA.Element)y).Name );
1602
            else
1603
               return 0;
1604
         }
1605
      }
1606
 
1607
      /// <summary>
1608
      /// This function will insert a relationship matrix table into the document, built from content
1609
      /// specified in options that are provided in the notes section of the special element that
1610
      /// has led to this function being called.
1611
      /// </summary>
1612
      /// <param name="theElement"></param>
1613
      /// <param name="recurse_level"></param>
1614
      private void processRelationshipMatrixElement( EA.Element theElement, int recurse_level )
1615
      {
1616
         int tableNum = 0;
1617
         Word.Table table = null;
1618
 
1619
         // process the options for the relationship matrix
1620
         EA_RelationshipMatrix EA_RelMatrix = new EA_RelationshipMatrix(EA_Utils);
1621
         if (!EA_RelMatrix.processRelationshipMatrixOptions( theElement ))
1622
         {
1623
            return;  // must have been an error
1624
         }
1625
 
1626
         // Scan the fromPackage to find out how many elements are present and so how many
1627
         // rows the matrix table will need
1628
         ElementAccumulator fromLister = new ElementAccumulator(EA_RelMatrix.fromElementTypes);
1629
         EA_Utils.findAndProcessPackageElements( EA_RelMatrix.fromPackage, fromLister, EA_RelMatrix.fromPackageRecursion );
1630
 
1631
         // Sort the "from" elements
1632
         elementSortByName sorter = new elementSortByName();
1633
         fromLister.Elements.Sort( sorter );
1634
 
1635
         // dictionary to support to-from table construction.
1636
         ArrayList fromToDictionary = new ArrayList();
1637
 
2090 ghuddy 1638
 
1639
         bool needFromToTable = false;
1640
         if (EA_RelMatrix.fromToTableTitle != null && EA_RelMatrix.fromToTableTitle.Length > 0)
1641
            needFromToTable = true;
1642
 
1643
         bool needToFromTable = false;
1644
         if (EA_RelMatrix.toFromTableTitle != null && EA_RelMatrix.toFromTableTitle.Length > 0)
1645
            needToFromTable = true;
1646
 
1647
         if (needFromToTable)
2088 ghuddy 1648
         {
2090 ghuddy 1649
            if (EA_RelMatrix.fromIntroText != null && EA_RelMatrix.fromIntroText.Length > 0)
2088 ghuddy 1650
            {
2092 ghuddy 1651
               appendAndSelectText( EA_RelMatrix.fromIntroText, styleName_Body1 );
2088 ghuddy 1652
            }
1653
 
2090 ghuddy 1654
            // create the from-to table in the word doc
2088 ghuddy 1655
            tableNum = createTable( EA_RelMatrix.fromToTableTitle, fromLister.Elements.Count + 1, 2 );
1656
            table = WordDocument.Tables[tableNum];
1657
 
1658
            table.Cell(1,1).Range.Text = EA_RelMatrix.fromColumnTitle;
1659
            table.Cell(1,1).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
1660
            table.Cell(1,1).Range.Font.Bold = 1;
1661
            table.Cell(1,2).Range.Text = EA_RelMatrix.toColumnTitle;
1662
            table.Cell(1,2).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
1663
            table.Cell(1,2).Range.Font.Bold = 1;
1664
         }
1665
 
2090 ghuddy 1666
         // Add from-to content to the from-to table. 
1667
         // NOTE: this code has to execute even if no from-to table is needed, in order to support the
1668
         // generation of a to-from table, assuming the user has requested one.
2088 ghuddy 1669
         int row = 2;
1670
         foreach(EA.Element fromElement in fromLister.Elements)
1671
         {
2090 ghuddy 1672
            // Output the left hand column text to the from-to table, if one has been requested
1673
            if (needFromToTable)
2088 ghuddy 1674
               table.Cell(row,1).Range.Text = fromElement.Name;
1675
 
1676
            // look at the elements connection collection to find references to the
1677
            // destination elements
1678
            EA.Collection conCollection = fromElement.Connectors;
1679
            bool firstCon = true;
1680
            foreach (EA.Connector thisCon in conCollection)
1681
            {
1682
               EA.Element destE = EA_Repository.GetElementByID( thisCon.SupplierID );
1683
               if (destE == null)
1684
                  continue;
1685
 
2090 ghuddy 1686
               // ignore self-referential connections
1687
               if (fromElement.ElementID == thisCon.SupplierID)
1688
                  continue;
1689
 
2088 ghuddy 1690
               // if the destination element is of a type that the user has requested to include...
1691
               if (!EA_RelMatrix.toElementTypes.Contains( destE.Type ))
1692
                  continue;
1693
 
2090 ghuddy 1694
               // Output the right hand column text to the from-to table, if one has been requested
1695
               if (needFromToTable)
2088 ghuddy 1696
               {
2090 ghuddy 1697
                  if (firstCon)
1698
                  {
1699
                     firstCon = false;
2088 ghuddy 1700
                     table.Cell(row,2).Range.Text = destE.Name;
2090 ghuddy 1701
                  }
1702
                  else
1703
                  {
2088 ghuddy 1704
                     table.Cell(row,2).Range.Text += destE.Name;
2090 ghuddy 1705
                  }
2088 ghuddy 1706
               }
1707
 
1708
               // Capture the from-to relationship in a dictionary where the key is the
1709
               // ID of the destination element, and the value is the from element name.
1710
               // This dictionary will enable rapid construction of the to-from table if
1711
               // the user has requested it, from the exact same relationship info used
2090 ghuddy 1712
               // to construct the from-to table.
1713
               if (needToFromTable)
2088 ghuddy 1714
               {
1715
                  DictionaryEntry newEntry = new DictionaryEntry(destE.ElementID, fromElement.Name);
1716
                  fromToDictionary.Add( newEntry );
1717
               }
1718
            }
1719
            row++;
1720
         }
1721
 
1722
         // Does user want a to-from table ?
2090 ghuddy 1723
         if (needToFromTable)
2088 ghuddy 1724
         {
1725
            ElementAccumulator toLister = new ElementAccumulator(EA_RelMatrix.toElementTypes);
1726
            EA_Utils.findAndProcessPackageElements( EA_RelMatrix.toPackage, toLister, EA_RelMatrix.toPackageRecursion );
1727
 
1728
            // Sort the "to" elements
1729
            toLister.Elements.Sort( sorter );
1730
 
2090 ghuddy 1731
            if (EA_RelMatrix.toIntroText != null && EA_RelMatrix.toIntroText.Length > 0)
2088 ghuddy 1732
            {
2092 ghuddy 1733
               appendAndSelectText( EA_RelMatrix.toIntroText, styleName_Body1 );
2088 ghuddy 1734
            }
1735
 
1736
            // create the table in the word doc
1737
            tableNum = createTable( EA_RelMatrix.toFromTableTitle, toLister.Elements.Count + 1, 2 );
1738
            table = WordDocument.Tables[tableNum];
1739
 
1740
            table.Cell(1,1).Range.Text = EA_RelMatrix.toColumnTitle;
1741
            table.Cell(1,1).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
1742
            table.Cell(1,1).Range.Font.Bold = 1;
1743
            table.Cell(1,2).Range.Text = EA_RelMatrix.fromColumnTitle;
1744
            table.Cell(1,2).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
1745
            table.Cell(1,2).Range.Font.Bold = 1;
1746
 
1747
            // To make the to-from table, we use the dictionary that was built when making the from-to
1748
            // table. The dictionary will allow rapid determination of what "from" items belong to each
1749
            // "to" item, without us having to go back to EA and enquire on the database.
1750
            row = 2;
2090 ghuddy 1751
            foreach(EA.Element toElement in toLister.Elements)
2088 ghuddy 1752
            {
2090 ghuddy 1753
               // left-column cell content
1754
               table.Cell(row,1).Range.Text = toElement.Name;
2088 ghuddy 1755
 
2090 ghuddy 1756
               // right-column cell content
2088 ghuddy 1757
               bool firstCon = true;
1758
               foreach (DictionaryEntry de in fromToDictionary)
1759
               {
2090 ghuddy 1760
                  if ((int)de.Key == toElement.ElementID)
2088 ghuddy 1761
                  {
1762
                     if (firstCon)
1763
                     {
1764
                        firstCon = false;
1765
                        table.Cell(row,2).Range.Text = (string)de.Value;
1766
                     }
1767
                     else
1768
                     {
1769
                        table.Cell(row,2).Range.Text += (string)de.Value;
1770
                     }
1771
                  }
1772
               }
1773
               row++;
1774
            }
1775
         }
1776
      }
1777
 
1778
 
1779
 
1780
      #endregion
1781
   }
1782
}