unit MapForm_SRC;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids,zCube_TLB,zCubeExplorer_TLB, Menus, StdCtrls, ExtCtrls, IniFiles,
  ColorsForm_SRC,Printers;

type

  THeaderCell = class
  public
    Text : string;
    Level : byte;
    Range : array[0..1] of integer;
    Expandable : boolean;
    Collapsed : boolean;
    Wrapable : boolean;
    Wraped : boolean;
    end;

  TMapForm = class(TForm)
    Grid: TStringGrid;
    Mapper: TPanel;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    DimensionsLB: TListBox;
    ColumnsLB: TListBox;
    RowsLB: TListBox;
    MainMenu1: TMainMenu;
    Map1: TMenuItem;
    Showdesigner1: TMenuItem;
    N1: TMenuItem;
    Hideemptyrows1: TMenuItem;
    Hideemptycolumns1: TMenuItem;
    N2: TMenuItem;
    Refresh1: TMenuItem;
    N3: TMenuItem;
    Savemap1: TMenuItem;
    Loadmap1: TMenuItem;
    Duplicate1: TMenuItem;
    N4: TMenuItem;
    procedure GridMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure GridDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure GridTopLeftChanged(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Showdesigner1Click(Sender: TObject);
    procedure Refresh1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ColumnsLBDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure ColumnsLBDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure RowsLBDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure RowsLBDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure DimensionsLBDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure DimensionsLBDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure Savemap1Click(Sender: TObject);
    procedure Loadmap1Click(Sender: TObject);
    procedure Duplicate1Click(Sender: TObject);
  private
    { Private declarations }
   Start : TDateTime;
   Finish : TDateTime;
   StartLoad : TDateTime;
   FinishLoad : TDateTime;
   StartMap : TDateTime;
   FinishMap : TDateTime;
   StartDisp : TDateTime;
   FinishDisp : TDateTime;
   RecCount : integer;
   FColList : TList;
   FRowList : TList;
   FFixWidth : integer;
   FLeftColWidth : integer;
   FTopRowHeight : integer;
   FMeasureCount : integer;
   map : iMap;
   FCube: ICube;
   FGridSettings: TGridSettings;
   procedure SetCube(const Value: ICube);
   procedure RebuildMap;
    procedure SetGridSettings(const Value: TGridSettings);
  public
    { Public declarations }
   procedure DisplayMap;
   procedure PrintMap;
   property Cube : ICube read FCube write SetCube;
   property GridSettings : TGridSettings read FGridSettings write SetGridSettings;
  end;

var
  MapForm: TMapForm;

implementation

uses
    MapSelectForm_SRC, PrintPreview_SRC;

{$R *.dfm}

procedure TMapForm.GridMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
   i,ac,ar,col,row : integer;
   s : string;

function CheckCollapseBtn(r1 : Trect) : boolean;
var
   Rect : Trect;
begin
Rect.Left := r1.Left + 1;
rect.Right := r1.Left + 11;
rect.Top := r1.Top + 1;
rect.Bottom := r1.Top + 11;

if PtInRect(rect,Point(x,y)) then
   Result := True
else
   Result := False;
end;

function CheckWrapBtn(r1 : Trect) : boolean;
var
   Rect : Trect;
begin
Rect.Left := r1.Left + 1;
rect.Right := r1.Left + 11;
rect.Top := r1.Bottom - 11;
rect.Bottom := r1.bottom - 1;

if PtInRect(rect,Point(x,y)) then
   Result := True
else
   Result := False;
end;

function CheckInColumns : integer;
var
   ac : integer;
   i : integer;
begin
Result := -1;
if (row < Grid.FixedRows) and (col >= Grid.FixedCols) then
   begin
   ac := col - Grid.FixedCols ;
   for i := 0 to FColList.Count - 1 do
       begin
       if (THeaderCell(FColList.Items[i]).Range[0] <= ac) and
          (THeaderCell(FColList.Items[i]).Range[1] >= ac) and
          (THeaderCell(FColList.Items[i]).Level = row) then
          begin
          ac := THeaderCell(FColList.Items[i]).Range[0];
          Break;
          end;
       end;
   if FMeasureCount > 1 then
      ac := (ac+1) div FMeasureCount;
   s := THeaderCell(FColList.Items[i]).Text;
   THeaderCell(FColList.Items[i]).Text := 'Wait!';
   Grid.repaint;
   THeaderCell(FColList.Items[i]).Text := s;
   Result := ac;
   end;
end;

function CheckInRows : integer;
var
   ar : integer;
   i : integer;
begin
Result := -1;
if (row >= Grid.FixedRows) and (col < Grid.FixedCols) then
   begin
   ar := row - Grid.FixedRows;
   for i := 0 to FRowList.Count - 1 do
       begin
       if (THeaderCell(FRowList.Items[i]).Range[0] <= ar) and
          (THeaderCell(FRowList.Items[i]).Range[1] >= ar) and
          (THeaderCell(FRowList.Items[i]).Level = col) then
          begin
          ar := THeaderCell(FRowList.Items[i]).Range[0];
          Break;
          end;
       end;
   s := THeaderCell(FRowList.Items[i]).Text;
   THeaderCell(FRowList.Items[i]).Text := 'Wait!';
   Grid.repaint;
   THeaderCell(FRowList.Items[i]).Text := s;
   Result := ar;
   end;
end;

begin
Grid.MouseToCell(x,y,col,row);
if (col >= Grid.FixedCols) and (row >= grid.FixedRows) then
   Exit;
if CheckWrapBtn(Grid.CellRect(col,row)) then
   begin
   ac := CheckInColumns;
   if ac >= 0 then
      begin
      if map.Columns[ac].Dimension.WrapType > 0 then
         map.WrapDateCell(True,ac);
      end
   else
      begin
      ar := CheckInRows;
      if map.Rows[ar].Dimension.WrapType > 0 then
         map.WrapDateCell(False,ar);
      end;
   DisplayMap;
   Grid.Repaint;
   end;
if CheckCollapseBtn(Grid.CellRect(col,row)) then
   begin
   ac := CheckInCOlumns;
   if ac >= 0 then
      begin
      if map.Columns[ac].Collapsed then
         map.ExpandColumn(ac)
      else
         map.CollapseColumn(ac);
      end
   else
      begin
      ar := CheckInRows;
      if map.Rows[ar].Collapsed then
         map.ExpandRow(ar)
      else
         map.CollapseRow(ar);
      end;
   DisplayMap;
   Grid.Repaint;
   end;
end;

procedure TMapForm.GridDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
   s : string;
   i,j,w1,w2,w3,linew,c,ac,ar,right,left : integer;
   fixw,lcw,oldr : integer;
   r1,r2 : TRect;
   po : array [0..2] of TPoint;
   f : double;
   cell : THeaderCell;
   DrawCanvas : TCanvas;

procedure DrawBtn(Rect : Trect);
var
   DC : THandle;
begin
with Grid.Canvas do
     begin
     DC := Handle;
     DrawEdge(DC, Rect, BDR_RAISEDOUTER, BF_BOTTOMRIGHT);          { black }
     Dec(Rect.Bottom);
     Dec(Rect.Right);
     DrawEdge(DC, Rect, BDR_RAISEDINNER, BF_TOPLEFT);              { btnhilite }
     Inc(Rect.Top);
     Inc(Rect.Left);
     DrawEdge(DC, Rect, BDR_RAISEDINNER, BF_BOTTOMRIGHT or BF_MIDDLE); { btnshad}
     end;
end;

procedure DrawPushBtn(Rect : Trect);
var
   DC : THandle;
begin
with Grid.Canvas do
     begin
     DC := Handle;
     DrawEdge(DC, Rect, BDR_SUNKENINNER, BF_TOPLEFT);              { black     }
     DrawEdge(DC, Rect, BDR_SUNKENOUTER, BF_BOTTOMRIGHT);          { btnhilite }
     Dec(Rect.Bottom);
     Dec(Rect.Right);
     Inc(Rect.Top);
     Inc(Rect.Left);
     DrawEdge(DC, Rect, BDR_SUNKENOUTER, BF_TOPLEFT or BF_MIDDLE); { btnshadow }
     end;
end;

begin
if not (gdfixed in State) and (Cube <> nil) then
   begin
   ac := ACol - Grid.FixedCols;
   ar := ARow - Grid.FixedRows;
   f := map.Cells[ac div FMeasureCount,ar,ac mod FMeasureCount];
   s := FloatToStrF(f,ffFixed,9,2);
   w1 := Grid.Canvas.TextWidth(s) + 14;
   if w1 > Grid.ColWidths[Acol] then
      Grid.ColWidths[Acol] := w1;
   if (gdFocused in State)  then
      Grid.Canvas.Brush.Color := GridSettings.FocusedCellColor
   else
      begin
      if gdSelected in State then
         Grid.Canvas.Brush.Color := GridSettings.SelectedCellColor
      else
         Grid.Canvas.Brush.Color := GridSettings.CellColor;
      end;
   Grid.Canvas.FillRect(Rect);
   if f > 0 then
      Grid.Canvas.Font.Assign(GridSettings.PositiveCellFont)
   else
   if f = 0 then
      Grid.Canvas.Font.Assign(GridSettings.ZeroCellFont)
   else
   if f < 0 then
      Grid.Canvas.Font.Assign(GridSettings.NegativeCellFont);
   Grid.Canvas.TextOut(Rect.Right - Grid.Canvas.TextWidth(s) - 2,Rect.Bottom - Grid.Canvas.TextHeight(s)-1,s);
   Exit;
   end;
if (gdfixed in State) and (Grid.Cells[ACol,ARow] <> '') then
   begin
   // draw button in fixed corner
   if (ARow < Grid.FixedRows) and (ACol < grid.FixedCols) then
      begin
      Rect.Top := 0;
      Rect.Left := 0;
      w1 := 0;
      for i := 0 to Grid.FixedCols - 1 do
          w1 := w1 +  Grid.ColWidths[i] + Grid.GridLineWidth;
      Rect.Right := w1;
      for i := 0 to grid.FixedRows - 1 do
          w1 := w1 + grid.RowHeights[i] + Grid.GridLineWidth;
      Rect.Bottom := w1;
      Grid.Canvas.Brush.Color := clBtnFace;
      Grid.Canvas.FillRect(Rect);
      Exit;
      end;
   // draw fixed columns;
   if (ARow <= Grid.FixedRows) and (ACol >= Grid.FixedCols) then
      begin
      ac := ACol - Grid.FixedCols;
      ar := ARow - Grid.FixedRows;
      s := '';
      for i := 0 to FColList.Count - 1 do
          begin
          cell := THeaderCell(FColList.Items[i]);
          if (cell.Range[0] <= ac) and (cell.Range[1] >= ac) and
             (cell.Level = ARow) then
             begin
             // calc left
             s := cell.Text;
             w1:= FFixWidth;
             linew := Grid.GridLineWidth;
             for j := Grid.LeftCol - Grid.FixedCols  to cell.Range[0] - 1 do
                 w1 := w1 + (grid.ColWidths[j+Grid.FixedCols] + linew);
             w2 := 0;
             for j := cell.Range[0] to cell.Range[1] do
                 w2 := w2 + (grid.ColWidths[j+Grid.FixedCols] + linew);

             if w1 < FFixWidth then
                left := FFixWidth
             else
                left := w1;
             w3 := w2 + left;
             if w3 > grid.GridWidth + FFixWidth then
                right := grid.GridWidth
             else
                right := w3;
             oldr := rect.Right;
             rect.Right := right;
             rect.left := left;
             w1 := Rect.Left + ((Rect.Right - Rect.Left) div 2) - (Grid.Canvas.TextWidth(Grid.Cells[ACol,ARow]) div 2);


             Grid.Canvas.Font.Assign(GridSettings.ColumnHeaderFont);
             Grid.Canvas.Brush.Color := GridSettings.ColumnHeaderColor;
             Grid.Canvas.Pen.Width := linew;


             r1.Left := rect.Left ;
             r1.Right := rect.Right ;
             r1.Top := rect.Top ;
             r1.Bottom := rect.Bottom ;
             Grid.Canvas.FrameRect(r1);


             Grid.Canvas.Brush.Color := clBlack;
             if (cell.Level <= Grid.FixedRows) then
                begin
                r1.Top := rect.Top;
                r1.Left := rect.left;
                r1.Bottom := (Grid.FixedRows) * (Grid.DefaultRowHeight + linew) + 1;
                r1.Right := oldr;

                r1.Left := r1.Left -1 ;
                r1.Right := r1.Right + 1;
                r1.Top := r1.Top -1;
                r1.Bottom := r1.Bottom + 1;

                Grid.Canvas.Brush.Color := clBlack;

                Grid.Canvas.Pen.Width := linew+4;
                Grid.Canvas.FrameRect(r1);
                Grid.Canvas.Brush.Color := GridSettings.ColumnHeaderColor;
                end;
             r1.Left := r1.left + linew;
             r1.Right := r1.Right - linew;
             r1.Top := r1.Top + linew;
             r1.Bottom := r1.Bottom - linew;
             if cell.Expandable or cell.Wrapable then
                Grid.Canvas.TextRect(r1,r1.Left+13,r1.Top+2,s)
             else
                Grid.Canvas.TextRect(r1,r1.Left+6,r1.Top+2,s);

             // draw button
             if cell.Expandable then
                begin
                Rect.Left := r1.Left + 1;
                rect.Right := r1.Left + 11;
                rect.Top := r1.Top + 1;
                rect.Bottom := r1.Top + 11;
                if cell.Collapsed then
                   DrawBtn(rect)
                else
                   DrawPushBtn(rect);
                end;

             if cell.Wrapable then
                begin
                Rect.Left := r1.Left + 1;
                rect.Right := r1.Left + 11;
                rect.Top := r1.Top + (Grid.DefaultRowHeight - 11);
                rect.Bottom := r1.Top + (Grid.DefaultRowHeight - 1);
                if not cell.Wraped then
                   DrawBtn(rect)
                else
                   DrawPushBtn(rect);
                end;
             Exit;
             end;
          end;
      end;
   end;
   //draw fixed rows
   if (ARow >= Grid.FixedRows) and (ACol <= Grid.FixedCols) then
      begin
      ac := ACol - Grid.FixedCols;
      ar := ARow - Grid.FixedRows;
      s := '';
      for i := FRowList.Count - 1 downto 0 do
          begin
          if (THeaderCell(FRowList.Items[i]).Range[0] <= ar) and
             (THeaderCell(FRowList.Items[i]).Range[1] >= ar) and
             (THeaderCell(FRowList.Items[i]).Level = ACol) then
             begin
             s := THeaderCell(FRowList.Items[i]).Text;
             fixw:= 0;
             for j := 0 to grid.FixedRows - 1 do
                 fixw := fixw + grid.Rowheights[j] + Grid.GridLineWidth;
             w1:= fixw;
             for j := Grid.TopRow-Grid.FixedRows to THeaderCell(FRowList.Items[i]).Range[0] - 1 do
                 w1 := w1 + (grid.Rowheights[j+Grid.FixedRows] + Grid.GridLineWidth);
             w2 := 0;
             for j := THeaderCell(FRowList.Items[i]).Range[0] to THeaderCell(FRowList.Items[i]).Range[1] do
                 w2 := w2 + (grid.Rowheights[j+Grid.FixedRows] + Grid.GridLineWidth);

             if w1 < fixw then
                left := fixw
             else
                left := w1;
             w3 := w2 + w1;
             if w3 > grid.GridHeight + fixw then
                right := grid.GridHeight
             else
                right := w3;
             oldr := rect.Bottom;
             rect.Bottom := right;
             rect.Top := left ;
             Grid.Canvas.Font.Assign(GridSettings.RowHeaderFont);
             Grid.Canvas.Brush.Color := GridSettings.RowheaderColor;
             Grid.Canvas.FrameRect(Rect);

             if (THeaderCell(FRowList.Items[i]).Level < Grid.FixedCols-1) and
                (THeaderCell(FRowList.Items[i]).Range[0] = ar) then
                begin
                Grid.Canvas.Brush.Color := clBlack;
                r1.Left := rect.Left -1;
                r1.Right := rect.Right + 1;
                r1.Top := rect.Top - 1;
                r1.Bottom := rect.Bottom + 1 ;
                Grid.Canvas.FrameRect(r1);
                Grid.Canvas.Brush.Color := GridSettings.RowheaderColor;
                end;
             if (THeaderCell(FRowList.Items[i]).Level <= Grid.FixedCols-1) and
                (THeaderCell(FRowList.Items[i]).Range[0] >= ar) then
                begin
                r1.Top := rect.Top;
                r1.Left := rect.left;
                w1 := 0;
                for j := 0 to Grid.FixedCols -1 do
                    w1 := w1 + grid.ColWidths[j] + Grid.GridLineWidth;
                r1.right := w1;
                r1.Bottom := oldr;
                Grid.Canvas.FillRect(r1);

                Grid.Canvas.Brush.Color := clBlack;
                Grid.Canvas.Pen.Width := grid.GridLineWidth;
                r1.Left := r1.Left -1 ;
                r1.Right := r1.Right + 1;
                r1.Top := r1.Top -1;
                r1.Bottom := r1.Bottom + 1;
                Grid.Canvas.FrameRect(r1);

                Grid.Canvas.Brush.Color := GridSettings.RowheaderColor;
                end;
             r1.Left := r1.left + Grid.GridLineWidth;
             r1.Right := r1.Right - Grid.GridLineWidth;
             r1.Top := r1.Top + Grid.GridLineWidth;
             r1.Bottom := r1.Bottom - Grid.GridLineWidth;


             if THeaderCell(FRowList.Items[i]).Expandable or
                THeaderCell(FRowList.Items[i]).Wrapable then
                Grid.Canvas.TextRect(Rect,Rect.Left+13,Rect.Top+2,s)
             else
                Grid.Canvas.TextRect(Rect,Rect.Left+6,Rect.Top+2,s);
             r1.Left := Rect.Left;
             r1.Right := Rect.Right;
             r1.Top := Rect.Top;
             r1.Bottom := rect.Bottom;
             if THeaderCell(FRowList.Items[i]).Expandable then
                begin
                Rect.Left := r1.Left + 1;
                rect.Right := r1.Left + 11;
                rect.Top := r1.Top + 1;
                rect.Bottom := r1.Top + 11;
                if THeaderCell(FRowList.Items[i]).Collapsed then
                   DrawBtn(rect)
                else
                   DrawPushBtn(rect);
                end;

             if THeaderCell(FRowList.Items[i]).Wrapable then
                begin
                Rect.Left := r1.Left + 1;
                rect.Right := r1.Left + 11;
                rect.Top := r1.Top + (Grid.DefaultRowHeight - 11);
                rect.Bottom := r1.Top + (Grid.DefaultRowHeight - 1);
                if not THeaderCell(FRowList.Items[i]).Wraped then
                   DrawBtn(rect)
                else
                   DrawPushBtn(rect);
                end;
             Exit;;
             end;
          end;
      end;
end;

procedure TMapForm.GridTopLeftChanged(Sender: TObject);
var
   i : integer;
begin
FFixWidth := 0;
for i := 0 to Grid.FixedCols -1 do
    begin
    FFixWidth := FFixWidth + grid.ColWidths[i] + Grid.GridLineWidth;
    end;
FLeftColWidth := 0;
for i := 0 to (Grid.LeftCol - Grid.FixedCols)-1 do
    begin
    FLeftColWidth := FLeftColWidth + grid.ColWidths[i] + Grid.GridLineWidth;
    end;
FTopRowHeight := 0;
for i := 0 to (Grid.TopRow-Grid.FixedRows) - 1 do
    begin
    FTopRowheight := FTopRowHeight + grid.RowHeights[i] + Grid.GridLineWidth;
    end;
Grid.Repaint;
end;

procedure TMapForm.DisplayMap;
var
   i,j,k,w,maxw,colw,cnt : integer;
   hc : THeaderCell;
   cell : ICell;
label
   AjustAgain;
begin
FColList.Clear;
FRowList.Clear;
FMeasureCount := cube.MeasureCount;

if map.ColumnsCount = 0 then
   Grid.ColCount := map.RowLevelCount + FMeasureCount
else
   Grid.ColCount := map.ColumnsCount*FMeasureCount + map.RowLevelCount;

Grid.RowCount := map.RowsCount + map.ColumnLevelCount;


for i := 0 to Grid.ColCount - 1 do
    for j := 0 to Grid.RowCount - 1 do
        Grid.Cells[i,j] := 'wait!';


Grid.FixedCols := map.RowLevelCount;

if FMeasureCount > 1 then
   begin
   Grid.FixedRows := map.ColumnLevelCount + 1;
   Grid.RowCount := Grid.RowCount + 1;
   end
else
   Grid.FixedRows := map.ColumnLevelCount;

for i := 0 to Grid.ColCount - 1  do
    Grid.ColWidths[i] := Grid.DefaultColWidth;

cnt := 0;
// fill column info
if (map.ColumnDimCount = 0) and (FMeasureCount > 1) then
   begin
   for j := 0 to FMeasureCount - 1 do
       begin
       Grid.Cells[i*FMeasureCount+Grid.FixedCols+j,Grid.FixedRows-1] := Cube.Measure[j].Name;
       hc := THeaderCell.Create;
       hc.Text := Cube.Measure[j].Name;
       hc.Level := Grid.FixedRows-1;
       hc.Range[0] := j;
       hc.Range[1] := hc.Range[0];
       FColList.Add(hc);
       end;
   end
else
   begin
   for i := 0 to map.ColumnsCount - 1 do
       begin
       cell := map.Columns[i];
       for j := 0 to grid.FixedRows - 1 do
           Grid.Cells[i*FMeasureCount+Grid.FixedCols,j] := 'a';
       Grid.Cells[i*FMeasureCount+Grid.FixedCols,cell.Level] := cell.Caption;
AjustAgain:
       w := Grid.Canvas.TextWidth(cell.Caption) + 14;
       maxw := Grid.ColWidths[i*FMeasureCount+Grid.FixedCols];
       k := 0;
       for j := i+1 to i+((cell.ChildCount-1)*FMeasureCount)-1 do
           begin
           colw := Grid.Canvas.TextWidth(Cube.Measure[k].Name) + 14;
           if Grid.ColWidths[j+Grid.FixedCols] < colw then
              Grid.ColWidths[j+Grid.FixedCols] := colw;
           maxw := maxw + Grid.ColWidths[j+Grid.FixedCols];
           Inc(k);
           if k = FMeasureCount then
              k := 0;
           end;
       if FMeasureCount = 1 then
          Grid.ColWidths[i*FMeasureCount+Grid.FixedCols] := w
       else
          begin
          colw := w div FMeasureCount;
          for j := 0 to FMeasureCount - 1 do
              begin
              if colw < (Grid.Canvas.TextWidth(Cube.Measure[j].Name) + 14) then
                 colw := Grid.Canvas.TextWidth(Cube.Measure[j].Name) + 14;
              end;
          for j := 0 to FMeasureCount - 1 do
              Grid.ColWidths[i*FMeasureCount+Grid.FixedCols+j] := colw;
          end;
       hc := THeaderCell.Create;
       hc.Text := cell.Caption;
       hc.Level := cell.Level;
       hc.Wrapable := cell.Wrapable;
       if hc.Wrapable then
          hc.Wraped := cell.Wrapped;
       hc.Expandable := cell.Expandable;
       if hc.Expandable then
          hc.Collapsed := cell.Collapsed;

       if cnt > 0 then
          begin
          if hc.Level <= THeaderCell(FColList.Items[cnt-1]).level then
             hc.Range[0] := THeaderCell(FColList.Items[cnt-1]).Range[1] + 1
          else
          if hc.Level > THeaderCell(FColList.Items[cnt-1]).level then
             hc.Range[0] := THeaderCell(FColList.Items[cnt-1]).Range[0] + 1;
          end
       else
          hc.Range[0] := 0;
       hc.Range[1] := hc.Range[0] + (cell.ChildCount * FMeasureCount) -1;
       FColList.Add(hc);
          Inc(cnt);
       if FMeasureCount > 1 then
          begin
          for j := 0 to FMeasureCount - 1 do
              begin
              Grid.Cells[i*FMeasureCount+Grid.FixedCols+j,Grid.FixedRows-1] := Cube.Measure[j].Name;
              hc := THeaderCell.Create;
              hc.Text := Cube.Measure[j].Name;
              hc.Level := Grid.FixedRows-1;
              if j > 0 then
                 begin
                    hc.Range[0] := THeaderCell(FColList.Items[cnt-1]).Range[1] + 1
                 end
              else
                 hc.Range[0] := THeaderCell(FColList.Items[cnt-1]).Range[0];
              hc.Range[1] := hc.Range[0];
              FColList.Add(hc);
              Inc(cnt);
              end;
          end;
       end;
   end;

for i := 0 to map.RowsCOunt - 1 do
    begin
    cell := map.Rows[i];
    for j := 0 to grid.FixedCols - 1 do
        Grid.Cells[j,i*FMeasureCount+Grid.FixedRows] := 'a';

    hc := THeaderCell.Create;
    hc.Text := cell.Caption;
    hc.Level := cell.Level;

    hc.Wrapable := cell.Wrapable;
    if hc.Wrapable then
       hc.Wraped := cell.Wrapped;
    hc.Expandable := cell.Expandable;
    if hc.Expandable then
       hc.Collapsed := cell.Collapsed;

    if i > 0 then
       begin
       if hc.Level <= THeaderCell(FRowList.Items[i-1]).level then
          hc.Range[0] := THeaderCell(FRowList.Items[i-1]).Range[1] + 1
       else
       if hc.Level > THeaderCell(FRowList.Items[i-1]).level then
          hc.Range[0] := THeaderCell(FRowList.Items[i-1]).Range[0] + 1;
       end
    else
       hc.Range[0] := 0;
    hc.Range[1] := hc.Range[0] + cell.ChildCount-1;
    FRowList.Add(hc);

    Grid.Cells[cell.Level,i+Grid.FixedRows] := hc.Text;
    w := Grid.Canvas.TextWidth(hc.Text) + 14;
    if w > Grid.ColWidths[hc.Level] then
       Grid.ColWidths[hc.Level] := w;

    end;
GridTopLeftChanged(nil);
end;



procedure TMapForm.FormCreate(Sender: TObject);
begin
FColList := TList.Create;
FRowList := TList.Create;
Grid.DoubleBuffered := True;
end;

procedure TMapForm.FormDestroy(Sender: TObject);
begin
FColList.Free;
FRowList.Free;
Map := nil;
FCube := nil;
end;

procedure TMapForm.SetCube(const Value: ICube);
begin
  FCube := Value;
  try
   Grid.Visible := False;
   map := CoMap.Create;
   map.Cube := FCube;
   Map.DimensionToRow(FCube.Dimension[0].FieldName,maBottom,0);
   Map.Build(False,False);
   DisplayMap;
   Grid.Visible := True;
   Caption := FCube.Name;
   except
      Close;
   end
end;

procedure TMapForm.Showdesigner1Click(Sender: TObject);
var
   i : integer;
begin
DimensionsLB.Clear;
ColumnsLB.Clear;
RowsLB.Clear;
if not ShowDesigner1.Checked then
   begin
   Mapper.Visible := False;
   Exit;
   end
else
   begin
   if Cube = nil then
      Exit;
   Mapper.Visible := True;
   for i := 0 to Cube.DimensionCount - 1 do
       DimensionsLB.AddItem(Cube.Dimension[i].Alias,nil);
   for i := 0 to Map.ColumnDimCount - 1 do
       begin
       ColumnsLB.AddItem(Map.ColumnDimensions[i].Alias,nil);
       DimensionsLB.Items.Delete(DimensionsLB.Items.IndexOf(ColumnsLB.Items[ColumnsLB.Count - 1]));
       end;
   for i := 0 to Map.RowDimCount - 1 do
       begin
       RowsLB.AddItem(Map.RowDimensions[i].Alias,nil);
       DimensionsLB.Items.Delete(DimensionsLB.Items.IndexOf(RowsLB.Items[RowsLB.Count - 1]));
       end;
   end;

end;

procedure TMapForm.Refresh1Click(Sender: TObject);
begin
DisplayMap;
end;

procedure TMapForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;


procedure TMapForm.RebuildMap;
var
   i : integer;
begin
Map.Clear;
// it's to not need to do so, but it only demonstrates different ways
// mapping columns and rows - maSingle and maBottom
if ColumnsLB.Count > 0 then
   Map.DimensionToColumn(ColumnsLB.Items[0],maSingle,0);
if RowsLB.Count > 0 then
   Map.DimensionToRow(RowsLB.Items[0],maSingle,0)
else
   begin
   Windows.MessageBox(Handle,PChar('Row always must contain ONE dimension!')
                  ,PChar('Map error')
                  ,MB_ICONERROR+MB_OK);
   Map.Clear;
   Map.DimensionToRow(Map.Cube.Dimension[0].FieldName,maSingle,0);
   end;
for i := 1 to ColumnsLB.COunt - 1 do
    Map.DimensionToColumn(ColumnsLB.Items[i],maBottom,0);
for i := 1 to RowsLB.COunt - 1 do
    Map.DimensionToRow(RowsLB.Items[i],maBottom,0);
Map.Build(HideEmptyColumns1.Checked,HideEmptyRows1.Checked);
DisplayMap;
end;

procedure TMapForm.ColumnsLBDragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
begin
if (Sender = DimensionsLB) or (Sender = RowsLB) then
   Accept := True;
end;


procedure TMapForm.ColumnsLBDragDrop(Sender, Source: TObject; X,
  Y: Integer);
var
   needRebuild : boolean;

procedure AddItem(NewLine : string);
begin
if ColumnsLB.ItemIndex < 0 then
   ColumnsLB.Items.Add(NewLine)
else
   ColumnsLB.Items.Insert(ColumnsLB.ItemAtPos(Point(X,Y),False),NewLine);
end;

begin
needRebuild := False;
if Source = ColumnsLB then
   begin
   AddItem(ColumnsLB.Items[ColumnsLB.ItemIndex]);
   ColumnsLB.Items.Delete(ColumnsLB.ItemIndex);
   needRebuild := True;
   end;
if Source = DimensionsLB then
   begin
   AddItem(DimensionsLB.Items[DimensionsLB.ItemIndex]);
   DimensionsLB.Items.Delete(DimensionsLB.ItemIndex);
   needRebuild := True;
   end;
if (Source = RowsLB) and (RowsLB.Count > 1) then
   begin
   AddItem(RowsLB.Items[RowsLB.ItemIndex]);
   RowsLB.Items.Delete(RowsLB.ItemIndex);
   needRebuild := True;
   end;
if needRebuild then
   RebuildMap;
end;

procedure TMapForm.RowsLBDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
if (Sender = DimensionsLB) or (Sender = ColumnsLB) then
   Accept := True;
end;

procedure TMapForm.RowsLBDragDrop(Sender, Source: TObject; X, Y: Integer);
var
   needRebuild : boolean;

procedure AddItem(NewLine : string);
begin
if RowsLB.ItemIndex < 0 then
   RowsLB.Items.Add(NewLine)
else
   RowsLB.Items.Insert(RowsLB.ItemAtPos(Point(X,Y),False),NewLine);
end;

begin
needRebuild := False;
if Source = RowsLB then
   begin
   AddItem(RowsLB.Items[RowsLB.ItemIndex]);
   RowsLB.Items.Delete(RowsLB.ItemIndex);
   needRebuild := True;
   end;
if Source = DimensionsLB then
   begin
   AddItem(DImensionsLB.Items[DimensionsLB.ItemIndex]);
   DimensionsLB.Items.Delete(DimensionsLB.ItemIndex);
   needRebuild := True;
   end;
if Source = ColumnsLB then
   begin
   AddItem(ColumnsLB.Items[ColumnsLB.ItemIndex]);
   ColumnsLB.Items.Delete(ColumnsLB.ItemIndex);
   needRebuild := True;
   end;
if needRebuild then
   RebuildMap;
end;


procedure TMapForm.DimensionsLBDragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
begin
if (Sender = RowsLB) or (Sender = ColumnsLB) then
   Accept := True;
end;

procedure TMapForm.DimensionsLBDragDrop(Sender, Source: TObject; X,
  Y: Integer);
var
   needRebuild : boolean;
begin
needRebuild := False;
if (Source = RowsLB)  and (RowsLB.Count > 1) then
   begin
   DimensionsLB.AddItem(RowsLB.Items[RowsLB.ItemIndex],nil);
   RowsLB.Items.Delete(RowsLB.ItemIndex);
   needRebuild := True;
   end;
if Source = ColumnsLB then
   begin
   DimensionsLB.AddItem(ColumnsLB.Items[ColumnsLB.ItemIndex],nil);
   ColumnsLB.Items.Delete(ColumnsLB.ItemIndex);
   needRebuild := True;
   end;
if needRebuild then
   RebuildMap;
end;

procedure TMapForm.Savemap1Click(Sender: TObject);
var
   s : string;
   ini : TIniFile;
   i : integer;
begin
s := InputBox('Saving map for ' + Cube.Name,'Enter name for current map','');
if s <> '' then
   begin
   try
      ini := TIniFile.Create(Cube.Name + '.map');
      ini.WriteInteger(s,'Columns',map.ColumnDimCount);
      for i := 0 to Map.ColumnDimCount - 1 do
          ini.WriteString(s,'C'+ IntToStr(i),map.ColumnDimensions[i].FieldName);
      ini.WriteInteger(s,'Rows',map.RowDimCount);
      for i := 0 to Map.RowDimCount - 1 do
          ini.WriteString(s,'R'+ IntToStr(i),map.RowDimensions[i].FieldName);
      finally
      if ini <> nil then
         ini.Free;
      end;
   end;
end;

procedure TMapForm.Loadmap1Click(Sender: TObject);
var
   ini : TIniFile;
   form : TMapSelectForm;
   i,j : integer;
   s : string;
begin
try
   ini := TIniFile.Create(Cube.Name + '.map');
   form := TMapSelectForm.Create(Self);
   ini.ReadSections(form.LB.Items);
   if form.ShowModal = mrOK then
      begin
      s := form.LB.Items[form.LB.ItemIndex];
      j := ini.ReadInteger(s,'Columns',0);
      if j > 0 then
         begin
         ColumnsLB.Clear;
         for i := 0 to j-1 do
             ColumnsLB.AddItem(ini.ReadString(s,'C'+IntToStr(i),''),nil);
         end;
      j := ini.ReadInteger(s,'Rows',0);
      if j > 0 then
         begin
         RowsLB.Clear;
         for i := 0 to j-1 do
             RowsLB.AddItem(ini.ReadString(s,'R'+IntToStr(i),''),nil);
         end;
      RebuildMap;
      Caption := FCube.Name + ' - ' + s;
      end;
   finally
   if form <> nil then
      form.Free;
   if ini <> nil then
      ini.Free;
   end;
end;

procedure TMapForm.Duplicate1Click(Sender: TObject);
var
   map : TMapForm;
begin
try
   map := TMapForm.Create(Owner);
   map.GridSettings := GridSettings;
   map.Cube := FCube;
   except
         map.Free;
   end;
end;


procedure TMapForm.SetGridSettings(const Value: TGridSettings);
begin
  FGridSettings := Value;
end;

procedure TMapForm.PrintMap;
var
   i,j,k,colcnt,rowcnt: integer;
   Rect : TRect;
   hc : THeaderCell;
   w1,w2,w3,pw,h1,h2,h3,ph : integer;
   s : string;
   f,ax,ay,af : double;
   mf : TMetaFile;
   mfc : TMetafileCanvas;
   CheckSelection : boolean;

procedure DrawPage;
var
   i,j,k : integer;
begin
// print column header - it's removed from common loop beacuse of different column count
// in header when measures > 1
j := 0;
i := 0;
while i < (colcnt - Grid.FixedCols) do
    begin
    if CheckSelection and (i < Grid.Selection.Left) then
       begin
       Inc(i);
       Continue;
       end;
    hc := FColList.Items[j];
    Inc(j);
    w1 := -1;
    for k := 0 to (hc.Range[0] + Grid.FixedCols) -1 do
        begin
        if CheckSelection and ((k < grid.Selection.Left) and (k > Grid.FixedCols-1))   then
           Continue;
        w1 := w1 + Grid.ColWidths[k] + 1;
        end;
    w2 := 0;
    for k := 0 to hc.Range[1] + Grid.FixedCols do
        begin
        if CheckSelection and ((k < grid.Selection.Left) and (k > Grid.FixedCols-1))  then
           Continue;
        w2 := w2 + Grid.ColWidths[k] + 1;
        end;
    h1 := 0;
    for k := 0 to hc.Level - 1 do
        h1 := h1 + Grid.RowHeights[k] + 1;
    h2 := 1;
    for k := 0 to Grid.FixedRows - 1 do
        h2 := h2 + Grid.RowHeights[k] + 1;
    Rect.Left := Trunc(w1 * ax);
    Rect.Right := Trunc(w2 * ax);
    Rect.Top := Trunc(h1 * ay);
    Rect.Bottom := Trunc(h2 * ay);
    mfc.Brush.Color := clBlack;;
    mfc.FrameRect(Rect);
    mfc.Brush.Color := GridSettings.ColumnHeaderColor;
    mfc.Font.Assign(GridSettings.ColumnHeaderFont);
    mfc.Font.Size := Trunc(mfc.Font.Size * af);
    Dec(Rect.Right);
    Dec(Rect.Bottom);
    Inc(Rect.Left);
    Inc(Rect.Top);
    w3 := mfc.TextWidth(hc.Text);
    w1 := ((Rect.Right - Rect.Left) div 2) - (w3 div 2) - 1;
    w2 := 3;
    mfc.TextRect(Rect,Rect.Left + w1,Rect.Top + w2,hc.Text);
    if hc.Range[0] >= i then
       Inc(i);
    if hc.Range[0] >= i then
       Break;
    end;

for j := 0 to rowcnt - 1  do
    begin
    for i := 0 to colcnt - 1 do
        begin
        if (i < Grid.FixedCols) and (j >= Grid.FixedRows) then
           begin
           if CheckSelection and (j < Grid.Selection.Top) then
              Continue;
           // print row header cells
           hc := FRowList.Items[j-Grid.FixedRows];
           h1 := 0;
           for k := 0 to (hc.Range[0] + Grid.FixedRows) - 1 do
               begin
               if CheckSelection and ((k < grid.Selection.Top) and (k > Grid.FixedRows-1)) then
                  Continue;
               h1 := h1 + Grid.RowHeights[k] + 1;
               end;
           h2 := 0;
           for k := 0 to hc.Range[1] + Grid.FixedRows do
               begin
               if CheckSelection and ((k < grid.Selection.Top) and (k > Grid.FixedRows-1))  then
                  Continue;
               h2 := h2 + Grid.RowHeights[k] + 1;
               end;
           w1 := 0;
           for k := 0 to hc.Level - 1 do
               w1 := w1 + Grid.ColWidths[k] + 1;
           w2 := 1;
           for k := 0 to Grid.FixedCols - 1 do
               w2 := w2 + Grid.ColWidths[k] + 1;
           Rect.Left := Trunc(w1*ax);
           Rect.Right := Trunc(w2*ax);
           Rect.Top := Trunc(h1*ay);
           Rect.Bottom := Trunc(h2*ay);
           mfc.Brush.Color := clBlack;
           mfc.FrameRect(Rect);
           mfc.Brush.Color := GridSettings.RowheaderColor;
           mfc.Font.Assign(GridSettings.RowHeaderFont);
           mfc.Font.Size := Trunc(mfc.Font.Size * af);
           Dec(Rect.Right);
           Dec(Rect.Bottom);
           Inc(Rect.Left);
           Inc(Rect.Top);
           w3 := mfc.TextHeight(hc.Text);
           w2 := ((Rect.Bottom - Rect.Top) div 2) - (w3 div 2) - 1;
           mfc.TextRect(Rect,Rect.Left + 8,Rect.Top + w2,hc.Text);
           end
        else
        // print data cells
        if (i >= Grid.FixedCols) and (j >= grid.FixedRows) then
           begin
           if CheckSelection then
              begin
              if i < Grid.Selection.Left then
                 Continue;
              if j < Grid.Selection.Top then
                 Continue;
              end;
           w1 := -1;
           for k := 0 to i-1 do
               begin
               if CheckSelection and ((k < grid.Selection.Left) and (k > Grid.FixedCols-1))  then
                  Continue;
               w1 := w1 + Grid.ColWidths[k] + 1;
               end;
           w2 := 0;
           for k := 0 to i do
               begin
               if CheckSelection and ((k < grid.Selection.Left) and (k > Grid.FixedCols-1))  then
                  Continue;
               w2 := w2 + Grid.ColWidths[k] + 1;
               end;
           h1 := 0;
           for k := 0 to j - 1 do
               begin
               if CheckSelection and ((k < grid.Selection.Top) and (k > Grid.FixedRows-1))  then
                  Continue;
               h1 := h1 + Grid.RowHeights[k] + 1;
               end;
           h2 := 1;
           for k := 0 to j do
               begin
               if CheckSelection and ((k < grid.Selection.Top) and (k > Grid.FixedRows-1))  then
                  Continue;
               h2 := h2 + Grid.RowHeights[k] + 1;
               end;
           Rect.Left := Trunc(w1*ax);
           Rect.Right := Trunc(w2*ax);
           Rect.Top := Trunc(h1*ay);
           Rect.Bottom := Trunc(h2*ay);
           w1 := i - Grid.FixedCols;
           w2 := j - Grid.FixedRows;
           f := Map.Cells[w1 div FMeasureCount,w2,w1 mod FMeasureCount];//[i-Grid.FixedCols,j-Grid.FixedRows,(i-Grid.FixedCols) mod FMeasureCount];
           s := FloatToStrF(f,ffNumber,9,2);
           mfc.Brush.Color := clBlack;
           mfc.FrameRect(Rect);
           mfc.Brush.Color := GridSettings.CellColor;
           if f > 0 then
              mfc.Font.Assign(GridSettings.PositiveCellFont)
           else
           if f = 0 then
              mfc.Font.Assign(GridSettings.ZeroCellFont)
           else
           if f < 0 then
              mfc.Font.Assign(GridSettings.NegativeCellFont);
           mfc.Font.Size := Trunc(mfc.Font.Size * af);
           Dec(Rect.Right);
           Dec(Rect.Bottom);
           Inc(Rect.Left);
           Inc(Rect.Top);
           w3 := mfc.TextWidth(s);
           w1 := (Rect.Right - Rect.Left) - (w3 + 8);
           w3 := mfc.TextHeight(hc.Text);
           w2 := (Rect.Bottom - Rect.Top) div 2 - (w3 div 2) - 1;
           mfc.TextRect(Rect,Rect.Left + w1,Rect.Top + w2,s);
           end;
        end;
    end;
end;

begin
if ((Grid.Selection.Right - Grid.Selection.Left) > 1) or
   ((Grid.Selection.Bottom - grid.Selection.Top) > 1)  then
   begin
   colcnt := grid.Selection.Right+1;
   rowcnt := grid.Selection.Bottom+1;
   CheckSelection := True;
   end
else
   begin
   colcnt := grid.ColCount;
   rowcnt := grid.RowCount;
   CheckSelection := False;
   end;
pw := 0;
for i := 0 to colcnt - 1 do
    begin
    if CheckSelection and
       ((i < grid.Selection.Left) and (i > Grid.FixedCols-1))   then
       Continue;
    pw := pw + Grid.ColWidths[i]+1;
    end;
ph := 0;
for i := 0 to rowcnt - 1 do
    begin
    if CheckSelection and
       ((i < grid.Selection.Top) and (i > Grid.FixedRows-1))   then
       Continue;
    ph := ph + Grid.RowHeights[i]+1;
    end;
try
   PrintPreviewForm := TPrintPreviewForm.Create(Application);
   PrintPreviewForm.Show;

   mf := TMetaFile.Create;
   mfc := TMetaFileCanvas.Create(mf,Printer.Handle);

   ax := 1;
   ay := 1;
   af := 1;

   DrawPage;
   mfc.Free;
   mf.SaveToFile('zCubeExplorerPrint.wmf');

   PrintPreviewForm.PrintPanel.Invalidate;
   PrintPreviewForm.PrintPanel.Update;
   SetMapMode(PrintPreviewForm.PrintPanel.Canvas.Handle,MM_ISOTROPIC);
   SetWindowExtEx(PrintPreviewForm.PrintPanel.Canvas.Handle,pw,ph,nil);
   ScaleWindowExtEx(PrintPreviewForm.PrintPanel.Canvas.Handle,pw,pw,ph,ph,0);
   SetViewPortExtEx(PrintPreviewForm.PrintPanel.Canvas.Handle,PrintPreviewForm.PrintPanel.Width,PrintPreviewForm.PrintPanel.Height,nil);
   SetViewPortOrgEx(PrintPreviewForm.PrintPanel.Canvas.Handle,0,0,nil);
   PrintPreviewForm.PrintPanel.Canvas.Draw(0,0,mf);
   while PrintPreviewForm.Visible do
         Application.ProcessMessages;
   if PrintPreviewForm.ModalResult = mrOk then
      begin
      mfc := TMetaFileCanvas.Create(mf,Printer.Handle);
      ay := Printer.PageHeight / ph;
      ax := Printer.PageWidth / pw;
      if ax < ay then
         af := ax
      else
         af := ay;
      if (ax < 1) or (ay < 1) then
         begin
         ax := 1;
         ay := 1;
         af := 1;
         end;
      mf.Clear;
      DrawPage;
      mfc.Free;
      mf.SaveToFile('zCubeExplorerPrint.wmf');
      Printer.Canvas.Draw(0,0,mf);
      end;

      finally
             PrintPreviewForm.Free;
             mf.Free;
      end;
end;

end.
