eda: Fixes for cross-bit compiling; ecere: Took out non-sense code no longer needed
[sdk] / eda / libeda / src / ers.ec
1 import "idList"
2
3 define pgs = 10; // print pgs size
4 define margin = 36;
5
6 public enum Orientation { portrait, landscape };
7
8 public enum RenderAction { addPage, closePage, levelStart, levelFinish, groupStart, groupFinish, actualRows };
9
10 static class PleaseWait : Window
11 {
12    isModal = true;
13    autoCreate = false;
14    borderStyle = fixed;
15    text = $"Please wait while the report is being generated...";
16    clientSize = { 400, 30 };
17    ProgressBar progress { this, anchor = { 0,0,0,0 } };
18 }
19
20 static PleaseWait pleaseWait { };
21
22 public class ReportTitle : Window
23 {
24    size = { 500, 32 };
25    borderStyle = contour;
26    background = white;
27
28    font = { $"Arial", 14, bold = true };
29
30    Label { this, foreground = black, anchor = { top = 4 }, labeledWindow = this };
31 }
32
33 class PreviewPage : Window
34 {
35    background = dimGray;
36    //size = { 850 + shadowS + pgs * 2, 1100 + shadowS + pgs * 2 };
37
38    public property Orientation orientation
39    {
40       set
41       {
42          if(value == portrait)
43             size = { 850 + shadowS + pgs * 2, 1100 + shadowS + pgs * 2 };
44          else if(value == landscape)
45             size = { 1100 + shadowS + pgs * 2, 850 + shadowS + pgs * 2 };
46          orientation = value;
47       }
48    }
49    Orientation orientation;
50
51    Page page;
52
53    void OnRedraw(Surface surface)
54    {
55       int x = clientSize.w - pgs - 1, y = clientSize.h - pgs - 1;
56
57       surface.SetBackground(black);
58
59       surface.Rectangle(pgs, pgs, x - shadowS, y - shadowS);
60       surface.Area(pgs + shadowS / 2, y - shadowS + 1, x, y);
61       surface.Area(x - shadowS + 1, pgs + shadowS / 2, x, y);
62
63       surface.SetBackground(white);
64
65       surface.Area(pgs + 1, pgs + 1, x - shadowS - 1, y - shadowS - 1);
66    }
67 }
68
69 public class Page : Window
70 {
71    background = white;
72
73 public:
74    property Orientation orientation
75    {
76       set
77       {
78          if(value == portrait)
79          {
80             size = { 850, 1100 };
81             inside.size = { 850, 1100 };
82          }
83          else if(value == landscape)
84          {
85             size = { 1100, 850 };
86             inside.size = { 1100, 850 };
87          }
88          orientation = value;
89       }
90    }
91
92    Window inside { this };
93
94    int headerHeight;
95    
96 private:
97    Orientation orientation;
98 }
99
100 public class ReportRender
101 {
102 public:
103    virtual void Render(ReportDestination destination, Report report);
104    virtual int GetPageNumber();
105 }
106
107 static ReportRenderNormal ersCurrentReport;
108 static int ersNumRows;
109
110 public void ERSProgressAdvanceLevelCheck()
111 {
112    if(!ersCurrentReport.level)
113    {
114       ersNumRows++;
115       ersNumRows = Min(ersNumRows, pleaseWait.progress.range);
116       pleaseWait.progress.progress = ersNumRows;
117       ((GuiApplication)__thisModule.application).ProcessInput(true);
118       pleaseWait.UpdateDisplay();
119    }
120 }
121
122 public void ERSProgressAdvance()
123 {
124    if(!ersNumRows) ersNumRows++;
125    ersNumRows++;
126    ersNumRows = Min(ersNumRows, pleaseWait.progress.range);
127    pleaseWait.progress.progress = ersNumRows;
128    if(ersNumRows == pleaseWait.progress.range || !(ersNumRows%100))
129    {
130       ((GuiApplication)__thisModule.application).ProcessInput(true);
131       pleaseWait.UpdateDisplay();
132    }
133 }
134
135 public class ReportRenderNormal : ReportRender
136 {
137 public:
138    int pageNumber;
139
140    void Render(ReportDestination destination, Report report)
141    {
142       bool dontAdvance = false;
143       bool nil;
144       level = 0;
145       overlap = 0;
146       renderAction = levelStart;
147       ersCurrentReport = this;
148       ersNumRows = 0;
149       if(!report.groupings[0].filtered)
150       {
151          pleaseWait.master = destination.master;
152          pleaseWait.Create();
153
154          pleaseWait.progress.range = report.groupings[0].rowsCount ? report.groupings[0].rowsCount : report.groupings[0].row.rowsCount;
155          pleaseWait.progress.progress = 0;
156          ((GuiApplication)__thisModule.application).ProcessInput(true);
157          pleaseWait.UpdateDisplay();
158       }
159       for(pageNumber = 1; true; pageNumber++)
160       {
161          Detail lastDetail = null;
162          page = Page { orientation = report.orientation };
163          destination.AddPage(page);
164          inside = page.inside;
165          inside.anchor = report.insideMarginAnchor;
166          insideSize = inside.size.h;
167
168          pageTop = 0;
169          if(pageNumber == 1)
170          {
171             if(report.reportHeader)
172             {
173                reportHeader = eInstance_New(report.reportHeader);
174                reportHeader.anchor = Anchor { left = 0, top = 0, right = 0 };
175                reportHeader.master = destination;
176                reportHeader.parent = inside;
177                
178                pageTop += reportHeader.size.h;
179                reportHeader.Create();
180             
181             }
182             /*if(report.reportFooter)
183             {
184                reportFooter = eInstance_New(report.reportFooter);
185                reportFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
186                reportFooter.master = destination;
187                reportFooter.parent = inside;
188                reportFooter.Create();
189             }*/
190          }
191
192          if(report.pageHeader)
193          {
194             pageHeader = eInstance_New(report.pageHeader);
195             pageHeader.anchor = Anchor { left = 0, top = pageTop, right = 0 };
196             pageHeader.master = destination;
197             pageHeader.parent = inside;
198             
199             pageTop += pageHeader.size.h;
200
201             pageHeader.Create();
202          }
203          
204          if(report.pageFooter)
205          {
206             pageFooter = eInstance_New(report.pageFooter);
207             pageFooter.master = destination;
208             pageFooter.parent = inside;
209             footerHeight = pageFooter.size.h;
210          }
211          else
212             footerHeight = 0;
213
214          if(report.rowDetail)
215          {
216             bool loop;
217             //int levelHead = 0;
218             //int levelFoot = 0;
219             pageTop += overlap;
220
221             if((renderAction != levelStart && renderAction != groupStart)|| level > 0)
222             {
223                int c;
224                for(c = 0; c < ((renderAction == groupStart) ? level : (level + 1)); c++)
225                {
226                   if(report.groupings[c].continuation)
227                   {
228                      Detail continuation = eInstance_New(report.groupings[c].continuation);
229                      continuation.level = c;
230                      AddDetailToPage(destination, continuation);
231                   }
232                }
233             }
234
235             loop = true;
236             for( ; loop; )
237             {
238                switch(renderAction)
239                {
240                   case levelStart:
241                      if(level == report.groupings.size - 1)
242                         renderAction = actualRows;
243                      else
244                         renderAction = groupStart;
245                      break;
246                   case levelFinish:
247                      if(level == 0)
248                      {
249                         // end of rows, end of last group, end of report
250                         // TESTING THIS HERE... (UNCOMMENTED AND ADDED CHECK FOR size == 1)
251                         if(report.groupings.size == 1 && report.groupings[level].footer)
252                         {
253                            Detail groupFooter = eInstance_New(report.groupings[level].footer);
254                            groupFooter.level = level;
255                            if(AddDetailToPage(destination, groupFooter))
256                            {
257                               //dontAdvance = true;
258                               loop = false;
259                               break;
260                            }
261                         }
262                         loop = false;
263                         break;
264                      }
265                      else
266                      {
267                         level--;
268                         renderAction = groupFinish;
269                      }
270                      break;
271                   case groupStart:
272                      lastDetail = null;
273                      if(report.Advance(report.groupings[level], level ? report.groupings[level - 1].groupId : 0, &dontAdvance))
274                      {
275                         report.ExecuteRowData(level);
276                         if(report.groupings[level].header)
277                         {
278                            Detail groupStart = eInstance_New(report.groupings[level].header);
279                            groupStart.level = level;
280
281                            if(AddDetailToPage(destination, groupStart))
282                            {
283                               dontAdvance = true;
284                               loop = false;
285                               break;
286                            }
287                            else if(overlap - groupStart.size.h /** 2*/ < 0)
288                            {
289                               overlap = 0;
290                               groupStart.Destroy(0);
291                               dontAdvance = true;
292                               loop = false;
293                               break;
294                            }
295                         }
296                         level++;
297                         renderAction = levelStart;
298                      }
299                      else
300                      {
301                         renderAction = levelFinish;
302                      }
303                      break;
304                   case groupFinish:
305                      if(lastDetail)
306                         lastDetail.isLast = true;
307                      if(report.groupings[level].footer)
308                      {
309                         Detail groupEnd = eInstance_New(report.groupings[level].footer);
310                         groupEnd.level = level;
311                         if(AddDetailToPage(destination, groupEnd))
312                         {
313                            //dontAdvance = true;
314                            loop = false;
315                            break;
316                         }
317                         else if(overlap - groupEnd.size.h < 0)
318                         {
319                            overlap = 0;
320                            groupEnd.Destroy(0);
321                            loop = false;
322                            break;
323                         }
324                      }
325                      renderAction = groupStart;
326                      break;
327                   case actualRows:
328                      if(report.Advance(report.groupings[level], level ? report.groupings[level - 1].groupId : 0, &dontAdvance))
329                      {
330                         Detail detail;
331                         if(AddDetailToPage(destination, (detail = eInstance_New(report.rowDetail))))
332                         {
333                            dontAdvance = true;
334                            loop = false;
335                            break;
336                         }
337                         else
338                         {
339                            report.ExecuteRowData(level);
340                            lastDetail = detail;
341                         }
342                      }
343                      else
344                      {
345                         renderAction = levelFinish;
346                      }
347                      break;
348
349                }
350             }
351             nil = report.nil;
352          }
353          else
354          {
355             nil = true;
356          }
357
358          // Cancel group headers if we didn't have space to display any row
359          if(!lastDetail)
360          {
361             Detail detail, prev;
362             for(detail = (Detail)inside.lastChild; detail; detail = prev)
363             {
364                prev = (Detail)detail.previous;
365                if(level > 0 && detail._class == report.groupings[level-1].header)
366                {
367                   detail.Destroy(0);
368                   level--;
369                   renderAction = groupStart;
370                }
371                else
372                {
373                   if(detail._class == report.groupings[level].footer);
374                   else
375                      lastDetail = detail;
376                   break;
377                }
378             }
379          }
380          if(lastDetail)
381             lastDetail.isLast = true;
382
383          if(nil && report.reportFooter)
384          {
385             reportFooter = eInstance_New(report.reportFooter);
386             reportFooter.master = destination;
387             reportFooter.parent = inside;
388             inside.anchor = { left = inside.anchor.left, top = inside.anchor.top };
389             inside.size = { inside.size.w, inside.size.h + reportFooter.size.h };
390             reportFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
391          }
392
393          if(report.pageFooter)
394          {
395             if(nil && report.reportFooter)
396                pageFooter.anchor = Anchor { left = 0, bottom = (int)reportFooter.size.h, right = 0 };
397             else
398                pageFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
399             
400             pageFooter.Create();
401          }
402          if(nil && report.reportFooter)
403          {
404             reportFooter.Create();
405          }
406          
407          destination.EndPage(page);
408
409          if(nil)
410             break;
411          
412          // still have to bump report footer if it does not fit...
413       }
414       pleaseWait.Destroy(0);
415    }
416
417    int GetPageNumber()
418    {
419       return pageNumber;
420    }
421
422    int level;
423
424 private:
425    int overlap, pageTop, insideSize, footerHeight;
426    RenderAction renderAction;
427
428    Page page;
429    Window inside;
430
431    Detail reportHeader;
432    Detail reportFooter;
433    Detail pageHeader;
434    Detail pageFooter;
435
436    // This function returns true if it there's no room and we should display on next page
437    bool AddDetailToPage(ReportDestination destination, Detail detail)
438    {
439       int detailSize;
440
441       detail.anchor = Anchor { left = 0, right = 0 };
442       detail.master = destination;
443       detail.parent = inside;
444
445       detailSize = detail.size.h;
446       overlap = (insideSize - pageTop - footerHeight) - detailSize;
447       
448       if(overlap < 0 && detail.keepTogether)
449       {
450          delete detail;
451          overlap = 0;
452          return true;
453       }
454       else
455       {
456          detail.anchor = Anchor { left = 0, top = pageTop, right = 0 };
457          pageTop += detailSize;
458
459          detail.Create();
460          // This will probably never go here... (Only report/page headers have keepTogether set to false)
461          /*if(overlap < 0)
462          {
463             printf("bug");
464          }*/
465
466       }
467       return false;
468    }
469 }
470
471 public class ReportDestination : Window
472 {
473 public:
474    Report report;
475
476    virtual void EndPage(Page page)
477    {
478       SetScrollArea(page.master.size.w, page.master.position.y + page.master.size.h, false);
479       SetScrollPosition((page.master.size.w - clientSize.w) / 2, 0);
480    }
481
482    virtual void AddPage(Page page);
483    virtual Report GetReport() { return null; }
484 private:
485    int pageCount;
486
487    List<PreviewPage> pages { };
488 }
489
490 public class PrintedReport : ReportDestination
491 {
492    displayDriver = "Win32Printer";
493    fullRender = true;
494
495    Page lastPage;
496
497    bool OnCreate()
498    {
499       if(report)
500          SetPrintingDocumentName(report.title);
501       return ReportDestination::OnCreate();
502    }
503
504    void AddPage(Page page)
505    {
506       if(pageCount && display)
507          display.NextPage();
508       lastPage = page;
509       page.anchor = { left = 0, top = 0, right = 0, bottom = 0};
510       page.master = this;
511       page.parent = this;
512       pageCount++;
513       page.Create();
514    }
515
516    void EndPage(Page page)
517    {
518       Update(null);
519       ((GuiApplication)__thisModule.application).ProcessInput(true);
520       ((GuiApplication)__thisModule.application).UpdateDisplay();
521       lastPage.Destroy(0);
522    }
523
524    Report GetReport()
525    {
526       return report;
527    }
528 }
529
530 public class ReportPreviewArea : ReportDestination
531 {
532    hasHorzScroll = true;
533    hasVertScroll = true;
534    dontHideScroll = true;
535    background = dimGray;
536
537    void AddPage(Page page)
538    {
539       PreviewPage previewPage { this, this, page = page, orientation = page.orientation, 
540                                    anchor = { top = pageCount * ((int)page.size.h + shadowS + pgs) } };
541       previewPage.Create();
542       page.anchor = { left = pgs, top = pgs, right = shadowS + pgs, bottom = shadowS + pgs};
543       page.master = previewPage;
544       page.parent = previewPage;
545       page.Create();
546       pageCount++;
547    }
548    
549    Report GetReport()
550    {
551       return report;
552    }
553
554    void OnResize(int width, int height)
555    {
556       scroll = { (scrollArea.w - width) / 2, scroll.y };
557    }
558 }
559
560 Array<FileFilter> csvFilters
561 { [
562    { 
563       $"Comma Separated Values Spreadsheet (*.csv)",
564       "csv"
565    },
566    { $"All files", null }
567 ] };
568
569 public class CSVReport : ReportDestination
570 {
571    hasHorzScroll = true;
572    hasVertScroll = true;
573    dontHideScroll = true;
574    background = dimGray;
575    
576    Page lastPage;
577
578    void AddPage(Page page)
579    {
580       int h;
581       if(pageCount && display)
582          display.NextPage();
583       lastPage = page;
584       page.master = this;
585       page.parent = this;
586       page.size = { MAXINT - 10, MAXINT - 10 };
587       h = page.size.h;
588       pageCount++;
589       page.Create();
590    }
591    
592    void PutString(File f, char * text)
593    {
594       char output[4096];
595       int s = 0, d = 0;
596       byte ch;
597
598       output[d++] = '"';
599       while((ch = text[s++]) && d < sizeof(output) - 1)
600       {
601          if(ch > 127)
602          {
603             int nb;
604             int keyCode = UTF8GetChar(text + s - 1, &nb);
605             s += nb - 1;
606             if(keyCode < 256)
607                ch = (byte)keyCode;
608             else
609                ch = '?';
610          }
611          if(ch == '\"') output[d++] = '\\';
612          output[d++] = ch;
613       }
614       output[d++] = '"';
615       output[d] = 0;
616       f.Puts(output);
617    }
618
619    FileDialog saveTo { type = save, text = $"Export as Spreadsheet (CSV)", filters = csvFilters.array, sizeFilters = csvFilters.count * sizeof(FileFilter) };
620
621    void EndPage(Page page)
622    {
623       char filePath[MAX_LOCATION];
624       strcpy(filePath, report.title);
625       strcat(filePath, ".csv");
626       saveTo.master = master;
627       saveTo.filePath = filePath;
628       if(saveTo.Modal())
629       {
630          File f = FileOpen(saveTo.filePath, write);
631          if(f)
632          {
633             Detail detail, first = null;         
634             for(detail = (Detail)page.inside.firstChild; detail && detail != first; detail = (Detail)detail.next)
635             {
636                if(!first) first = detail;
637                if(eClass_IsDerived(detail._class, class(Detail)))
638                {
639                   Label label, first = null;
640       
641                   if(detail._class == report.pageFooter) continue;
642                   if(detail._class == report.groupings[0].header)
643                      f.Puts("\n");
644                   for(label = (Label)detail.firstChild; label && label != first; label = (Label)label.next)
645                   {
646                      if(!first) first = label;
647                      if(label._class == class(ReportTitle) || eClass_IsDerived(label._class, class(Label)))
648                      {
649                         char * text = label.text;
650                         if(label != first)f.Puts(",");
651                         if(text)
652                            PutString(f, text);
653                      }
654                   }
655                   if(detail._class == report.groupings[0].header)
656                      f.Puts("\n");
657                   f.Puts("\n");
658                }
659             }
660          }
661          delete f;
662       }
663       lastPage.Destroy(0);
664    }
665
666    Report GetReport()
667    {
668       return report;
669    }
670 }
671
672 public class IdFilter : struct
673 {
674 public:
675    Id id;
676    Field field;
677    
678    bool RowMatch(Row row)
679    {
680       Id value;
681       if(!field)
682          return false;
683       if(!row.GetData(field, value))
684          return false;
685       return value == id;
686    }
687 }
688
689 public class Grouping
690 {
691 public:
692    Id groupId;
693    Row row;
694    Field field, fieldLink;
695    bool filtered;
696    Array<IdFilter> filters { };
697
698    // Contractors have a list of trades they're in
699    Field listFieldLink;
700
701    // Portfolios have the list of contacts for them, Portfolio id goes in fieldLink
702    Field reverseListFieldLink;
703    Grouping reverseLink;
704
705    bool activeOnly;
706    Field activeField;
707    uint rowsCount;
708
709    subclass(Detail) header;
710    subclass(Detail) continuation;
711    subclass(Detail) footer;
712
713    ~Grouping()
714    {
715       filters.Free();
716       delete row;
717    }
718    
719    virtual bool ShouldSkip()
720    {
721       return false;
722    }
723
724    virtual bool Advance(Id linkId, bool *dontAdvance)
725    {
726       bool result;
727       IdList reverseIdList = null;
728
729       if(dontAdvance && *dontAdvance)
730       {
731          *dontAdvance = false;
732          return !row.nil;
733       }
734       if(!reverseLink && fieldLink && row.nil)
735          result = row.Find(fieldLink, middle, nil, linkId);
736       else
737          result = row.Next();
738       if(!result)
739          return false;
740       
741       if(reverseLink)
742          reverseLink.row.GetData(reverseListFieldLink, reverseIdList);
743
744       if(activeOnly && !activeField)
745          activeField = row.tbl.FindField("Active");
746
747       while(result)
748       {
749          if(!ersCurrentReport.level)
750          {
751             ersNumRows++;
752             ersNumRows = Min(ersNumRows, pleaseWait.progress.range);
753             pleaseWait.progress.progress = ersNumRows;
754             ((GuiApplication)__thisModule.application).ProcessInput(true);
755             pleaseWait.UpdateDisplay();
756          }
757
758          if(reverseLink)
759          {
760             Id id = 0;
761             row.GetData(fieldLink, id);
762             if(!reverseIdList || !reverseIdList.Includes(id))
763                result = false;
764          }
765          else if(listFieldLink)
766          {
767             IdList list = null;
768             row.GetData(listFieldLink, list);
769             if(!list || !list.Includes(linkId))
770                result = false;
771             delete list;
772          }
773          else if(fieldLink)
774          {
775             Id id = 0;
776             row.GetData(fieldLink, id);
777             if(id != linkId)
778             {
779                row.Last();
780                row.Next();
781                return false;
782             }
783          }
784          if(activeOnly)
785          {
786             bool active = true;
787             row.GetData(activeField, active);
788             if(!active)
789                result = false;
790          }
791          if(result && filtered && filters.size)
792          {
793             int f;
794             for(f = 0; f < filters.size && result && filters[f].field; f++)
795             {
796                Id id = 0;
797                row.GetData(filters[f].field, id);
798                if(id != filters[f].id)
799                {
800                   result = false;
801                   break;
802                }
803             }
804          }
805          if(result && ShouldSkip())
806             result = false;
807
808          if(result)
809          {
810             if(field)
811             {
812                groupId = 0;
813                row.GetData(field, groupId);
814             }
815             delete reverseIdList;
816             return true;
817          }
818          result = row.Next();
819       }
820       delete reverseIdList;
821       return result;
822    }
823 }
824
825 public class Report
826 {
827 public:
828    Orientation orientation;
829    Anchor insideMarginAnchor;
830
831    Array<Grouping> groupings { };
832
833    property String title
834    {
835       set
836       {
837          delete title;
838          if(value)
839             title = CopyString(value);
840       }
841    }
842    String title;
843
844    ReportRender render;
845
846    subclass(Detail) reportHeader;
847    subclass(Detail) reportFooter;
848
849    subclass(Detail) pageHeader;
850    subclass(Detail) pageFooter;
851
852    subclass(Detail) rowDetail;
853
854    virtual bool Advance(Grouping grouping, Id linkId, bool *dontAdvance)
855    {
856       return grouping.Advance(linkId, dontAdvance);
857    }
858    
859    virtual bool ExecuteData(Database db)
860    {
861       return false;
862    }
863
864    virtual void ExecuteRowData(int group);
865    virtual void OnReset();
866
867    property bool nil
868    {
869       get
870       {
871          if(groupings && groupings.size && groupings[0].row)
872             return groupings[0].row.nil;
873          return true;
874       }
875    }
876
877    ~Report()
878    {
879       groupings.Free();
880       delete title;
881       delete render;
882    }
883 }
884
885 public class Detail : Window
886 {
887 public:
888    bool keepTogether;
889    bool isLast;
890    int level;
891
892    subclass(Detail) rowDetail;
893 }