Код:
MODULE SacredTables;
IMPORT Views, Strings, Ports, Controls, Properties, Stores, Controllers, Fonts, Meta, Dialog
,StdLog;
TYPE
String = POINTER TO ARRAY 256 OF CHAR;
CellProperties* = RECORD (* оформление ячейки!!! *)
alignX*, (* -1 влево, 0 по центру, 1 - вправо*)
alignY*:INTEGER; (* -1 вверх, 0 по центру, 1 - вниз *)
color*, bkColor* : Ports.Color;
font* : Fonts.Font;
END;
(* Это как для связи с данными, так и для оформления!!! *)
TableData* = POINTER TO ABSTRACT RECORD
END;
TableDataContainer* = RECORD (* это чтобы Мету обмануть... *)
realData* : TableData;
END;
TableValue = RECORD (Meta.Value) (* шо за магия такая??? *)
table : TableDataContainer
END;
Control* = POINTER TO EXTENSIBLE RECORD (Controls.Control)
origin*, (* сдвиг (прокрутка) *)
fixed*, (* непрокручиваемых столбцов/колонок *)
size* : Ports.Point; (* размер отображения *)
cursor* : INTEGER; (* позиция курсора (выделенной строки) *)
END;
TableDataExample* = POINTER TO RECORD(TableData)
END;
VAR
test* : TableDataContainer; (* для диалога *)
TE : TableDataExample;
defaultCell : CellProperties;
PROCEDURE RoundedDIV(I1, I2 : INTEGER) : INTEGER;
VAR
temp : INTEGER;
BEGIN
temp := I1 DIV I2;
IF ABS(I1-temp*I2)< ABS(I1-(temp+1)*I2) THEN
RETURN temp
ELSE
RETURN temp+1
END;
END RoundedDIV;
PROCEDURE (this:TableData) CellProperties* (colIndex, rowIndex : INTEGER; VAR properties: CellProperties), NEW, EXTENSIBLE;
BEGIN
IF rowIndex=-1 THEN
properties.alignX := 0;
ELSE
properties.alignX := 1;
END;
properties.alignY := 0;
IF (rowIndex <0) OR (colIndex < 0) THEN
properties.bkColor := Ports.grey12;
END;
END CellProperties;
PROCEDURE (this:TableData) RowHeight* (rowIndex:INTEGER):INTEGER, NEW, EXTENSIBLE;
BEGIN
RETURN 3*Ports.mm
END RowHeight;
PROCEDURE (this:TableData) ColWidth* (colIndex:INTEGER):INTEGER, NEW, EXTENSIBLE;
BEGIN
RETURN 20*Ports.mm
END ColWidth;
PROCEDURE (this:TableData) RowCount*():INTEGER, NEW, EXTENSIBLE;
BEGIN
RETURN 14 (* не считая заголовков*)
END RowCount;
PROCEDURE (this:TableData) ColCount* ():INTEGER, NEW, EXTENSIBLE;
BEGIN
RETURN 13 (* не считая заголовков*)
END ColCount;
PROCEDURE (this:TableData) Item* (ColIndex, RowIndex:INTEGER; OUT Str : ARRAY OF CHAR), NEW, ABSTRACT;
PROCEDURE (this:Control) GetRealData* ():TableData, NEW;
VAR
ok : BOOLEAN;
data : TableValue;
BEGIN
IF this.item.Valid() THEN
this.item.GetVal(data, ok);
ELSE
ok := FALSE;
END;
IF ok & (data.table.realData#NIL) THEN
RETURN data.table.realData
ELSE
RETURN NIL
END;
END GetRealData;
PROCEDURE (this:Control) Width* ():INTEGER, NEW; (* полная ширина всей таблицы *)
VAR
i, w: INTEGER;
data : TableData;
BEGIN
w:=0;
data := this.GetRealData();
IF data # NIL THEN
FOR i:=-1 TO data.ColCount()-1 DO
INC(w, data.ColWidth(i))
END;
END;
RETURN w
END Width;
PROCEDURE (this:Control) GetHeight* ():INTEGER, NEW; (* полная высота всей таблицы *)
VAR
i, h: INTEGER;
data : TableData;
BEGIN
h:=0;
data := this.GetRealData();
IF data # NIL THEN
FOR i:=-1 TO data.RowCount()-1 DO
INC(h, data.RowHeight(i))
END;
END;
RETURN h
END GetHeight;
PROCEDURE (this:Control) OriginWidth* ():INTEGER, NEW; (* ширина сдвинутой части таблицы *)
VAR
i, w: INTEGER;
data : TableData;
BEGIN
w:=0;
data := this.GetRealData();
IF data # NIL THEN
FOR i:=this.fixed.x TO this.fixed.x+this.origin.x-1 DO
INC(w, data.ColWidth(i))
END;
END;
RETURN w
END OriginWidth;
(* высота сдвинутой части таблицы *)
PROCEDURE (this:Control) OriginHeight* ():INTEGER, NEW;
VAR
data : TableData;
BEGIN
data := this.GetRealData();
IF data # NIL THEN
RETURN this.origin.y-1 - this.fixed.y;
END;
RETURN 0
END OriginHeight;
PROCEDURE (v: Control) ScrolledRowsOnPage():INTEGER, NEW;
VAR
w, h, y, height, rowInd, res, rowCount : INTEGER;
data : TableData;
BEGIN
data := v.GetRealData();
res := 0;
IF data # NIL THEN
v.context.GetSize(w, h);
rowInd := -1; (*v.Origin.y;*)
y := data.RowHeight(rowInd); (* t *)
rowCount := data.RowCount();
WHILE (y < h) & (rowInd < rowCount) DO
IF rowInd >= v.origin.y+v.fixed.y THEN
INC(res); (* добавляем строчку *)
END;
INC(rowInd);
IF rowInd = v.fixed.y THEN (* отобразили все непрокручиваемые *)
INC(rowInd, v.origin.y);
END;
height := data.RowHeight(rowInd);
INC(y, height);
END;
END;
RETURN res
END ScrolledRowsOnPage;
PROCEDURE (v: Control) Restore* (f: Views.Frame; l, t, r, b: INTEGER);
(* SacredTables ForexMain Fonts Views*)
VAR
rowInd, colInd, (* индексы текущей ячейки *)
x, y, (* расположение левого верхнего угла текущей ячейки *)
xStr, yStr, (* позиция вывода строки с учётом выравнивания *)
width, height : INTEGER; (* ширина и высота текущей ячейки *)
rowCount, colCount : INTEGER; (* число строк и колонок. Кэш-переменные, для ускорения*)
data : TableData;
otstup : INTEGER;
str : ARRAY 100 OF CHAR;
strWidth, (* ширина строки *)
strHeight, asc, dsc : INTEGER; (* высота строки *)
properties : CellProperties; (* оформление ячейки *)
BEGIN
(*l, t, r, b - Это не границы отображения, это - границы той части, которую надо перерисовать!!! *)
otstup := 2 * Ports.mm DIV 10;
data := v.GetRealData();
defaultCell.font.GetBounds(asc, dsc, strWidth);
strHeight := asc-dsc;
IF data = NIL THEN
str := "No data";
strWidth := defaultCell.font.StringWidth(str$);
width := f.r-f.l; (* ширина контрола*)
height := f.b-f.t; (* высота контрола *)
xStr := (width - strWidth) DIV 2;
yStr := (height + strHeight) DIV 2;
f.DrawString(xStr, yStr, defaultCell.color, str$, defaultCell.font);
RETURN
END;
rowCount := data.RowCount();
colCount := data.ColCount();
rowInd := -1; (*v.Origin.y;*)
y := 0; (* t *)
f.DrawRect(l, t, r, b, Ports.fill, defaultCell.bkColor); (* зальём всю область *)
WHILE (y < b) & (rowInd < rowCount) DO
height := data.RowHeight(rowInd);
IF y+height > t THEN
colInd := -1; (*v.Origin.x;*)
x := 0; (* l *)
f.DrawLine(x, y, x+width, y, f.dot, Ports.grey25); (**)
WHILE (x < r) & (colInd < colCount) DO
width := data.ColWidth(colInd);
IF x+width > l THEN
properties := defaultCell;
data.CellProperties(colInd, rowInd, properties);
data.Item(colInd, rowInd, str);
IF defaultCell.bkColor#properties.bkColor THEN (* перекрасим, только если надо *)
f.DrawRect(x, y, x+width, y+height, Ports.fill, properties.bkColor);
(* f.DrawRect(x, y, x+width, y+height, f.dot, Ports.black);*)
END;
(* с выравниванием по высоте не так просто... *)
properties.font.GetBounds(asc, dsc, strWidth);
strHeight := asc-dsc;
CASE properties.alignY OF
-1: yStr := y+strHeight+otstup;
|0: yStr := y+(height+strHeight) DIV 2+otstup;
|1: yStr := y+height-otstup;
END;
strWidth := properties.font.StringWidth(str);
CASE properties.alignX OF
-1: xStr := x+otstup;
|0: xStr := x + (width - strWidth) DIV 2;
|1: xStr := x + width - strWidth-otstup;
END;
f.DrawString(xStr, yStr, properties.color, str, properties.font);
END; (* IF *)
INC(colInd);
IF colInd = v.fixed.x THEN (* отобразили все непрокручиваемые *)
INC(colInd, v.origin.x);
END;
IF y + height >= b THEN (* это последняя строчка *)
(* рисуем линии слева *)
f.DrawLine(x, MIN(t, y + height), x, MIN(b, y + height), f.dot, Ports.grey25); (* вертикаль слева*)
(* а если это самый правый то и справа *)
IF colInd = colCount THEN (* вертикаль справа*)
f.DrawLine(x+width, MIN(t, y + height), x+width, MIN(b, y + height), f.dot, Ports.grey25);
END;
END;
x := x + width;
END; (* цикл по столбцам (Х) *)
(* нарисуем горизонтали *)
f.DrawLine(MIN(l, x), y, MIN(r, x), y, f.dot, Ports.grey25); (* горизонталь сверху *)
(* а если это самый нижний то и снизу *)
IF rowInd = rowCount-1 THEN (* горизонталь снизу *)
f.DrawLine(MIN(l, x), y+height, MIN(r, x), y+height, f.dot, Ports.grey25);
END;
END; (* IF *)
INC(rowInd);
IF rowInd = v.fixed.y THEN (* отобразили все непрокручиваемые *)
INC(rowInd, v.origin.y);
END;
y := y + height;
END; (* цикл по строкам (Y) *)
END Restore;
PROCEDURE (v: Control) Internalize2- (VAR rd: Stores.Reader);
VAR version: INTEGER;
BEGIN
rd.ReadVersion(0, 1, version);
IF ~rd.cancelled THEN
rd.ReadInt(v.origin.y);
rd.ReadInt(v.origin.x);
rd.ReadInt(v.fixed.y);
rd.ReadInt(v.fixed.x);
END
END Internalize2;
PROCEDURE (v: Control) Externalize2- (VAR wr: Stores.Writer);
BEGIN
wr.WriteVersion(0);
wr.WriteInt(v.origin.y);
wr.WriteInt(v.origin.x);
wr.WriteInt(v.fixed.y);
wr.WriteInt(v.fixed.x);
END Externalize2;
PROCEDURE (v: Control) CopyFromSimpleView2- (source: Controls.Control);
BEGIN
WITH source: Control DO
v.origin.y := source.origin.y;
v.origin.x := source.origin.x;
v.fixed.y := source.fixed.y;
v.fixed.x := source.fixed.x;
END
END CopyFromSimpleView2;
PROCEDURE (v: Control) HandlePropMsg2- (VAR msg: Properties.Message);
VAR
sizeProp: Properties.SizeProp;
prop: Properties.Property;
BEGIN
WITH msg: Properties.SizePref DO
IF (msg.w = Views.undefined) OR (msg.h = Views.undefined) THEN
msg.w := v.size.x;
msg.h := v.size.y (* размеры по-умолчанию *)
END
(* | msg: Properties.ResizePref DO
msg.horFitToWin := TRUE;
msg.verFitToWin := TRUE *)
| msg: Properties.FocusPref DO
msg.setFocus := TRUE (* обязательно, иначе прокрутки не будет!!! *)
| msg: Properties.PollMsg DO
NEW(sizeProp);
sizeProp.width := v.size.x;
sizeProp.height := v.size.y;
sizeProp.valid := {Properties.size};
sizeProp.known := {Properties.size};
Properties.Insert(msg.prop, sizeProp)
| msg: Properties.SetMsg DO
prop := msg.prop;
WHILE prop # NIL DO
WITH prop: Properties.SizeProp DO
IF Properties.size IN prop.valid THEN v.size.x := prop.width; v.size.y := prop.height; END
ELSE
END;
prop := prop.next
END;
Views.Update(v, Views.keepFrames)
ELSE (* игнорировать другие сообщения *)
END
END HandlePropMsg2;
PROCEDURE (v: Control) HandleCtrlMsg2- (f: Views.Frame;
VAR msg: Controllers.Message; VAR focus: Views.View);
VAR
w, h : INTEGER;
changed : BOOLEAN;
data : TableData;
colCount, rowCount : INTEGER;
BEGIN
data := v.GetRealData();
IF data = NIL THEN
RETURN
END;
colCount := data.ColCount();
rowCount := data.RowCount();
changed := TRUE; (* временно *)
WITH msg: Controllers.ScrollMsg DO
v.context.GetSize(w, h); (* получаем текущий размер *)
IF msg.vertical THEN
CASE msg.op OF
Controllers.decLine: IF v.origin.y>0 THEN DEC(v.origin.y); END;
|Controllers.incLine: IF v.origin.y+ v.ScrolledRowsOnPage() < rowCount THEN INC(v.origin.y); END;
|Controllers.decPage: IF v.origin.y>0 THEN DEC(v.origin.y, v.ScrolledRowsOnPage());
v.origin.y := MAX(v.origin.y, 0) END;
|Controllers.incPage: IF v.origin.y+ v.ScrolledRowsOnPage() < rowCount THEN INC(v.origin.y, v.ScrolledRowsOnPage()); END;
|Controllers.gotoPos: v.origin.y := msg.pos;
END;
ELSE
CASE msg.op OF
Controllers.decLine: IF v.origin.x>0 THEN DEC(v.origin.x); END;
|Controllers.incLine: IF v.OriginWidth()+w<v.Width() THEN INC(v.origin.x); END;
|Controllers.decPage: IF v.origin.x>0 THEN DEC(v.origin.x); END;
|Controllers.incPage: IF v.origin.x<colCount THEN INC(v.origin.x); END;
|Controllers.gotoPos: v.origin.x := RoundedDIV(msg.pos, data.ColWidth(-1));
END;
END;
msg.done := TRUE;
IF changed THEN Views.Update(v, Views.keepFrames) END
| msg: Controllers.PollSectionMsg DO (* определяем необходимость и параметры прокрутки *)
v.context.GetSize(w, h); (* получаем текущий размер *)
IF msg.vertical THEN
msg.wholeSize := rowCount;
(* StdLog.Int(v.Height()); *)
msg.partSize := v.ScrolledRowsOnPage();
msg.partPos := v.origin.y;
ELSE
msg.wholeSize := v.Width();
msg.partSize := w;
msg.partPos := v.OriginWidth();
END;
msg.valid := msg.partSize < msg.wholeSize;
msg.done := TRUE
ELSE
END;
END HandleCtrlMsg2;
PROCEDURE (c: Control) CheckLink- (VAR ok: BOOLEAN);
VAR item: Meta.Item; mod: Meta.Name; name: Meta.Name;
BEGIN
item := c.item; ok := TRUE;
IF (item.typ = Meta.recTyp) THEN
item.GetTypeName(mod, name);
IF (mod = "SacredTables") & (name = "TableDataContainer") THEN
ok := TRUE
ELSE
ok := FALSE
END
ELSE ok := FALSE
END
END CheckLink;
PROCEDURE (c: Control) Update- (f: Views.Frame; op, from, to: INTEGER);
BEGIN
Views.Update(c, Views.keepFrames)
END Update;
PROCEDURE (this:TableDataExample) Item* (colIndex, rowIndex:INTEGER; OUT str : ARRAY OF CHAR);
BEGIN
ASSERT(rowIndex>=-1); ASSERT(rowIndex<this.RowCount());
ASSERT(colIndex>=-1); ASSERT(colIndex<this.ColCount());
IF colIndex=-1 THEN
Strings.IntToString(rowIndex, str);
ELSIF rowIndex=-1 THEN
Strings.IntToString(colIndex, str);
ELSE
Strings.IntToString(colIndex*rowIndex, str);
END;
END Item;
(*
PROCEDURE (c: Control) HandleViewMsg2- (f: Views.Frame; VAR msg: Views.Message);
BEGIN
Views.Update(c, Views.rebuildFrames);
END HandleViewMsg2;
PROCEDURE (c: Control) UpdateList- (f: Views.Frame);
BEGIN
END UpdateList;
*)
PROCEDURE Deposit*;
VAR v: Control;
BEGIN
NEW(v);
Views.Deposit(v)
END Deposit;
PROCEDURE DepositControl*;
VAR
p: Controls.Prop;
c: Control;
BEGIN
NEW(p);
p.link := ""; p.label := ""; p.guard := ""; p.notifier := "";
p.level := 0; p.opt[Controls.sorted] := FALSE;
NEW(c);
Controls.OpenLink(c, p);
c.size.x := 62 * Ports.mm;
c.size.y := 18 * Ports.mm; (* размеры по-умолчанию *)
Views.Deposit(c)
END DepositControl;
PROCEDURE Link*;
BEGIN
NEW(TE);
test.realData := TE;
Dialog.Update(test);
END Link;
BEGIN
defaultCell.alignX := -1; (* -1 влево, 0 по центру, 1 - вправо*)
defaultCell.alignY := 0; (* -1 вверх, 0 по центру, 1 - вниз*)
defaultCell.color := Ports.black;
defaultCell.bkColor := Ports.white;
defaultCell.font := Fonts.dir.Default();
END SacredTables.
:commander: "SacredTables.Deposit; StdCmds.Open"
:commander: "SacredTables.DepositControl;StdCmds.PasteView;StdCmds.PasteViewGuard"
Плюс пример использования:
Код:
StdCoder.Decode I3aMgJqQIJqQotmPYB4.,0 1Ak...uD....58FTuPE,5TWyqlKrqKKrGrtu
mdGLmGorCquU2hgnRAXDFTvMUn7FTvMf1G2sETfPdPMHfP9fQbf9hOO9vR7ONbvMoedhgrRiio
edFWUkTeoxhmhgnpZHZijJC7ONbvM0.,.S.Sr,E.0k,5TWyql.bnayKmKKqGomC5XzET1.PuP.
MHT9N9ntumaU2,CJuyKtQC98P9PP7ONbXmb.2.gs0k2kS9.,E.cUOor8rqOpoKantId9NhOO9v
R704TXyKt.bHfEWUmL.6..D.Df.E.Cc4xhmNHT9NQCbWBxhYhA704D.CbB,708T1U.Ey4.J.dP
.2U.2.IEb8R7vI5fQT9PNPNZvQRdJ.0U1U.2.4AS.a80E.IE4ql8rmGKe4KlmqmCLLCoruKu8r
rmKWKqtMw50sQRtET1Y6cwE.0..272.7sI4aXdQKaY7J,MM8agNNCbi6RK4G50......VuI3.,
6.EUvEF2cXD,zF.2kmI.,O41EU4IG2kwL,3Qw5uPR1CbEhin3h0hioZijph2hgnRAZz16.,UMV
.U.F.6,AA...o3NmooBL1Nmo.2U...6UpO7gg26cd7AWzkTU.G..3IXkVyKrUgViQeotETfPd1
6F9vQ0ksP2.UnpZ1xB.G20U1VV,.Z1...bf9.EWE.8T0E...H.0U0.,c.16QBa.BX,M,2.,k.m
Ew71lbAqwr6nLv78ssH2.C2..C,2..600U..N6.,E.0ohCL.AnW8Utj00My7kx8O0MCT7Hb...
.
--- end of encoding ---
Вешь, конечно, наколенная, но как идея - сойдёт. Связь с данными происходит через потомков TableData. Соответственно эти данные можно брать какие угодно и откуда угодно.
Можно переписать это так, что таблица будет отображать не только текст, но и графику.
P.S. Исправил ошибку. Спасибо Ивану Кузьмицкому.
P.P.S. Откорректировал стиль.