(* Copyright 2021-2023 Anton Krotov This file is part of CEdit. CEdit is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. CEdit is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with CEdit. If not, see . *) MODULE Tabs; IMPORT List, K := KolibriOS, RW, U := Utils; CONST btnID* = 100; btnClose* = btnID + 100; btnLeft* = btnID - 1; btnRight* = btnID - 2; tabHeight* = 22; curTabHeight = 26; scrWidth = 15; btnCloseColor* = 0EF999FH; modifColor = 0FF0000H; strLen = 30; TYPE tItem = POINTER TO RECORD (List.tItem) val: ARRAY strLen + 1 OF CHAR; modified: BOOLEAN END; tTabs* = POINTER TO RECORD strings: List.tList; first, current: INTEGER; width, height: INTEGER; x, y, freeX: INTEGER END; PROCEDURE DblClicked* (t: tTabs; x, y: INTEGER): BOOLEAN; RETURN (x > t.freeX) & U.between(t.y, y, t.y + t.height - 1) END DblClicked; PROCEDURE drawTab (t: tTabs; id, x, y, width, height: INTEGER; s: ARRAY OF CHAR; modified: BOOLEAN); CONST btnCloseSize = 14; VAR x2, y2, color, closeColor, closeForeColor, textColor: INTEGER; left, top: INTEGER; BEGIN IF id = t.current THEN INC(height, curTabHeight - tabHeight); DEC(y, curTabHeight - tabHeight) END; color := K.colors.work; textColor := K.colors.work_text; DEC(x); INC(width); x2 := x + width - 1; y2 := y + height - 1; K.DrawRect(x, y, width, height, color); K.DrawLine(x, y, x2, y, K.colors.line); K.DrawLine(x2, y, x2, y2, K.colors.line); top := y + 3; IF id # t.current THEN K.DrawLine(x2 - 1, y2, x, y2, K.colors.line); closeColor := K.colors.button; closeForeColor := K.colors.button_text ELSE INC(top, (curTabHeight - tabHeight) DIV 2); closeColor := btnCloseColor; closeForeColor := 0FFFFFFH END; K.DrawLine(x, y2, x, y, K.colors.line); K.DrawText866bk(x + K.fontWidth + K.fontWidth DIV 2, y + (height - K.fontHeight) DIV 2, textColor, color, s); IF modified THEN K.DrawText866bk(x + K.fontWidth DIV 2, y + (height - K.fontHeight) DIV 2, modifColor, color, "*") END; K.CreateButton(id + ORD({30}) + btnID, x + 1, y - 1, width - 1, height - 1, 0, ""); left := x + width - btnCloseSize - 5; K.CreateButton(id + btnClose, left, top, btnCloseSize, btnCloseSize, closeColor, ""); K.DrawLine(left + 5, top + 4, left + btnCloseSize - 4, top + btnCloseSize - 5, closeForeColor); K.DrawLine(left + 4, top + 4, left + btnCloseSize - 4, top + btnCloseSize - 4, closeForeColor); K.DrawLine(left + 4, top + 5, left + btnCloseSize - 5, top + btnCloseSize - 4, closeForeColor); K.DrawLine(left + 4, top + btnCloseSize - 4, left + btnCloseSize - 4, top + 4, closeForeColor); K.DrawLine(left + 4, top + btnCloseSize - 5, left + btnCloseSize - 5, top + 4, closeForeColor); K.DrawLine(left + 5, top + btnCloseSize - 4, left + btnCloseSize - 4, top + 5, closeForeColor); END drawTab; PROCEDURE tabWidth (tab: tItem): INTEGER; RETURN (LENGTH(tab.val) + 5)*K.fontWidth END tabWidth; PROCEDURE Width (t: tTabs; pos, n: INTEGER): INTEGER; VAR res, i: INTEGER; item: List.tItem; BEGIN res := 0; i := pos; item := List.getItem(t.strings, i); WHILE (item # NIL) & (i <= n) DO INC(res, tabWidth(item(tItem))); item := item.next; INC(i) END RETURN res END Width; PROCEDURE draw* (t: tTabs); VAR x, y, xmax, n, width, i: INTEGER; item: List.tItem; scroll: BOOLEAN; BEGIN y := t.y; x := t.x; K.DrawRect(x, y - (curTabHeight - tabHeight), t.width + (2*scrWidth + 2), t.height + (curTabHeight - tabHeight) - 1, K.colors.work); IF Width(t, 0, t.strings.count - 1) > t.width THEN INC(x, 2*scrWidth); K.DeleteButton(btnLeft); K.DeleteButton(btnRight); K.CreateButton(btnLeft, t.x, y, scrWidth, t.height - 1, K.colors.button, "<"); K.CreateButton(btnRight, t.x + scrWidth, y, scrWidth, t.height - 1, K.colors.button, ">"); scroll := TRUE ELSE t.first := 0; scroll := FALSE END; xmax := x + t.width - 1; n := t.strings.count - 1; FOR i := 0 TO n DO K.DeleteButton(i + btnID) END; WHILE (n >= 0) & (Width(t, n, t.strings.count - 1) <= t.width) DO DEC(n) END; IF n < 0 THEN n := 0 ELSE INC(n) END; IF n < t.first THEN t.first := n END; K.DrawRect(x, y, t.width, t.height - 1, K.colors.work); K.DrawLine(x, y + tabHeight - 1, x + t.width - 1 + 2*scrWidth*(1 - ORD(scroll)), y + tabHeight - 1, K.colors.line); item := List.getItem(t.strings, t.first); n := t.first; WHILE (item # NIL) & (x <= xmax) DO width := tabWidth(item(tItem)); IF x + width - 1 <= xmax THEN drawTab(t, n, x + 1, y, width, t.height, item(tItem).val, item(tItem).modified); INC(n); INC(x, width); item := item.next ELSE item := NIL END END; t.freeX := x END draw; PROCEDURE setText (item: tItem; s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN IF LENGTH(s) > strLen THEN FOR i := 0 TO strLen - 4 DO item.val[i] := s[i] END; item.val[strLen - 3] := 0X; U.append8(item.val, "...") ELSE COPY(s, item.val) END END setText; PROCEDURE add* (t: tTabs; s: ARRAY OF CHAR); VAR item: tItem; BEGIN NEW(item); setText(item, s); item.modified := FALSE; List.append(t.strings, item) END add; PROCEDURE modify* (t: tTabs; n: INTEGER; val: BOOLEAN); VAR item: List.tItem; BEGIN item := List.getItem(t.strings, n); IF item(tItem).modified # val THEN item(tItem).modified := val; draw(t) END END modify; PROCEDURE rename* (t: tTabs; n: INTEGER; s: ARRAY OF CHAR); VAR item: List.tItem; BEGIN item := List.getItem(t.strings, n); setText(item(tItem), s) END rename; PROCEDURE delete* (t: tTabs; n: INTEGER); VAR item: List.tItem; BEGIN item := List.getItem(t.strings, n); List.delete(t.strings, item); DISPOSE(item) END delete; PROCEDURE scroll* (t: tTabs; btn: INTEGER); VAR pos: INTEGER; BEGIN pos := t.first + ORD(btn = btnRight) - ORD(btn = btnLeft); IF pos < 0 THEN pos := 0 ELSIF pos >= t.strings.count THEN pos := t.strings.count - 1 END; t.first := pos END scroll; PROCEDURE switch* (t: tTabs; n: INTEGER); BEGIN IF (0 <= n) & (n < t.strings.count) THEN t.current := n; IF n < t.first THEN t.first := 0 END; WHILE Width(t, t.first, n) > t.width DO INC(t.first) END END END switch; PROCEDURE setArea* (t: tTabs; x, y, width, height: INTEGER); BEGIN t.x := x; t.y := y; t.width := width - 2*scrWidth; t.height := height END setArea; PROCEDURE create* (): tTabs; VAR res: tTabs; BEGIN NEW(res); res.strings := List.create(NIL); res.current := 0; res.first := 0 RETURN res END create; END Tabs.