cmvbt/src/TableVBT.m3


 Copyright 1996-2000 Critical Mass, Inc. All Rights Reserved. 
 See file COPYRIGHT-CMASS for details. 

MODULE TableVBT;

IMPORT GridSplit, VBT, TextVBT;
IMPORT HVSplit, Axis, Font;
IMPORT Pixmap, ShadowedFeedbackVBT, PaintOp, Shadow, SwitchVBT;
IMPORT Rect, Point, Cursor;
IMPORT Split, ShadowedBarVBT, HighlightVBT, VBTClass;

REVEAL
  Private = HVSplit.T BRANDED OBJECT END;

  T = Public BRANDED OBJECT
    hl: HighlightVBT.T;
    grid: Content;
    hdg: Heading;
  OVERRIDES
    init := Init;
    contents := Contents;
    heading := DefaultHeading;
    content := DefaultContent;
    insert  := DefaultInsert;
    delete  := DefaultDelete;
    numrows := NumRows;
reshape := Reshape;
  END;

TYPE
  Heading = GridSplit.T BRANDED OBJECT
    grid: GridSplit.T;
    state: State;
    which: INTEGER := -1;
    lmp: Point.T; (* last mouse position *)
    resizing: BOOLEAN := FALSE;
  OVERRIDES
    mouse    := Mouse;
    position := Position;
  END;

  Content = GridSplit.T OBJECT
    parent: T;
    loc: GridSplit.Location;
  OVERRIDES
    mouse := ContentMouse;
    reshape := ContentReshape;
  END;

  State = {None, RowResize, Select};

PROCEDURE DefaultHeading (<*UNUSED*>v: T;
                           <*UNUSED*>r: CARDINAL;
                           <*UNUSED*>READONLY cd: VBT.MouseRec) =
  BEGIN
    (* Empty body *)
  END DefaultHeading;

PROCEDURE DefaultContent (<*UNUSED*>v: T;
                           <*UNUSED*>r: CARDINAL;
                           <*UNUSED*>READONLY cd: VBT.MouseRec) =
  BEGIN
    (* Empty body *)
  END DefaultContent;

VAR
  litegray := PaintOp.FromRGB (0.75, 0.75, 0.75);
  darkgray := PaintOp.FromRGB (0.25, 0.25, 0.25);
  litemedigray := PaintOp.FromRGB(0.675, 0.675, 0.675);
  white    := PaintOp.FromRGB (1.00, 1.00, 1.00);
  black    := PaintOp.FromRGB (0.00, 0.00, 0.00);

TYPE
  Header = SwitchVBT.T OBJECT
    which: CARDINAL;
    owner: T;
  OVERRIDES
    callback := Callback;
  END;

PROCEDURE Callback (self: Header; READONLY cd: VBT.MouseRec) =
BEGIN
  self.owner.heading(self.which, cd);
END Callback;

PROCEDURE Contents(v: T): GridSplit.T =
  BEGIN
    RETURN v.grid;
  END Contents;

PROCEDURE ContentReshape(v: Content; READONLY cd: VBT.ReshapeRec) RAISES {} =
  VAR
    r: Rect.T;
  BEGIN
    IF v.loc.row >= 0 AND v.loc.pl = GridSplit.Placement.InsideCell THEN
      r := GridSplit.GetCoord (v, top := v.loc.row, bottom := v.loc.row);
      HighlightVBT.SetRect(v, r, inset := LAST(CARDINAL));
    END;
    GridSplit.T.reshape(v, cd);
  END ContentReshape;

PROCEDURE Position (v: Heading; READONLY cd: VBT.PositionRec) =
  VAR
    loc: GridSplit.Location;
  BEGIN
    IF v.state = State.None THEN
      loc := GridSplit.Locate (v, cd.cp.pt);
      VBT.SetCage (v, VBT.CageFromPosition(cd.cp));
      IF loc.pl = GridSplit.Placement.VerMargin THEN
        VBT.SetCursor (v, hresize);
      ELSE
        VBT.SetCursor (v, Cursor.DontCare);
      END;
    ELSIF cd.cp.gone THEN
      VBT.SetCursor (v, hresize);
      VBT.SetCage (v, VBT.GoneCage);
    ELSE
      VBT.SetCage (v, VBT.CageFromPosition(cd.cp));
    END;
  END Position;

PROCEDURE Mouse(v: Heading; READONLY cd: VBT.MouseRec) RAISES {} =
  VAR
    loc := GridSplit.Locate (v, cd.cp.pt);
  BEGIN
    CASE cd.clickType OF
    | VBT.ClickType.FirstDown =>
      VBT.SetCage (v, VBT.CageFromPosition(cd.cp));
      IF loc.row > 0 THEN
        v.which := loc.row;
      ELSIF loc.pl = GridSplit.Placement.VerMargin AND loc.row = 0 THEN
        v.state := State.RowResize;
        v.which := loc.col;
        v.lmp := cd.cp.pt;
        VBT.SetCursor (v, hresize);
      ELSE
        GridSplit.T.mouse (v, cd);
      END;
    | VBT.ClickType.LastUp =>
      VBT.SetCursor (v, Cursor.DontCare);
      IF loc.col > -1 AND loc.row = 0 AND v.state = State.RowResize THEN
        VAR
          delta := cd.cp.pt.h - v.lmp.h;
          which := v.which;
          newsize: INTEGER;
        BEGIN
          IF which < 0 THEN
            which := 0;
            delta := -delta;
          END;

          newsize := GridSplit.GetColWidth(v, which) + delta;

          IF newsize > 0 THEN
            GridSplit.SetColWidth (v, which, newsize);
            GridSplit.SetColWidth (v.grid, which, newsize);
          END;
        END;
      ELSE
        GridSplit.T.mouse(v, cd);
      END;
      v.state := State.None;
    ELSE
      v.state := State.None;
    END;
  END Mouse;

PROCEDURE ContentMouse(v: Content; READONLY cd: VBT.MouseRec) RAISES {} =
  VAR
first: BOOLEAN := TRUE;
    r: Rect.T;
  BEGIN
    v.loc := GridSplit.Locate (v, cd.cp.pt);
    CASE cd.clickType OF
    | VBT.ClickType.FirstDown =>
      IF v.loc.row >= 0 AND v.loc.pl = GridSplit.Placement.InsideCell THEN
        r := GridSplit.GetCoord (v, top := v.loc.row, bottom := v.loc.row);
        HighlightVBT.Invert (v, r, inset := LAST(CARDINAL));
        v.parent.content(v.loc.row, cd);
      END;
    ELSE
    END;
    GridSplit.T.mouse(v, cd);
  END ContentMouse;

PROCEDURE Init(v: T;
               READONLY names: ARRAY OF TEXT;
               READONLY sizes: ARRAY OF CARDINAL;
               font: Font.T;
               nrows: CARDINAL := 0;
               rowsize, colsize: CARDINAL;
               op: PaintOp.T := PaintOp.BgFg;
               txt: Pixmap.T := Pixmap.Solid): T =
VAR
  ncols: CARDINAL := NUMBER(names);

  shadow := Shadow.New(size := 0.5, bg := PaintOp.Bg,
                                    fg := op,
                                    light := litegray,
                                    dark  := darkgray);

  cq := PaintOp.MakeColorQuad (litemedigray, black);

BEGIN

  <* ASSERT NUMBER(names) = NUMBER(sizes) *>

  v.grid := NEW(Content, parent := v).init (ncols := ncols,
                                       nrows := nrows,
                                       rowsize := rowsize,
                                       colsize := colsize,
                                       margin := 1,
                                       bg := darkgray,
                                       fg := white,
                                       txt := txt);

  (* We should use a better method for setting the size of col headings. *)

  v.hdg := NEW(Heading, grid := v.grid).init (ncols := ncols,
                                      nrows := 1,
                                      rowsize := 30,
                                      colsize := colsize,
                                      margin := 1,
                                      bg := darkgray,
                                      fg := white,
                                      txt := txt);

  EVAL HVSplit.T.init(v, Axis.T.Ver);
  Split.AddChild (v, v.hdg);
  Split.AddChild (v, NEW(ShadowedBarVBT.T)
                        .init(Axis.T.Ver, shadow,
                         Shadow.Style.Chiseled));
  v.hl := HighlightVBT.New(v.grid);
  Split.AddChild (v, v.hl);

  FOR i := FIRST(names) TO LAST(names) DO
    WITH inside = NEW(ShadowedFeedbackVBT.T)
                           .init(TextVBT.New(names[i],
                                 fnt := font, bgFg := cq,
                                 halign := 0.0),
                                 shadow := shadow),
         btn = NEW(Header,
                   owner := v,
                   which := i).init (inside) DO

       GridSplit.Put (v.hdg, 0, i, btn);
       GridSplit.SetColWidth (v.hdg, i, sizes[i]);
       GridSplit.SetColWidth (v.grid,    i, sizes[i]);
    END;
  END;

  FOR row := 0 TO nrows - 1 DO
    FOR col := 0 TO ncols - 1 DO
      GridSplit.Put (v.grid, row, col, TextVBT.New(""));
    END;
  END;

  RETURN v;
END Init;

PROCEDURE NumRows(v: T): CARDINAL =
  BEGIN
    RETURN GridSplit.NumRows(v.grid);
  END NumRows;

PROCEDURE DefaultInsert(v: T; row: CARDINAL; READONLY data: ARRAY OF VBT.T) =
  VAR
    numrows := GridSplit.NumRows(v.grid);
  BEGIN
    IF row > numrows THEN
      row := numrows;
    END;
    GridSplit.InsertRow(v.grid, row, data);
  END DefaultInsert;

PROCEDURE DefaultDelete(v: T; row: CARDINAL; VAR data: ARRAY OF VBT.T) =
  BEGIN
    GridSplit.RemoveRow(v.grid, row, data);
  END DefaultDelete;

VAR
  hresize :=  Cursor.FromName (ARRAY OF TEXT {"XC_sb_h_double_arrow"});

<*UNUSED*> VAR
  vresize :=  Cursor.FromName (ARRAY OF TEXT {"XC_sb_v_double_arrow"});

BEGIN
END TableVBT.