ecere: Added ChangeChars(); eda/ers: ; Avoiding bad filenames
[sdk] / eda / libeda / src / ers.ec
index 37cbcc3..878925f 100644 (file)
@@ -5,6 +5,8 @@ define margin = 36;
 
 public enum Orientation { portrait, landscape };
 
+public enum PageFormat { custom, letter, legal, ledger };
+
 public enum RenderAction { addPage, closePage, levelStart, levelFinish, groupStart, groupFinish, actualRows };
 
 static class PleaseWait : Window
@@ -12,7 +14,7 @@ static class PleaseWait : Window
    isModal = true;
    autoCreate = false;
    borderStyle = fixed;
-   text = "Please wait while the report is being generated...";
+   text = $"Please wait while the report is being generated...";
    clientSize = { 400, 30 };
    ProgressBar progress { this, anchor = { 0,0,0,0 } };
 }
@@ -25,29 +27,53 @@ public class ReportTitle : Window
    borderStyle = contour;
    background = white;
 
-   font = { "Arial", 14, bold = true };
+   font = { $"Arial", 14, bold = true };
 
    Label { this, foreground = black, anchor = { top = 4 }, labeledWindow = this };
 }
 
+static define dpi = 100;
 class PreviewPage : Window
 {
    background = dimGray;
-   //size = { 850 + shadowS + pgs * 2, 1100 + shadowS + pgs * 2 };
 
-   public property Orientation orientation
+   public property Page page
    {
       set
       {
-         if(value == portrait)
-            size = { 850 + shadowS + pgs * 2, 1100 + shadowS + pgs * 2 };
-         else if(value == landscape)
-            size = { 1100 + shadowS + pgs * 2, 850 + shadowS + pgs * 2 };
-         orientation = value;
+         page = value;
+         if(page && page.report)
+         {
+            switch(page.report.pageFormat)
+            {
+               case letter:
+                  if(page.report.orientation == landscape)
+                     size = { 11*dpi + shadowS + pgs * 2, 8.5*dpi + shadowS + pgs * 2 };
+                  else
+                     size = { 8.5*dpi + shadowS + pgs * 2, 11*dpi + shadowS + pgs * 2 };
+                  break;
+               case legal:
+                  if(page.report.orientation == landscape)
+                     size = { 14*dpi + shadowS + pgs * 2, 8.5*dpi + shadowS + pgs * 2 };
+                  else
+                     size = { 8.5*dpi + shadowS + pgs * 2, 14*dpi + shadowS + pgs * 2 };
+                  break;
+               case ledger:
+                  if(page.report.orientation == landscape)
+                     size = { 17*dpi + shadowS + pgs * 2, 11*dpi + shadowS + pgs * 2 };
+                  else
+                     size = { 11*dpi + shadowS + pgs * 2, 17*dpi + shadowS + pgs * 2 };
+                  break;
+               case custom:
+                  if(page.report.orientation == landscape && page.report.pageSize.w > page.report.pageSize.h)
+                     size = { page.report.pageSize.w + shadowS + pgs * 2, page.report.pageSize.h + shadowS + pgs * 2 };
+                  else
+                     size = { page.report.pageSize.h + shadowS + pgs * 2, page.report.pageSize.w + shadowS + pgs * 2 };
+                  break;
+            }
+         }
       }
    }
-   Orientation orientation;
-
    Page page;
 
    void OnRedraw(Surface surface)
@@ -69,47 +95,35 @@ class PreviewPage : Window
 public class Page : Window
 {
    background = white;
-   //size = { 612, 792 };
 
-   public property Orientation orientation
+public:
+   property Report report
    {
       set
       {
-         if(value == portrait)
-         {
-            size = { 850, 1100 };
-            inside.size = { 850, 1100 };
-         }
-         else if(value == landscape)
+         report = value;
+         if(report)
          {
-            size = { 1100, 850 };
-            inside.size = { 1100, 850 };
+            size = report.pageSize;
+            inside.size = report.pageSize;
          }
-         orientation = value;
       }
+      get { return report; }
    }
-   Orientation orientation;
 
-   public PageInside inside { this }; // , size = { 612, 792 }
+   Window inside { this };
 
-   public int headerHeight;
-   
-}
+   int headerHeight;
 
-public class PageInside : Window
-{
-   public OldList details;
-
-   ~PageInside()
-   {
-      details.Free(null);
-   }
+private:
+   Report report;
 }
 
 public class ReportRender
 {
-   public virtual void Render(ReportDestination destination, Report report);
-   public virtual int GetPageNumber();
+public:
+   virtual void Render(ReportDestination destination, Report report);
+   virtual int GetPageNumber();
 }
 
 static ReportRenderNormal ersCurrentReport;
@@ -127,15 +141,21 @@ public void ERSProgressAdvanceLevelCheck()
    }
 }
 
-public class ReportRenderNormal : ReportRender
+public void ERSProgressAdvance()
 {
-   OldList pages;
-
-   ~ReportRender()
+   if(!ersNumRows) ersNumRows++;
+   ersNumRows++;
+   ersNumRows = Min(ersNumRows, pleaseWait.progress.range);
+   pleaseWait.progress.progress = ersNumRows;
+   if(ersNumRows == pleaseWait.progress.range || !(ersNumRows%100))
    {
-      pages.Free(null);
+      ((GuiApplication)__thisModule.application).ProcessInput(true);
+      pleaseWait.UpdateDisplay();
    }
+}
 
+public class ReportRenderNormal : ReportRender
+{
 public:
    int pageNumber;
 
@@ -148,21 +168,24 @@ public:
       renderAction = levelStart;
       ersCurrentReport = this;
       ersNumRows = 0;
-      if(!report.groupings._[0].filtered)
+      if(!report.groupings[0].filtered)
       {
          pleaseWait.master = destination.master;
          pleaseWait.Create();
-         pleaseWait.progress.range = report.groupings._[0].row.tbl.rowsCount;
+
+         pleaseWait.progress.range = report.groupings[0].rowsCount ? report.groupings[0].rowsCount : report.groupings[0].row.rowsCount;
          pleaseWait.progress.progress = 0;
          ((GuiApplication)__thisModule.application).ProcessInput(true);
          pleaseWait.UpdateDisplay();
       }
-      for(pageNumber = 1; true; pageNumber++)
+      for(pageNumber = 1; ; pageNumber++)
       {
-         page = Page { orientation = report.orientation };
+         Detail lastDetail = null;
+         page = Page { report = report };
          destination.AddPage(page);
          inside = page.inside;
-         inside.anchor = report.insideMarginAnchor;
+         inside.anchor = Anchor { left = report.insideMarginAnchor.left.distance, top = report.insideMarginAnchor.top.distance,
+                                  right = report.insideMarginAnchor.right.distance, bottom = report.insideMarginAnchor.bottom.distance };
          insideSize = inside.size.h;
 
          pageTop = 0;
@@ -171,21 +194,20 @@ public:
             if(report.reportHeader)
             {
                reportHeader = eInstance_New(report.reportHeader);
+               reportHeader.anchor = Anchor { left = 0, top = 0, right = 0 };
                reportHeader.master = destination;
                reportHeader.parent = inside;
-               reportHeader.anchor = Anchor { left = 0, top = 0, right = 0 };
-               
+
                pageTop += reportHeader.size.h;
-               //inside.details.Add(OldLink { data = reportHeader });
                reportHeader.Create();
-            
+
             }
             /*if(report.reportFooter)
             {
                reportFooter = eInstance_New(report.reportFooter);
+               reportFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
                reportFooter.master = destination;
                reportFooter.parent = inside;
-               reportFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
                reportFooter.Create();
             }*/
          }
@@ -193,16 +215,15 @@ public:
          if(report.pageHeader)
          {
             pageHeader = eInstance_New(report.pageHeader);
+            pageHeader.anchor = Anchor { left = 0, top = pageTop, right = 0 };
             pageHeader.master = destination;
             pageHeader.parent = inside;
-            pageHeader.anchor = Anchor { left = 0, top = pageTop, right = 0 };
-            
+
             pageTop += pageHeader.size.h;
-            //inside.details.Add(OldLink { data = pageHeader });
 
             pageHeader.Create();
          }
-         
+
          if(report.pageFooter)
          {
             pageFooter = eInstance_New(report.pageFooter);
@@ -213,7 +234,7 @@ public:
          else
             footerHeight = 0;
 
-         if(report.rowDetail)
+         if(report.rowDetail || (renderAction == levelFinish && level > 0) || renderAction == groupFinish)
          {
             bool loop;
             //int levelHead = 0;
@@ -225,8 +246,12 @@ public:
                int c;
                for(c = 0; c < ((renderAction == groupStart) ? level : (level + 1)); c++)
                {
-                  if(report.groupings._[c].continuation)
-                     AddDetailToPage(destination, eInstance_New(report.groupings._[c].continuation));
+                  if(report.groupings[c].continuation)
+                  {
+                     Detail continuation = eInstance_New(report.groupings[c].continuation);
+                     continuation.level = c;
+                     AddDetailToPage(destination, continuation);
+                  }
                }
             }
 
@@ -246,9 +271,11 @@ public:
                      {
                         // end of rows, end of last group, end of report
                         // TESTING THIS HERE... (UNCOMMENTED AND ADDED CHECK FOR size == 1)
-                        if(report.groupings.size == 1 && report.groupings._[level].footer)
+                        if(report.groupings.size == 1 && report.groupings[level].footer)
                         {
-                           if(AddDetailToPage(destination, eInstance_New(report.groupings._[level].footer)))
+                           Detail groupFooter = eInstance_New(report.groupings[level].footer);
+                           groupFooter.level = level;
+                           if(AddDetailToPage(destination, groupFooter))
                            {
                               //dontAdvance = true;
                               loop = false;
@@ -265,12 +292,15 @@ public:
                      }
                      break;
                   case groupStart:
-                     if(report.Advance(report.groupings._[level], level ? report.groupings._[level - 1].groupId : 0, &dontAdvance))
+                     lastDetail = null;
+                     if(report.Advance(report.groupings[level], level ? report.groupings[level - 1].groupId : 0, &dontAdvance))
                      {
                         report.ExecuteRowData(level);
-                        if(report.groupings._[level].header)
+                        if(report.groupings[level].header)
                         {
-                           Detail groupStart = eInstance_New(report.groupings._[level].header);
+                           Detail groupStart = eInstance_New(report.groupings[level].header);
+                           groupStart.level = level;
+
                            if(AddDetailToPage(destination, groupStart))
                            {
                               dontAdvance = true;
@@ -295,16 +325,20 @@ public:
                      }
                      break;
                   case groupFinish:
-                     if(report.groupings._[level].footer)
+                     if(lastDetail)
+                        lastDetail.isLast = true;
+                     if(report.groupings[level].footer)
                      {
-                        Detail groupEnd = eInstance_New(report.groupings._[level].footer);
+                        Detail groupEnd = eInstance_New(report.groupings[level].footer);
+                        int hh = (insideSize - pageTop - footerHeight) - groupEnd.size.h;
+                        groupEnd.level = level;
                         if(AddDetailToPage(destination, groupEnd))
                         {
                            //dontAdvance = true;
                            loop = false;
                            break;
                         }
-                        else if(overlap - groupEnd.size.h < 0)
+                        else if(hh < 0)   // Use the value before calling AddDetailToPage()
                         {
                            overlap = 0;
                            groupEnd.Destroy(0);
@@ -315,16 +349,20 @@ public:
                      renderAction = groupStart;
                      break;
                   case actualRows:
-                     if(report.Advance(report.groupings._[level], level ? report.groupings._[level - 1].groupId : 0, &dontAdvance))
+                     if(report.Advance(report.groupings[level], level ? report.groupings[level - 1].groupId : 0, &dontAdvance))
                      {
-                        if(AddDetailToPage(destination, eInstance_New(report.rowDetail)))
+                        Detail detail;
+                        if(AddDetailToPage(destination, (detail = eInstance_New(report.rowDetail))))
                         {
                            dontAdvance = true;
                            loop = false;
                            break;
                         }
                         else
+                        {
                            report.ExecuteRowData(level);
+                           lastDetail = detail;
+                        }
                      }
                      else
                      {
@@ -334,13 +372,38 @@ public:
 
                }
             }
-            nil = report.nil;
+            nil = report.nil && renderAction != groupFinish && (renderAction != levelFinish || level == 0);
          }
          else
          {
             nil = true;
          }
 
+         // Cancel group headers if we didn't have space to display any row
+         if(!lastDetail)
+         {
+            Detail detail, prev;
+            for(detail = (Detail)inside.lastChild; detail; detail = prev)
+            {
+               prev = (Detail)detail.previous;
+               if(level > 0 && detail._class == report.groupings[level-1].header)
+               {
+                  detail.Destroy(0);
+                  level--;
+                  renderAction = groupStart;
+               }
+               else
+               {
+                  if(detail._class == report.groupings[level].footer);
+                  else
+                     lastDetail = detail;
+                  break;
+               }
+            }
+         }
+         if(lastDetail)
+            lastDetail.isLast = true;
+
          if(nil && report.reportFooter)
          {
             reportFooter = eInstance_New(report.reportFooter);
@@ -357,23 +420,19 @@ public:
                pageFooter.anchor = Anchor { left = 0, bottom = (int)reportFooter.size.h, right = 0 };
             else
                pageFooter.anchor = Anchor { left = 0, bottom = 0, right = 0 };
-            
-            //inside.details.Add(OldLink { data = pageFooter });
+
             pageFooter.Create();
          }
          if(nil && report.reportFooter)
          {
-            //inside.details.Add(OldLink { data = reportFooter });
             reportFooter.Create();
          }
 
-         pages.Add(OldLink { data = page });
-         
          destination.EndPage(page);
 
          if(nil)
             break;
-         
+
          // still have to bump report footer if it does not fit...
       }
       pleaseWait.Destroy(0);
@@ -391,7 +450,7 @@ private:
    RenderAction renderAction;
 
    Page page;
-   PageInside inside;
+   Window inside;
 
    Detail reportHeader;
    Detail reportFooter;
@@ -403,12 +462,13 @@ private:
    {
       int detailSize;
 
+      detail.anchor = Anchor { left = 0, right = 0 };
       detail.master = destination;
       detail.parent = inside;
 
       detailSize = detail.size.h;
       overlap = (insideSize - pageTop - footerHeight) - detailSize;
-      
+
       if(overlap < 0 && detail.keepTogether)
       {
          delete detail;
@@ -419,12 +479,14 @@ private:
       {
          detail.anchor = Anchor { left = 0, top = pageTop, right = 0 };
          pageTop += detailSize;
-         //inside.details.Add(OldLink { data = detail });
 
          detail.Create();
          // This will probably never go here... (Only report/page headers have keepTogether set to false)
-         if(overlap < 0)
-            return true;
+         /*if(overlap < 0)
+         {
+            printf("bug");
+         }*/
+
       }
       return false;
    }
@@ -432,12 +494,8 @@ private:
 
 public class ReportDestination : Window
 {
-
-   int pageCount;
-
-   OldList pages;
 public:
-   Report report;
+   public property Report report { watchable set { report = value; } get { return report; } }
 
    virtual void EndPage(Page page)
    {
@@ -445,36 +503,42 @@ public:
       SetScrollPosition((page.master.size.w - clientSize.w) / 2, 0);
    }
 
-   virtual void AddPage(Page page)
-   {
-   }
-
-   virtual Report GetReport()
-   {
-      return null;
-   }
-
+   virtual void AddPage(Page page);
+   virtual Report GetReport() { return null; }
 private:
-   ~ReportDestination()
-   {
-      pages.Free(null);
-   }
+   Report report;
+   int pageCount;
+
+   List<PreviewPage> pages { };
 }
 
 public class PrintedReport : ReportDestination
 {
    displayDriver = "Win32Printer";
+   fullRender = true;
 
    Page lastPage;
 
+   bool OnCreate()
+   {
+      if(report)
+         SetPrintingDocumentName(report.title);
+      return ReportDestination::OnCreate();
+   }
+
+   watch(report)
+   {
+      size = report.pageSize;
+   };
+
    void AddPage(Page page)
    {
       if(pageCount && display)
          display.NextPage();
       lastPage = page;
+      page.anchor = { left = 0, top = 0, right = 0, bottom = 0};
       page.master = this;
       page.parent = this;
-      page.anchor = { left = 0, top = 0, right = 0, bottom = 0};
       pageCount++;
       page.Create();
    }
@@ -502,17 +566,15 @@ public class ReportPreviewArea : ReportDestination
 
    void AddPage(Page page)
    {
-      PreviewPage previewPage { this, this, page = page, orientation = page.orientation, 
-                                   anchor = { top = pageCount * ((int)page.size.h + shadowS + pgs) } };
+      PreviewPage previewPage { this, this, page = page, anchor = { top = pageCount * ((int)page.size.h + shadowS + pgs) } };
       previewPage.Create();
+      page.anchor = { left = pgs, top = pgs, right = shadowS + pgs, bottom = shadowS + pgs};
       page.master = previewPage;
       page.parent = previewPage;
-      page.anchor = { left = pgs, top = pgs, right = shadowS + pgs, bottom = shadowS + pgs};
-      pages.Add(OldLink { data = previewPage });
       page.Create();
       pageCount++;
    }
-   
+
    Report GetReport()
    {
       return report;
@@ -520,22 +582,18 @@ public class ReportPreviewArea : ReportDestination
 
    void OnResize(int width, int height)
    {
-      SetScrollPosition((scrollArea.w - width) / 2, scroll.y);
-   }
-
-   ~ReportPreviewArea()
-   {
+      scroll = { (scrollArea.w - width) / 2, scroll.y };
    }
 }
 
-static FileFilter csvFilters[] =
-{
-   { 
-      "Comma Separated Values Spreadsheet (*.csv)",
+Array<FileFilter> csvFilters
+{ [
+   {
+      $"Comma Separated Values Spreadsheet (*.csv)",
       "csv"
    },
-   { "All files", null }
-};
+   { $"All files", null }
+};
 
 public class CSVReport : ReportDestination
 {
@@ -543,24 +601,22 @@ public class CSVReport : ReportDestination
    hasVertScroll = true;
    dontHideScroll = true;
    background = dimGray;
-   
+
    Page lastPage;
 
    void AddPage(Page page)
    {
-      int h;
       if(pageCount && display)
          display.NextPage();
       lastPage = page;
       page.master = this;
       page.parent = this;
       page.size = { MAXINT - 10, MAXINT - 10 };
-      h = page.size.h;
       pageCount++;
       page.Create();
    }
-   
-   void PutString(File f, char * text)
+
+   void PutString(File f, const char * text)
    {
       char output[4096];
       int s = 0, d = 0;
@@ -587,12 +643,13 @@ public class CSVReport : ReportDestination
       f.Puts(output);
    }
 
-   FileDialog saveTo { type = save, text = "Export as Spreadsheet (CSV)", filters = csvFilters, sizeFilters = sizeof(csvFilters) };
+   FileDialog saveTo { type = save, text = $"Export as Spreadsheet (CSV)", filters = csvFilters.array, sizeFilters = csvFilters.count * sizeof(FileFilter) };
 
    void EndPage(Page page)
    {
       char filePath[MAX_LOCATION];
       strcpy(filePath, report.title);
+      ChangeChars(filePath, "/\\:*?\"|<>", '_');
       strcat(filePath, ".csv");
       saveTo.master = master;
       saveTo.filePath = filePath;
@@ -601,29 +658,29 @@ public class CSVReport : ReportDestination
          File f = FileOpen(saveTo.filePath, write);
          if(f)
          {
-            Detail detail, first = null;         
+            Detail detail, first = null;
             for(detail = (Detail)page.inside.firstChild; detail && detail != first; detail = (Detail)detail.next)
             {
                if(!first) first = detail;
                if(eClass_IsDerived(detail._class, class(Detail)))
                {
                   Label label, first = null;
-      
+
                   if(detail._class == report.pageFooter) continue;
-                  if(detail._class == report.groupings._[0].header) 
+                  if(detail._class == report.groupings[0].header)
                      f.Puts("\n");
                   for(label = (Label)detail.firstChild; label && label != first; label = (Label)label.next)
                   {
                      if(!first) first = label;
                      if(label._class == class(ReportTitle) || eClass_IsDerived(label._class, class(Label)))
                      {
-                        char * text = label.text;
+                        const char * text = label.text;
                         if(label != first)f.Puts(",");
                         if(text)
                            PutString(f, text);
                      }
                   }
-                  if(detail._class == report.groupings._[0].header) 
+                  if(detail._class == report.groupings[0].header)
                      f.Puts("\n");
                   f.Puts("\n");
                }
@@ -645,7 +702,7 @@ public class IdFilter : struct
 public:
    Id id;
    Field field;
-   
+
    bool RowMatch(Row row)
    {
       Id value;
@@ -657,13 +714,6 @@ public:
    }
 }
 
-public class ArrayIdFilters : OldArray
-{
-   type = class(IdFilter);
-public:
-   IdFilter * const _;
-}
-
 public class Grouping
 {
 public:
@@ -671,7 +721,7 @@ public:
    Row row;
    Field field, fieldLink;
    bool filtered;
-   ArrayIdFilters filters { };
+   Array<IdFilter> filters { };
 
    // Contractors have a list of trades they're in
    Field listFieldLink;
@@ -682,6 +732,7 @@ public:
 
    bool activeOnly;
    Field activeField;
+   uint rowsCount;
 
    subclass(Detail) header;
    subclass(Detail) continuation;
@@ -689,9 +740,10 @@ public:
 
    ~Grouping()
    {
+      filters.Free();
       delete row;
    }
-   
+
    virtual bool ShouldSkip()
    {
       return false;
@@ -713,7 +765,7 @@ public:
          result = row.Next();
       if(!result)
          return false;
-      
+
       if(reverseLink)
          reverseLink.row.GetData(reverseListFieldLink, reverseIdList);
 
@@ -767,11 +819,11 @@ public:
          if(result && filtered && filters.size)
          {
             int f;
-            for(f = 0; f < filters.size && result && filters._[f].field; f++)
+            for(f = 0; f < filters.size && result && filters[f].field; f++)
             {
                Id id = 0;
-               row.GetData(filters._[f].field, id);
-               if(id != filters._[f].id)
+               row.GetData(filters[f].field, id);
+               if(id != filters[f].id)
                {
                   result = false;
                   break;
@@ -798,22 +850,18 @@ public:
    }
 }
 
-public class ArrayGroupings : OldArray
-{
-   type = class(Grouping);
-public:
-   Grouping * const _;
-}
-
 public class Report
 {
 public:
-   Orientation orientation;
+   public property Orientation orientation { set { orientation = value; UpdateSize(); } get { return orientation; } }
+   public property PageFormat pageFormat { set { pageFormat = value; UpdateSize(); } get { return pageFormat; } }
+
+   Size pageSize;
    Anchor insideMarginAnchor;
 
-   ArrayGroupings groupings { };
+   Array<Grouping> groupings { };
 
-   property String title
+   property const String title
    {
       set
       {
@@ -838,32 +886,62 @@ public:
    {
       return grouping.Advance(linkId, dontAdvance);
    }
-   
+
    virtual bool ExecuteData(Database db)
    {
       return false;
    }
 
-   virtual void ExecuteRowData(int group)
-   {
-   }
+   virtual void ExecuteRowData(int group);
+   virtual void OnReset();
 
    property bool nil
    {
       get
       {
-         if(groupings && groupings.size && groupings._[0].row)
-            return groupings._[0].row.nil;
+         if(groupings && groupings.size && groupings[0].row)
+            return groupings[0].row.nil;
          return true;
       }
    }
 
-private:   
+private:
+   Orientation orientation;
+   PageFormat pageFormat;
+
+   void UpdateSize()
+   {
+      switch(pageFormat)
+      {
+         case letter:
+            if(orientation == landscape)
+               pageSize = { 11*dpi, 8.5*dpi };
+            else
+               pageSize = { 8.5*dpi, 11*dpi };
+            break;
+         case legal:
+            if(orientation == landscape)
+               pageSize = { 14*dpi, 8.5*dpi };
+            else
+               pageSize = { 8.5*dpi, 14*dpi };
+            break;
+         case ledger:
+            if(orientation == landscape)
+               pageSize = { 17*dpi, 11*dpi };
+            else
+               pageSize = { 11*dpi, 17*dpi };
+            break;
+      }
+   }
+
+   Report()
+   {
+      property::pageFormat = letter;
+   }
+
    ~Report()
    {
-      /*int c;
-      for(c = 0; c<groupings.size; c++)
-         delete groupings._[c];*/
+      groupings.Free();
       delete title;
       delete render;
    }
@@ -873,6 +951,8 @@ public class Detail : Window
 {
 public:
    bool keepTogether;
+   bool isLast;
+   int level;
 
    subclass(Detail) rowDetail;
 }