3 define pgs = 10; // print pgs size
6 public enum Orientation { portrait, landscape };
8 public enum RenderAction { addPage, closePage, levelStart, levelFinish, groupStart, groupFinish, actualRows };
10 static class PleaseWait : Window
15 text = $"Please wait while the report is being generated...";
16 clientSize = { 400, 30 };
17 ProgressBar progress { this, anchor = { 0,0,0,0 } };
20 static PleaseWait pleaseWait { };
22 public class ReportTitle : Window
25 borderStyle = contour;
28 font = { $"Arial", 14, bold = true };
30 Label { this, foreground = black, anchor = { top = 4 }, labeledWindow = this };
33 class PreviewPage : Window
36 //size = { 850 + shadowS + pgs * 2, 1100 + shadowS + pgs * 2 };
38 public property Orientation orientation
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 };
49 Orientation orientation;
53 void OnRedraw(Surface surface)
55 int x = clientSize.w - pgs - 1, y = clientSize.h - pgs - 1;
57 surface.SetBackground(black);
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);
63 surface.SetBackground(white);
65 surface.Area(pgs + 1, pgs + 1, x - shadowS - 1, y - shadowS - 1);
69 public class Page : Window
72 //size = { 612, 792 };
74 public property Orientation orientation
81 inside.size = { 850, 1100 };
83 else if(value == landscape)
86 inside.size = { 1100, 850 };
91 Orientation orientation;
93 public PageInside inside { this }; // , size = { 612, 792 }
95 public int headerHeight;
99 public class PageInside : Window
101 public OldList details;
109 public class ReportRender
111 public virtual void Render(ReportDestination destination, Report report);
112 public virtual int GetPageNumber();
115 static ReportRenderNormal ersCurrentReport;
116 static int ersNumRows;
118 public void ERSProgressAdvanceLevelCheck()
120 if(!ersCurrentReport.level)
123 ersNumRows = Min(ersNumRows, pleaseWait.progress.range);
124 pleaseWait.progress.progress = ersNumRows;
125 ((GuiApplication)__thisModule.application).ProcessInput(true);
126 pleaseWait.UpdateDisplay();
130 public class ReportRenderNormal : ReportRender
142 void Render(ReportDestination destination, Report report)
144 Detail lastDetail = null;
145 bool dontAdvance = false;
149 renderAction = levelStart;
150 ersCurrentReport = this;
152 if(!report.groupings._[0].filtered)
154 pleaseWait.master = destination.master;
156 pleaseWait.progress.range = report.groupings._[0].row.tbl.rowsCount;
157 pleaseWait.progress.progress = 0;
158 ((GuiApplication)__thisModule.application).ProcessInput(true);
159 pleaseWait.UpdateDisplay();
161 for(pageNumber = 1; true; pageNumber++)
163 page = Page { orientation = report.orientation };
164 destination.AddPage(page);
165 inside = page.inside;
166 inside.anchor = report.insideMarginAnchor;
167 insideSize = inside.size.h;
172 if(report.reportHeader)
174 reportHeader = eInstance_New(report.reportHeader);
175 reportHeader.master = destination;
176 reportHeader.parent = inside;
177 reportHeader.anchor = Anchor { left = 0, top = 0, right = 0 };
179 pageTop += reportHeader.size.h;
180 //inside.details.Add(OldLink { data = reportHeader });
181 reportHeader.Create();
184 /*if(report.reportFooter)
186 reportFooter = eInstance_New(report.reportFooter);
187 reportFooter.master = destination;
188 reportFooter.parent = inside;
189 reportFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
190 reportFooter.Create();
194 if(report.pageHeader)
196 pageHeader = eInstance_New(report.pageHeader);
197 pageHeader.master = destination;
198 pageHeader.parent = inside;
199 pageHeader.anchor = Anchor { left = 0, top = pageTop, right = 0 };
201 pageTop += pageHeader.size.h;
202 //inside.details.Add(OldLink { data = pageHeader });
207 if(report.pageFooter)
209 pageFooter = eInstance_New(report.pageFooter);
210 pageFooter.master = destination;
211 pageFooter.parent = inside;
212 footerHeight = pageFooter.size.h;
224 if((renderAction != levelStart && renderAction != groupStart)|| level > 0)
227 for(c = 0; c < ((renderAction == groupStart) ? level : (level + 1)); c++)
229 if(report.groupings._[c].continuation)
232 lastDetail.isLast = true;
233 AddDetailToPage(destination, eInstance_New(report.groupings._[c].continuation));
244 if(level == report.groupings.size - 1)
245 renderAction = actualRows;
247 renderAction = groupStart;
252 // end of rows, end of last group, end of report
253 // TESTING THIS HERE... (UNCOMMENTED AND ADDED CHECK FOR size == 1)
254 if(report.groupings.size == 1 && report.groupings._[level].footer)
256 if(AddDetailToPage(destination, eInstance_New(report.groupings._[level].footer)))
258 //dontAdvance = true;
269 renderAction = groupFinish;
273 if(report.Advance(report.groupings._[level], level ? report.groupings._[level - 1].groupId : 0, &dontAdvance))
275 report.ExecuteRowData(level);
276 if(report.groupings._[level].header)
278 Detail groupStart = eInstance_New(report.groupings._[level].header);
279 if(AddDetailToPage(destination, groupStart))
285 else if(overlap - groupStart.size.h /** 2*/ < 0)
288 groupStart.Destroy(0);
295 renderAction = levelStart;
299 renderAction = levelFinish;
304 lastDetail.isLast = true;
305 if(report.groupings._[level].footer)
307 Detail groupEnd = eInstance_New(report.groupings._[level].footer);
308 if(AddDetailToPage(destination, groupEnd))
310 //dontAdvance = true;
314 else if(overlap - groupEnd.size.h < 0)
322 renderAction = groupStart;
325 if(report.Advance(report.groupings._[level], level ? report.groupings._[level - 1].groupId : 0, &dontAdvance))
328 if(AddDetailToPage(destination, (detail = eInstance_New(report.rowDetail))))
336 report.ExecuteRowData(level);
342 renderAction = levelFinish;
355 if(nil && report.reportFooter)
357 reportFooter = eInstance_New(report.reportFooter);
358 reportFooter.master = destination;
359 reportFooter.parent = inside;
360 inside.anchor = { left = inside.anchor.left, top = inside.anchor.top };
361 inside.size = { inside.size.w, inside.size.h + reportFooter.size.h };
362 reportFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
365 if(report.pageFooter)
367 if(nil && report.reportFooter)
368 pageFooter.anchor = Anchor { left = 0, bottom = (int)reportFooter.size.h, right = 0 };
370 pageFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
372 //inside.details.Add(OldLink { data = pageFooter });
375 if(nil && report.reportFooter)
377 //inside.details.Add(OldLink { data = reportFooter });
378 reportFooter.Create();
381 pages.Add(OldLink { data = page });
383 destination.EndPage(page);
388 // still have to bump report footer if it does not fit...
390 pleaseWait.Destroy(0);
401 int overlap, pageTop, insideSize, footerHeight;
402 RenderAction renderAction;
412 // This function returns true if it there's no room and we should display on next page
413 bool AddDetailToPage(ReportDestination destination, Detail detail)
417 detail.master = destination;
418 detail.parent = inside;
420 detailSize = detail.size.h;
421 overlap = (insideSize - pageTop - footerHeight) - detailSize;
423 if(overlap < 0 && detail.keepTogether)
431 detail.anchor = Anchor { left = 0, top = pageTop, right = 0 };
432 pageTop += detailSize;
433 //inside.details.Add(OldLink { data = detail });
436 // This will probably never go here... (Only report/page headers have keepTogether set to false)
447 public class ReportDestination : Window
456 virtual void EndPage(Page page)
458 SetScrollArea(page.master.size.w, page.master.position.y + page.master.size.h, false);
459 SetScrollPosition((page.master.size.w - clientSize.w) / 2, 0);
462 virtual void AddPage(Page page)
466 virtual Report GetReport()
478 public class PrintedReport : ReportDestination
480 displayDriver = "Win32Printer";
487 SetPrintingDocumentName(report.title);
488 return ReportDestination::OnCreate();
491 void AddPage(Page page)
493 if(pageCount && display)
498 page.anchor = { left = 0, top = 0, right = 0, bottom = 0};
503 void EndPage(Page page)
506 ((GuiApplication)__thisModule.application).ProcessInput(true);
507 ((GuiApplication)__thisModule.application).UpdateDisplay();
517 public class ReportPreviewArea : ReportDestination
519 hasHorzScroll = true;
520 hasVertScroll = true;
521 dontHideScroll = true;
522 background = dimGray;
524 void AddPage(Page page)
526 PreviewPage previewPage { this, this, page = page, orientation = page.orientation,
527 anchor = { top = pageCount * ((int)page.size.h + shadowS + pgs) } };
528 previewPage.Create();
529 page.master = previewPage;
530 page.parent = previewPage;
531 page.anchor = { left = pgs, top = pgs, right = shadowS + pgs, bottom = shadowS + pgs};
532 pages.Add(OldLink { data = previewPage });
542 void OnResize(int width, int height)
544 SetScrollPosition((scrollArea.w - width) / 2, scroll.y);
552 Array<FileFilter> csvFilters
555 $"Comma Separated Values Spreadsheet (*.csv)",
558 { $"All files", null }
561 public class CSVReport : ReportDestination
563 hasHorzScroll = true;
564 hasVertScroll = true;
565 dontHideScroll = true;
566 background = dimGray;
570 void AddPage(Page page)
573 if(pageCount && display)
578 page.size = { MAXINT - 10, MAXINT - 10 };
584 void PutString(File f, char * text)
591 while((ch = text[s++]) && d < sizeof(output) - 1)
596 int keyCode = UTF8GetChar(text + s - 1, &nb);
603 if(ch == '\"') output[d++] = '\\';
611 FileDialog saveTo { type = save, text = $"Export as Spreadsheet (CSV)", filters = csvFilters.array, sizeFilters = csvFilters.count * sizeof(FileFilter) };
613 void EndPage(Page page)
615 char filePath[MAX_LOCATION];
616 strcpy(filePath, report.title);
617 strcat(filePath, ".csv");
618 saveTo.master = master;
619 saveTo.filePath = filePath;
622 File f = FileOpen(saveTo.filePath, write);
625 Detail detail, first = null;
626 for(detail = (Detail)page.inside.firstChild; detail && detail != first; detail = (Detail)detail.next)
628 if(!first) first = detail;
629 if(eClass_IsDerived(detail._class, class(Detail)))
631 Label label, first = null;
633 if(detail._class == report.pageFooter) continue;
634 if(detail._class == report.groupings._[0].header)
636 for(label = (Label)detail.firstChild; label && label != first; label = (Label)label.next)
638 if(!first) first = label;
639 if(label._class == class(ReportTitle) || eClass_IsDerived(label._class, class(Label)))
641 char * text = label.text;
642 if(label != first)f.Puts(",");
647 if(detail._class == report.groupings._[0].header)
664 public class IdFilter : struct
670 bool RowMatch(Row row)
675 if(!row.GetData(field, value))
681 public class ArrayIdFilters : OldArray
683 type = class(IdFilter);
688 public class Grouping
693 Field field, fieldLink;
695 ArrayIdFilters filters { };
697 // Contractors have a list of trades they're in
700 // Portfolios have the list of contacts for them, Portfolio id goes in fieldLink
701 Field reverseListFieldLink;
702 Grouping reverseLink;
707 subclass(Detail) header;
708 subclass(Detail) continuation;
709 subclass(Detail) footer;
716 virtual bool ShouldSkip()
721 virtual bool Advance(Id linkId, bool *dontAdvance)
724 IdList reverseIdList = null;
726 if(dontAdvance && *dontAdvance)
728 *dontAdvance = false;
731 if(!reverseLink && fieldLink && row.nil)
732 result = row.Find(fieldLink, middle, nil, linkId);
739 reverseLink.row.GetData(reverseListFieldLink, reverseIdList);
741 if(activeOnly && !activeField)
742 activeField = row.tbl.FindField("Active");
746 if(!ersCurrentReport.level)
749 ersNumRows = Min(ersNumRows, pleaseWait.progress.range);
750 pleaseWait.progress.progress = ersNumRows;
751 ((GuiApplication)__thisModule.application).ProcessInput(true);
752 pleaseWait.UpdateDisplay();
758 row.GetData(fieldLink, id);
759 if(!reverseIdList || !reverseIdList.Includes(id))
762 else if(listFieldLink)
765 row.GetData(listFieldLink, list);
766 if(!list || !list.Includes(linkId))
773 row.GetData(fieldLink, id);
784 row.GetData(activeField, active);
788 if(result && filtered && filters.size)
791 for(f = 0; f < filters.size && result && filters._[f].field; f++)
794 row.GetData(filters._[f].field, id);
795 if(id != filters._[f].id)
802 if(result && ShouldSkip())
810 row.GetData(field, groupId);
812 delete reverseIdList;
817 delete reverseIdList;
822 public class ArrayGroupings : OldArray
824 type = class(Grouping);
832 Orientation orientation;
833 Anchor insideMarginAnchor;
835 ArrayGroupings groupings { };
837 property String title
843 title = CopyString(value);
850 subclass(Detail) reportHeader;
851 subclass(Detail) reportFooter;
853 subclass(Detail) pageHeader;
854 subclass(Detail) pageFooter;
856 subclass(Detail) rowDetail;
858 virtual bool Advance(Grouping grouping, Id linkId, bool *dontAdvance)
860 return grouping.Advance(linkId, dontAdvance);
863 virtual bool ExecuteData(Database db)
868 virtual void ExecuteRowData(int group)
876 if(groupings && groupings.size && groupings._[0].row)
877 return groupings._[0].row.nil;
886 for(c = 0; c<groupings.size; c++)
887 delete groupings._[c];*/
893 public class Detail : Window
899 subclass(Detail) rowDetail;