Код:
(O) TempGraphic.Open
MODULE TempGraphic;
IMPORT Views, Ports, Models, Stores, Controllers, ObxRandom, TextViews, TextModels;
CONST
bandH = 10 * Ports.mm;
TYPE
Plot = POINTER TO RECORD (Views.View)
data: POINTER TO ARRAY OF INTEGER; (* % *)
topBand: INTEGER; (* Верхний отображаемый столбец *)
blocks, focus: Context
END;
Context = POINTER TO RECORD (Models.Context)
(* Описатель контекста для внедряемых отображений *)
next: Context;
base: Plot; (* контейнер - наш график *)
view: Views.View; (* внедряемое отображение. Mожет быть не только TextView,
но и любой другой *)
l, t, r, b: INTEGER (* cached bounding box of contained view *)
END;
PROCEDURE CalcRange (t, b: INTEGER; OUT beg, end: INTEGER);
BEGIN
beg := t DIV bandH;
end := b DIV bandH + 1
END CalcRange;
PROCEDURE DrawBand(f: Views.Frame; fW, num, val: INTEGER);
BEGIN
f.DrawRect(0, num * bandH, SHORT(ENTIER(fW / 100 * val)), (num + 1) * bandH, 0, Ports.blue)
END DrawBand;
PROCEDURE (v: Plot) Restore (f: Views.Frame; l, t, r, b: INTEGER);
VAR beg, end, len, i: INTEGER;
w, h: INTEGER;
c: Context;
BEGIN
v.context.GetSize(w, h);
len := LEN(v.data);
beg := v.topBand;
end := beg + h DIV bandH + 1;
FOR i := MIN(beg, len-1) TO MIN(end, len-1) DO
DrawBand(f, w, i-beg, v.data[i])
END;
(*Создаем подкадры для всех внедренных отображений. Дальше их отрисовку обеспечит среда*)
c := v.blocks; i := 0;
WHILE c # NIL DO
(* Рисуем рамочку *)
f.DrawRect(c.l, c.t - v.topBand*bandH, c.r, c.b - v.topBand*bandH, 0, Ports.red);
Views.InstallFrame(f, c.view, c.l, c.t - v.topBand*bandH, i, c = v.focus); INC(i); c := c.next
END
END Restore;
PROCEDURE ^ NewBlock (v: Plot; l, t, r, b: INTEGER);
PROCEDURE ^ CheckBlock (v: Plot; x, y: INTEGER): Context;
PROCEDURE (v: Plot) HandleCtrlMsg (f: Views.Frame; VAR msg: Controllers.Message;
VAR focus: Views.View);
VAR w, h, len, page: INTEGER;
l, t, r, b: INTEGER;
c: Context;
PROCEDURE InputRect; (* Считываем прямоугольник для нового блока *)
VAR x, y: INTEGER;
mod: SET;
isDown: BOOLEAN;
BEGIN
f.Input(x, y, mod, isDown);
l := x; t := y;
REPEAT
f.Input(x, y, mod, isDown)
UNTIL ~isDown;
IF x < l THEN
r := l; l := x
ELSE
r := x
END;
IF y < t THEN
b := t; t := y
ELSE
b := y
END
END InputRect;
BEGIN
v.context.GetSize(w, h);
page := h DIV bandH;
len := LEN(v.data);
WITH msg: Controllers.PollSectionMsg DO (* Опрос состояния прокрутки, можно возвращать
в любых единицах, главное - отношение трех величин *)
IF msg.vertical THEN (* Отрабатываем только вертикальную прокрутку *)
msg.wholeSize := len;
msg.partPos := v.topBand;
msg.partSize := page;
msg.valid := TRUE;
msg.done := TRUE
END
| msg: Controllers.ScrollMsg DO
IF msg.vertical THEN
CASE msg.op OF
| Controllers.decLine: v.topBand := MAX(0, v.topBand-1)
| Controllers.incLine: v.topBand := MIN(v.topBand+1, len-1)
| Controllers.decPage: v.topBand := MAX(0, v.topBand-page)
| Controllers.incPage: v.topBand := MIN(v.topBand+page, len-1)
| Controllers.gotoPos: v.topBand := MAX(0, MIN(msg.pos, len-1))
END;
msg.done := TRUE;
Views.Update(v, Views.rebuildFrames)
END
| msg: Controllers.TrackMsg DO
c := CheckBlock(v, msg.x, msg.y); (* Проверяем, нет ли уже в точке блока. *)
IF c = NIL THEN (* Если нет, то создаем новый *)
InputRect;
IF r - l < 30 * Ports.mm THEN r := l + 30 * Ports.mm END;
IF b - t < 20 * Ports.mm THEN b := t + 20 * Ports.mm END;
INC(t, v.topBand*bandH); INC(b, v.topBand*bandH);
NewBlock(v, l, t, r, b)
ELSE
IF c = v.focus THEN (* Если в этой точке уже фокусированный блок, то говорим среде,
что обработку сообщения надо передать ему *)
focus := c.view
ELSE
v.focus := c (* Иначе устанавливаем новый фокус *)
END
END;
Views.Update(v, Views.rebuildFrames)
| msg: Controllers.PollCursorMsg DO (* Устанавливаем форму курсора *)
c := CheckBlock(v, msg.x, msg.y);
IF c = NIL THEN (* Если блоков нет, то крестовый курсор *)
msg.cursor := Ports.graphicsCursor
ELSIF c = v.focus THEN (* Если фокусированный блок, то спросим у него самого *)
focus := v.focus.view
ELSE (* Если нефокусированный блок, то стрелка *)
msg.cursor := Ports.arrowCursor
END
ELSE
(* Если есть фокус, то передаем все остальные сообщения ему *)
IF v.focus # NIL THEN
focus := v.focus.view
END
END
END HandleCtrlMsg;
PROCEDURE CheckBlock (v: Plot; x, y: INTEGER): Context;
VAR c: Context;
BEGIN
INC(y, v.topBand*bandH);
c := v.blocks;
WHILE c # NIL DO
IF (c.l <= x) & (x < c.r) &
(c.t <= y) & (y < c.b) THEN
RETURN c
END;
c := c.next
END;
RETURN NIL
END CheckBlock;
PROCEDURE NewBlock (v: Plot; l, t, r, b: INTEGER);
VAR block: Context;
BEGIN
NEW(block);
block.view := TextViews.dir.New(TextModels.dir.NewFromString("Текстовый блок"));
block.l := l; block.t := t; block.r := r; block.b := b;
block.next := v.blocks;
v.blocks := block;
Stores.Join(v, block.view);
block.view.InitContext(block);
v.focus := block
END NewBlock;
(* Context *)
PROCEDURE (c: Context) ThisModel (): Models.Model;
BEGIN
RETURN NIL
END ThisModel;
PROCEDURE (c: Context) GetSize (OUT w, h: INTEGER);
BEGIN
w := c.r - c.l;
h := c.b - c.t
END GetSize;
PROCEDURE (c: Context) Normalize (): BOOLEAN;
BEGIN
RETURN TRUE
END Normalize;
PROCEDURE Init (v: Plot);
VAR i: INTEGER;
BEGIN
NEW(v.data, 10000);
FOR i := 0 TO LEN(v.data)-1 DO
v.data[i] := SHORT(ENTIER(ObxRandom.Uniform()* 100))
END
END Init;
PROCEDURE Open* ;
VAR v: Plot;
BEGIN
NEW(v);
Init(v);
Views.OpenAux(v, "Отчет")
END Open;
END TempGraphic.