home *** CD-ROM | disk | FTP | other *** search
- (* ---------------------------------------------------------------------- *)
- (* ETIKETT.PAS *)
- (* ein Programm zum Editieren, Speichern und Drucken von Etiketten *)
- (* ---------------------------------------------------------------------- *)
-
- PROGRAM et (Input, Output, fil);
-
- CONST { Abmessungen der Etiketten : }
- xmax = 32; { Laenge des Etiketts horizontal }
- ymax = 8; { Laenge des Etiketts vertikal }
- xoffs = 4; { Abstand der Etiketten horizontal }
- yoffs = 1; { Abstand der Etiketten vertikal }
- colnum = 2; { Anzahl der Etiketten pro Zeile }
- { Cursorpositionen: }
- xhome = 20; { X-Pos. vor der Etikettecke l. o. }
- yhome = 13; { Y-Pos. vor der Etikettecke l. o. }
- { sonstige Konstanten: }
- max = 50; { Anzahl der gepufferten Seiten }
- joker = '#'; { Jokerzeichen fuer Nummernfeld }
-
- TYPE
- string20 = STRING[20];
-
- VAR
- etikett : ARRAY [1..max, 1..ymax]
- OF STRING[xmax]; { Dateipuffer }
- pagebuffer : ARRAY [1..ymax] OF STRING[xmax]; { Verschiebepuffer }
- marked : ARRAY [1..max] OF BOOLEAN; { Markierung zum Druck }
- print_buffer: ARRAY [1..ymax] OF STRING[140]; { Etikettenzeile }
- i, j, { universelle Schleifenzaehler }
- bufcnt, { zaehlt Anzahl der Etiketten im Buffer }
- Page, maxpage, { aktuelle bzw. letzte beschriebene Seite }
- first, last, { fuer Blockmarkierungen }
- xcur, ycur : INTEGER; { Cursorposition 1..xmax bzw. 1..ymax }
- answ : CHAR;
- name : string20;
- fil : TEXT;
- ins, Exit : BOOLEAN;
-
- (* ---------------------------------------------------------------------- *)
- (* Leeren des programm-internen Druckpuffers *)
-
- PROCEDURE empty_buffer;
- BEGIN
- FOR i := 1 TO ymax DO
- BEGIN
- WriteLn (Lst, print_buffer[i]); print_buffer[i]:='';
- END;
- FOR i := 1 TO yoffs DO WriteLn(Lst);
- bufcnt := 0;
- END;
-
- (* ---------------------------------------------------------------------- *)
- (* eine Zeile der aktuellen Seite im Etikettenpuffer loeschen *)
-
- PROCEDURE clear_line (Line: INTEGER);
- BEGIN
- Delete(etikett[Page][Line], 1, Length(etikett[Page,Line]));
- FOR j := 1 TO xmax DO Insert(' ', etikett[Page,Line], 1);
- END;
-
- (* ---------------------------------------------------------------------- *)
- (* die ganze aktuelle Seite im Etikettenpuffer loeschen *)
-
- PROCEDURE clear_page;
- BEGIN FOR i := 1 TO ymax DO clear_line(i); END;
-
- (* ====================================================================== *)
- (* Editor , Kern von ETIKETT *)
-
- PROCEDURE edit;
-
- PROCEDURE clear_menu; (* Menu/Dialogfeld loeschen *)
- VAR i: INTEGER;
- BEGIN
- GotoXY(1,2); (* Zeilen bis Zeilenende loeschen *)
- FOR i := 1 TO 4 DO BEGIN ClrEol; WriteLn; END;
- GotoXY(1,2); LowVideo;
- END;
-
- PROCEDURE show_page; { aktuelle Seite anzeigen }
- VAR i: INTEGER;
- BEGIN
- IF marked[Page] THEN LowVideo; { andere Schriftintensitaet o. invers }
- FOR i := 1 TO ymax DO
- BEGIN
- GotoXY (Succ(xhome), yhome+i); Write (etikett[Page,i]);
- END;
- IF marked[Page] THEN NormVideo; { wieder normale Schrift }
- GotoXY(20,1); Write('Datei: ',name);
- GotoXY(50,1); Write('Seite ', Page:2, '(', maxpage:2, ')');
- END;
-
- PROCEDURE clear_all_markings;
- BEGIN
- FOR i := 1 TO max DO marked[i] := FALSE;
- first := Succ(max); last := 0;
- END;
-
- PROCEDURE screen; { Rahmen zeichnen und aktuelle Seite zeigen }
- BEGIN
- FOR i := 1 TO ymax DO { Rahmen zeichnen. Die Zeichen fuer den Rand }
- BEGIN { koennen nach Geschmack geaendert werden: }
- GotoXY(xhome-1, yhome+i); Write('!'); { linke Begrenzung }
- GotoXY(xhome+xmax+2,yhome+i); Write('!'); { rechte Begrenzung }
- END;
- FOR i := 1 TO xmax+2 DO
- BEGIN
- GotoXY(xhome+i-1,yhome); Write('-');{ obere Begrenzung }
- GotoXY(xhome+i-1,yhome+ymax+1); Write('-');{ untere Begrenzung }
- END;
- GotoXY(xhome-1, yhome); Write('+'); { Ecke links oben }
- GotoXY(xhome-1, yhome+ymax+1); Write('+'); { Ecke links unten }
- GotoXY(xhome+xmax+2,yhome); Write('+'); { Ecke rechts oben }
- GotoXY(xhome+xmax+2,yhome+ymax+1); Write('+'); { Ecke rechts unten }
- FOR i := 1 TO ymax DO print_buffer[i] := ''; { Druckpuffer initial. }
- bufcnt := 0; show_page;
- END; { screen }
-
- (* ---------------------------------------------------------------------- *)
- (* elementare Editorfunktionen: *)
-
- PROCEDURE cursor_up;
- BEGIN
- IF ycur > 1 THEN ycur := Pred(ycur) ELSE ycur := ymax
- END;
-
- PROCEDURE cursor_down;
- BEGIN
- IF ycur < ymax THEN ycur := Succ(ycur) ELSE ycur := 1;
- END;
-
- PROCEDURE cursor_left;
- BEGIN
- IF xcur > 1 THEN xcur := Pred(xcur)
- ELSE
- BEGIN { Zeilenwechsel }
- xcur := xmax; cursor_up
- END;
- END;
-
- PROCEDURE cursor_right;
- BEGIN
- IF xcur < xmax THEN xcur := Succ(xcur)
- ELSE
- BEGIN { Zeilenwechsel }
- xcur := 1; cursor_down
- END;
- END;
-
- (* ---------------------------------------------------------------------- *)
- (* Editorfunktionen mit ganzen Seiten: *)
-
- PROCEDURE page_up;
- BEGIN
- xcur := 1; ycur := 1;
- IF Page > 1 THEN
- BEGIN
- Page := Pred(Page); show_page
- END
- END;
-
- PROCEDURE page_down;
- BEGIN
- xcur := 1; ycur := 1; { Cursor links oben auf die n. Seite }
- IF Page < max THEN { wenn noch nicht am Pufferende }
- IF (maxpage = Page) AND ins THEN { im INS-Mode hinter der letzt. }
- BEGIN { Seite eine n. Seite aufmachen }
- Page := Succ(Page); maxpage := Page; clear_page; show_page;
- END
- ELSE IF maxpage > Page THEN { oder nur weiterblaettern }
- BEGIN
- Page := Succ(Page); show_page
- END
- END;
-
- PROCEDURE first_page; { auf die erste Seite springen }
- BEGIN
- Page := 1; show_page
- END;
-
- PROCEDURE last_page; { auf die letzte Seite springen }
- BEGIN
- Page := maxpage; show_page
- END;
-
- PROCEDURE delete_page; { aktuelle Seite entfernen }
- VAR i, j: INTEGER;
- BEGIN
- IF maxpage > 1 THEN maxpage := Pred(maxpage);
- IF Page > maxpage THEN Page := maxpage
- ELSE
- BEGIN
- FOR j := Page TO maxpage DO
- BEGIN
- FOR i := 1 TO ymax DO etikett[j,i] := etikett[Succ(j),i];
- marked[j] := marked[Succ(j)];
- END;
- END;
- show_page
- END;
-
- PROCEDURE copy_page; { aktuelle Seite verdoppeln }
- VAR i, j: INTEGER;
- BEGIN
- maxpage := Succ(maxpage); Page := Succ(Page);
- FOR j := maxpage DOWNTO Page DO
- BEGIN
- FOR i := 1 TO ymax DO etikett[j,i] := etikett[Pred(j),i];
- marked[j] := marked[Succ(j)]
- END;
- show_page
- END;
-
- PROCEDURE move_page; { verschiebt alle markierte Seiten in ihrer }
- VAR i, j, { Reihenfolge vor die aktuelle Seite }
- aktpage : INTEGER;
- markedbuffer: BOOLEAN;
-
- BEGIN
- FOR aktpage := maxpage DOWNTO 1 DO { von hinten n. vorn durchgehen }
- IF marked[aktpage] THEN
- BEGIN
- FOR i := 1 TO ymax DO
- pagebuffer[i] := etikett[aktpage,i]; { Seite puffern }
- IF aktpage < Page THEN { nach vorne verschieben }
- FOR j := aktpage TO Pred(Page) DO
- FOR i := 1 TO ymax DO etikett[j,i] := etikett[Succ(j),i]
- ELSE { nach hinten verschieben }
- FOR j := aktpage DOWNTO Succ(Page) DO
- FOR i := 1 TO ymax DO etikett[j,i] := etikett[Pred(j),i];
- FOR i := 1 TO ymax DO
- etikett[Page,i] := pagebuffer[i]; { Seite zurueckholen }
- markedbuffer := marked[aktpage]; { Marker mit verschieben }
- marked[aktpage] := marked[Page];
- marked[Page] := markedbuffer;
- show_page
- END
- END;
-
- (* ---------------------------------------------------------------------- *)
- (* weitere einfache Editorfunktionen: *)
-
- PROCEDURE insert_char; { Zeichen einfuegen }
- VAR i: INTEGER;
- BEGIN
- Delete(etikett[Page,ycur], xmax, 1);
- Insert(answ, etikett[Page,ycur], xcur);
- FOR i := xcur TO xmax DO Write(etikett[Page,ycur][i]);
- cursor_right;
- END;
-
- PROCEDURE overwrite_char; { Zeichen ueberschreiben }
- BEGIN
- etikett[Page,ycur][xcur] := answ; Write(answ); cursor_right;
- END;
-
- PROCEDURE delete_char; { Zeichen entfernen }
- VAR i: INTEGER;
- BEGIN
- Delete(etikett[Page,ycur], xcur, 1);
- Insert(' ', etikett[Page,ycur], xmax);
- FOR i := xcur TO xmax DO Write(etikett[Page,ycur][i]);
- END;
-
- PROCEDURE backspace; { Zeichen links entfernen }
- VAR i: INTEGER;
- BEGIN
- IF xcur > 1 THEN
- BEGIN
- xcur := Pred(xcur); GotoXY(xhome+xcur,yhome+ycur);
- Delete(etikett[Page,ycur], xcur, 1);
- Insert(' ', etikett[Page,ycur], xmax);
- FOR i := xcur TO xmax DO Write(etikett[Page,ycur][i]);
- END
- END;
-
- PROCEDURE delete_line; { Zeile entfernen }
- VAR i: INTEGER;
- BEGIN
- FOR i := ycur TO Pred(ymax) DO
- etikett[Page,i] := etikett[Page,Succ(i)];
- FOR i := 1 TO xmax DO etikett[Page,ymax][i] := ' ';
- FOR i := ycur TO ymax DO
- BEGIN
- GotoXY(Succ(xhome),yhome+i); Write(etikett[Page,i]);
- END;
- xcur := 1;
- END; { delete_line }
-
- PROCEDURE carriage_return; { zur naechsten Zeile oder Zeile einfuegen }
- VAR i: INTEGER;
- BEGIN
- IF ins THEN
- BEGIN
- FOR i := ymax DOWNTO Succ(ycur) DO
- etikett[Page,i] := etikett[Page,Pred(i)];
- FOR i := 1 TO xmax DO etikett[Page,ycur][i] := ' ';
- FOR i := ycur TO ymax DO
- BEGIN
- GotoXY(Succ(xhome),yhome+i); Write(etikett[Page,i]);
- END;
- xcur := 1;
- END
- ELSE
- BEGIN
- cursor_down; xcur := 1;
- END;
- END; { carriage_return }
-
- PROCEDURE change_insert_mode; { Wechsel INS/OVERWRITE }
- BEGIN
- ins := NOT ins; GotoXY (70,1);
- IF ins THEN Write('insert ') ELSE Write('overwrite');
- END; { change_insert_mode }
-
- PROCEDURE change_marking; { Wechsel der Markierung der aktuellen Seite }
- BEGIN
- marked[Page] := NOT marked[Page]; show_page;
- END; { change_marking }
-
- PROCEDURE first_marking; { Blockanfang, nur seitenweise }
- VAR i: INTEGER;
- BEGIN
- first := Page;
- FOR i := first TO last DO marked[i] := TRUE;
- show_page;
- END;
-
- PROCEDURE last_marking; { Blockende, nur seitenweise }
- VAR i: INTEGER;
- BEGIN
- last := Page;
- FOR i := first TO last DO marked[i] := TRUE;
- show_page;
- END;
-
- (* ---------------------------------------------------------------------- *)
- (* zentrale Druckroutine: *)
-
- PROCEDURE print;
- VAR
- xnum : ARRAY [0..5] OF INTEGER; { Spaltenpositionen der Ziffern }
- ynum, { Zeilenposition der Ziffern }
- posnum, { Anzahl der Spaltenpositionen }
- anz, i, j, k, { Anzahl der Exemplare, Indizes }
- anfnum, { Anfangs- und }
- endnum, { Endzahl bei Ausgabe }
- lfdnr, { laufende Nummer des Etiketts }
- x, y, n : INTEGER;
- any_marking,
- found : BOOLEAN; { Platzhalter gefunden }
-
- (* -------------------------------------------------------------------- *)
- (* Routine zum Suchen und Ersetzen der Joker: *)
-
- PROCEDURE search_num_fields ;
- VAR i : INTEGER;
- tmp: STRING[xmax];
-
- BEGIN
- i := 1; found := FALSE; posnum := 0; ynum := 0;
- WHILE (i <= ymax) AND NOT found DO
- BEGIN
- tmp := etikett[Page,i]; found := (Pos(joker,tmp)<>0);
- i := Succ(i);
- END;
- IF found THEN
- BEGIN { Abfrage, falls nirgends Joker }
- ynum := Pred(i);
- FOR i := xmax DOWNTO 1 DO
- BEGIN
- IF tmp[i] = joker THEN
- BEGIN
- xnum[posnum] := i; posnum := Succ(posnum)
- END;
- END; { for }
- END; { if }
- END; { search_num_fields }
-
- (* -------------------------------------------------------------------- *)
- (* Umwandlung Integerzahl in ASCII-Zeichen: *)
-
- FUNCTION ziffer (zahl, stelle: INTEGER): INTEGER;
- { Liefert einzelne Ziffern von zahl zurueck,
- z.B. fuer stelle = 0 die Einerziffer. }
- BEGIN
- ziffer := (Trunc(zahl/Exp(stelle*Ln(10))) MOD 10)
- END;
-
- (* -------------------------------------------------------------------- *)
- (* Einsetzen der Ziffern in den Druckpuffer: *)
-
- PROCEDURE fillnums;
- VAR i: INTEGER;
- BEGIN
- FOR i := Pred(posnum) DOWNTO 0 DO
- BEGIN
- etikett[Page,ynum][xnum[i]] := Chr(Ord('0')+ziffer(lfdnr,i));
- GotoXY(xhome+xnum[i],yhome+ynum);
- Write(etikett[Page,ynum][xnum[i]]);
- END;
- END; { fillnums }
-
- (* -------------------------------------------------------------------- *)
- (* ein einzelnes Etikett drucken: *)
-
- PROCEDURE print_one_etikett;
- BEGIN
- fillnums; bufcnt := Succ(bufcnt); { ein Etikett dazuladen }
- FOR y := 1 TO ymax DO
- BEGIN
- print_buffer[y] := Concat(print_buffer[y], etikett[Page,y]);
- IF bufcnt <> colnum THEN
- FOR i := 1 TO xoffs DO
- print_buffer[y] := Concat(print_buffer[y], ' ');
- END; { for y }
- IF bufcnt = colnum THEN empty_buffer;
- END; { print_one_etikett }
-
- (* -------------------------------------------------------------------- *)
- (* Ausdruck fuer eine ganze Seite besorgen: *)
-
- PROCEDURE print_page;
- BEGIN
- show_page; search_num_fields;
- IF NOT found THEN
- BEGIN
- anfnum := 1; endnum := 1;
- END;
- lfdnr := anfnum;
- WHILE lfdnr <= endnum DO
- BEGIN
- i := 1;
- WHILE i <= anz DO
- BEGIN
- print_one_etikett;
- IF KeyPressed THEN
- BEGIN
- GotoXY(1,5); Write('Druck abbrechen (j/n) ?'); Read(answ);
- IF answ IN ['j','J'] THEN
- BEGIN
- i := anz; lfdnr := endnum; Page := maxpage;
- END;
- GotoXY(1,5); ClrEol; (* Zeile bis Zeilenende loeschen *)
- END;
- i := Succ(i);
- END;
- lfdnr := Succ(lfdnr);
- END;
- FOR j := posnum-1 DOWNTO 0 DO { Bildschirm wiederherstellen }
- BEGIN
- GotoXY(xhome+xnum[j],yhome+ynum); Write(joker);
- etikett[Page,ynum][xnum[j]] := joker;
- END;
- END;
-
-
- BEGIN { print }
- clear_menu;
- Write('Wieviele Exemplare ? '); ReadLn(anz);
- Write(' Anfangszahl ? '); ReadLn(anfnum);
- Write(' Endzahl ? '); ReadLn(endnum);
- k := Page; any_marking := FALSE;
- FOR Page := 1 TO maxpage DO
- IF marked[Page] THEN
- BEGIN any_marking := TRUE; print_page END;
- Page := k;
- IF NOT any_marking THEN print_page;
- clear_menu; NormVideo; show_page;
- END; { print }
-
- (* ---------------------------------------------------------------------- *)
- (* Etiketten in Datei speichern: *)
-
- PROCEDURE save;
- VAR i, j: INTEGER;
- BEGIN
- Assign (fil, name); ReWrite (fil); (* Datei zum schreiben oeffnen *)
- FOR j := 1 TO maxpage DO
- FOR i := 1 TO ymax DO WriteLn (fil, etikett[j,i]);
- Close (fil);
- END;
-
- (* ---------------------------------------------------------------------- *)
- (* Menues: *)
-
- PROCEDURE mmenu;
- BEGIN
- clear_menu;
- WriteLn
- ('Zeichen- ^E: hoch ^X: runter ^S: links ^D: rechts ^G: loesch.');
- WriteLn(' ^H: Rueckschritt');
- WriteLn
- ('Zeile - ^W: erste ^Z: letzte ^A: Anfang ^F: Ende ^Y: loesch.');
- WriteLn
- ('andere - ^Q ^K Einf. - ^I: ein/aus');
- NormVideo; GotoXY(1,1); Write(' ');
- END;
-
- PROCEDURE kmenu;
- BEGIN
- clear_menu;
- WriteLn('Datei - D: Beenden Q: Abbrechen S: Sichern und weiter');
- WriteLn('Block - B: Anfang K: Ende H: vergessen');
- WriteLn('Druck - P: aktuelle Seite oder alle markierte Seiten drucken');
- Write ('(LEERTASTE - weiter)');
- NormVideo; GotoXY(3,1);
- END;
-
- PROCEDURE qmenu;
- BEGIN
- clear_menu;
- WriteLn
- ('Seite - E: vorher. X: naechs. R: erste C: letzte Y: loesch.');
- WriteLn
- (' K: kopie. V: versch. M: Markierung ein/aus');
- Write ('(LEERTASTE - weiter)');
- NormVideo; GotoXY(3,1);
- END;
-
- (* ---------------------------------------------------------------------- *)
- (* Etiketten editieren: *)
-
- BEGIN { edit }
- Page := 1; ins := FALSE; xcur := 1; ycur := 1; Exit := FALSE;
- mmenu; clear_all_markings; screen; change_insert_mode;
- REPEAT
- GotoXY(xhome+xcur,yhome+ycur);
- REPEAT UNTIL KeyPressed; (* auf einen Tastendruck warten *)
- (* gedrueckte Taste ohne Bildschirmecho von Tastatur lesen: *)
- Read(Kbd, answ);
-
- (* WORDSTAR - aehnliche Cursor - Steuertasten *)
- (* das Zeichen '^' vor einem Buchstaben bedeutet druecken der Control- *)
- (* Taste in Verbindung mit der entsprechenden Taste. '^S' bedeutet also *)
- (* 'Control-S', ASCII-Code = 19 *)
-
- CASE answ OF
- ' '..'z': IF ins THEN insert_char (* normale Textzeichen *)
- ELSE overwrite_char;
- ^S : cursor_left;
- ^D : cursor_right;
- ^E : cursor_up;
- ^X : cursor_down;
- ^A : xcur := 1;
- ^F : xcur := xmax;
- ^W : ycur := 1;
- ^Z : ycur := ymax;
- ^I : change_insert_mode;
- ^G : delete_char;
- ^H : backspace;
- ^Y : delete_line;
- ^M : carriage_return;
- ^K : BEGIN
- GotoXY(1,1); Write('^K'); kmenu;
- REPEAT UNTIL KeyPressed; Read(Kbd,answ); Write(answ);
- CASE answ OF
- 'd','D',^D : BEGIN save; Exit := TRUE; END;
- 'q','Q',^Q : Exit := TRUE;
- 's','S',^S : save;
- 'b','B',^B : first_marking;
- 'k','K',^K : last_marking;
- 'h','H',^H : BEGIN
- clear_all_markings; show_page;
- END;
- 'p','P',^P : print;
- ELSE ; (* manche Compiler verlaufen sich sonst! *)
- END; { case }
- mmenu;
- END; { ^K }
- ^Q : BEGIN
- GotoXY(1,1); Write('^Q'); qmenu;
- REPEAT UNTIL KeyPressed; Read(Kbd,answ); Write(answ);
- CASE answ OF
- 'e','E',^E : page_up;
- 'x','X',^X : page_down;
- 'r','R',^R : first_page;
- 'c','C',^C : last_page;
- 'y','Y',^Y : delete_page;
- 'k','K',^K : copy_page;
- 'v','V',^V : move_page;
- 'm','M',^M : change_marking;
- ELSE ;
- END; { case }
- mmenu;
- END; { ^Q }
- ELSE ;
- END; { case }
- UNTIL Exit
- END; { edit }
-
- (* ---------------------------------------------------------------------- *)
- (* Funktion testet Vorhandensein einer Datei: *)
- (* Achtung: Turbo-Pascal spezifisch! Also anpassen. *)
-
- FUNCTION existfile (name: string20): BOOLEAN;
- VAR Dummy: FILE OF BYTE;
- BEGIN
- Assign(Dummy, name);
- {$I-} (* I/O-Fehlerbehandlung durch Laufzeitsystem anschalten *)
- ReSet(Dummy); (* Datei zur Probe eroeffnen *)
- {$I+} (* ... und wieder aktivieren *)
- existfile := (IOResult = 0); (* wenn Datei vorh., ist 'IoRes. = 0' *)
- Close(Dummy);
- END; { existfile }
-
- (* ---------------------------------------------------------------------- *)
- (* Datei in Speicher laden: *)
-
- PROCEDURE load;
- BEGIN
- maxpage := 0; Assign(fil, name); ReSet(fil);
- WHILE NOT Eof(fil) DO
- BEGIN
- maxpage := Succ(maxpage);
- FOR i := 1 TO ymax DO
- IF NOT Eof(fil) THEN ReadLn(fil, etikett[maxpage,i])
- ELSE clear_line(i); (* zu "kurze" Etiketten ergaenzen *)
- END;
- Close (fil);
- END;
-
- BEGIN { main program }
- ClrScr; { Bildschirm loeschen }
- Write('Name der Etikettendatei ? '); ReadLn(name);
- IF name <> '' THEN
- BEGIN
- IF Pos('.', name) = 0 THEN
- name := Concat(name,'.eti'); { Standarderweiterung anhaengen }
- answ := 'j';
- IF existfile(name) THEN load
- ELSE
- BEGIN
- Write('Datei ',name,' ist neu. Weitermachen ? ');
- ReadLn(answ); maxpage := 1; Page := 1; clear_page;
- END;
- IF answ IN ['j','J'] THEN
- BEGIN
- ClrScr;
- edit;
- IF bufcnt <> 0 THEN
- empty_buffer; { uebrige Etiketten nach Beenden drucken }
- ClrScr;
- END;
- END;
- END. { main program }
- (* ---------------------------------------------------------------------- *)