hodza/BlackBox.Shkola.2014 huggingface dataset:
Obx/Docu/ru/Wrappers.odc

Overview by Example: ObxWrappers
(C) Перевод на русский язык: Чернышов Л.Н., 2005.

Этот пример реализует обертку (wrapper). Обертки - это отображения (views), которые содержат другие отображения. В частности обертка может иметь тот же самый размер как отображение(я), которое(ые) она оборачивает. Так она может комбинировать свои собственные функциональные возможности с возможностями этого(этих) отображения(ий).

Например:
  отладочная обертка записывает сообщения, полученные обернутым отображением, в рабочий журнал;
  фоновая обертка добавляет цвет фона, по которому его обернутое отображение отрисовано (которое обычно не имеет никакого цвета фона, то есть, которое имеет прозрачный фон);
  обертка уровня содержит несколько многоуровневых отображений, например, графическое отображение графа с наложенным отображением заголовка;
  терминальная обертка содержит терминальную сессию и оборачивает стандартное текстовое отображение, содержащее с журнал сессии;
  обертка связывания отфильтровывает сообщения контроллера, так что обернутая картинка становится только для чтения и т.д., придумать можно что угодно! Обертки демонстрируют мощность композиции, при которой функциональные возможности различных объектов могут быть объединены очень простым способом без необходимости использования сложных языковых механизмов вроде наследования.

Наша обертка просто копирует в рабочий журнал каждую нажатую клавишу.

Реализация обертки примечательна в трех отношениях. Во-первых, обертка использует контекст отображения, которое она оборачивает, она не имеет собственного контекста. Это возможно только потому, что обертка - точно такого размера, как обернутое отображение, потому что общий контекст не может возвращать разные размеры в его GetSize-методе.
Во-вторых, она использует модель обернутого отображения (если таковая имеется!) как свою собственную, это означает, что ее метод ThisModel возвращает модель обернутой картинки, и что CopyFromModelView передает новую модель обернутому отображению.
В-третьих, обертка реализована гибко, она может обертывать отображения как с моделью, так и без нее. Эта гибкость сосредоточена в процедуре CopyFromModelView (чье название, признаем, плохо подходит для тех особых случаев, когда обернутые отображения не имеют моделей).

	 ObxWrappers.Wrap  (*выберите отображение (одну) перед запуском этой команды*)
	 ObxWrappers.Unwrap  (*выберите отображение (одну) перед запуском этой команды*)

		(* <== например, выберите это отображение *)

и затем вводите какие-либо символы, и смотрите что происходит в рабочем журнале

ObxWrapperssources
Obx/Mod/Actions.odc

MODULE ObxActions;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Services, Views, TextModels, TextMappers, TextViews, Math;

	CONST stepSize = 1000;

	TYPE
		PrimeAction = POINTER TO RECORD (Services.Action)
			to, current, divisor: INTEGER;
			f: TextMappers.Formatter
		END;

	VAR
		primeDlg*: RECORD
			to*: INTEGER;
		END;

	PROCEDURE Step (VAR cur, divisor: INTEGER; f: TextMappers.Formatter);
		VAR end, sqrtCur: INTEGER;
	BEGIN
		end := divisor + stepSize;
		sqrtCur := SHORT(ENTIER(Math.Sqrt(cur)));
		WHILE (divisor <= sqrtCur) & (divisor < end) & (cur MOD divisor # 0) DO divisor := divisor + 2 END;
		IF divisor > sqrtCur THEN	(* cur is a prime *)
			f.WriteInt(cur); f.WriteLn;
			divisor := 3; cur := cur + 2
		ELSIF divisor < end THEN	(* cur is not a prime, test next one *)
			divisor := 3; cur := cur + 2
		ELSE	(* time exhausted, continue test next time *)
		END
	END Step;

	PROCEDURE (a: PrimeAction) Do;
	BEGIN
		Step(a.current, a.divisor, a.f);
		IF a.current <= a.to THEN
			Services.DoLater(a, Services.now)
		ELSE
			Views.OpenView(TextViews.dir.New(a.f.rider.Base()))
		END
	END Do;

	PROCEDURE StartPrimes*;
		VAR a: PrimeAction;
	BEGIN
		NEW(a);
		a.to := primeDlg.to; a.current := 3; a.divisor := 3;
		a.f.ConnectTo(TextModels.dir.New());
		a.f.WriteInt(2); a.f.WriteLn;
		Services.DoLater(a, Services.now)
	END StartPrimes;

END ObxActions.
Obx/Mod/Address0.odc

MODULE ObxAddress0;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	VAR
		adr*: RECORD
			name*:	ARRAY 64 OF CHAR;
			city*:	ARRAY 24 OF CHAR;
			country*:	ARRAY 16 OF CHAR;
			customer*:	INTEGER;
			update*:	BOOLEAN
		END;

	PROCEDURE OpenText*;
	BEGIN
		
	END OpenText;
	
END ObxAddress0.
Obx/Mod/Address1.odc

MODULE ObxAddress1;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Views, TextModels, TextMappers, TextViews;

	VAR
		adr*: RECORD
			name*:	ARRAY 64 OF CHAR;
			city*:		ARRAY 24 OF CHAR;
			country*:	ARRAY 16 OF CHAR;
			customer*:	INTEGER;
			update*:	BOOLEAN
		END;

	PROCEDURE OpenText*;
		VAR t: TextModels.Model; f: TextMappers.Formatter; v: Views.View;
	BEGIN
		t := TextModels.dir.New();	(* create a new text editor object *)
		f.ConnectTo(t);		(* connect a formatter to this object *)
		f.WriteString(adr.name); f.WriteTab;
		f.WriteString(adr.city); f.WriteTab;
		f.WriteString(adr.country); f.WriteTab;
		f.WriteInt(adr.customer); f.WriteTab;
		f.WriteBool(adr.update); f.WriteLn;
		v := TextViews.dir.New(t);	(* create a visual component for the text object *)
		Views.OpenView(v)	(* open the visual component in its own window *)
	END OpenText;

END ObxAddress1.
Obx/Mod/Address2.odc

MODULE ObxAddress2;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Files, Converters, Views, Dialog, TextModels, TextMappers, TextViews;

	VAR
		adr*: RECORD
			name*:	ARRAY 64 OF CHAR;
			city*:		ARRAY 24 OF CHAR;
			country*:	ARRAY 16 OF CHAR;
			customer*:	INTEGER;
			update*:	BOOLEAN
		END;

	PROCEDURE OpenText*;
		VAR loc: Files.Locator; name: Files.Name; conv: Converters.Converter;
			v: Views.View; t: TextModels.Model; f: TextMappers.Formatter;
	BEGIN
		loc := NIL; name := ""; conv := NIL;
		v := Views.Old(Views.ask, loc, name, conv);
		IF (v # NIL) & (v IS TextViews.View) THEN
			t := v(TextViews.View).ThisModel();
			f.ConnectTo(t);
			f.SetPos(t.Length());
			f.WriteString(adr.name); f.WriteTab;
			f.WriteString(adr.city); f.WriteTab;
			f.WriteString(adr.country); f.WriteTab;
			f.WriteInt(adr.customer); f.WriteTab;
			f.WriteBool(adr.update); f.WriteLn;
			Views.OpenView(v);
			adr.name := ""; adr.city := ""; adr.country := ""; adr.customer := 0; adr.update := FALSE;
			Dialog.Update(adr)	(* update all controls for adr *)
		END
	END OpenText;

END ObxAddress2.
Obx/Mod/Bitmap.odc

MODULE ObxBitmap;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT SYSTEM, WinApi, Views, HostPorts;

	TYPE
		View = POINTER TO RECORD (Views.View)
			bmp: WinApi.HBITMAP;	(* handle of bitmap *)
			w, h: INTEGER	(* size of bitmap *)
		END;

	PROCEDURE (v: View) Restore (f: Views.Frame; l, t, r, b: INTEGER);
		VAR dc, bdc: WinApi.HDC; x, y, res: INTEGER;
	BEGIN
		dc := f.rider(HostPorts.Rider).port.dc;	(* get actual dc *)
		x := f.gx DIV f.unit;	(* get origin of view in dc *)
		y := f.gy DIV f.unit;
		bdc := WinApi.CreateCompatibleDC(dc);	(* create dc for bitmap *)
		res := WinApi.SelectObject(bdc, v.bmp);
		res := WinApi.BitBlt(dc, x, y, v.w, v.h, bdc, 0, 0, WinApi.SRCCOPY);	(* copy bitmap to screen *)
		res := WinApi.DeleteDC(bdc)
	END Restore;

	PROCEDURE Deposit*;
		VAR v: View; pat: ARRAY 32 OF INTEGER;
	BEGIN
		NEW(v);
		pat[0] := 000000000H; pat[1] := 0DCDDDD5DH; pat[2] := 0BABBBB3BH; pat[3] := 076777777H;
		pat[4] := 0EEEEEE6EH; pat[5] := 0DCDDDD5DH; pat[6] := 0BABBBB3BH; pat[7] := 076777777H;
		pat[8] := 0EEEEEE6EH; pat[9] := 0DCDDDD5DH; pat[10] := 0BABBBB3BH; pat[11] := 076777777H;
		pat[12] := 0EEEEEE6EH; pat[13] := 0DCDDDD5DH; pat[14] := 0BABBBB3BH; pat[15] := 076777777H;
		pat[16] := 0EEEEEE6EH; pat[17] := 0DCDDDD5DH; pat[18] := 0BABBBB3BH; pat[19] := 076777777H;
		pat[20] := 0EEEEEE6EH; pat[21] := 0DCDDDD5DH; pat[22] := 0BABBBB3BH; pat[23] := 076777777H;
		pat[24] := 0EEEEEE6EH; pat[25] := 0DCDDDD5DH; pat[26] := 0BABBBB3BH; pat[27] := 076777777H;
		pat[28] := 0EEEEEE6EH; pat[29] := 0DCDDDD5DH; pat[30] := 0BABBBB3BH; pat[31] := 000000000H;
		v.bmp := WinApi.CreateBitmap(32, 32, 1, 1, SYSTEM.ADR(pat));
		v.w := 32; v.h := 32;
		Views.Deposit(v)
	END Deposit;

END ObxBitmap.
Obx/Mod/Caps.odc

MODULE ObxCaps;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Stores, Models, TextModels, TextControllers;

	PROCEDURE Do*;
		VAR c: TextControllers.Controller; beg, end: INTEGER;
			r: TextModels.Reader; ch: CHAR;
			buf: TextModels.Model; w: TextModels.Writer; script: Stores.Operation;
	BEGIN
		c := TextControllers.Focus();
		IF (c # NIL) & c.HasSelection() THEN
			c.GetSelection(beg, end);
			(* upper case text will be copied into this buffer *)
			buf := TextModels.CloneOf(c.text); w := buf.NewWriter(NIL);
			r := c.text.NewReader(NIL); r.SetPos(beg);
			r.ReadChar(ch);
			WHILE (r.Pos() <= end) & ~r.eot DO
				IF (ch >= "a") & (ch <= "z") THEN ch := CAP(ch) END;
				w.WriteChar(ch);
				r.ReadChar(ch)
			END;
			Models.BeginScript(c.text, "Caps", script);
			c.text.Delete(beg, end); c.text.Insert(beg, buf, 0, end - beg);
			Models.EndScript(c.text, script)
		END
	END Do;

END ObxCaps.
Obx/Mod/ContIter.odc

MODULE ObxContIter;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Views, Containers, Controls;

	PROCEDURE Do*;
	(** focus the first control whose label is "magic name" **)
		VAR c: Containers.Controller;v: Views.View;
	BEGIN
		c := Containers.Focus();
		IF c # NIL THEN
			c.GetFirstView(Containers.any, v);
			WHILE (v # NIL) & ~((v IS Controls.Control) & (v(Controls.Control).label = "magic name")) DO
				c.GetNextView(Containers.any, v)
			END;
			IF v # NIL THEN
				c.SetFocus(v)
			END
		END
	END Do;

END ObxContIter.
Obx/Mod/ControlShifter.odc

MODULE ObxControlShifter;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Ports, FormControllers;

	PROCEDURE Shift*;
		VAR c: FormControllers.Controller; sel: FormControllers.List;
	BEGIN
		c := FormControllers.Focus();
		IF (c # NIL) & c.HasSelection() THEN
			sel := c.GetSelection();	(* generates a list with references to the selected views *)
			WHILE sel # NIL DO
				c.form.Move(sel.view, 10 * Ports.mm, 0);	(* move to the right *)
				sel := sel.next
			END
		END
	END Shift;

END ObxControlShifter.
Obx/Mod/Conv.odc

MODULE ObxConv;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Files, Stores, TextModels, TextViews;

	PROCEDURE ImportText* (f: Files.File; OUT s: Stores.Store);
		VAR r: Stores.Reader; t: TextModels.Model; w: TextModels.Writer;
			byte: SHORTCHAR; ch: CHAR; len: INTEGER;
	BEGIN
		ASSERT(f # NIL, 20); ASSERT(s = NIL, 21);
		(* ASSERT(f.type = "TEXT", 22);	(* the type is platform specific*) *)
		r.ConnectTo(f); r.SetPos(0);
		len := f.Length();
		t := TextModels.dir.New(); w := t.NewWriter(NIL);
		WHILE len # 0 DO
			r.ReadSChar(byte);
			ch := byte;	(* should translate character set here *)
			w.WriteChar(ch); DEC(len)
		END;
		s := TextViews.dir.New(t)
	END ImportText;

	PROCEDURE ExportText* (s: Stores.Store; f: Files.File);
		VAR w: Stores.Writer; t: TextModels.Model; r: TextModels.Reader;
			len: INTEGER; ch: CHAR; byte: SHORTCHAR;
	BEGIN
		ASSERT(s # NIL, 20); ASSERT(f # NIL, 21); ASSERT(f.Length() = 0, 22);
		ASSERT(s IS TextViews.View, 23);
		w.ConnectTo(f); w.SetPos(0);
		t := s(TextViews.View).ThisModel();
		len := t.Length();
		r := t.NewReader(NIL);
		WHILE len # 0 DO
			r.ReadChar(ch);
			byte := SHORT(ch);	(* should translate character set here *)
			w.WriteSChar(byte);
			DEC(len)
		END
	END ExportText;

END ObxConv.
Obx/Mod/Count0.odc

MODULE ObxCount0;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT TextModels, TextControllers, StdLog;

	PROCEDURE Do*;
	(** use TextCmds.SelectionGuard as guard for this command **)
		VAR c: TextControllers.Controller; from, to, schars, chars, views: INTEGER;
			rd: TextModels.Reader;
	BEGIN
		c := TextControllers.Focus();
		IF (c # NIL) & c.HasSelection() THEN
			c.GetSelection(from, to);	(* get selection range; from < to *)
			rd := c.text.NewReader(NIL);	(* create a new reader for this text model *)
			rd.SetPos(from);	(* set the reader to beginning of selection *)
			rd.Read;				(* read the first element of the text selection *)
			schars := 0; chars := 0; views := 0;	(* counter variables *)
			WHILE rd.Pos() # to DO	(* read all elements of the text selection *)
				IF rd.view # NIL THEN	(* element is a view *)
					INC(views)
				ELSIF rd.char < 100X THEN	(* element is a Latin-1 character *)
					INC(schars)
				ELSE	(* element is Unicode character *)
					INC(chars)
				END;
				rd.Read		(* read next element of the text selection *)
			END;
			StdLog.String("Latin-1 characters: "); StdLog.Int(schars); StdLog.Ln;
			StdLog.String("Unicode characters: "); StdLog.Int(chars); StdLog.Ln;
			StdLog.String("Views: "); StdLog.Int(views); StdLog.Ln;
			StdLog.Ln
		END
	END Do;

END ObxCount0.
Obx/Mod/Count1.odc

MODULE ObxCount1;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT TextMappers, TextControllers, StdLog;

	PROCEDURE Do*;
	(** use TextCmds.SelectionGuard as guard for this command **)
		VAR c: TextControllers.Controller; from, to, ints, reals, strings: INTEGER;
			s: TextMappers.Scanner;
	BEGIN
		c := TextControllers.Focus();
		IF (c # NIL) & c.HasSelection() THEN
			c.GetSelection(from, to);	(* get selection range; from < to *)
			s.ConnectTo(c.text);	(* connect scanner to this text model *)
			s.SetPos(from);	(* set the reader to beginning of selection *)
			s.Scan;				(* read the first symbol of the text selection *)
			ints := 0; reals := 0; strings := 0;	(* counter variables *)
			WHILE s.Pos() < to DO	(* read all symbols starting in the text selection *)
				IF s.type = TextMappers.int THEN	(* symbol is an integer number *)
					INC(ints)
				ELSIF s.type = TextMappers.real THEN	(* symbol is a real number *)
					INC(reals)
				ELSIF s.type = TextMappers.string THEN	(* symbol is a string/identifier *)
					INC(strings)
				END;
				s.Scan		(* read next symbol of the text selection *)
			END;
			StdLog.String("Integers: "); StdLog.Int(ints); StdLog.Ln;
			StdLog.String("Reals: "); StdLog.Int(reals); StdLog.Ln;
			StdLog.String("Strings: "); StdLog.Int(strings); StdLog.Ln;
			StdLog.Ln
		END
	END Do;

END ObxCount1.
Obx/Mod/Db.odc

MODULE ObxDb;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Views, TextModels, TextMappers, TextViews, TextControllers;

	CONST
		int = TextMappers.int; string = TextMappers.string; real = TextMappers.real; invalid = TextMappers.invalid;

	TYPE
		Node = POINTER TO RECORD
			next: Node;
			id: INTEGER;
			name: TextMappers.String;
			value: REAL
		END;

	VAR list: Node;

	PROCEDURE Enter (id: INTEGER; name: TextMappers.String; value: REAL);
		VAR n, h, p: Node;
	BEGIN	(* insert a new tuple into the list at its correct position *)
		NEW(n); n.id := id; n.name := name; n.value := value;
		h := list; p := NIL; WHILE (h # NIL) & (h.id <= id) DO p := h; h := h.next END;
		IF p # NIL THEN	(* insert between p and h *)
			p.next := n
		ELSE	(* insert at beginning *)
			list := n
		END;
		n.next := h
	END Enter;

	PROCEDURE EnterData*;
		VAR c: TextControllers.Controller; beg, end: INTEGER;
			s: TextMappers.Scanner; id: INTEGER;
			name: TextMappers.String; value: REAL;
	BEGIN	(* read a text selection and split it into an integer, string, and real field *)
		c := TextControllers.Focus();
		IF (c # NIL) & c.HasSelection() THEN
			c.GetSelection(beg, end);
			s.ConnectTo(c.text); s.SetPos(beg);
			s.Scan;
			WHILE (s.type = TextMappers.int) & (s.Pos() <= end) DO
				IF s.type = int THEN id := s.int; s.Scan ELSE s.type := TextMappers.invalid END;
				IF s.type = string THEN name := s.string; s.Scan ELSE s.type := invalid END;
				IF s.type = real THEN value := SHORT(s.real); s.Scan ELSE s.type := invalid END;
				Enter(id, name, value)	(* enter the new tuple into a global list *)
			END;
			c.SelectAll(FALSE)
		END
	END EnterData;

	PROCEDURE ListData*;
		VAR t: TextModels.Model; n: Node; f: TextMappers.Formatter;
	BEGIN	(* write all entered tuples into a new text *)
		t := TextModels.dir.New();
		f.ConnectTo(t); f.SetPos(0);
		n := list;
		WHILE n # NIL DO
			f.WriteInt(n.id); f.WriteTab; f.WriteString(n.name); f.WriteTab;
			(* WriteRealForm allows to format real numbers in a flexible way, here rounding to two digits after the comma *)
			f.WriteRealForm(n.value, 7, 0, -2, TextModels.digitspace);	(* "-2" indicates 2 digits after comma *)
			f.WriteLn;
			n := n.next
		END;
		Views.OpenView(TextViews.dir.New(t))
	END ListData;

	PROCEDURE Reset*;
	BEGIN	(* release data structure so that garbage collector can reclaim memory *)
		list := NIL
	END Reset;

END ObxDb.
Obx/Mod/Dialog.odc

MODULE ObxDialog;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Dialog;

	VAR
		dialog*: RECORD
			list*: Dialog.List;
			combo*: Dialog.Combo;
			sum-: INTEGER;
			disable*: BOOLEAN;
			elems*: INTEGER
		END;
		sel*: Dialog.Selection; (* hadled separately to avoid race condition in notifier *)

	PROCEDURE SetList (IN l: Dialog.List; n: INTEGER);
		VAR i: INTEGER;
	BEGIN
		l.SetLen(n);
		i := 0;
		WHILE i # n DO
			CASE i OF
			| 0: l.SetItem(i, "zero")
			| 1: l.SetItem(i, "one")
			| 2: l.SetItem(i, "two")
			| 3: l.SetItem(i, "three")
			| 4: l.SetItem(i, "four")
			| 5: l.SetItem(i, "five")
			| 6: l.SetItem(i, "six")
			| 7: l.SetItem(i, "seven")
			| 8: l.SetItem(i, "eight")
			| 9: l.SetItem(i, "nine")
			| 10: l.SetItem(i, "ten")
			| 11: l.SetItem(i, "eleven")
			END;
			INC(i)
		END
	END SetList;

	PROCEDURE SelNotifier* (op, from, to: INTEGER);
	BEGIN
		IF op = Dialog.set THEN	(* recalculate sum of selected numbers *)
			dialog.sum := (to - from + 1) * (to + from) DIV 2
		ELSIF op = Dialog.excluded THEN	(* correct sum after deselection of some numbers *)
			WHILE from <= to DO DEC(dialog.sum, from); INC(from) END
		ELSIF op = Dialog.included THEN	(* correct sum after selection of some numbers *)
			WHILE from <= to DO INC(dialog.sum, from); INC(from) END
		END;
		Dialog.Update(dialog)	(* show new dialog.sum *)
	END SelNotifier;

	PROCEDURE ElemsNotifier* (op, from, to: INTEGER);
	BEGIN
		IF op = Dialog.changed THEN
			IF dialog.elems < 0 THEN
				dialog.elems := 0; Dialog.Update(dialog)
			ELSIF dialog.elems > 12 THEN
				dialog.elems := 12; Dialog.Update(dialog)
			END;
			SetList(dialog.list, dialog.elems);	(* rebuild list *)
			Dialog.UpdateList(dialog.list)	(* update list  *)
		END
	END ElemsNotifier;

	PROCEDURE ListGuard* (VAR par: Dialog.Par);
	BEGIN
		par.disabled := dialog.disable
	END ListGuard;

BEGIN
	dialog.elems := 2; SetList(dialog.list, dialog.elems);
	sel.SetLen(12);
	sel.SetItem(0, "zero"); sel.SetItem(1, "one"); sel.SetItem(2, "two");
	sel.SetItem(3, "three"); sel.SetItem(4, "four"); sel.SetItem(5, "five");
	sel.SetItem(6, "six"); sel.SetItem(7, "seven"); sel.SetItem(8, "eight");
	sel.SetItem(9, "nine"); sel.SetItem(10, "ten"); sel.SetItem(11, "eleven");
	dialog.combo.SetLen(12);
	dialog.combo.SetItem(0, "zero"); dialog.combo.SetItem(1, "one"); dialog.combo.SetItem(2, "two");
	dialog.combo.SetItem(3, "three"); dialog.combo.SetItem(4, "four"); dialog.combo.SetItem(5, "five");
	dialog.combo.SetItem(6, "six"); dialog.combo.SetItem(7, "seven"); dialog.combo.SetItem(8, "eight");
	dialog.combo.SetItem(9, "nine"); dialog.combo.SetItem(10, "ten"); dialog.combo.SetItem(11, "eleven")
END ObxDialog.
Obx/Mod/Fact.odc

MODULE ObxFact;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT
		Stores, Models, TextModels, TextControllers, Integers;

	PROCEDURE Read(r: TextModels.Reader; VAR x: Integers.Integer);
		VAR i, len, beg: INTEGER; ch: CHAR; buf: POINTER TO ARRAY OF CHAR;
	BEGIN
		r.ReadChar(ch);
		WHILE ~r.eot & (ch <= " ") DO r.ReadChar(ch) END;
		ASSERT(~r.eot & (((ch >= "0") & (ch <= "9")) OR (ch = "-")));
		beg := r.Pos() - 1; len := 0;
		REPEAT INC(len); r.ReadChar(ch) UNTIL r.eot OR (ch < "0") OR (ch > "9");
		NEW(buf, len + 1);
		i := 0; r.SetPos(beg);
		REPEAT r.ReadChar(buf[i]); INC(i) UNTIL i = len;
		buf[i] := 0X;
		Integers.ConvertFromString(buf^, x)
	END Read;

	PROCEDURE Write(w: TextModels.Writer; x: Integers.Integer);
		VAR i: INTEGER;
	BEGIN
		IF Integers.Sign(x) < 0 THEN w.WriteChar("-") END;
		i := Integers.Digits10Of(x);
		IF i # 0 THEN
			REPEAT DEC(i); w.WriteChar(Integers.ThisDigit10(x, i)) UNTIL i = 0
		ELSE w.WriteChar("0")
		END
	END Write;

	PROCEDURE Compute*;
		VAR beg, end, i, n: INTEGER; ch: CHAR;
			s: Stores.Operation;
			r: TextModels.Reader; w: TextModels.Writer; attr: TextModels.Attributes;
			c: TextControllers.Controller;
			x: Integers.Integer;
	BEGIN
		c := TextControllers.Focus();
		IF (c # NIL) & c.HasSelection() THEN
			c.GetSelection(beg, end);
			r := c.text.NewReader(NIL); r.SetPos(beg); r.ReadChar(ch);
			WHILE ~r.eot & (beg < end) & (ch <= " ") DO r.ReadChar(ch); INC(beg) END;
			IF ~r.eot & (beg < end) THEN
				r.ReadPrev; Read(r, x);
				end := r.Pos(); r.ReadPrev; attr :=r.attr;
				IF (Integers.Sign(x) > 0) & (Integers.Compare(x, Integers.Long(MAX(LONGINT))) <= 0) THEN
					n := SHORT(Integers.Short(x)); i := 2; x := Integers.Long(1);
					WHILE i <= n DO x := Integers.Product(x, Integers.Long(i)); INC(i) END;
					Models.BeginScript(c.text, "computation", s);
					c.text.Delete(beg, end);
					w := c.text.NewWriter(NIL); w.SetPos(beg); w.SetAttr(attr);
					Write(w, x);
					Models.EndScript(c.text, s)
				END
			END
		END
	END Compute;

END ObxFact.

Menu entry:

	"Compute Factorial"	""	"ObxFact.Compute"	"TextCmds.SelectionGuard"