#1 mORMot 1 » Moving setting of fInHeaderOrFooter in mORMotReport » 2015-12-10 04:18:46

kevinday
Replies: 1

I propose moving the setting of fInHeaderOrFooter in mORMotReport from DoHeaderFooterInternal() to DoHeader and DoFooter;

The reason for this is so that we can be in that mode while the event handlers StartPageHeader, EndPageHeader, StartPageFooter, EndPageFooter can be called when in Header/Footer mode.

This enables unlimited flexibility with customer headers and footers with DrawTextAt etc.  Examples of this use would be in the generation of customer account statements or invoices.

Once again thanks for a great product.

Suggested code below of DoHeaderFooterInternal and DoHeader and DoFooter.

procedure TGDIPages.DoHeader;
begin
  fHeaderDone := true;
  if (fHeaderLines.Count = 0) then exit;
  SaveLayout;
  fInHeaderOrFooter := true;
  try
    if Assigned(fStartPageHeader) then
      fStartPageHeader(Self);
    Font.Color := clBlack;
    DoHeaderFooterInternal(fHeaderLines);
    if Assigned(fEndPageHeader) then
      fEndPageHeader(Self);
    GetLineHeight;
    inc(fCurrentYPos,fLineHeight shr 2); // add a small header gap
    fHeaderHeight := fCurrentYPos-fPageMarginsPx.Top;
  finally
    fInHeaderOrFooter := false;
    RestoreSavedLayout;
  end;
end;

procedure TGDIPages.DoFooter;
begin
  if (fFooterLines.Count = 0) then exit;
  SaveLayout;
  fInHeaderOrFooter := true;
  try
    fCurrentYPos :=
      fPhysicalSizePx.y - fPageMarginsPx.bottom - fFooterHeight + fFooterGap;
    if Assigned(fStartPageFooter) then
      fStartPageFooter(Self);
    DoHeaderFooterInternal(fFooterLines);
    if Assigned(fEndPageFooter) then
      fEndPageFooter(Self);
  finally
    fInHeaderOrFooter := false;
    RestoreSavedLayout;
  end;
end;

procedure TGDIPages.DoHeaderFooterInternal(Lines: TObjectList);
var i: integer;
begin
  SaveLayout;
  try
    for i := 0 to Lines.Count -1 do
      with THeaderFooter(Lines[i]) do
      begin
        SavedState := State;
        PrintFormattedLine(Text, State.Flags);
      end;
  finally
    RestoreSavedLayout;
  end;
end;

Regards,
Kevin.

#3 mORMot 1 » Ribbon Application Notification of Marking Action » 2015-12-02 08:04:15

kevinday
Replies: 1

Hi,

I would like to add the ability for the ribbon to be notified when a mark sql action popup menu item has been clicked by the user.  This is so that I can update a "mark status" that I can display on my reports to show on the report what the report contains (if an item is marked since a mark action or query has been performed I clear the mark status to "Marked Items").

What I propose is in mORMotToolbar:

  TMarkActionEvent = procedure (Sender : TObject; const aRecordClass : TSqLRecordClass; aSQLAction : TSQLAction) or object;

A TSQLLister.OnMarkAction property

private
    FOnMarkAction: TMarkActionEvent;
public
    property OnMarkAction: TMarkActionEvent read FOnMarkAction write FOnMarkAction;

and a call to OnMarkAction event handler in  TSQLLister.ActionButtonClick()

begin
  if fReportDetailedIndex<0 then
    exit; // avoid recursive call after GB.Down := false below
  aAction := isActionButton(Sender);
  case A of
    actNoAction: exit;
    actUnMarkAll:
      // (un)marking are standard actions
      TableToGrid.SetMark(A);
    actMark:
      if Sender.InheritsFrom(TSynToolButton) then
        Btn.DoDropDown else
      if Sender.InheritsFrom(TMenuItem) then begin
        // actMarkAllEntries..actMarkBeforeOneYear are regrouped in
        // the only one aAction=actMark button
        TableToGrid.SetMark(TSQLAction(TMenuItem(Sender).Tag));
        if Assigned(FOnMarkAction) then
          FOnMarkAction(Sender, fClass, TSQLAction(TMenuItem(Sender).Tag));
      end
    else begin

etc.

Thanks,
Kevin.

#4 Re: mORMot 1 » mORMoti18n.SetCurrentLanguage() » 2015-12-02 07:44:43

Hi AB,

I don't understand the need for that,  why couldn't one assume that clients locale should already be set correctly to what they need and want?

Regarding to creating a ticket, my user id only works for this forum and there is no ability to register in the synopse.info section, I cannot create a ticket unless I login as guest.

Regards,
Kevin.

#5 mORMot 1 » mORMoti18n.SetCurrentLanguage() » 2015-11-16 04:44:25

kevinday
Replies: 4

Hi AB,

With the English language, they can be different format settings.  For example the Australian date format is d/m/y rather than m/d/y.  However in mORMoti18n.SetCurrentLanguage() the data formats are always reset to US formats.  I suggest that the call to GetFormatSettings be removed or then not changing the thread locale, so that the users FormatSettings will remain intact.

Regards,
Kevin Day.

#6 Re: mORMot 1 » Suggest new Marked actions » 2015-11-11 00:16:20

Hi Arnaud,

One more thing should Mark in the TSQLAction declarations be lower case to be consistent with the rest (I had missed that when I previously suggested the change)?  I notice that "actmark" is taken off when creating the  menu items but not "actMark" via use of TrimLeftLowerCase() in TSQLRecord.CaptionName().

Cheers,
Kevin Day.

#7 mORMot 1 » Fix for TSQLRibbon.ChangeColorScheme AccessViolation » 2015-11-10 23:55:13

kevinday
Replies: 1

Hi Arnaud,

Thank you for a great library.  Thank you for allowing me to contribute.

I found a bug in the TSQLRibbon.ChangeColorScheme() method in mORMotToolbar when ChangeColorScheme() is called with just the ColorScheme and no other parameters.  An access violation is thrown on the line

if (Body<>nil) and Body.AdvOfficePagerStyler.InheritsFrom(TAdvOfficePagerOfficeStyler) then
  TAdvOfficePagerOfficeStyler(Body.AdvOfficePagerStyler).Style :=  TOfficePagerStyle(StatusBarStyler.Style);

The issue is that in this case the StatusBarStyler is nil

I propose a fix to the above line to:

  if (Body <> nil) and Body.AdvOfficePagerStyler.InheritsFrom(TAdvOfficePagerOfficeStyler) then
    TAdvOfficePagerOfficeStyler(Body.AdvOfficePagerStyler).Style := TOfficePagerStyle(StatusStyle);


Full code for this method thus would be:

procedure TSQLRibbon.ChangeColorScheme(const ColorScheme: TToolBarStyle;
  PanelStyler: TAdvPanelStyler; StatusBarStyler: TAdvOfficeStatusBarOfficeStyler;
  CustomStyle: TMemoryStream);
var
  PreviewStyle: TPreviewMenuStyle;
  StatusStyle: TOfficeStatusBarStyle;
  TBStyler: TAdvToolBarOfficeStyler;
  i, curr: integer;
  C: TComponent;
begin
  if not fToolBar.ToolBarStyler.InheritsFrom(TAdvToolBarOfficeStyler) then
    exit;
  TBStyler := TAdvToolBarOfficeStyler(ToolBar.ToolBarStyler);
  if TBStyler = nil then
    exit;
  TBStyler.Style := ColorScheme;
  case ColorScheme of // bulky TMS styles don't match :(
    bsCustom: begin
        if CustomStyle <> nil then begin
          CustomStyle.Seek(0, soFromBeginning);
          CustomStyle.ReadComponent(TBStyler);
        end;
        PreviewStyle := AdvPreviewMenuStylers.psOffice2003Olive;
        StatusStyle := AdvOfficeStatusBarStylers.psOffice2003Olive;
      end;
    bsOffice2007Silver: begin
        PreviewStyle := AdvPreviewMenuStylers.psOffice2007Silver;
        StatusStyle := AdvOfficeStatusBarStylers.psOffice2007Silver;
      end;
    bsOfficeXP: begin
        PreviewStyle := AdvPreviewMenuStylers.psOfficeXP;
        StatusStyle := AdvOfficeStatusBarStylers.psWindowsXP;
      end;
    bsWindowsVista .. high(TToolBarStyle): begin
        PreviewStyle := TPreviewMenuStyle(ColorScheme);
        StatusStyle := TOfficeStatusBarStyle(pred(ColorScheme));
      end;
  else begin
      PreviewStyle := TPreviewMenuStyle(ColorScheme);
      StatusStyle := TOfficeStatusBarStyle(ColorScheme);
    end;
  end;
  if (PreviewMenu <> nil) and PreviewMenu.Styler.InheritsFrom(TAdvPreviewMenuOfficeStyler) then
    TAdvPreviewMenuOfficeStyler(PreviewMenu.Styler).Style := PreviewStyle;
  if StatusBarStyler <> nil then
    StatusBarStyler.Style := StatusStyle;
  if PanelStyler <> nil then
    PanelStyler.Style := TToolBarStyleToPanel[ColorScheme];
  TBStyler.CaptionAppearance.Assign(TBStyler.GroupAppearance.CaptionAppearance);
  with TBStyler.GroupAppearance.TabAppearance do begin
    TBStyler.PagerCaption.TextColor := TextColor;
    TBStyler.PagerCaption.TextColorExtended := TextColorSelected;
  end;
//** Change here
  if (Body <> nil) and Body.AdvOfficePagerStyler.InheritsFrom(TAdvOfficePagerOfficeStyler) then
    TAdvOfficePagerOfficeStyler(Body.AdvOfficePagerStyler).Style := TOfficePagerStyle(StatusStyle);
    // update colors for windows
  for i := 0 to Application.ComponentCount - 1 do begin
    C := Application.Components[i];
    if C.InheritsFrom(TCustomForm) then
      SetStyle(C, TBStyler); // will set style for all embedded components
  end;
    // update report colors on every ribbon page
  curr := fToolBar.ActivePageIndex;
  for i := 0 to high(Page) do
    with Page[i] do begin
      if Report <> nil then
        Report.Color := TBStyler.QATAppearance.ColorTo;
      if i = curr then
        List.Invalidate; // repaint list first row with new colors
    end;
  if Form <> nil then
    Form.Invalidate; // whole form redraw
end;

#8 Re: mORMot 1 » Suggest new Marked actions » 2015-11-10 22:49:39

Thank you Arnaud. 

Additional to this we also need to patch TSQLLister.SetToolBar() in mORMotToolBar

to change
if (A2<actmarkOlderThanOneDay) or (A2>actmarkOlderThanOneYear) or

to

if (A2<actmarkForToday) or (A2>actmarkOlderThanOneYear) or

Full Code:

function TSQLLister.SetToolBar(const aToolBarName: string; const aActions;
  ActionIsNotButton: pointer): TSynToolBar;
var TypeName: PShortString;
    A,iTB,iGB,iM,img: integer;
    GB: TSynToolButton;
    iAction: cardinal;
    M: TMenuItem;
    EN: boolean;
    A2: TSQLAction;
    ActionNames: TStringDynArray;
begin
  result := nil;
  if fPage=nil then
    exit;
  // on existing Toolbar: update its buttons from aActions, and exit
  for iTB := 0 to fPage.ToolBarCount-1 do
    // test exact match, not with SameText(), since Caption can be translated
    if fPage.ToolBars[iTB].Caption=aToolBarName then begin
      result := fPage.ToolBars[iTB];
      for iGB := 0 to result.ComponentCount-1 do begin
        GB := TSynToolButton(result.Components[iGB]);
        if isActionButton(GB)<>0 then begin
          img := GB.ImageIndex;
          EN := GetBit(aActions,img+1);
          GB.Enabled := EN;  // enable or disable buttons
          for iM := 0 to fMenu.Items.Count-1 do
          with fMenu.Items[iM] do
            if ImageIndex=img then
              Enabled := EN; // enable or disable popup menu item
        end;
      end;
      break;
    end;
  if result<>nil then
     exit; // we have found the toolbar
  // no Toolbar: create one with its buttons; also create associated popup menu
  EN := false;
  for A := 0 to fActionMax do
    if GetBit(aActions,A) then begin
      EN := true;
      break;
    end;
  if not EN then
    exit; // aActions=[] -> no toolbar to add
  if fMenu=nil then begin
    fMenu := TSynPopupMenu.Create(fGrid);
    fMenu.Images := ImageList16;
  end;
  result := fPage.CreateToolBar;
  try
{$ifdef USETMSPACK}
    result.BeginUpdate;
    result.AutoPositionControls := true;
    result.ShowOptionIndicator := false;
    result.AutoSize := true;
{$else}
    result.Images := ImageList32;
{$endif}
    result.Caption := aToolBarName;
    SetLength(ActionNames,fActionMax+1);
    TypeName := @fClient.Model.Actions^.NameList;
    for iAction := 0 to fActionMax do begin
      ActionNames[iAction] := fClass.CaptionNameFromRTTI(TypeName); // expanded caption
      inc(PByte(TypeName),ord(TypeName^[0])+1); // next enumerate value name
{$ifndef USETMSPACK}
    end;
    for iAction := fActionMax downto 0 do begin // TToolBar adds at 1st position
{$endif}
      if GetBit(aActions,iAction) then // is this enumerate value inside aActions?
        with result.CreateToolButton(ActionButtonClick,iAction,1,ActionNames[iAction],
           ActionHints,fShortCutUsed,60,ImageList32) do begin
          if GetBit(ActionIsNotButton,iAction) then
            Style := bsCheck;
          // create associated sub menu entry
          if Style<>bsCheck then begin
            NewMenuItem(fMenu,Caption,iAction-1);
            if TSQLAction(iAction)=actMark then
              // actMarkAllEntries..actMarkBeforeOneYear are regrouped in
              // an only one aAction=actMark
              with PTypeInfo(TypeInfo(TSQLAction))^.EnumBaseType^ do
              for A2 := actMarkAllEntries to actmarkInverse do
//                if (A2<actmarkOlderThanOneDay) or (A2>actmarkOlderThanOneYear) or
                if (A2<actmarkForToday) or (A2>actmarkOlderThanOneYear) or
                 (TableToGrid.FieldIndexTimeLogForMark>=0) then begin
                  if A2=actmarkInverse then
                    CreateSubMenuItem('-',iAction,nil);
                  CreateSubMenuItem(fClass.CaptionNameFromRTTI(GetEnumName(A2)),
                    iAction,nil,iAction-1,integer(A2));
                end;
          end;
        end;
    end;
    M := TMenuItem.Create(fMenu);
    M.Caption := '-';
    fMenu.Items.Add(M);
  finally
{$ifdef USETMSPACK}
    result.EndUpdate;
{$endif}
  end;
end;

#9 mORMot 1 » Suggest new Marked actions » 2015-11-09 05:55:41

kevinday
Replies: 4

Hi There,

I am not sure how the best way to contribute code.  I would like to add marked actions so that  TSQLAction in unit mORMot.pas would now look like:

  /// standard actions for User Interface generation
  TSQLAction = (
    /// action not defined
    actNoAction,
    /// Mark rows (standard action)
    // - display sub-menu with actmarkAllEntries..actmarkBeforeOneYear items
    actMark,
    /// UnMark all rows (standard action)
    actUnmarkAll,
    /// Mark all rows
    actmarkAllEntries,
    /// Mark rows for today
    actMarkForToday,
    /// Mark rows for This Week
    actMarkForThisWeek,
    /// Mark rows for this month
    actMarkForThisMonth,
    /// Mark rows for today
    actMarkForYestday,
    /// Mark rows for Last Week
    actMarkForLastWeek,
    /// Mark rows for Last month
    actMarkForLastMonth,
    /// Mark rows After one day
    actmarkOlderThanOneDay,
    /// Mark rows older than one week
    actmarkOlderThanOneWeek,
    /// Mark rows older than one month
    actmarkOlderThanOneMonth,
    /// Mark rows older than one half year
    actmarkOlderThanSixMonths,
    /// Mark rows older than one year
    actmarkOlderThanOneYear,
    /// Inverse Mark values (ON->OFF, OFF->ON)
    actmarkInverse);

Then also in mORMotUI.pas - TSQLTableToGrid.SetMark() would look like:

procedure TSQLTableToGrid.SetMark(aAction: TSQLAction);
var
  i: integer;
  V, Time: Int64;
  Time2: Int64;
const
  DIFFTIME: array [actMarkOlderThanOneDay .. actMarkOlderThanOneYear] of double = (1, 7, 31, 183,
    365); // 183 = more or less half a year
begin
  if NotDefined then
    Exit;
  with TDrawGrid(Owner) do
    case aAction of
      actmarkAllEntries:
        for i := 1 to RowCount do
          Marked[i] := true;
      actUnmarkAll:
        if fMarked <> nil then
          Finalize(fMarked);
      actmarkInverse:
        for i := 1 to RowCount do
          Marked[i] := not Marked[i];
      actMarkForToday, actMarkForThisWeek, actMarkForThisMonth, actMarkForYestday,
        actMarkForLastWeek, actMarkForLastMonth: begin
          case aAction of
            actMarkForToday: begin
                PTimeLogBits(@Time)^.From(Date, true);
                PTimeLogBits(@Time2)^.From(Date + 1, true);
              end;
            actMarkForThisWeek: begin
                PTimeLogBits(@Time)^.From(StartOfTheWeek(Date), true);
                PTimeLogBits(@Time2)^.From(EndOfTheWeek(Date) + 1, true);
              end;
            actMarkForThisMonth: begin
                PTimeLogBits(@Time)^.From(StartOfTheMonth(Date), true);
                PTimeLogBits(@Time2)^.From(EndOfTheMonth(Date) + 1, true);
              end;
            actMarkForYestday: begin
                PTimeLogBits(@Time)^.From(Date - 1, true);
                PTimeLogBits(@Time2)^.From(Date, true);
              end;
            actMarkForLastWeek: begin
                PTimeLogBits(@Time)^.From(IncWeek(StartOfTheWeek(Date), -1), true);
                PTimeLogBits(@Time2)^.From(StartOfTheWeek(Date), true);
              end;
            actMarkForLastMonth: begin
                PTimeLogBits(@Time)^.From(IncMonth(StartOfTheMonth(Date), -1), true);
                PTimeLogBits(@Time2)^.From(StartOfTheMonth(Date), true);
              end;
          end;
          if FieldIndexTimeLogForMark >= 0 then begin
            for i := 1 to RowCount do begin
              SetInt64(Table.Get(i, fFieldIndexTimeLogForMark), V);
              if (V >= Time) and (V <= Time2) then
                Marked[i] := true;
            end;
          end;
        end;
      actMarkOlderThanOneDay .. actMarkOlderThanOneYear:
        if FieldIndexTimeLogForMark >= 0 then begin
          // use TDateTime calculation because TTimeLog is not duration compatible
          PTimeLogBits(@Time)^.From(Now - DIFFTIME[aAction], true);
          for i := 1 to RowCount do begin
            SetInt64(Table.Get(i, fFieldIndexTimeLogForMark), V);
            if (V > 0) and (V <= Time) then
              Marked[i] := true;
          end;
        end;
    else
      Exit;
    end;
  TDrawGrid(Owner).Invalidate; // refresh screen
end;

Thanks.  mORMot is a great framework!

Kevin Day

#10 mORMot 1 » Suggested Chages to GDIPages » 2015-11-06 01:11:58

kevinday
Replies: 1

Hi Arnaud,

I suggest the following changes to Tgdipages to allow one to have lines around totals on columns such that there is a line close above the total and one close below the total.  I propose the following additions / changes for this:

Firstly in private section - overload LineInternal() as below

      procedure LineInternal(start, finish : integer; doubleline : boolean); overload;
      procedure LineInternal(aty, start, finish : integer; doubleline : boolean); overload;

New LineInternal() bodies

procedure TGDIPages.LineInternal(start, finish : integer; doubleline : boolean);
begin
  LineInternal(fCurrentYPos + (GetLineHeight shr 1), start, finish, doubleline);
end;

procedure TGDIPages.LineInternal(aty, start, finish : integer; doubleline : boolean);
var Y: integer;
begin
  if (Self <> nil) and (fCanvas <> nil) then
    with fCanvas do begin
      Pen.Width := MulDiv(fDefaultLineWidth, Self.Font.size, 8);
      if fsBold in Self.Font.style then Pen.Width := Pen.Width + 1;
      if doubleline then begin
        Y := aty - (Pen.Width);
        MoveTo(start, Y);
        LineTo(finish, Y);
        MoveTo(start, Y + (Pen.Width * 2));
        LineTo(finish, Y + (Pen.Width * 2));
      end else begin
        Y := aty - (Pen.Width shr 1);
        MoveTo(start, Y);
        LineTo(finish, Y);
      end;
    end;
end;

Then finally the new public method DrawColumnLine()

procedure TGDIPages.DrawColumnLine(ColIndex: integer; aAtTop: boolean;
  aDoDoubleLine: boolean);
var Y: integer;
begin
  if aAtTop then Y := fCurrentYPos - 1
  else Y := fCurrentYPos + fLineHeight + 1;
  with fColumns[ColIndex] do LineInternal(Y, ColLeft, ColRight, aDoDoubleLine);
end;

#11 mORMot 1 » Fix for Bug in SynCommons.IdemPCharU() » 2015-11-03 22:02:38

kevinday
Replies: 1

Hi, 

I found a bug in SynCommons.IdemPCharU().

The corrected code below:

function IdemPCharU(p, up: PUTF8Char): boolean;
begin
  result := false;
  if (p=nil) or (up=nil) then
    exit;
  while up^<>#0 do begin
    if GetNextUTF8Upper(p)<>ord(up^) then
      exit;
    inc(up);
//    inc(p); already incremented by GetNextUTF8Upper()
  end;
  result := true;
end;

Cheers,
Kevin Day

Board footer

Powered by FluxBB