Repository: velorek1/c-edit Branch: master Commit: 253369833412 Files: 32 Total size: 163.4 KB Directory structure: gitextract_9g946023/ ├── LICENSE ├── README.md ├── TODO ├── TODO.md └── src/ ├── Makefile ├── TODO.md ├── edbuf.c ├── edbuf.h ├── editor.c ├── editor.h ├── fileb.c ├── fileb.h ├── global.c ├── global.h ├── keyb.c ├── keyb.h ├── listbox.c ├── listbox.h ├── main.c ├── menu.c ├── menu.h ├── opfile.c ├── opfile.h ├── rterm.c ├── rterm.h ├── scbuf.c ├── scbuf.h ├── t.txt ├── tm.c ├── tm.h ├── ui.c └── ui.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 Velorek1 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ C-EDIT Project in C language (NO NCURSES) ========================================= C-EDIT for linux - ALPHA - A linux text editor in the style of the MSDOS EDIT - WITHOUT USING NCURSES Works well with Termux! :) * WARNING: DO NOT EDIT YOUR PRECIOUS FILES WITH THIS EDITOR!! Contact me at: velorek1@gmail.com **NEW: AUGUST 2025** - Fixed flickering issues in status bar - Added pageup/down home/end keys History: - 2024 -> Dynamyc buffer added (vector of lineS) - 2023 -> Previous demo version kept in https://github.com/velorek1/C-editold - 2022 -> I have rewritten the screenbuffer and fixed polling issues (lynx: https://github.com/velorek1/lynx). Ideally, a keyboard library abstraction would be nice to expand the editor's possibilities.** - 2018 -> Start of the project; basic demo concepts developed (https://oldstuff286.blogspot.com/2018/03/c-edit-in-progress.html) TO INSTALL: * Download or clone repository. * Type "cd src", "make" and "./cedit" to execute. So far I have implemented: * A very simple Double Screen Buffer with a simple linked list in memory for greater display control. * Rudimentary Text User Interface with Windows, Textbox, Keyboard handling, Color schemes, etc. * Automatic display resizing. * Open file dialog with a Listbox and SCROLL capabilities to navigate and find the file to open. * Rudimentary Edit buffer with vertical and horizontal scroll (vector of lines). * A millisecond timer for animations. * Timers and kbhit with no polling for optimate CPU usage. * Greater modularity than Demo version * Rudimentary syntax highlighting Files in /src/: =============== * rterm.c -> code for the library that handles output with Ansi Functions and kbhit() in linux console (with its header file) * listbox.c -> code for the circular linked list responsible for handling menus * scbuf.c -> code for controlling display with a double screen buffer (with its header file) * keyb.c -> module to control keyboard input. * opfile.c -> module that list files with scroll capabilities to select the file to open. * ui.c -> module with user interface widgets: alert windows, confirmation windows, textbox, etc. * fileb.c -> module for single file operations. * tm.c -> module for a millisecond timer for animations (C11) * main.c -> Editor in C in the style of MSDOS EDIT (In progress). * global.c -> Global variables and main animation * menu.c -> Drop-down menu * editor.c -> Code for the editor section * edbuf.c -> Buffer - vector of lines As a screen buffer I have implemented a dynamic structure in memory that allows to save the current screen so that you can display new things on top and then go back to the previous (saved) screen. (simple linked list) TO-DO: * Clean code * Complete menus / dialogs * READ MODE * FULL UTF 8! - SUPPORT change from char to win_t/wchar in editor buffer * Copy/Paste/Cut routines / Highlight text * Create Keyboard abstraction to make it more portable (bottom-up fashion!) * Address glitches/bugs ![Alt text](cedit3.jpg?raw=true "Demo") ![Alt text](cedit4.jpg?raw=true "Demo") ================================================ FILE: TODO ================================================ * Top Priority: Check https://github.com/velorek1/ceditbuf/ for latest development implementations (2024( - Code a more backward compatible timer : tm.c (done!) - Create Keyboard abstraction to make it more portable (bottom-up fashion!) - Rewrite screen buffer (in progress). - Rewrite and merge listbox with openfiledialog and menus. - Turn fw into a library to display long text with scroll. - Change editBuffer from static array to dynamic structure (in progress) - Expand behaviour of ENTER and BACKSPACE and REAL TAB SUPPORT! (in progress) - Add horizontal scroll (in progress) * Less Priority: - Highlight text and copy/paste function - Find text - Full UTF8 support? ================================================ FILE: TODO.md ================================================ * Top Priority: (2025) - Complete menus / dialogs - READ MODE - FULL UTF 8 - SUPPORT change char to win_t/wchar in editor buffer - Copy/Paste/Cut routines / Highlight text - Create Keyboard abstraction to make it more portable (bottom-up fashion!) - Address glitches/bugs ================================================ FILE: src/Makefile ================================================ .POSIX: TARGET = cedit CC = cc CFLAGS = -Wall -Wextra -fsigned-char LDFLAGS = LDLIBS = # The list of object files. OBJS = main.o rterm.o listbox.o scbuf.o OBJS += ui.o fileb.o global.o menu.o editor.o OBJS += keyb.o tm.o edbuf.o opfile.o # the list of files to clean TOCLEAN = cedit $(OBJS) RM ?= rm -f all: $(TARGET) clean: $(RM) $(TOCLEAN) cedit: $(OBJS) $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS) # INCLUDE DEPENDENCIES to compile depending on which # source has been touched. main.o: main.c rterm.h listbox.h scbuf.h keyb.h ui.h fileb.h tm.h fileb.o: fileb.c fileb.h keyb.o: keyb.c rterm.h keyb.h listbox.o: listbox.c scbuf.h keyb.h editor.o: scbuf.h keyb.h rterm.h rterm.o: rterm.c menu.o: listbox.c scbuf.h ui.h keyb.h rterm.h tm.o: tm.c tm.h global.o: global.c global.h scbuf.o: scbuf.c rterm.h keyb.h scbuf.h ui.o: ui.c scbuf.h rterm.h keyb.h opfile.o: opfile.c scbuf.h rterm.h listbox.h keyb.h tm.h global.h ui.h ================================================ FILE: src/TODO.md ================================================ # ceditbuf C-edit with dynamic buffer (in development) ================================================ FILE: src/edbuf.c ================================================ #include #include #include #include "edbuf.h" // create new list element of type VLINES from the supplied text string VLINES *_newline(VLINES temp) { VLINES *newp; newp = (VLINES *) malloc (sizeof(VLINES)); newp->index = temp.index; memcpy(newp->linea,temp.linea,sizeof(temp.linea)+1); newp->next = NULL; return newp; } // Delete first element on list whose item field matches the given text // NOTE!! delete requests for elements not in the list are silently ignored :-) void _RemoveThing(VLINES **head, int index) { BOOL present = FALSE; VLINES *old; VLINES **tracer = head; if ((*tracer)->index==index) present=TRUE; while((*tracer) && !(present)){ if ((*tracer)->index==index) present=TRUE; tracer = &(*tracer)->next; } if(present) { old = *tracer; *tracer = (*tracer)->next; free(old); // free up remainder of list element } } void _hardupdateLINE(VLINES **head, int index, VLINES temp) { int i=0; BOOL present = FALSE; VLINES **tracer = head; if ((*tracer)->index==index) present=TRUE; while((*tracer) && !(present)){ if ((*tracer)->index==index) {present=TRUE; break;} tracer = &(*tracer)->next; } if(present) { for (i=0; ilinea[i].ch = temp.linea[i].ch; (*tracer)->linea[i].attrib = temp.linea[i].attrib; (*tracer)->linea[i].specialChar = temp.linea[i].specialChar; } } } void _deletetheList(VLINES **head) { /* deref head_ref to get the real head */ VLINES *current = *head; VLINES *next = NULL; VLINES **tracer=head; while (current != NULL) { next = current->next; free(current); current = next; } /* deref head_ref to affect the real head back in the caller. */ *tracer = NULL; } // updatelement: remove from list the first instance of an element VLINES *_update(VLINES *head, int index, VLINES temp) { VLINES *p; for (p = head; p != NULL; p = p -> next) { if (p -> index == index) { break; } } p-> index = temp.index; return head; } // getObject VLINES *_getObject(VLINES *head, int index) { VLINES *p; for (p = head; p != NULL; p = p -> next) { if (p -> index == index) { break; } } //p-> index = temp.index; return p; } // deleteline: remove from list the first instance of an element // containing a given text string // NOTE!! delete requests for elements not in the list are silently ignored VLINES *_deleteline(VLINES *head, int index) { VLINES *p, *prev; prev = NULL; for (p = head; p != NULL; p = p -> next) { if (p -> index == index) { if(prev == NULL) head = p-> next; else prev -> next = p -> next; free(p); // remove rest of VLINES return head; } prev = p; } return NULL; } /* addfront: add new VLINES to front of list */ /* example usage: start = (addfront(start, newelement("burgers")); */ VLINES *_addfront(VLINES *head, VLINES *newp) { newp -> next = head; return newp; } /* addend: add new VLINES to the end of a list */ /* usage example: start = (addend(start, newelement("wine")); */ VLINES *_addatend (VLINES *head, VLINES *newp) { VLINES *p2; if (head == NULL) return newp; // now find the end of list for (p2 = head; p2 -> next != NULL; p2 = p2 -> next) ; p2 -> next = newp; return head; } void _printlist(VLINES **head) // this routine uses pointer-to-pointer techniques :-) { VLINES **tracer = head; int index=0, i = 0; while ((*tracer) != NULL) { index = (*tracer)->index; printf("LINE %d | ", index); for (i=0; i < MAX_LINE_SIZE; i++) printf("%c",(*tracer)->linea[i].ch); printf("\n"); tracer = &(*tracer)->next; } } int _length(VLINES **head) // this routine uses pointer-to-pointer techniques :-) { int count=0; VLINES **tracer = head; if (*head == NULL) return -1; while ((*tracer) != NULL) { count = count +1; tracer = &(*tracer)->next; } return count; } void _reindex(VLINES **head) { int count=0; VLINES *p=NULL; VLINES **tracer = head; while ((*tracer) != NULL) { p = *tracer; p->index=count; count = count +1; tracer = &(*tracer)->next; } } void _deleteObject(VLINES **head,int index, BOOL sort){ VLINES *p=*head; if (index == 0 || _length(head) <=1 || p->index == index ) _RemoveThing(head,index); else _deleteline(*head,index); if (sort == TRUE) _reindex(head); } int _dumpLine(VLINES *head, long index, VLINES *line){ //Dumps contents of desired line from buffer into temporary line int i=-1; char ch=0; char attrib = 0; char specialChar = 0; VLINES *aux = NULL; //auxiliary pointer aux = _getObject(head, index); memset(line, '\0',sizeof(&line)); //Clear memory for temporary line //Does the line exist? if (aux != NULL) { for (i=0; ilinea[i].ch; attrib = aux->linea[i].attrib; specialChar = aux->linea[i].specialChar; line->linea[i].ch = ch; line->linea[i].attrib = attrib; line->linea[i].specialChar = specialChar; } i = aux->index; } return i; } int _updateLine(VLINES *head, long index, VLINES *line){ //Copies contents of desired line from temporary line to buffer int i=0; char ch=0; char specialChar = 0; char attrib = 0; VLINES *aux = NULL; //auxiliary pointer aux = _getObject(head, index); //Does the line exist? if (aux != NULL) { for (i=0; ilinea[i].ch; specialChar = line->linea[i].specialChar; attrib = line->linea[i].attrib; aux->linea[i].ch = ch; aux->linea[i].specialChar = specialChar; aux->linea[i].attrib = attrib; } i = aux->index; } return i; } CHARBUF _getSingleChar(VLINES *head, long X, long Y ){ //Dumps contents of desired line from buffer into temporary line char ch=0; char attrib = 0; char specialChar = 0; CHARBUF retvalues; VLINES *aux = NULL; //auxiliary pointer aux = _getObject(head, Y); //Does the line exist? if (aux != NULL) { ch = aux->linea[X].ch; specialChar = aux->linea[X].specialChar; attrib = aux->linea[X].attrib; } else { ch = '\0'; specialChar = '\0'; attrib = '\0'; } retvalues.ch = ch; retvalues.attrib = attrib; retvalues.specialChar = specialChar; return retvalues; } int findEndline(VLINES line) { char ch = 0; int i=0; int result = 0; do { ch = line.linea[i].ch; //write_ch(i,1,ch,F_RED,B_WHITE); if(ch == 0x00 || ch == 0x10 || ch == 0x0A) //ch = 00 break; i++; } while(i < MAX_LINE_SIZE); result = i; ch = 0; return result; } BOOL isLineTerminated(VLINES line) { BOOL flag=FALSE; char ch=0; int i = 0; for (i=0; i< MAX_LINE_SIZE; i++){ ch = line.linea[i].ch; if(ch == 0x10 || ch == 0x0A) flag = TRUE; } return flag; } ================================================ FILE: src/edbuf.h ================================================ #ifndef _EDBUF_H_ #define _EDBUF_H_ #include #include #define MAX_LINE_SIZE 512 #define TRUE 1 #define FALSE 0 typedef int BOOL; typedef struct _charbuf { char ch; char specialChar; char attrib; }CHARBUF; typedef struct _vlines { int index; CHARBUF linea[MAX_LINE_SIZE]; struct _vlines *next; } VLINES; /* Adapted from Kernighan and Pike's "The Practice of Programming" pp.46 et seq. (Addison-Wesley 1999) */ // create new list element of type VLINES from the supplied text string VLINES *_newline(VLINES temp); VLINES *_addfront(VLINES *head, VLINES *newp); VLINES *_addatend (VLINES *head, VLINES *newp); VLINES *_addmiddle (VLINES *head, VLINES *newp); VLINES *_update(VLINES *head, int index, VLINES temp); VLINES *_getObject(VLINES *head, int index); void _hardupdateLINE(VLINES **head, int index, VLINES temp); void _RemoveThing(VLINES **head, int index); void _deletetheList(VLINES **head); VLINES *_deleteline(VLINES *head, int index); void _deleteObject(VLINES **head,int index, BOOL sort); int _length(VLINES **head); void _printlist(VLINES **head); void _reindex(VLINES **head); int _dumpLine(VLINES *head, long index, VLINES *line); int _updateLine(VLINES *head, long index, VLINES *line); CHARBUF _getSingleChar(VLINES *head, long X, long Y ); int findEndline(VLINES line); BOOL isLineTerminated(VLINES line); #endif ================================================ FILE: src/editor.c ================================================ /* Main editor section C·edit * for a Text User Interface * Last modified: 6/04/2024 * @author:velorek */ #include #include #include #include #include "rterm.h" #include "scbuf.h" #include "ui.h" #include "tm.h" #include "keyb.h" #include "global.h" #include "editor.h" #include "fileb.h" #include "edbuf.h" int oldEndLine= 0; wchar_t convertChar(char c1, char c2) { // Combine the bytes into a single string char temp[3]; temp[0] = c1; temp[1] = c2; temp[2] = '\0'; wchar_t wchar; mbstowcs(&wchar, temp, 1); return wchar; } void linetoScreenRAW(long whereY, VLINES tempLine){ //dump temporary Line to screen buffer - RAW MODE int i=0; int attrib = EDIT_FORECOLOR; int tempEndLine = findEndline(tempLine); //if (tempEndLine <0) return; for (i=0; i 31 && ch < 127) || ch < 0) { // A KEY IS PRESSED AND ADDED TO EDIT BUFFER // FIRST check for negative chars and special characters newch=ch; accentchar[0] = 0; accentchar[1] = newch; //Check whether we are on Readmode // if (fileModified != FILE_READMODE) { //Process normal printable chars first if (ch == 27) return 0; if ((ch > 31 && ch < 127) || ch < 0) { if (ch < 0) { read_accent(&newch, accentchar); newch = accentchar[1]; } //if ((posBufX != endLine) && (shiftH>0)) return 0; //check if we are at the limit of our display to print chars if (cursorX < new_columns-2) cursorX++; if (posBufX < MAX_LINE_SIZE && cursorX == new_columns-2) {shiftH++;} //write_num(screen1,20,2,currentLine,B_CYAN,F_WHITE,1); //SYNTAX HIGHLIGHTING DEMO //Highlight numbers in GREEN if ((accentchar[1] >= 48) && (accentchar[1] <=57)) attrib = FH_GREEN; //Highlight special characters in CYAN if ((accentchar[1] >= 33) && (accentchar[1] <=47)) attrib = FH_CYAN; aux = _getObject(edBuf1, posBufY); if ((accentchar[1] >= 58) && (accentchar[1] <=64)) attrib = FH_CYAN; aux = _getObject(edBuf1, posBufY); if ((accentchar[1] >= 91) && (accentchar[1] <=96)) attrib = FH_CYAN; aux = _getObject(edBuf1, posBufY); if ((accentchar[1] >= 123) && (accentchar[1] <=126)) attrib = FH_CYAN; aux = _getObject(edBuf1, posBufY); //FIRST TIME -> CREATE LINES IN BUFFER if (aux == NULL) { //FIRST CHAR - if not we create a new line in buffer tempLine.index = posBufY; //Add spaces if cursor is not at (0,0) if (posBufX != 0) { for (i=0; i posBufX) { tempLine.index = posBufY; //with every line index is incremented if (insertMode == FALSE){ for (i=endLine; i>=posBufX; i--){ tempLine.linea[i+1].ch = tempLine.linea[i].ch; tempLine.linea[i+1].specialChar = tempLine.linea[i].specialChar; tempLine.linea[i+1].attrib = tempLine.linea[i].attrib; } } tempLine.linea[posBufX].ch = accentchar[1]; tempLine.linea[posBufX].specialChar = accentchar[0]; tempLine.linea[posBufX].attrib = attrib; posBufX = posBufX + 1; _updateLine(edBuf1, posBufY, &tempLine); linetoScreenRAW(cursorY,tempLine); } else { // POSBUFX >= ENDLINE: Cursor is at the end or further away from latest text //ADD SPACES IF CURSOR IS NOT AT THE END OF THE LINE AND LINE ALREADY EXISTS if(posBufX > endLine) { for(i = endLine; i < posBufX; i++) { tempLine.linea[i].ch = FILL_CHAR; tempLine.linea[i].specialChar = 0; tempLine.linea[i].attrib = EDIT_FORECOLOR; } } tempLine.linea[posBufX].ch = accentchar[1]; tempLine.linea[posBufX].specialChar = accentchar[0]; tempLine.linea[posBufX].attrib = attrib; posBufX = posBufX + 1; tempLine.linea[posBufX].ch = END_LINE_CHAR; tempLine.linea[posBufX].specialChar = 0; tempLine.linea[posBufX].attrib = attrib; _updateLine(edBuf1, posBufY, &tempLine); linetoScreenRAW(cursorY,tempLine); //the cursor returns to its place when scrolling horizontally if (posBufX < MAX_LINE_SIZE && cursorX == new_columns-2) {cursorX--;} } } if (shiftH>0){ buffertoScreen(1); } } //record previous values old_cursorX = cursorX; old_cursorY = cursorY; oldposBufX = posBufX; oldposBufY = posBufY; oldEndLine = endLine; //HANDLE ENTER KEY if (ch == K_ENTER) { // ENTER key update_ch(cursorX, cursorY, ' ', EDITAREACOL, EDITAREACOL); // Move cursor down if not at the bottom of the screen if (cursorY < new_rows - 3) { cursorY++; } cursorX = START_CURSOR_X; // If the buffer pointer is at the end, create a new empty line if (_length(&edBuf1) <= posBufY) { // Create a new line without characters in the current line in the edit buffer memset(&tempLine, '\0', sizeof(tempLine)); tempLine.index = _length(&edBuf1); tempLine.linea[0].ch = END_LINE_CHAR; tempLine.linea[0].specialChar = 0; tempLine.linea[0].attrib = 0; edBuf1 = _addatend(edBuf1, _newline(tempLine)); } else { // SPLIT LINE INTO TWO _dumpLine(edBuf1, posBufY, &tempLine); endLine = findEndline(tempLine); // Create a new line to insert the split content memset(&splitLine, '\0', sizeof(splitLine)); memset(&tempLine, '\0', sizeof(tempLine)); tempLine.index = _length(&edBuf1); edBuf1 = _addatend(edBuf1, _newline(tempLine)); // Move lines below the current line down by one for (j = _length(&edBuf1) - 1; j > posBufY; j--) { _dumpLine(edBuf1, j, &tempLine); _updateLine(edBuf1, j + 1, &tempLine); if (j + START_CURSOR_Y < new_rows - 3) { cleanSection(j + START_CURSOR_Y, 0, findEndline(tempLine)); } } // Split the current line at the cursor position _dumpLine(edBuf1, posBufY, &tempLine); memset(&splitLine, '\0', sizeof(splitLine)); // Copy characters before the cursor to the current line for (i = 0; i < posBufX; i++) { splitLine.linea[i].ch = tempLine.linea[i].ch; splitLine.linea[i].specialChar = tempLine.linea[i].specialChar; splitLine.linea[i].attrib = tempLine.linea[i].attrib; } if (isLineTerminated(splitLine) == FALSE) { splitLine.linea[i].ch = END_LINE_CHAR; } _updateLine(edBuf1, posBufY, &splitLine); // Move characters after the cursor to the next line memset(&splitLine, '\0', sizeof(splitLine)); j = 0; for (i = posBufX; i < endLine; i++) { splitLine.linea[j].ch = tempLine.linea[i].ch; splitLine.linea[j].specialChar = tempLine.linea[i].specialChar; splitLine.linea[j].attrib = tempLine.linea[i].attrib; j++; } if (isLineTerminated(splitLine) == FALSE) { splitLine.linea[j].ch = END_LINE_CHAR; } // Update the new line with the split content _updateLine(edBuf1, posBufY + 1, &splitLine); // Clean the screen for the current line and refresh the buffer cleanSection(cursorY - 1, 0, findEndline(tempLine)); buffertoScreen(TRUE); } // Scroll if needed when cursor is at the bottom of the screen if (_length(&edBuf1) > vdisplayArea && cursorY == new_rows - 3) { buffertoScreen(1); currentLine++; } // Move buffer pointer positions posBufY++; posBufX = 0; } if(ch == K_BACKSPACE) { // BACKSPACE key update_ch(cursorX, cursorY, ' ', EDITAREACOL, EDITAREACOL); if (posBufX == findEndline(tempLine)) { // Cursor is at the end of the line tempLine.linea[posBufX-1].ch = 0; tempLine.linea[posBufX-1].specialChar = 0; tempLine.linea[posBufX-1].attrib = 0; } else { // Shift characters to the left if we are not at the end of the line if (posBufX != 0) { for(i = posBufX - 1; i < findEndline(tempLine) - 1; i++) { tempLine.linea[i].ch = tempLine.linea[i + 1].ch; tempLine.linea[i].specialChar = tempLine.linea[i + 1].specialChar; tempLine.linea[i].attrib = tempLine.linea[i + 1].attrib; } // Clear the last character in the line tempLine.linea[i].ch = 0; tempLine.linea[i].specialChar = 0; tempLine.linea[i].attrib = 0; } } // Remove line if we continue pressing backspace at the beginning of a line if(cursorX == START_CURSOR_X && cursorY > START_CURSOR_Y) { if (posBufY > 0) { memset(&tempLine, '\0', sizeof(tempLine)); memset(&splitLine, '\0', sizeof(splitLine)); _dumpLine(edBuf1, posBufY - 1, &tempLine); if (findEndline(tempLine) < 1) { // Previous line is empty, remove it for (j = posBufY - 1; j < _length(&edBuf1) - 1; j++) { _dumpLine(edBuf1, j + 1, &tempLine); _hardupdateLINE(&edBuf1, j, tempLine); } _deleteObject(&edBuf1, _length(&edBuf1) - 1, FALSE); } else { // Merge two lines endLine = findEndline(tempLine); _dumpLine(edBuf1, posBufY, &splitLine); for (i = 0; i < findEndline(splitLine); i++) { tempLine.linea[endLine + i].ch = splitLine.linea[i].ch; tempLine.linea[endLine + i].attrib = splitLine.linea[i].attrib; tempLine.linea[endLine + i].specialChar = splitLine.linea[i].specialChar; } tempLine.linea[endLine + i].ch = END_LINE_CHAR; _hardupdateLINE(&edBuf1, posBufY - 1, tempLine); // Update subsequent lines for (j = posBufY; j < _length(&edBuf1) - 1; j++) { _dumpLine(edBuf1, j + 1, &tempLine); _hardupdateLINE(&edBuf1, j, tempLine); } _deleteObject(&edBuf1, _length(&edBuf1) - 1, FALSE); } posBufY--; cursorY--; if (cursorY == new_rows - 3 && currentLine > 0) currentLine--; buffertoScreen(1); _dumpLine(edBuf1, posBufY, &tempLine); // Adjust cursor position after merging lines if (findEndline(splitLine) != 0 && findEndline(splitLine) != findEndline(tempLine)) { posBufX = findEndline(tempLine) + 1; cursorX = posBufX + START_CURSOR_X; } else { if (endLine != findEndline(tempLine)) { cursorX = START_CURSOR_X; posBufX = cursorX - 1; } else { posBufX = findEndline(tempLine) + 1; cursorX = posBufX + START_CURSOR_X; } } } } // Normal backspace within a line if(cursorX > START_CURSOR_X) { cursorX--; posBufX--; _updateLine(edBuf1, posBufY, &tempLine); } // Clean up and refresh the line on screen cleanSection(cursorY, findEndline(tempLine), 2); linetoScreenRAW(cursorY, tempLine); } if (deleteKeyPressed == 1) { deleteKeyPressed = 0; // DEL key if (posBufX == findEndline(tempLine)) { // Cursor is at the end of the line if (posBufY < _length(&edBuf1) - 1) { // If there is a next line, merge it with the current line _dumpLine(edBuf1, posBufY + 1, &splitLine); endLine = findEndline(tempLine); // Copy the characters from the next line to the end of the current line for (i = 0; i < findEndline(splitLine); i++) { tempLine.linea[endLine + i].ch = splitLine.linea[i].ch; tempLine.linea[endLine + i].attrib = splitLine.linea[i].attrib; tempLine.linea[endLine + i].specialChar = splitLine.linea[i].specialChar; } tempLine.linea[endLine + i].ch = END_LINE_CHAR; // Update the buffer with the modified line _hardupdateLINE(&edBuf1, posBufY, tempLine); // Remove the next line from the buffer for (j = posBufY + 1; j < _length(&edBuf1); j++) { _dumpLine(edBuf1, j + 1, &tempLine); _hardupdateLINE(&edBuf1, j, tempLine); } _deleteObject(&edBuf1, _length(&edBuf1) - 1, FALSE); // Update the screen buffertoScreen(1); } } else { // Cursor is in the middle of the line, so delete the character at the cursor position for (i = posBufX; i < findEndline(tempLine); i++) { tempLine.linea[i].ch = tempLine.linea[i + 1].ch; tempLine.linea[i].specialChar = tempLine.linea[i + 1].specialChar; tempLine.linea[i].attrib = tempLine.linea[i + 1].attrib; } tempLine.linea[i].ch = 0; // Nullify the last character // Update the buffer with the modified line _updateLine(edBuf1, posBufY, &tempLine); // Update the screen cleanSection(cursorY, findEndline(tempLine), 2); linetoScreenRAW(cursorY, tempLine); } } if(ch == K_TAB) { //TAB key sends spaces for convenience for (i=posBufX; i 0) tempLine.linea[inlineChar].ch = ch; } //SYNTAX HIGHLIGHTING DEMO //Highlight numbers in GREEN attrib = EDIT_FORECOLOR; if ((ch >= 48) && (ch <=57)) attrib = FH_GREEN; //Highlight special characters in CYAN if ((ch >= 33) && (ch <=47)) attrib = FH_CYAN; if ((ch >= 58) && (ch <=64)) attrib = FH_CYAN; if ((ch >= 91) && (ch <=96)) attrib = FH_CYAN; if ((ch >= 123) && (ch <=126)) attrib = FH_CYAN; tempLine.linea[inlineChar].attrib = attrib; //TABs are converted into spaces /* if(ch == K_TAB) { for (tabcount=0;tabcount', SCROLLBAR_ARR, SCROLLBAR_FORE,0); if (strlen(fileName) == 0) strcpy(fileName,"UNTITLED"); write_str(screen1,(new_columns / 2) - (strlen(fileName) / 2), 2, fileName, MENU_PANEL, MENU_FOREGROUND0,0); if (force_update) dump_screen(screen1); } ================================================ FILE: src/editor.h ================================================ /* ======================================================================== - HEADER - Module with main editor in C·edit @author : Velorek @version : 1.0 Last modified: 06/04/2024 ======================================================================== */ #ifndef _EDITOR_H_ #define _EDITOR_H_ #include "scbuf.h" #include "global.h" #include "edbuf.h" #include int process_input(char ch); int special_keys(); int control_keys(char ch); void linetoScreen(long whereY, VLINES tempLine); void linetoScreenRAW(long whereY, VLINES tempLine); void cleanScreenLine(long whereY); void cleanSection(long whereY, long start, int amount); void buffertoScreen(BOOL raw); int editor_section(char ch); wchar_t convertChar(char c1, char c2); //int filetoBuffer(FILE * filePtr); void buffertoFile(char *fileName); int filetoBuffer(char *fileName); void flush_editarea(int force_update); #endif ================================================ FILE: src/fileb.c ================================================ /* ====================================================================== Module to describe basic file operations. @author : Velorek @version : 1.0 LAST MODIFIED : 14/04/2019 - Rename headers ====================================================================== */ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include "global.h" #include "fileb.h" /*====================================================================*/ /* FUNCTIONS - CODE */ /*====================================================================*/ /*----------------------*/ /* Check if file exists */ /*----------------------*/ int file_exists(char *fileName) { int ok=0; if(access(fileName, F_OK) != -1) { ok = 1; //File exists } else { ok = 0; } return ok; } /*---------------*/ /* Get file size */ /*---------------*/ long getfileSize(FILE * filePtr) { long sz=0; if(filePtr != NULL) { fseek(filePtr, 0L, SEEK_END); sz = ftell(filePtr); rewind(filePtr); } return sz; } /*---------------------*/ /* Count lines in File */ /*---------------------*/ long countLinesFile(FILE * filePtr) { char ch=0; long counterA = 0; if(filePtr != NULL) { rewind(filePtr); //Make sure we are at the beginning ch = getc(filePtr); //Peek ahead in the file while(!feof(filePtr)) { if(ch == END_LINE_CHAR) { counterA++; } ch = getc(filePtr); } } return counterA; } /*-----------------*/ /* Check file type */ /*-----------------*/ long checkFile(FILE * filePtr) { char ch=0; long counterA = 0; if(filePtr != NULL) { rewind(filePtr); //Make sure we are at the beginning ch = getc(filePtr); //Peek ahead in the file while(!feof(filePtr)) { if(ch < 9) { //discard accents if(ch > -60) counterA++; } ch = getc(filePtr); } } return counterA; } //check whether the file is a text file int openandcheckFile(char *fileName) { unsigned char ch=0; long counterA = 0; FILE *fp; openFile(&fp, fileName, "r"); if(fp != NULL) { rewind(fp); //Make sure we are at the beginning ch = getc(fp); //Peek ahead in the file while(!feof(fp)) { if (ch == 0x00) {closeFile(fp); fp = NULL; return 1;} if(ch < 9) { //discard accents if(ch > 196) counterA++; } ch = getc(fp); } } //If there are more strange characters than half the size of the file probably a binary file if (counterA > (getfileSize(fp)/2)) { if (fp != NULL) closeFile(fp); fp = NULL; return 1; } if (fp != NULL) closeFile(fp); fp = NULL; return 0; } /*-----------*/ /* Open file */ /*-----------*/ int openFile(FILE ** filePtr, char fileName[], char *mode) /* Open file. @return ok = 1 ? 0 Success! */ { int ok = 0; *filePtr = fopen(fileName, mode); if(*filePtr != NULL) { //File opened correctly. ok = 1; } else { //Error ok = 0; } return ok; } /*------------*/ /* Close file */ /*------------*/ int closeFile(FILE * filePtr) { /* Close file @return ok: */ int ok=0; if(filePtr != NULL) { ok = fclose(filePtr); } return ok; } ================================================ FILE: src/fileb.h ================================================ /* ======================================================================== - HEADER - Module to handle basic file operations @author : Velorek @version : 1.0 Last modified: 31/08/2025 Open and check file ======================================================================== */ #ifndef _FILEB_H_ #define _FILEB_H_ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include "scbuf.h" /*====================================================================*/ /* CONSTANTS */ /*====================================================================*/ #define CONFIGFILE "cedit.cfg" /*====================================================================*/ /* FUNCTION PROTOTYPES */ /*====================================================================*/ int openFile(FILE ** filePtr, char fileName[], char *mode); int closeFile(FILE * filePtr); long getfileSize(FILE * filePtr); long countLinesFile(FILE * filePtr); long checkFile(FILE * filePtr); int openandcheckFile(char *fileName); int file_exists(char *fileName); #endif ================================================ FILE: src/global.c ================================================ #include "global.h" #include "rterm.h" #include "time.h" // GLOBALS : SCREEN1 : ANIMATED | SCREEN2 : BASE SCREEN LISTCHOICE *listBox1 = NULL; SCREENCELL *screen1=NULL; SCREENCELL *screen2=NULL; SCREENCELL *screen3=NULL; NTIMER cursor_timer1; NTIMER timer2; NTIMER timer3; //NTIMER timer4; SCROLLDATA scrollData; VLINES *edBuf1=NULL; //Buffer vector of lines(1022 chars) VLINES tempLine; //C-Edit Theme int EDITAREACOL= B_BLUE; int EDIT_FORECOLOR=FH_WHITE; int STATUSBAR =B_BLACK; int STATUSMSG =F_WHITE; int MENU_PANEL =B_WHITE; int MENU2_PANEL = B_BLACK; int MENU_SELECTOR =B_RED; int MENU_FOREGROUND0 =F_BLACK; int MENU2_FOREGROUND0 = F_WHITE; int MENU_FOREGROUND1= FH_WHITE; int EDITWINDOW_BACK= B_BLUE; int EDITWINDOW_FORE= F_WHITE; int SCROLLBAR_BACK= B_WHITE; int SCROLLBAR_FORE= FH_WHITE; int SCROLLBAR_SEL= B_CYAN; int SCROLLBAR_ARR= B_BLACK; int WINDOW_TITLEB = B_BLACK; int WINDOW_TITLEF = F_WHITE; int new_rows = 0, new_columns = 0, old_rows = 0, old_columns = 0; // Terminal dimensions int cursorX = START_CURSOR_X, cursorY = START_CURSOR_Y; int old_cursorX = START_CURSOR_X, old_cursorY = START_CURSOR_Y; long posBufX = 0, posBufY = 0; long oldposBufX = 0, oldposBufY = 0; long shiftH = 0; int unwantedChars = 0; //try to avoid printing unwanted characgters from cursor chars counter //TEXT BUFFER POINTERS int hdisplayLimit=0; //horizontal scroll, last char position in line that allows scroll int currentColumn=0; //horizontal scroll, pointer to current char position in line long linesinFile=0; //vertical scroll long vdisplayLimit=0; //vertical scroll, last line that allows scroll int vdisplayArea = 0; //size in lines of the allowed displayed area // calculated when screen resizes int hdisplayArea = 0; //size in lines of the allowed displayed area // calculated when screen resizes long currentLine=0; //verticall scroll, pointer to current top line in scroll int vscrollActive=0; //vertical scroll, vertical scroll is posible //extern int hscrollActive; //horizontal scroll, horizontal scroll is posible int programStatus = 0; //signal for overall program Status char fileName[MAXFILENAME]; char fullPath[MAXFILENAME]; FILE *filePointer; int fileModified=FILE_UNMODIFIED; int deleteKeyPressed = 0; char aboutMSG[7][MAXLINE] = {ABOUT_ASC_0,ABOUT_ASC_1,ABOUT_ASC_2,ABOUT_ASC_3,ABOUT_ASC_4,ABOUT_ASC_5}; char help[HELPLINES][MAXLINE] = { HELP0, HELP1, HELP2, HELP3, HELP4, HELP5, HELP6, HELP7, HELP8, HELP9, HELP10, HELP11, HELP12, HELP13, HELP14, HELP15, HELP16, HELP17, HELP18, HELP19, HELP20 }; int initCEDIT(){ get_terminal_dimensions(&new_rows,&new_columns); //Init timer init_timer(&cursor_timer1,TIMER_SPEED); init_timer(&timer2,TIMER_SPEED2); init_timer(&timer3,TIMER_SPEED3); //init_timer(&timer4,ABOUT_SPEED); timer2.ticks=0; timer3.ticks=-1; //timer4.ticks=0; cursor_timer1.ticks=0; //Init Text buffer VLINES tempLine = {0}; memset(&fileName, '\0',sizeof(fileName)); //Clear fileName memset(&tempLine, '\0',sizeof(tempLine)); //Clear memory for temporary line //Create one screen in memory if (screen1 != NULL) deleteList(&screen1); if (screen2 != NULL) deleteList(&screen2); //Init 2 : Create 2 Screens for a double buffer approach old_rows=new_rows; old_columns=new_columns; create_screen(&screen1); create_screen(&screen2); //how many lines are vertical display area consists of vdisplayArea = new_rows - 4; hdisplayArea = new_columns - 2; shiftH=0; return 0; } int _animation(){ /* Timer for animations - Display time and clean cursor */ time_t mytime = time(NULL); char *time_str = ctime(&mytime); char temp[4]; temp[0] = '['; temp[1] = ANIMATION[timer2.ticks]; temp[2] = ']'; temp[3] = '\0'; get_terminal_dimensions(&new_rows,&new_columns); resetAnsi(0); time_str[strlen(time_str) - 1] = '\0'; //display system time update_str(new_columns - strlen(time_str) - 1, 0, time_str, MENU_PANEL, MENU_FOREGROUND0); update_str(new_columns - strlen(time_str) - 5, 0, temp, MENU_PANEL, MENU_FOREGROUND0); printf("\n"); //update only the screen bits that change if(timer2.ticks > 6) timer2.ticks = 0; //return a signal if screen size changes if (new_rows != old_rows || new_columns != old_columns) return -1; else return 0; } ================================================ FILE: src/global.h ================================================ /* * Global variables are intented to be placed here * Last modified: 9/7/2022 * @author: velorek * */ #ifndef _GLOBAL_H_ #define _GLOBAL_H_ #include "scbuf.h" #include "tm.h" #include "edbuf.h" #include "listbox.h" #define TIMER_SPEED 100 //animation #define TIMER_SPEED2 30 //cursor #define TIMER_SPEED3 100 //temporary messages //#define ABOUT_SPEED 300 //about animation #define END_LINE_CHAR 0x0A #define FILL_CHAR 0x20 #define ENDSIGNAL -1 #define ESC_KEY '\e' #define ROWS_FAILSAFE 25 #define COLUMNS_FAILSAFE 80 #define TRUE 1 #define FALSE 0 #define CURSOR_CHAR '|' typedef int BOOL; //USER-DEFINED MESSAGES extern SCREENCELL *screen1; extern SCREENCELL *screen2; extern NTIMER cursor_timer1; extern NTIMER timer2; extern NTIMER timer3; //extern NTIMER timer4; extern size_t animation; extern int centerX; //extern LISTCHOICE listBox1; extern SCROLLDATA scrollData; extern VLINES *edBuf1; //Buffer vector of lines(512 chars) extern VLINES tempLine; //COLOR SCHEME extern int EDITAREACOL; extern int EDIT_FORECOLOR; extern int STATUSBAR; extern int STATUSMSG; extern int MENU_PANEL; extern int MENU2_PANEL; extern int MENU_SELECTOR; extern int MENU_FOREGROUND0; extern int MENU2_FOREGROUND0; extern int MENU_FOREGROUND1; extern int EDITWINDOW_BACK; extern int EDITWINDOW_FORE; extern int SCROLLBAR_BACK; extern int SCROLLBAR_FORE; extern int SCROLLBAR_SEL; extern int SCROLLBAR_ARR; extern int WINDOW_TITLEB; // to be accessible from opfile.c extern int WINDOW_TITLEF; //GOODBYE MSG - ASCII ART #define cedit_ascii_1 " _____ ______ _ _ _ \n" #define cedit_ascii_2 " / ____| | ____| | (_) | \n" #define cedit_ascii_3 "| | ______| |__ __| |_| |_ \n" #define cedit_ascii_4 "| | |______| __| / _` | | __|\n" #define cedit_ascii_5 "| |____ | |___| (_| | | |_ \n" #define cedit_ascii_6 " \\_____| |______\\__,_|_|\\__|\n" #define TAB_SPACES 8 //ABOUT - ASCII ART #define ABOUT_ASC_0 "______________________________" #define ABOUT_ASC_1 " _ __ " #define ABOUT_ASC_2 " / _|__ _|.___ " #define ABOUT_ASC_3 " \\_ |__(_]| | v0.1 " #define ABOUT_ASC_4 "______________________________" #define ABOUT_ASC_5 " Coded by V3l0rek " #define ANIMATION "||//--\\\\" #define HELP0 "C-EDIT \0" #define HELP1 "======= \0" #define HELP2 "C-EDIT is a terminal TUI curses text editor. \0" #define HELP3 "It offers both vertical and horizontal scroll \0" #define HELP4 "for file buffer navigation. It also features a \0" #define HELP5 "variety of animations and has a built-in open file \0" #define HELP6 "dialog to easily locate the files in directories. \0" #define HELP7 "___________________________________________________\0" #define HELP8 "[F1] or [ALT + H] -> Display help \0" #define HELP9 "[F2] or [CTRL + L] -> Activate menu \0" #define HELP10 "[Arrow Keys] -> Navigate file buffer \0" #define HELP11 "[CTRL + A] -> Quick Load | CTRL + N -> New file \0" #define HELP12 "[ALT + O] -> Open file | \0" #define HELP13 "[ALT + S] -> Save file \0" #define HELP14 "[ESC] or [CTRL + C] or [ALT+X] -> Exit program \0" #define HELP15 "[S] Hide/Show table | CTRL or ALT may be used. \0" #define HELP16 "___________________________________________________\0" #define HELP17 "This program was coded in C & Vim from 2018-2024. \0" #define HELP18 "Some of the techniques used can be found in my \0" #define HELP19 "personal blog : oldstuff286.blogspot.com \0" #define HELP20 "Peace! - by v3l0r3k \0" #define HELPLINES 21 //USER-DEFINED MESSAGES #define UNKNOWN "UNTITLED" #define WINDOWMSG "DISPLAY IS TOO SMALL. PLEASE, RESIZE WINDOW" #define STATUS_BAR_MSG1 " [C-Edit] | F2,CTRL+L: MENU | F1: HELP" #define STATUS_BAR_MSG2 " [C-Edit] Press ESC to exit menu. " #define STATUS_BAR_MSG3 " ENTER: SELECT | <- -> ARROW KEYS " #define WLEAVE_MSG "\n Are you sure\n you want to quit?" #define WSAVE_MSG ":\nFile saved successfully!" #define WSAVELABEL_MSG "[-] File:" #define WINFO_NOPEN "Error:\nThere isn't any file open!" #define WINFO_SIZE "-File size: " #define WINFO_SIZE2 "\n-No. of lines: " #define WINFO_SIZE3 "\n-File name: " #define WCHECKFILE2_MSG " File does not exist! \n\n A new buffer will be created. \n" #define WCHECKFILE_MSG " This file isn't a|text file. Program may crash.|Open anyway?" #define WINFONOTYET_MSG "Not implemented yet!" #define WCHECKLINES_MSG " File longer than 3000 \n lines. You'll view those \n lines as read Mode! " #define WMODIFIED_MSG "File has been modified|Save current buffer?" #define WFILEEXISTS_MSG " File exists. \n Overwrite?" #define WFILEINREADMODE_MSG " File is on read mode. \n" #define START_CURSOR_X 1 #define START_CURSOR_Y 2 //MENU CONSTANTS #define HOR_MENU -1 #define FILE_MENU 0 #define OPT_MENU 1 #define HELP_MENU 2 #define YESNO_MENU 3 #define OK_MENU 4 #define MAX_FILENAME 255 #define MAX_LINES 100000 #define MAXLINE 255 //DROP-DOWN MENUS #define OPTION_1 0 #define OPTION_2 1 #define OPTION_3 2 #define OPTION_4 3 #define OPTION_5 4 #define OPTION_6 5 #define OPTION_NIL -1 //Reset option #define CONFIRMATION 1 #define K_LEFTMENU -1 //Left arrow key pressed while in menu #define K_RIGHTMENU -2 //Right arrow key pressed while in menu #define DONT_UPDATE -5 //MENU CONSTANTS #define HOR_MENU -1 #define FILE_MENU 0 #define OPT_MENU 1 #define HELP_MENU 2 #define YESNO_MENU 3 #define OK_MENU 4 #define MAXFILENAME 100 #define OK_MENU2 5 #define COLORS_MENU 6 //FILE CONSTANTS #define FILE_MODIFIED 1 #define FILE_UNMODIFIED 0 #define FILE_READMODE 2 extern char aboutMSG[7][MAXLINE]; extern char help[HELPLINES][MAXLINE]; extern int new_rows, new_columns, old_rows, old_columns; // Terminal dimensions extern int cursorX, cursorY; //position on screen X:[0, columns -2] Y:[0, rows-3] extern int old_cursorX, old_cursorY; extern long posBufX, posBufY; //position in the edit buffer X:[0,1022] Y:[0,13000] extern long oldposBufX, oldposBufY; //position in the edit buffer X:[0,1022] Y:[0,13000] extern long shiftH; //for horizontal scroll extern int unwantedChars; int _animation(); int initCEDIT(); //TEXT BUFFER POINTERS extern VLINES *edBuf1; //Buffer vector of lines(1022 chars) extern VLINES tempLine; extern FILE *filePointer; extern int vdisplayArea; //size in lines of the allowed displayed area // calculated when screen resizes extern int hdisplayArea; //size in lines of the allowed displayed area // calculated when screen resizes extern int hdisplayLimit; //horizontal scroll, last char position in line that allows scroll extern int currentColumn; //horizontal scroll, pointer to current char position in line extern long linesinFile; //vertical scroll extern long vdisplayLimit; //vertical scroll, last line that allows scroll extern long currentLine; //verticall scroll, pointer to current top line in scroll extern int vscrollActive; //vertical scroll, vertical scroll is posible extern int programStatus; //signal for overall program status extern char fileName[MAXFILENAME]; extern char fullPath[MAXFILENAME]; extern int fileModified; extern int deleteKeyPressed; //extern int hscrollActive; //horizontal scroll, horizontal scroll is posible #endif ================================================ FILE: src/keyb.c ================================================ /* ====================================================================== Module to control keyboard input. @author : Velorek @version : 1.0 LAST MODIFIED : 14/04/2019 Rename headers ====================================================================== */ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include "rterm.h" #include "keyb.h" /*====================================================================*/ /* FUNCTIONS - CODE */ /*====================================================================*/ /*----------------------------------*/ /* Read ESC-key char with its trail */ /*----------------------------------*/ int read_keytrail(char chartrail[5]){ /* New implementation: Trail of chars found in keyboard.c If K_ESCAPE is captured read a trail up to 5 characters from the console. This is to control the fact that some keys may change according to the terminal and expand the editor's possibilities. Eg: F2 can be either 27 79 81 or 27 91 91 82. */ char ch; int i; chartrail[0] = K_ESCAPE; for(i = 1; i < 5; i++) { if(kbhit(1) == 1) { ch=readch(); chartrail[i] = ch; } else { chartrail[i] = 0; } } resetch(); return 1; } /*----------------------------------*/ /* Read Accents and Special Chars */ /*----------------------------------*/ int read_accent(char *ch, char accentchar[2]) { /* Input Ref: ch, accentchar @return : 1 - SET 1 | 2 - SET 2 | 0 - NO ACCENT CHAR */ int result; result = 0; //Accents and special chars accentchar[0] = 0; accentchar[1] = *ch; if(*ch == SPECIAL_CHARS_SET1) { accentchar[0] = SPECIAL_CHARS_SET1; //Accents and special chars SET1 accentchar[1] = readch(); result = 1; resetch(); } if(*ch == SPECIAL_CHARS_SET2) { accentchar[0] = SPECIAL_CHARS_SET2; //Accents and special chars SET2 accentchar[1] = readch(); result = 2; resetch(); } return result; } ================================================ FILE: src/keyb.h ================================================ /* ======================================================================== - HEADER - Module to handle keyboard input in Linux and create a further layer of abstraction. @author : Velorek @version : 1.0 Last modified: 14/04/2019 Rename Headers ======================================================================== */ #ifndef _KEYB_H_ #define _KEYB_H_ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include "rterm.h" /*====================================================================*/ /* KEYS - CONSTANTS */ /*====================================================================*/ #define K_ENTER 13 #define K_CAPS 91 #define K_BACKSPACE 127 #define K_TAB 9 #define K_ESCAPE 27 #define K_F1_TRAIL "\eOP\0\0" #define K_F1_TRAIL2 "\e[[A\0" #define K_F2_TRAIL "\eOQ\0\0" #define K_F2_TRAIL2 "\e[[B\0" #define K_F3_TRAIL "\eOR\0\0" #define K_F3_TRAIL2 "\e[[C\0" #define K_F4_TRAIL "\eOS\0\0" #define K_F4_TRAIL2 "\e[[D\0" #define K_UP_TRAIL "\e[A\0\0" #define K_DOWN_TRAIL "\e[B\0\0" #define K_RIGHT_TRAIL "\e[C\0\0" #define K_LEFT_TRAIL "\e[D\0\0" #define K_PAGEDOWN_TRAIL "\e[6~" #define K_PAGEUP_TRAIL "\e[5~" #define K_HOME_TRAIL "\e[H" #define K_END_TRAIL "\e[F" #define K_HOME_TRAIL2 "\e[1~" #define K_END_TRAIL2 "\e[4~" #define K_DELETE "\e[3~" #define K_CTRL_A 0x01 #define K_CTRL_B 0x02 #define K_CTRL_C 0x03 #define K_CTRL_D 0x04 #define K_CTRL_E 0x05 #define K_CTRL_J 0x0A #define K_CTRL_L 0x0C #define K_CTRL_H 0x08 #define K_CTRL_M 0x0D #define K_CTRL_N 0x0E #define K_CTRL_O 0x0F #define K_CTRL_S 0x14 #define K_ALT_X "\ex" #define K_ALT_L "\el" #define K_ALT_S "\es" #define K_ALT_W "\ew" #define K_ALT_H "\eh" #define K_ALT_N "\en" #define K_ALT_O "\eo" #define K_ALT_W "\ew" #define K_ALT_A "\ea" #define K_ALT_I "\ei" #define K_ALT_C "\ec" #define K_ALT_F "\ef" #define K_ALT_P "\ep" #define K_ALT_S "\es" #define K_ALT_V "\ev" #define K_ALT_D "\ed" #define ESC3X "\e\e\e" #define ESC2X "\e\e" #define SPECIAL_CHARS_SET1 -61 #define SPECIAL_CHARS_SET2 -62 /*====================================================================*/ /* FUNCTION PROTOTYPES */ /*====================================================================*/ int read_keytrail(char chartrail[5]); int read_accent(char *ch, char accentchar[2]); #endif ================================================ FILE: src/listbox.c ================================================ /*====================================================================*/ /* +ListBox with double linked list and selection menu in C with * horizontal scroll. +Scroll function added. Last modified : 01/04/2024 + Horizontal option implementation started + ESC_KEY added + X,Y option added for different options - Pending: horizontal scroll Coded by Velorek. Raw output Target OS: Linux. */ /*====================================================================*/ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include #include #include "rterm.h" #include "listbox.h" #include "keyb.h" #include "tm.h" #include "global.h" /*====================================================================*/ /* CODE */ /*====================================================================*/ int double_escape=0; int selectorLimit = 15; //by default selectoLimit is 15 when spaces are added int newrows=0, newcolumns=0; int listrows=0, listcolumns=0; char KTRAIL0[5]={0}; char KTRAIL1[5]={0}; /* --------------------- */ /* Dynamic List routines */ /* --------------------- */ // create new list element of type LISTCHOICE from the supplied text string LISTCHOICE *newitem(char *text,unsigned setX, unsigned setY,unsigned foreColor, unsigned backColor) { LISTCHOICE *newp; newp = (LISTCHOICE *) malloc(sizeof(LISTCHOICE)); newp->item = (char *)malloc(strlen(text) + 1); strcpy(newp->item, text); newp->next = NULL; newp->back = NULL; newp->setX = setX; newp->setY = setY; newp->foreColor = foreColor; newp->backColor = backColor; return newp; } // deleleteList: remove list from memory void removeList(LISTCHOICE ** head) { LISTCHOICE *aux = *head; LISTCHOICE *next = NULL; while(aux != NULL) { next = aux->next; free(aux->item); free(aux); //remove item aux = next; } *head = NULL; } /* addatend: add new LISTCHOICE to the end of a list */ /* usage example: listBox1 = (addatend(listBox1, newitem("Item")); */ LISTCHOICE *addatend(LISTCHOICE * head, LISTCHOICE * newp) { LISTCHOICE *p2; if(head == NULL) { newp->index = 0; newp->back = NULL; return newp; } // now find the end of list for(p2 = head; p2->next != NULL; p2 = p2->next) ; p2->next = newp; newp->back = p2; newp->index = newp->back->index + 1; return head; } /* ---------------- */ /* Listbox routines */ /* ---------------- */ void gotoIndex(LISTCHOICE ** aux, SCROLLDATA * scrollData, unsigned indexAt) //Go to a specific location on the list. { LISTCHOICE *aux2; unsigned counter = 0; *aux = listBox1; aux2 = *aux; while(counter != indexAt) { aux2 = aux2->next; counter++; } //Highlight current item displayItem(aux2, scrollData, SELECT_ITEM); //Update pointer *aux = aux2; } void printlist(LISTCHOICE * head, SCROLLDATA * scrollData, unsigned displayLimit) { /* Displays the items contained in the list with the properties specified in scrollData. */ LISTCHOICE *aux; unsigned wherey, counter = 0; aux = head; gotoIndex(&aux, scrollData, 0); /* Save values */ //wherex = scrollData->wherex; wherey = scrollData->wherey; do { displayItem(aux, scrollData, UNSELECT_ITEM); aux = aux->next; counter++; scrollData->selector++; // wherey++ } while(counter != displayLimit); scrollData->selector = wherey; //restore value } void loadlist(LISTCHOICE * head, SCROLLDATA * scrollData, unsigned indexAt) { /* Displays the items contained in the list with the properties specified in scrollData. */ LISTCHOICE *aux; unsigned wherey, counter = 0; aux = head; gotoIndex(&aux, scrollData, indexAt); /* Save values */ //wherex = scrollData->wherex; wherey = scrollData->wherey; do { displayItem(aux, scrollData, UNSELECT_ITEM); aux = aux->next; counter++; scrollData->selector++; // wherey++ } while(counter != scrollData->displayLimit); scrollData->selector = wherey; //restore value } int query_length(LISTCHOICE ** head) { //Return no. items in a list. { LISTCHOICE *aux; unsigned itemCount = 0; aux = *head; while(aux->next != NULL) { aux = aux->next; itemCount++; } return itemCount; } } void displayItem(LISTCHOICE * aux, SCROLLDATA * scrollData, int select) //Select or unselect item animation { size_t i=0; wchar_t newchar=0; resetAnsi(0); switch (select) { case SELECT_ITEM: if ((aux->setX == -1) || (aux->setY== -1 )) gotoxy(scrollData->wherex, scrollData->selector); else gotoxy(aux-> setX, aux -> setY); if ((aux->foreColor == -1) || (aux->backColor== -1 )) outputcolor(scrollData->foreColor1, scrollData->backColor1); else outputcolor(aux->foreColor, aux->backColor); //printf("%s\n", aux->item); for (i=0;iselectorLimit;i++) { if(i < strlen(aux->item)){ newchar=aux->item[i]; printf("%lc", newchar); } else if (scrollData->addSpaces != 0) printf("%c", 0x20); } printf("\n"); break; case UNSELECT_ITEM: if ((aux->setX == -1) || (aux->setY== -1 )) gotoxy(scrollData->wherex, scrollData->selector); else gotoxy(aux -> setX, aux -> setY); outputcolor(scrollData->foreColor0, scrollData->backColor0); //printf("%s\n", aux->item); for (i=0;iselectorLimit;i++) { if(i < strlen(aux->item)){ newchar = aux->item[i]; printf("%lc", newchar); } else if (scrollData->addSpaces != 0) printf("%c", 0x20); } printf("\n"); break; } } int move_selector(LISTCHOICE ** selector, SCROLLDATA * scrollData) { /* Creates animation by moving a selector highlighting next item and unselecting previous item */ LISTCHOICE *aux; unsigned scrollControl = 0, continueScroll = 0, circular = CIRCULAR_INACTIVE; //Auxiliary pointer points to selector. aux = *selector; //Circular list animation when not scrolling. if(aux->index == scrollData->listLength - 1 && scrollData->scrollActive == SCROLL_INACTIVE && scrollData->scrollDirection == DOWN_SCROLL) { //After last item go back to the top. displayItem(aux, scrollData, UNSELECT_ITEM); scrollData->selector = scrollData->wherey; gotoIndex(&aux, scrollData, 0); *selector = aux; circular = CIRCULAR_ACTIVE; } if(aux->index == 0 && scrollData->scrollActive == SCROLL_INACTIVE && scrollData->scrollDirection == UP_SCROLL) { //Before first item go back to the bottom. displayItem(aux, scrollData, UNSELECT_ITEM); scrollData->selector = scrollData->wherey + scrollData->listLength - 1; gotoIndex(&aux, scrollData, scrollData->listLength - 1); *selector = aux; circular = CIRCULAR_ACTIVE; } //Check if we do the circular list animation. //If active, we skip the code one time. if(circular == CIRCULAR_INACTIVE) { //Check if we are within boundaries. if((aux->next != NULL && scrollData->scrollDirection == DOWN_SCROLL) || (aux->back != NULL && scrollData->scrollDirection == UP_SCROLL)) { //Unselect previous item displayItem(aux, scrollData, UNSELECT_ITEM); //Check whether we move UP or Down switch (scrollData->scrollDirection) { case UP_SCROLL: //Calculate new top index if scroll is active //otherwise it defaults to 0 (top) if(scrollData->scrollActive == SCROLL_ACTIVE) scrollControl = scrollData->currentListIndex; else scrollControl = 0; //Move selector if(aux->back->index >= scrollControl) { scrollData->selector--; //whereY-- aux = aux->back; //Go to previous item } else { if(scrollData->scrollActive == SCROLL_ACTIVE) continueScroll = 1; else continueScroll = 0; } break; case DOWN_SCROLL: //Calculate bottom index limit if scroll is ACTIVE //Otherwise it defaults to scrollData->ListLength-1 if(scrollData->scrollActive == SCROLL_ACTIVE) scrollControl = scrollData->currentListIndex + (scrollData->displayLimit - 1); else scrollControl = scrollData->listLength - 1; //Move selector if(aux->next->index <= scrollControl) { aux = aux->next; //Go to next item scrollData->selector++; //whereY++; } else { if(scrollData->scrollActive == SCROLL_ACTIVE) continueScroll = 1; else continueScroll = 0; } break; } /* //Metrics gotoxy(6, 5); printf("Length:%d|Index:%d|Memory addr:%p", scrollData->listLength, aux->index, aux); gotoxy(6, 6); printf("Scroll Limit: %d|IsScActive?:%d|ContinueScroll: %d", scrollControl, scrollData->scrollActive, continueScroll); */ //Highlight new item displayItem(aux, scrollData, SELECT_ITEM); // update_screen(screen1); //Update selector pointer *selector = aux; } } //update_screen(screen1); circular = CIRCULAR_INACTIVE; return continueScroll; } char selectorMenu(LISTCHOICE * aux, SCROLLDATA * scrollData) { char ch=0; int keypressed=0; int control = 0; int continueScroll=0; unsigned counter = 0; char chartrail[5]; //Go to and select expected item at the beginning gotoIndex(&aux, scrollData, scrollData->currentListIndex); if(scrollData->scrollDirection == DOWN_SCROLL && scrollData->currentListIndex != 0) { //If we are going down we'll select the last item //to create a better scrolling transition (animation) for(counter = 0; counter < scrollData->displayLimit; counter++) { scrollData->scrollDirection = DOWN_SCROLL; move_selector(&aux, scrollData); } } else { //Do nothing if we are going up. Selector is always at the top item. } //It break the loop everytime the boundaries are reached. //to reload a new list to show the scroll animation. while(control != CONTINUE_SCROLL) { if (timerC(&timer2) == TRUE){ //Animation in global.c if (_animation() == -1) {scrollData->itemIndex = -1;double_escape = 1; break;} } //check listbox orientation and change keys if (scrollData->orientation == VERTICAL || scrollData->orientation == VERTICALWITHBREAK){ strcpy(KTRAIL0, K_UP_TRAIL); strcpy(KTRAIL1, K_DOWN_TRAIL); } else { strcpy(KTRAIL1, K_RIGHT_TRAIL); strcpy(KTRAIL0, K_LEFT_TRAIL); } keypressed = kbhit(1); if (keypressed == 1) ch = readch(); else ch = 0; //if enter key pressed - break loop if(ch == K_ENTER) control = CONTINUE_SCROLL; //Break the loop if (ch == 'x' || ch == K_BACKSPACE || ch == K_CTRL_C){ double_escape=1; scrollData->itemIndex = -1; break; } if (ch == K_ESCAPE) // escape key { strcpy(chartrail, "\0"); read_keytrail(chartrail); //If mode VERTICALWITHBREAK is selected; selector menu is interrupted when left/right arrow keys are pressed if ((scrollData->orientation == VERTICALWITHBREAK) && (strcmp(chartrail,K_LEFT_TRAIL))==0) return K_LEFTMENU; if ((scrollData->orientation == VERTICALWITHBREAK) && (strcmp(chartrail,K_RIGHT_TRAIL))==0) return K_RIGHTMENU; if (chartrail[0] == K_ESCAPE && chartrail[1] == 0) { double_escape = 1; scrollData->itemIndex = -1; break; } if (strcmp(chartrail, K_ALT_X) == 0) { break; } if (strcmp(chartrail, KTRAIL0) == 0) { // escape key + A => arrow key up //Move selector up scrollData->scrollDirection = UP_SCROLL; continueScroll = move_selector(&aux, scrollData); //Break the loop if we are scrolling if (scrollData->scrollActive == SCROLL_ACTIVE && continueScroll == 1) { control = CONTINUE_SCROLL; //Update data scrollData->currentListIndex = scrollData->currentListIndex - 1; scrollData->selector = scrollData->wherey; scrollData->item = aux->item; scrollData->itemIndex = aux->index; //Return value ch = control; } } // escape key + B => arrow key down if (strcmp(chartrail, KTRAIL1) == 0) { //Move selector down scrollData->scrollDirection = DOWN_SCROLL; continueScroll = move_selector(&aux, scrollData); //Break the loop if we are scrolling if (scrollData->scrollActive == SCROLL_ACTIVE && continueScroll == 1) { control = CONTINUE_SCROLL; //Update data scrollData->currentListIndex = scrollData->currentListIndex + 1; scrollData->selector = scrollData->wherey; scrollData->item = aux->item; scrollData->itemIndex = aux->index; scrollData->scrollDirection = DOWN_SCROLL; } //Return value ch = control; } } } if (ch == K_ENTER || ch == K_ENTER2) // enter key { //Pass data of last item selected scrollData->item = aux->item; scrollData->itemIndex = aux->index; } return ch; } //method to change how wide the selector is void setselectorLimit(int num){ selectorLimit = num; } void resetScrollData(SCROLLDATA *scrollData) { scrollData->scrollActive = 0; //To know whether scroll is active or not. scrollData->scrollLimit = 0; //Last index for scroll. scrollData->listLength = 0; //Total no. of items in the list scrollData->currentListIndex = 0; //Pointer to new sublist of items when scrolling. scrollData->displayLimit = 0; //No. of elements to be displayed. scrollData->selectorLimit = selectorLimit; //No. of chars per item display scrollData->scrollDirection = 0; //To keep track of scrolling Direction. scrollData->selector = 0; //Y++ scrollData->wherex = 0; scrollData->wherey = 0; scrollData->backColor0 = 0; //0 unselected; 1 selected scrollData->foreColor0 = 0; scrollData->backColor1 = 0; scrollData->foreColor1 = 0; scrollData->itemIndex = 0; scrollData->addSpaces = 0; scrollData->orientation = VERTICAL; } char listBox(LISTCHOICE * head, unsigned whereX, unsigned whereY, SCROLLDATA * scrollData, unsigned bColor0, unsigned fColor0, unsigned bColor1, unsigned fColor1, unsigned displayLimit,unsigned listorientation, unsigned addSpaces, unsigned locked) { unsigned list_length = 0; //unsigned currentIndex = 0; int scrollLimit = 0; unsigned currentListIndex = 0; char ch=0; LISTCHOICE *aux=NULL; //Init values double_escape=0; resetScrollData(scrollData); scrollData->orientation = listorientation; // Query size of the list list_length = query_length(&head) + 1; //Save calculations for SCROLL and store DATA scrollData->displayLimit = displayLimit; scrollLimit = list_length - scrollData->displayLimit; //Careful with negative integers if(scrollLimit < 0 || displayLimit > list_length) scrollData->displayLimit = list_length; //Failsafe for overboard values scrollData->scrollLimit = scrollLimit; scrollData->listLength = list_length; scrollData->wherex = whereX; scrollData->wherey = whereY; scrollData->selector = whereY; scrollData->backColor0 = bColor0; scrollData->backColor1 = bColor1; scrollData->foreColor0 = fColor0; scrollData->foreColor1 = fColor1; scrollData->addSpaces = addSpaces; get_terminal_dimensions(&listrows,&listcolumns); //Check whether we have to activate scroll or not //and if we are within bounds. [1,list_length) ch=0; aux = head; printlist(aux, scrollData, scrollData->displayLimit); if (locked == LOCKED) { if(list_length > scrollData->displayLimit && scrollLimit > 0 && displayLimit > 0) { //Scroll is possible scrollData->scrollActive = SCROLL_ACTIVE; currentListIndex = 0; //We listBox1 the scroll at the top index. scrollData->currentListIndex = currentListIndex; //Scroll loop animation. Finish with ENTER. loadlist(aux, scrollData, currentListIndex); do { currentListIndex = scrollData->currentListIndex; loadlist(aux, scrollData, currentListIndex); gotoIndex(&aux, scrollData, currentListIndex); /* gotoxy(6, 4); printf("Current List Index: %d:%d\n", scrollData->currentListIndex, aux->index); */ ch = selectorMenu(aux, scrollData); if (double_escape == 1) { scrollData->itemIndex = -1; locked = FALSE; ch= K_ESCAPE; break; } if (_animation() == -1) { scrollData->itemIndex = -1; double_escape = 1; ch = K_ESCAPE; locked = FALSE; break; } } while(ch != K_ENTER); } else { //Scroll is not possible. //Display all the elements and create selector // scrollData->scrollActive = SCROLL_INACTIVE; scrollData->currentListIndex = 0; scrollData->displayLimit = list_length; //Default to list_length loadlist(head, scrollData, 0); ch = selectorMenu(head, scrollData); if (double_escape == 1) { scrollData->itemIndex = -1; locked = FALSE; ch= K_ESCAPE; //break; } } } return ch; } ================================================ FILE: src/listbox.h ================================================ /* ======================================================================== - HEADER - Module to implement a listBox integrated with LYNX. @author : Velorek @version : 1.0 Last modified: 26/12/2022 ======================================================================== */ #ifndef _LISTBOX_H_ #define _LISTBOX_H_ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include "scbuf.h" /*====================================================================*/ /* CONSTANTS */ /*====================================================================*/ //Scroll Control values. #define SCROLL_ACTIVE 1 #define SCROLL_INACTIVE 0 #define CONTINUE_SCROLL -1 #define DOWN_SCROLL 1 #define UP_SCROLL 0 #define SELECT_ITEM 1 #define UNSELECT_ITEM 0 #define CIRCULAR_ACTIVE 1 #define CIRCULAR_INACTIVE 0 #define LOCKED 1 #define NOT_LOCKED 0 #define VERTICAL 0 #define HORIZONTAL 1 #define VERTICALWITHBREAK 2 /*====================================================================*/ /* TYPEDEF STRUCTS DEFINITIONS */ /*====================================================================*/ typedef struct _listchoice { unsigned index; // Item number char *item; // Item string int setX; //X,Y position int setY; //-1 to ignore int foreColor; //-1 to ignore int backColor; //-1 to ignore struct _listchoice *next; // Pointer to next item struct _listchoice *back; // Pointer to previous item } LISTCHOICE; typedef struct _scrolldata { unsigned scrollActive; //To know whether scroll is active or not. unsigned scrollLimit; //Last index for scroll. unsigned listLength; //Total no. of items in the list unsigned currentListIndex; //Pointer to new sublist of items when scrolling. unsigned displayLimit; //No. of elements to be displayed. unsigned selectorLimit; //how many items of string item can be displayed unsigned scrollDirection; //To keep track of scrolling Direction. unsigned wherex; unsigned wherey; unsigned selector; //Y++ unsigned backColor0; //0 unselected; 1 selected unsigned foreColor0; unsigned backColor1; unsigned foreColor1; unsigned addSpaces; //whether to add spaces or not unsigned orientation; unsigned displayMetrics; //to show metrics on screen/reuse code for text viewing char *item; int itemIndex; //-1 means ESC was pressed LISTCHOICE *head; //store head of the list } SCROLLDATA; /*====================================================================*/ /* GLOBAL VARIABLES */ /*====================================================================*/ extern LISTCHOICE *listBox1; //Head pointer. /*====================================================================*/ /* PROTOTYPES OF FUNCTIONS */ /*====================================================================*/ //DYNAMIC LINKED LIST FUNCTIONS void removeList(LISTCHOICE ** head); LISTCHOICE *addatend(LISTCHOICE * head, LISTCHOICE * newp); LISTCHOICE *newitem(char *text,unsigned setX, unsigned setY,unsigned foreColor, unsigned backColor); //LISTBOX FUNCTIONS //void addItems(LISTCHOICE ** listBox1); char listBox(LISTCHOICE * selector, unsigned whereX, unsigned whereY, SCROLLDATA * scrollData, unsigned bColor0, unsigned fColor0, unsigned bColor1, unsigned fColor1, unsigned displayLimit,unsigned listorientation, unsigned addSpaces, unsigned locked); void loadlist(LISTCHOICE * head, SCROLLDATA * scrollData, unsigned indexAt); void printlist(LISTCHOICE * head, SCROLLDATA * scrollData, unsigned displayLimit); void gotoIndex(LISTCHOICE ** aux, SCROLLDATA * scrollData, unsigned indexAt); int query_length(LISTCHOICE ** head); int move_selector(LISTCHOICE ** head, SCROLLDATA * scrollData); char selectorMenu(LISTCHOICE * aux, SCROLLDATA * scrollData); void displayItem(LISTCHOICE * aux, SCROLLDATA * scrollData, int select); void resetScrollData(SCROLLDATA *scrollData); void setselectorLimit(int num); /*====================================================================*/ /* FUNCTION PROTOTYPES */ /*====================================================================*/ #endif ================================================ FILE: src/main.c ================================================ /* C-edit project * Last updated - 29/8/2024 * New drop-down menu implementation * Added more modularisation: editor.c menu.c * Scrollbars added */ #include #include #include #include "listbox.h" #include "scbuf.h" #include "rterm.h" #include "tm.h" #include "global.h" #include "keyb.h" #include "edbuf.h" #include "ui.h" #include "menu.h" #include "editor.h" #include "opfile.h" #include "fileb.h" //Prototypes void draw_screen(); void cursor_tick(); int special_keys(); int control_keys(char ch); void _resizeScreen(); wchar_t currentChar = 0; char tempMessage[150]; int displayMessage(char *temporaryMessage, int x, int y, int fColor, int bColor, int timeDuration); void update_indicators(); int oldPositionY = 4; int oldPositionX = 2; void draw_screen(){ //BASE SCREEN IS STORED IN SCREEN 2 int i=0; if (screen1 != NULL) deleteList(&screen1); //if (screen2 != NULL) deleteList(&screen2); //Init 2 : Create 2 Screens for a double buffer approach old_rows=new_rows; old_columns=new_columns; create_screen(&screen1); create_screen(&screen2); //SCREEN 2 screen_color(screen1, EDITAREACOL, EDIT_FORECOLOR, FILL_CHAR); //Failsafe just in case it can't find the terminal dimensions if(old_rows == 0) old_rows = ROWS_FAILSAFE; if(old_columns == 0) old_columns = COLUMNS_FAILSAFE; //Draw upper and lower bars for(i = 0; i < old_columns; i++) { write_ch(screen1, i, 1, FILL_CHAR, MENU_PANEL, MENU_PANEL,0); } for(i = 0; i < old_columns; i++) { write_ch(screen1, i, old_rows, FILL_CHAR, STATUSBAR, STATUSMSG,1); } // Text messages write_str(screen1, 0, 1, "File Options Help", MENU_PANEL, MENU_FOREGROUND0,0); write_str(screen1, 0, 1, "F", MENU_PANEL, F_RED,0); write_str(screen1, 7, 1, "p", MENU_PANEL, F_RED,0); write_str(screen1, 15, 1, "H", MENU_PANEL, F_RED,0); write_str(screen1, 0, old_rows, STATUS_BAR_MSG1, STATUSBAR, STATUSMSG,0); /* Frames */ //window appearance and scroll bar for(i = 2; i < old_rows; i++) { write_ch(screen1,old_columns-1, i, ' ', SCROLLBAR_BACK, SCROLLBAR_FORE,0); //Scroll bar write_ch(screen1,0, i, VER_LINE, EDITWINDOW_BACK, EDITWINDOW_FORE,0); //upper vertical line box-like char } for(i = 0; i < old_columns; i++) { write_ch(screen1,i, 2, HOR_LINE, EDITWINDOW_BACK, EDITWINDOW_FORE,0); //horizontal line box-like char write_ch(screen1,i, old_rows - 1, ' ', EDITWINDOW_BACK, EDITWINDOW_FORE,0); } write_ch(screen1,0, 2, UPPER_LEFT_CORNER, EDITWINDOW_BACK, EDITWINDOW_FORE,0); //upper-left box-like char //horizontal scroll bar for(i = 0; i < old_columns; i++) { write_ch(screen1,i, old_rows - 1, FILL_CHAR, SCROLLBAR_BACK, SCROLLBAR_FORE,0); } //Window-appearance write_ch(screen1,old_columns-1, 2, UPPER_RIGHT_CORNER, EDITWINDOW_BACK, EDITWINDOW_FORE,0); //right window corner write_ch(screen1,old_columns-1, old_rows - 1, LOWER_RIGHT_CORNER, EDITWINDOW_BACK, EDITWINDOW_FORE,0); write_ch(screen1, 0, old_rows - 1, LOWER_LEFT_CORNER, EDITWINDOW_BACK, EDITWINDOW_FORE,0); //Scroll symbols write_ch(screen1,old_columns-1, 3, '^', SCROLLBAR_ARR, SCROLLBAR_FORE,0); write_ch(screen1, old_columns-1, old_rows - 2, 'v', SCROLLBAR_ARR, SCROLLBAR_FORE,0); write_ch(screen1, old_columns-1, 4, '*', SCROLLBAR_SEL, SCROLLBAR_FORE,0); write_ch(screen1, 2, old_rows - 1, '*', SCROLLBAR_SEL, SCROLLBAR_FORE,0); write_ch(screen1, 1, old_rows - 1, '<', SCROLLBAR_ARR, SCROLLBAR_FORE,0); write_ch(screen1, old_columns - 2, old_rows - 1, '>', SCROLLBAR_ARR, SCROLLBAR_FORE,0); if (strlen(fileName) == 0) strcpy(fileName, "UNTITLED"); write_str(screen1,(new_columns / 2) - (strlen(fileName) / 2), 2, fileName, MENU_PANEL, MENU_FOREGROUND0,0); dump_screen(screen1); //Save screen for later //copy_screen(screen2,screen1); } void cursor_tick(void){ //CURSOR ANIMATION char drawChar=FILL_CHAR; char drawChar0=FILL_CHAR; int attrib = 0; wchar_t code_point; //GET CHAR BEING POINTED AT FROM EDIT BUFFER if (edBuf1 != NULL)_dumpLine(edBuf1, posBufY, &tempLine); drawChar = tempLine.linea[posBufX].ch; drawChar0 = tempLine.linea[posBufX].specialChar; attrib = tempLine.linea[posBufX].attrib; //CHECK FOR SPECIAL CHARACTERS AND CONVERT THEM TO UNICODE TO PRINT //IF CURSOR IS ON A CHAR HIGHLIGHT IT IN YELLOW if (drawChar0 != 0 && drawChar != '\0') code_point = ((drawChar0 & 0x1F) << 6) | (drawChar & 0x3F); else { if (drawChar == 0 || drawChar == END_LINE_CHAR) drawChar = FILL_CHAR; code_point = drawChar; } if (cursor_timer1.ticks % 2 == 0) update_ch(cursorX, cursorY, CURSOR_CHAR, B_BLUE, F_WHITE); else update_ch(cursorX, cursorY, code_point, EDITAREACOL, FH_YELLOW); //REST ANSI VALUES TO HAVE HIGH INTENSITY COLORS resetAnsi(0); //CLEAR LAST POSITION OF CURSOR WITH FILL_CHAR OR GO BACK TO PREVIOUS CHAR if ((old_cursorX != cursorX) || (old_cursorY != cursorY)){ //old? if (edBuf1 != NULL)_dumpLine(edBuf1, oldposBufY, &tempLine); drawChar = tempLine.linea[oldposBufX].ch; drawChar0 = tempLine.linea[oldposBufX].specialChar; attrib = tempLine.linea[oldposBufX].attrib; //always clear cursor cell (avoids false misprints) update_ch(old_cursorX, old_cursorY, ' ', EDITAREACOL, EDIT_FORECOLOR); //then try to update to its previous state if (drawChar0 != 0 && drawChar != '\0') code_point = ((drawChar0 & 0x1F) << 6) | (drawChar & 0x3F); else { if (drawChar == 0 || drawChar == END_LINE_CHAR) drawChar = FILL_CHAR; code_point = drawChar; } update_ch(old_cursorX, old_cursorY, code_point, EDITAREACOL, attrib); } if (old_cursorY != cursorY) { //Point to line in buffer when Y position changes if (edBuf1 != NULL) _dumpLine(edBuf1, posBufY, &tempLine); } //printf("\n"); //write_num(screen1, 10,10, cursorX, B_GREEN, F_WHITE,1); //write_num(screen1, 10,11, cursorY, B_RED, F_WHITE,1); //write_num(screen1, 10,12, old_cursorX, B_GREEN, F_WHITE,1); //write_num(screen1, 10,12, old_cursorY, B_GREEN, F_WHITE,1); //write_num(screen1, 10,14, posBufX, B_RED, F_WHITE,1); //write_num(screen1, 10,15, posBufY, B_RED, F_WHITE,1); //write_num(screen1, 10,16, findEndline(tempLine), BH_BLUE, F_WHITE,1); //write_num(screen1, 10,17, _length(&edBuf1), B_RED, F_WHITE,1); //for (int i=0; i vdisplayArea ) { write_ch(screen1,new_columns-1, oldPositionY, ' ', SCROLLBAR_BACK, SCROLLBAR_FORE,1); positionY = posBufY; scrollRatio = _length(&edBuf1) / vdisplayArea; scrollIndicator = positionY / scrollRatio; percentage = (scrollIndicator * 100) / vdisplayArea; scrollBar = ((vdisplayArea-3) * percentage)/100; if (positionY == 1) percentage = 0; if (percentage > 100) percentage = 100; if (scrollBar+2>=new_rows-5) scrollBar = new_rows - 7; write_ch(screen1,new_columns-1, 4+scrollBar, '*', SCROLLBAR_SEL, SCROLLBAR_FORE,1); oldPositionY = 4+scrollBar; pep = percentage; } if (posBufX>0){ positionX = posBufX; write_ch(screen1, oldPositionX,new_rows-1, ' ', SCROLLBAR_BACK, SCROLLBAR_FORE,1); oldPositionX = 2+hscrollBar; hscrollRatio = MAX_LINE_SIZE / hdisplayArea; hscrollIndicator = positionX / hscrollRatio; hpercentage = (hscrollIndicator * 100) / hdisplayArea; hscrollBar = (((hdisplayArea) * hpercentage)/100); if (hscrollBar+2>=new_columns-3) hscrollBar = new_columns-5; write_ch(screen1, 2+hscrollBar,new_rows-1, '*', SCROLLBAR_SEL, SCROLLBAR_FORE,1); oldPositionX = 2+hscrollBar; } strcpy(info, "\0"); sprintf(info, "|LINES: %d| L: %ld C: %ld [%d%c] ", _length(&edBuf1), posBufY, posBufX,pep,'%'); update_str(new_columns - 40, new_rows, info, STATUSBAR, STATUSMSG); } int main(int argc, char *argv[]) { char ch=0; char old_ch=0; int esc_key = 0; int keypressed = 0; //Init Terminal init_term(); initCEDIT(); draw_screen(); resetch(); strcpy(fileName, "\0"); //tempLine.linea[0].ch = END_LINE_CHAR; //tempLine.linea[posBufX].ch = END_LINE_CHAR; //tempLine.linea[posBufX].specialChar = 0; //tempLine.linea[posBufX].attrib = EDIT_FORECOLOR; if(argc > 1) { //Does the file exist? Open or create? strcpy(fileName, argv[1]); filetoBuffer(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); fileModified= FILE_UNMODIFIED; } do{ //end flag from any part of the program if (programStatus == ENDSIGNAL) break; //Time animation & resize window if (timerC(&timer3) == TRUE){ displayMessage(tempMessage, (new_columns/2)-strlen(tempMessage)/2, 1, FH_WHITE, B_RED, 5); } if (timerC(&timer2) == TRUE){ _animation(); //Refresh screen size buffers if terminal dimension changes if (new_rows != old_rows || new_columns != old_columns) { _resizeScreen(); } } //Cursor Animation if (timerC(&cursor_timer1) == TRUE){ //Animation cursor_tick(); //if (keypressed == FALSE) { esc_key = 0;} } //PROCESS INPUT keypressed = kbhit(1); if (keypressed == TRUE) { keypressed = FALSE; //try to catch and avoid printing unwanted chars with cursor keys old_ch = ch; ch=readch(); if (old_ch==ESC_KEY) {unwantedChars = 1; esc_key = 1;} if (ch==ESC_KEY) {unwantedChars = 1; esc_key = 1;} if (unwantedChars>0) esc_key = 1; //Keys with a escape sequenece if (ch == ESC_KEY) {//buffertoScreen(0, 0,FALSE); esc_key = special_keys(); cursor_tick(); ch = 0;} else { //Capture control keys if ((ch>0 && ch< 0x0F) && (ch!=K_ENTER && ch != K_TAB)){buffertoScreen(FALSE); esc_key= control_keys(ch); ch=0;} else //Process raw edit input from keyboard in editor.c { //try to avoid printing unwanted chars with cursor keys if (ch != 0 && esc_key==0 && unwantedChars == 0&& posBufX 0) { strcpy(fileName, tempfileName); filetoBuffer(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); }//buffertoScreen(0, 0, 0); } if (ch == K_CTRL_N){ if (openFileDialog(fileName,fullPath) == 1){ filetoBuffer(fileName); flush_editarea(0); buffertoScreen(0); } dump_screen(screen1); } return returnValue; } //ESCAPE-RELATED KEYS int special_keys() { /* MANAGE SPECIAL KEYS */ /* New implementation: Trail of chars found in keyboard.c If K_ESCAPE is captured read a trail up to 5 characters from the console. This is to control the fact that some keys may change according to the terminal and expand the editor's possibilities. Eg: F2 can be either 27 79 81 or 27 91 91 82. */ //cursorY : position on screen //posBufY : position in buffer int esc_key = 0; char chartrail[5]; char returnMenuChar=0; int menuCounter = 0; int countCh = 0; int ok = -1; char tempfileName[MAXFILENAME]; old_cursorX = cursorX; old_cursorY = cursorY; oldposBufX = posBufX; oldposBufY = posBufY; strcpy(chartrail, "\0"); read_keytrail(chartrail); //Read trail after ESC key //only ESC key detected, finish program if (chartrail[0] == ESC_KEY && chartrail[1]==0) { buffertoScreen(0); if (fileModified == FILE_MODIFIED) { ok=yesnoWindow(WMODIFIED_MSG, "Alert Window"); switch (ok){ case 0: //save file and exit strcpy(chartrail, K_ALT_S); break; case 1: //don't save and exit return ENDSIGNAL; break; case 2: //cancel keep running c-edit buffertoScreen(0); break; } } else //regular escape with no modifications return ENDSIGNAL; } //Check key trails for special keys starting with ESC. //FUNCTION KEYS : F1 - F4 if(strcmp(chartrail, K_F2_TRAIL) == 0 || strcmp(chartrail, K_F2_TRAIL2) == 0) { handlemenus(&returnMenuChar, &menuCounter,TRUE); } else if(strcmp(chartrail, K_F3_TRAIL) == 0 || strcmp(chartrail, K_F3_TRAIL2) == 0) { } else if(strcmp(chartrail, K_F1_TRAIL) == 0 || strcmp(chartrail, K_F1_TRAIL2) == 0) { flush_editarea(0); buffertoScreen(0); displayHelp(); } else if(strcmp(chartrail, K_F4_TRAIL) == 0 || strcmp(chartrail, K_F4_TRAIL2) == 0) { // ARROW KEYS } else if(strcmp(chartrail, K_LEFT_TRAIL) == 0) { //Left-arrow key unwantedChars++; if(cursorX > 1){ cursorX = cursorX - 1; //editScroll.bufferX--; } if (posBufX>0) posBufX--; //adjust shiftH when going back to account for the fact that cursorX=1 is not enough if (cursorX == new_columns - 4) shiftH++; if (cursorX == 1 && shiftH>0) {shiftH--;buffertoScreen(1);} } else if(strcmp(chartrail, K_RIGHT_TRAIL) == 0) { //Right-arrow key unwantedChars++; if(cursorX < new_columns - 3){ cursorX = cursorX + 1; //shiftH = 0; } else{ if (posBufX 2) { cursorY = cursorY - 1; }else{ //scrolling up if (currentLine>0) { currentLine--; buffertoScreen(1); } } if (posBufY > 0) posBufY--; } else if(strcmp(chartrail, K_DOWN_TRAIL) == 0) { //Down-arrow key //Check if there are more lines to go to unwantedChars++; if (posBufY<_length(&edBuf1)-1){ if(cursorY < new_rows - 3) { //stay put if we are are the end of the viewing area cursorY = cursorY + 1; } else{ //scrolling down if (_length(&edBuf1) > vdisplayArea ) { currentLine++; buffertoScreen(1); } } posBufY++; } } else if(strcmp(chartrail, K_PAGEDOWN_TRAIL) == 0) { //Page Down key vdisplayLimit = _length(&edBuf1) - vdisplayArea; if (_length(&edBuf1) > vdisplayArea ) { if (currentLine + vdisplayArea <= vdisplayLimit ) currentLine = currentLine + vdisplayArea; else currentLine = vdisplayLimit; // stop at the limit of our scroll posBufY = currentLine; cursorY = START_CURSOR_Y; buffertoScreen(1); } } else if(strcmp(chartrail, K_PAGEUP_TRAIL) == 0) { //Page Down key if (_length(&edBuf1) > vdisplayArea ) { if (currentLine >= vdisplayArea) currentLine = currentLine - vdisplayArea; else currentLine = 0; // stop at the top posBufY = currentLine; cursorY = START_CURSOR_Y; buffertoScreen(1); } }else if(strcmp(chartrail, K_HOME_TRAIL) == 0 || strcmp(chartrail, K_HOME_TRAIL2) == 0 ) { if (_length(&edBuf1) > vdisplayArea ) { currentLine=0; cursorY = START_CURSOR_Y; posBufY=0; buffertoScreen(1); } }else if(strcmp(chartrail, K_END_TRAIL) == 0 || strcmp(chartrail, K_END_TRAIL2) == 0 ) { if (_length(&edBuf1) > vdisplayArea ) { currentLine=_length(&edBuf1)-vdisplayArea; cursorY = new_rows-3; posBufY=_length(&edBuf1)-1; buffertoScreen(1); } } else if(strcmp(chartrail, K_DELETE) == 0) { deleteKeyPressed = 1; editor_section(0); //delete button; } else if(strcmp(chartrail, K_ALT_F) == 0) { returnMenuChar=FILE_MENU; menuCounter=FILE_MENU; handlemenus(&returnMenuChar, &menuCounter,FALSE); } else if(strcmp(chartrail, K_ALT_P) == 0) { returnMenuChar=OPT_MENU; menuCounter=OPT_MENU; handlemenus(&returnMenuChar, &menuCounter,FALSE); } else if(strcmp(chartrail, K_ALT_H) == 0) { returnMenuChar=HELP_MENU; menuCounter=HELP_MENU; handlemenus(&returnMenuChar, &menuCounter,FALSE); } else if(strcmp(chartrail, K_ALT_O) == 0) { //openFileHandler(); //Open file Dialog buffertoScreen(0); dump_screen(screen1); if (openFileDialog(fileName,fullPath) == 1){ //is it a binary file? filetoBuffer(fileName); flush_editarea(0); buffertoScreen(0); } dump_screen(screen1); } else if(strcmp(chartrail, K_ALT_N) == 0) { //newDialog(currentFile); // New file //refresh_screen(-1); resetch(); } else if(strcmp(chartrail, K_ALT_A) == 0) { //saveasDialog(currentFile); //Save as.. file //refresh_screen(-1); } else if(strcmp(chartrail, K_ALT_S) == 0) { //Save file flush_editarea(0); buffertoScreen(0); strcpy(tempMessage, "[File saved!]\0"); if (strcmp(fileName, "UNTITLED") == 0) { countCh=inputWindow("File:", tempfileName, "Save file as...",28,2,48); if (countCh>0) { strcpy(fileName, tempfileName); buffertoFile(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); timer3.ticks=0; } } else{ buffertoFile(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); timer3.ticks=0; } //a bit convoluted but it works / exit if user selcts yes after saving if (ok==0){printf("File saved!. Exiting...\n"); return ENDSIGNAL; } } else if(strcmp(chartrail, K_ALT_W) == 0) { //if(strcmp(currentFile, UNKNOWN) == 0) //saveasDialog(currentFile); //Write to file //else { //saveDialog(currentFile); //} //refresh_screen(-1); } else if(strcmp(chartrail, K_ALT_X) == 0) { /* if(fileModified == 1) exitp = confirmation(); //Shall we exit? Global variable! else exitp = EXIT_FLAG; } } */ } esc_key = 1; return esc_key; } /* void linetoScreen(long whereY, VLINES tempLine, BOOL clean){ //dump temporary Line to screen buffer int i,j=0; for (i=0; i16) if (i<=strlen(auth)) printf("%c", auth[i-1]); printf("\n"); i++; } } while (timer2.ticks <30); //log // _printlist(&edBuf1); // printf("%ld:%ld\n", posBufX, posBufY); // printf("\n%ld\n",sizeof(&edBuf1)); if (screen1 != NULL) deleteList(&screen1); if (screen2 != NULL) deleteList(&screen2); if (listBox1 != NULL) removeList(&listBox1); _deletetheList(&edBuf1); //free edit Buffer resetAnsi(0); showcursor(); resetAnsi(0); } void _resizeScreen(){ //redraw everything when screen size changes get_terminal_dimensions(&new_rows, &new_columns); if (screen1 != NULL) deleteList(&screen1); if (screen2 != NULL) deleteList(&screen2); create_screen(&screen1); create_screen(&screen1); draw_screen(); flush_editarea(0); hdisplayArea = new_columns - 2; vdisplayArea = new_rows - 4; //if screen resizes file pointers are rewound to the beginning currentLine = 0; posBufX = 0; posBufY = 0; shiftH = 0; buffertoScreen(0); dump_screen(screen1); cursorX = START_CURSOR_X; cursorY = START_CURSOR_Y; } int displayMessage(char *temporaryMessage, int x, int y, int fColor, int bColor, int timeDuration){ //display a temporary nessage controlled by Timer3 if (timer3.ticks == -1) { write_str(screen1, x,y, temporaryMessage, B_WHITE, F_WHITE,1); return 0; } if (timer3.ticks >= timeDuration) { stop_timer(&timer3); write_str(screen1, x,y, temporaryMessage, B_WHITE, F_WHITE,1); return 0; } write_str(screen1, x,y, temporaryMessage, bColor, fColor,1); return 1; } ================================================ FILE: src/menu.c ================================================ /* Module to display Menus on C·edit * for a Text User Interface * TextBox * Window * Last modified: 6/4/2024 * @author:velorek */ #include #include "rterm.h" #include "scbuf.h" #include "ui.h" #include "menu.h" #include "tm.h" #include "keyb.h" #include "global.h" #include "editor.h" #include "opfile.h" #include "fileb.h" /*--------------------------------------------*/ /* Load current menu into circular linked list*/ /*--------------------------------------------*/ void loadmenus(int choice) { if(choice == HOR_MENU) { if (listBox1 != NULL) removeList(&listBox1); listBox1 = addatend(listBox1, newitem("File", 0, 1,-1,-1)); listBox1 = addatend(listBox1, newitem("Options", 7, 1,-1,-1)); listBox1 = addatend(listBox1, newitem("Help", 16, 1,-1,-1)); } if(choice == FILE_MENU) { if (listBox1 != NULL) removeList(&listBox1); listBox1 = addatend(listBox1, newitem("New", -1, -1,-1,-1)); listBox1 = addatend(listBox1, newitem("Open", -1, -1,-1,-1)); listBox1 = addatend(listBox1, newitem("Quick load", -1, -1,-1,-1)); listBox1 = addatend(listBox1, newitem("Save", -1, -1,-1,-1)); listBox1 = addatend(listBox1, newitem("Save as...", -1, -1,-1,-1)); listBox1 = addatend(listBox1, newitem("Exit", -1, -1,-1,-1)); } if(choice == OPT_MENU) { if (listBox1 != NULL) removeList(&listBox1); listBox1 = addatend(listBox1, newitem("File Info", -1, -1,-1,-1)); listBox1 = addatend(listBox1, newitem("Find...", -1, -1,-1,-1)); listBox1 = addatend(listBox1, newitem("Colors", -1, -1,-1,-1)); } if(choice == HELP_MENU) { if (listBox1 != NULL) removeList(&listBox1); listBox1 = addatend(listBox1, newitem("Help...", -1, -1,-1,-1)); listBox1 = addatend(listBox1, newitem("About", -1, -1,-1,-1)); } /* if(choice == YESNO_MENU) { add_item(mylist, "[YES]", (columns / 2) - 11, (rows / 2) + 2, MENU2_PANEL, MENU2_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1); add_item(mylist, "[NO]", (columns / 2) - 3, (rows / 2) + 2, MENU2_PANEL, MENU2_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1); add_item(mylist, "[CANCEL]", (columns / 2) + 4, (rows / 2) + 2, MENU2_PANEL, MENU2_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1); } if(choice == OK_MENU) { add_item(mylist, "[OK]", (columns / 2) - 1, (rows / 2) + 2, MENU2_PANEL, MENU2_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1); } if(choice == OK_MENU2) { add_item(mylist, "[OK]", (columns / 2) - 1, (rows / 2) + 3, MENU2_PANEL, MENU2_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1); } if(choice == COLORS_MENU) { add_item(mylist, "C-Edit Theme", (columns / 2) - 6, (rows / 2) - 2, MENU_PANEL, MENU_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1); add_item(mylist, "Classic Theme", (columns / 2) - 6, (rows / 2) -1, MENU_PANEL, MENU_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1); add_item(mylist, "Dark Theme", (columns / 2) - 6, (rows / 2), MENU_PANEL, MENU_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1); } */ } /*--------------- Menu loop It depends on two values passed by reference stored in the function from where this routine is called: -returnMenuChar -menuCounter It does some tricky modulo operation to cycle back */ void handlemenus(char *returnMenuChar, int *menuCounter, BOOL horizontalMenu) { setselectorLimit(15); copy_screen(screen2,screen1); if (horizontalMenu) *returnMenuChar= horizontal_menu(); do{ switch (*returnMenuChar) { case 0: *menuCounter=FILE_MENU; *returnMenuChar=filemenu(); if (*returnMenuChar != DONT_UPDATE) xor_update(screen2,screen1); break; case 1: *menuCounter=OPT_MENU; *returnMenuChar=optionsmenu(); xor_update(screen2,screen1); break; case 2: *menuCounter=HELP_MENU; *returnMenuChar=helpmenu(); xor_update(screen2,screen1); break; default: break; } //convert -2 from right cursor to 1 to advance if (*returnMenuChar == -2 ) *returnMenuChar = 1; *menuCounter=*menuCounter + *returnMenuChar; if (*returnMenuChar == K_ENTER) break; if (*returnMenuChar == DONT_UPDATE) break; if (*returnMenuChar != ESC_KEY ) { copy_screen(screen1,screen2); //euclidian modulo, we circle back through the different switch cases *returnMenuChar = ((*menuCounter % 3) + 3) % 3; } } while (*returnMenuChar != ESC_KEY); //xor_update(screen2,screen1); if (*returnMenuChar != DONT_UPDATE){ copy_screen(screen1,screen2); dump_screen(screen1); } } //Entry menu- horizontal char horizontal_menu() { char ch=0; dump_screen(screen1); //Save current screen to screen2 loadmenus(HOR_MENU); //load menus from ui.c onto lisBox in (global.c) //dump_screen(screen1); ch = listBox(listBox1, 0, 1 , &scrollData, MENU_PANEL, MENU_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1, 3, HORIZONTAL,0,1); //delete listbox if(ch == ESC_KEY) scrollData.itemIndex = ch;; //to avoid warning removeList(&listBox1); //return option passed in scrollData (global.c) return scrollData.itemIndex; } /*-------------------------*/ /* Display File menu */ /*-------------------------*/ char filemenu() { char ch=0; int countCh = 0; char tempfileName[MAXFILENAME]; int ok2 = -1; int retvalue = 0; write_str(screen1,0, new_rows, STATUS_BAR_MSG2, STATUSBAR, STATUSMSG,1); write_str(screen1,0, 1, "File", MENU_SELECTOR, MENU_FOREGROUND1,1); loadmenus(FILE_MENU); draw_window(screen1,0, 2, 13, 9, MENU_PANEL, MENU_FOREGROUND0,0, 1,0,1,1); ch = listBox(listBox1, 3, 3 , &scrollData, MENU_PANEL, MENU_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1, -1, VERTICALWITHBREAK,0,1); // copy_screen(screen1,screen2); // dump_screen(screen1); //return if right and left arrow keys are pressed if (ch == K_RIGHTMENU || ch == K_LEFTMENU) return ch; if(scrollData.itemIndex == OPTION_1) { flush_editarea(0); buffertoScreen(0); countCh=inputWindow("File:", tempfileName, "[+] New file",26,2,44); if (countCh>0) { strcpy(fileName, tempfileName); filetoBuffer(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); }//buffertoScreen(0, 0, 0); ch=0; return DONT_UPDATE; } if(scrollData.itemIndex == OPTION_2) { //openFile Dialog in opfile.c flush_editarea(0); buffertoScreen(0); if (openFileDialog(fileName,fullPath) == 1){ filetoBuffer(fileName); flush_editarea(0); buffertoScreen(0); } dump_screen(screen1); return DONT_UPDATE; } if(scrollData.itemIndex == OPTION_3) { //External Module - Open file dialog. //openFileHandler(); flush_editarea(0); buffertoScreen(0); countCh=inputWindow("File:", tempfileName, "Quick load...",26,2,44); if (countCh>0) { //check if it's a binary file if (openandcheckFile(tempfileName) == 1){ ok2 = yesnoWindow(WCHECKFILE_MSG, "Alert window"); switch (ok2){ case 0: // open binary file anyway retvalue=1; break; case 1: //don't open binary file strcpy (fileName,"\0"); strcpy (fullPath,"\0"); retvalue=0; break; case 2: //cancel is the same as no here strcpy (fileName,"\0"); strcpy (fullPath,"\0"); retvalue=0; break; } }else{ retvalue = 1; } if (retvalue==1){ strcpy(fileName, tempfileName); filetoBuffer(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); } }//buffertoScreen(0, 0, 0); return DONT_UPDATE; } if(scrollData.itemIndex == OPTION_4) { //Save file flush_editarea(0); buffertoScreen(0); if (strcmp(fileName, "UNTITLED") == 0) { countCh=inputWindow("File:", tempfileName, "Save file as...",26,2,44); if (countCh>0) { strcpy(fileName, tempfileName); buffertoFile(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); } } else{ buffertoFile(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); } return DONT_UPDATE; } if(scrollData.itemIndex == OPTION_5) { //Save as option flush_editarea(0); buffertoScreen(0); countCh=inputWindow("File:", fileName, "Save file as...",26,2,44); if (countCh>0) { buffertoFile(fileName); flush_editarea(0); buffertoScreen(0); dump_screen(screen1); } return DONT_UPDATE; } if(scrollData.itemIndex == OPTION_6) { //Exit option //if(fileModified == 1) //exitp = confirmation(); //Shall we exit? Global variable! //else programStatus = ENDSIGNAL; } //restore previous screen //dump_screen(screen1); return ch; } /*--------------------------*/ /* Display Options menu */ /*--------------------------*/ char optionsmenu() { //int setColor; char ch=0; write_str(screen1,6, 1, "Options", MENU_SELECTOR, MENU_FOREGROUND1,1); write_str(screen1, 0, new_rows, STATUS_BAR_MSG2, STATUSBAR, STATUSMSG,1); loadmenus(OPT_MENU); draw_window(screen1,6, 2, 19, 6, MENU_PANEL, MENU_FOREGROUND0,0, 1,0,1,1); ch = listBox(listBox1, 9, 3 , &scrollData, MENU_PANEL, MENU_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1, -1, VERTICALWITHBREAK,0,1); if(scrollData.itemIndex == OPTION_1) { //File Info //fileInfoDialog(); } if(scrollData.itemIndex == OPTION_3) { //Set Colors /* setColor = colorsWindow(mylist,COLORSWTITLE); setColorScheme(setColor); checkConfigFile(setColor); //save new configuration in config file *///refresh_screen(1); } return ch; } /*--------------------------*/ /* Display Help menu */ /*--------------------------*/ char helpmenu() { char ch= 0; write_str(screen1,15, 1, "Help", MENU_SELECTOR, MENU_FOREGROUND1,1); write_str(screen1, 0, new_rows, STATUS_BAR_MSG2, STATUSBAR, STATUSMSG,1); loadmenus(HELP_MENU); draw_window(screen1,15, 2, 25, 5, MENU_PANEL, MENU_FOREGROUND0, 0,1,0,1,1); ch = listBox(listBox1, 18, 3 , &scrollData, MENU_PANEL, MENU_FOREGROUND0, MENU_SELECTOR, MENU_FOREGROUND1, -1, VERTICALWITHBREAK,0,1); if(scrollData.itemIndex == OPTION_1 && ch>0) { //Help dialog //help_info(); flush_editarea(0); buffertoScreen(0); displayHelp(); } if(scrollData.itemIndex == OPTION_2 && ch>0) { //About info flush_editarea(0); buffertoScreen(0); displayAbout(); } return ch; } //ABOUT int displayAbout(void) { char ch = 0; int i = 0; int colAnimation = F_BLACK; int keypressed = 0; int colCounter = 0; copy_screen(screen2, screen1); window(screen1, (new_columns / 2) - 18, (new_rows / 2) - 6, (new_columns / 2) + 17, (new_rows) / 2 + 3, B_WHITE, F_BLACK, B_BLACK, 1, 1, 1); for (i = 0; i < 6; i++) { write_str(screen1, (new_columns / 2) - 15, (new_rows / 2) - 5 + i, aboutMSG[i], B_WHITE, F_BLACK, 0); } write_str(screen1, (new_columns / 2) - 11, (new_rows / 2) - 7, "[+] ABOUT INFORMATION", B_BLACK, FH_WHITE, 1); // write_str(screen1, (new_columns / 2) - 25, (new_rows / 2) + 2, //aboutMSG[3], B_YELLOW, F_BLACK, 0); write_str(screen1, (new_columns / 2) - 1, (new_rows / 2) + 2, "[OK]", B_RED, FH_WHITE, 1); dump_screen(screen1); if (kbhit(100) == 1) ch = readch(); ch = 0; i = 0; colCounter = 0; //pulsating animation do { keypressed = kbhit(30); if (timerC(&timer2) == TRUE) { //About animation write_str(screen1, (new_columns / 2) - 15, (new_rows / 2) - 5 + i, aboutMSG[i], B_WHITE, colAnimation, 1); i++; if (i == 6) { i = 0; switch (colCounter){ case 1: colAnimation = FH_BLACK; break; case 2: colAnimation = FH_BLACK; break; case 3: colAnimation = FH_BLACK; break; case 4: colAnimation = FH_BLACK; break; case 5: colAnimation = FH_BLACK; break; case 6: colAnimation = FH_WHITE; break; case 7: colAnimation = FH_WHITE; break; case 8: colAnimation = FH_WHITE; break; case 9: colAnimation = F_BLACK; break; case 10: colAnimation = F_BLACK; break; } colCounter++; if (colCounter ==11) colCounter = 0; } //if terminal resizes if (_animation() == -1) break; } //Process keys if (keypressed == 1) { ch = readch(); keypressed = 0; //Read special keys if (ch == K_ESCAPE) { ch = readch(); if (ch == ESC_KEY) break; } } } while (ch != K_ENTER); resetch(); copy_screen(screen1, screen2); if (listBox1 != NULL) removeList(&listBox1); dump_screen(screen1); return ch; } void addItemsHelp(LISTCHOICE **listBox1, char textarray[][MAXLINE], int rows) { int h=0; //Load items into the list. //if (*listBox1 != NULL) removeList(listBox1); for (h = 0; h < rows-1; h++) { //*ch = textarray[h]; *listBox1 = addatend(*listBox1, newitem(textarray[h],-1,-1,-1,-1)); } *listBox1 = addatend(*listBox1, newitem(textarray[rows-1],-1,-1,B_WHITE,FH_BLUE)); } int displayHelp(void) { char ch = 0; resetScrollData(&scrollData); if (listBox1 != NULL) removeList(&listBox1); setselectorLimit(26*2-1); //scrollData.selectorLimit = (26 * 2) - 1; //No. of chars per item display //scrollData.selectorLimit = 100; //No. of chars per item display //create_screen(&screen2); copy_screen(screen2, screen1); window(screen1, (new_columns / 2) - 26, (new_rows / 2) - 8, (new_columns / 2) + 26, (new_rows) / 2 + 8, B_WHITE, F_BLACK, B_BLACK, 1, 1, 1); write_str(screen1, (new_columns / 2) - 12, (new_rows / 2) - 9, "[+] MAIN HELP INFORMATION", B_BLACK, FH_WHITE, 1); write_str(screen1, (new_columns / 2) - 1, (new_rows / 2) + 7, "[OK]", B_RED, F_WHITE, 1); dump_screen(screen1); addItemsHelp(&listBox1, help, HELPLINES); if (listBox1 != NULL) ch = listBox(listBox1, (new_columns / 2) - 24, (new_rows / 2) - 7, &scrollData, B_WHITE, F_BLACK, B_WHITE, FH_WHITE, 14, VERTICAL,1,LOCKED); copy_screen(screen1, screen2); // if (screen2 != NULL) // deleteList(&screen2); if (listBox1 != NULL) removeList(&listBox1); dump_screen(screen1); return ch; } ================================================ FILE: src/menu.h ================================================ /* ======================================================================== - HEADER - Module with all menus on C·EDIT @author : Velorek @version : 1.0 Last modified: 06/04/2024 ======================================================================== */ #ifndef _MENU_H_ #define _MENU_H_ #include "scbuf.h" #include void loadmenus(int choice); char horizontal_menu(); char filemenu(); char optionsmenu(); char helpmenu(); void credits(); void handlemenus(char *returnMenuChar, int *menuCounter, BOOL horizontalMenu); int displayAbout(); int displayHelp(void); #endif ================================================ FILE: src/opfile.c ================================================ /*====================================================================*/ /* OPEN FILE MODULE +ListFiles with double linked list and selection menu in C. +Scroll function added. +Integrated with C-EDIT A window is drawn to the buffer and all of the scroll animations are drawn to the terminal on raw mode to have a better scrolling animation. Once the file is selected, the window is closed and the previous screen is painted to the terminal again. Last modified : 11/1/2019 - Switch to readch() instead of getch() 06/04/2019 - Corrected all memory leaks 14/04/2019 - Rename headers 22/04/2020 - Added Set File to Open 23/04/2023 - Merged with Lynx and vastly simplified Coded by Velorek. Target OS: Linux. */ /*====================================================================*/ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include #include #include #include "rterm.h" #include "scbuf.h" #include "listbox.h" #include "tm.h" #include "keyb.h" #include "global.h" #include "opfile.h" #include "ui.h" #include "fileb.h" /*====================================================================*/ /* GLOBAL VARIABLES */ /*====================================================================*/ int window_x1 = 0, window_y1 = 0, window_x2 = 0, window_y2 = 0; int ndirs=0, nfiles=0; /*====================================================================*/ /* CODE */ /*====================================================================*/ int listFiles(LISTCHOICE ** listBox1, char *directory) { DIR *d; struct dirent *dir; int i; char temp[MAX_ITEM_LENGTH]; int lenDir; //length of directory ndirs=0; nfiles=0; if (*listBox1 != NULL) removeList(listBox1); *listBox1 = addatend(*listBox1, newitem("[..]",-1,-1,FH_BLUE,B_WHITE)); // ".." //Start at current directory d = opendir(directory); //Find directories and add them to list first if(d) { while((dir = readdir(d)) != NULL) { if(dir->d_type == DT_DIR) { lenDir = strlen(dir->d_name); //Check length of directory //Directories are displayed between brackets [directory] if(lenDir > MAX_ITEM_LENGTH - 2) { //Directory name is long. CROP memset(&temp, '\0',sizeof(temp)); //Clear memory for temporary line strcpy(temp,"\0"); strcpy(temp, "["); for(i = 1; i < MAX_ITEM_LENGTH - 1; i++) { temp[i] = dir->d_name[i - 1]; } temp[MAX_ITEM_LENGTH - 1] = ']'; } else { //Directory's name is shorter than display //Add spaces to item string. memset(&temp, '\0',sizeof(temp)); //Clear memory for temporary line strcpy(temp,"\0"); strcpy(temp, "["); for(i = 1; i < lenDir + 1; i++) { temp[i] = dir->d_name[i - 1]; } temp[lenDir + 1] = ']'; } //Add all directories except CURRENTDIR and CHANGEDIR if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0) { ndirs++; *listBox1 = addatend(*listBox1, newitem(temp, -1,-1,FH_BLUE,B_WHITE));} } } } closedir(d); //Find files and add them to list after directories d = opendir(directory); if(d) { while((dir = readdir(d)) != NULL) { if(dir->d_type == DT_REG) { //only list valid fiels if(strlen(dir->d_name) > MAX_ITEM_LENGTH) { for(i = 0; i < MAX_ITEM_LENGTH; i++) { temp[i] = dir->d_name[i]; } } else { memset(&temp, '\0',sizeof(temp)); //Clear memory for temporary line strcpy(temp,"\0"); strcpy(temp, dir->d_name); } nfiles++; *listBox1 = addatend(*listBox1, newitem(temp, -1,-1,-1,-1)); } } closedir(d); } return 0; } void addItems(LISTCHOICE **listBox1) { int h=0; //Load items into the list. //if (listBox1 != NULL) removeList(listBox1); for (h = 0; h <10; h++) { //*ch = textarray[h]; *listBox1 = addatend(*listBox1, newitem("Test",-1,-1,-1,-1)); } } int openFileDialog(char fileName[MAXFILENAME], char fullPath[MAXFILENAME]){ char ch=0; char path[MAXFILENAME]; char bit[MAXFILENAME]; char ndirstr[100]; size_t i=0; int ok2 =0; int retvalue=0; char nfilestr[100]; char currentPath[4] = "./\0"; retvalue =0; setselectorLimit(23); window_y1 = (new_rows / 2) - 10; window_y2 = (new_rows / 2) + 10; window_x1 = (new_columns / 2) - 13; window_x2 = (new_columns / 2) + 13; copy_screen(screen2,screen1); old_rows = new_rows; old_columns = new_columns; do{ draw_window(screen1,window_x1, window_y1, window_x2, window_y2,B_WHITE,F_BLACK,B_BLACK,1,1,1,0); draw_window(screen1,window_x1, window_y2-3, window_x2, window_y2,B_CYAN,F_WHITE,B_BLACK,1,0,0,0); dump_screen(screen1); listFiles(&listBox1,currentPath); sprintf(ndirstr, "[%d] Directories", ndirs); sprintf(nfilestr, "[%d] Files", nfiles); write_str(screen1,window_x1+2,window_y2-2,ndirstr,B_CYAN,FH_WHITE,1); write_str(screen1,window_x1+2,window_y2-1,nfilestr,B_CYAN,F_WHITE,1); write_str(screen1,window_x1+1,window_y2-4, " PRESS [ESC] TO EXIT ",B_BLACK,FH_WHITE,1); write_str(screen1,window_x1+2,window_y1,"Select file...",B_BLACK,FH_WHITE,1); ch = listBox(listBox1, window_x1+3,window_y1+1, &scrollData, B_WHITE, F_BLACK,B_RED, FH_WHITE, 15, VERTICAL,1,LOCKED); if (_animation() == -1) {ch=K_ESCAPE; break;} if (ch == 0x27) {break;} if (ch == K_ENTER){ if (scrollData.item[0] == '[') { //directory memset(&path, '\0',sizeof(path)); //Clear memory for temporary line memset(&bit, '\0',sizeof(bit)); //Clear memory for temporary line strcpy(bit,"\0"); strcpy(path,"\0"); for (i=1; i #include #include #include #include "rterm.h" #include "scbuf.h" #include "listbox.h" #include "tm.h" #include "keyb.h" #include "global.h" /*====================================================================*/ /* CONSTANTS */ /*====================================================================*/ #define MAX_ITEM_LENGTH 15 /*====================================================================*/ /* CONSTANT VALUES */ /*====================================================================*/ /*====================================================================*/ /* FUNCTION PROTOTYPES */ /*====================================================================*/ int listFiles(LISTCHOICE ** listBox1, char *directory); void addItems(LISTCHOICE **listBox1); int openFileDialog(char fileName[MAXFILENAME], char fullPath[MAXFILENAME]); #endif ================================================ FILE: src/rterm.c ================================================ /* ====================================================================== Module to control some display routines that implement ANSI functions. on LINUX Terminals @author : Velorek (routines extracted from the internet) @version : 1.0 LAST MODIFIED : 18/01/2023 Kbhit with poll control added ====================================================================== */ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include #include #include #include #include #include #include #include "rterm.h" #include "keyb.h" /*====================================================================*/ /* GLOBAL VARIABLES */ /*====================================================================*/ struct winsize max; static struct termios term, failsafe; static int peek_character = -1; /*====================================================================*/ /* FUNCTIONS - CODE */ /*====================================================================*/ /*-------------------------------------*/ /* Initialize new terminal i/o settings*/ /*-------------------------------------*/ void pushTerm() { //Save terminal settings in failsafe to be retrived at the end tcgetattr(0, &failsafe); } /*---------------------------*/ /* Reset terminal to failsafe*/ /*---------------------------*/ int resetTerm() { //tcsetattr(0, TCSANOW, &failsafe); /* flush and reset */ if (tcsetattr(0,TCSAFLUSH,&failsafe) < 0) return -1; return 0; } /*--------------------------------------.*/ /* Detect whether a key has been pressed.*/ /*---------------------------------------*/ int kbhit(int timeout_ms) { struct pollfd fds = {STDIN_FILENO, POLLIN, 0}; fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); int ret = poll(&fds, 1, timeout_ms); fcntl(STDIN_FILENO, F_SETFL, 0); if (ret > 0) { return 1; } else if (ret == 0) { return 0; // timeout occurred } else { return -1; // error occurred } } /*----------------------*/ /*Read char with control*/ /*----------------------*/ char readch() { char ch; if(peek_character != -1) { ch = peek_character; peek_character = -1; return ch; } read(0, &ch, 1); return ch; } void resetch() { //Clear ch term.c_cc[VMIN] = 0; tcsetattr(0, TCSANOW, &term); peek_character = 0; } /*----------------------------------*/ /* Move cursor to specified position*/ /*----------------------------------*/ void gotoxy(int x, int y) { printf("%c[%d;%df", 0x1B, y, x); } /*---------------------*/ /* Change colour output*/ /*---------------------*/ void outputcolor(int foreground, int background) { printf("%c[%d;%dm", 0x1b, foreground, background); } /*-----------------------------------------------------*/ /* Change the whole color of the screen by applying CLS*/ /*-----------------------------------------------------*/ void screencol(int x) { gotoxy(0, 0); outputcolor(0, x); printf("%c[2J", 0x1b); outputcolor(0, 0); } /*-----------------------*/ /* Reset ANSI ATTRIBUTES */ /*-----------------------*/ void resetAnsi(int x) { switch (x) { case 0: //reset all colors and attributes printf("%c[0m", 0x1b); break; case 1: //reset only attributes printf("%c[20m", 0x1b); break; case 2: //reset foreg. colors and not attrib. printf("%c[39m", 0x1b); break; case 3: //reset back. colors and not attrib. printf("%c[49m", 0x1b); break; default: break; } } /*------------------------*/ /* Get terminal dimensions*/ /*------------------------*/ int get_terminal_dimensions(int *rows, int *columns) { ioctl(0, TIOCGWINSZ, &max); *columns = max.ws_col; *rows = max.ws_row; return 0; } /*--------------------------*/ /* Ansi function hide cursor*/ /*--------------------------*/ void hidecursor() { printf("\e[?25l"); } /*--------------------------*/ /* Ansi function show cursor*/ /*--------------------------*/ void showcursor() { printf("\e[?25h"); } /*----------------------------------*/ /* Get cursor position */ /*----------------------------------*/ int get_pos(int *y, int *x) { char buf[30]={0}; int ret, i, pow; char ch; *y = 0; *x = 0; struct termios term, restore; tcgetattr(0, &term); tcgetattr(0, &restore); term.c_lflag &= ~(ICANON|ECHO); tcsetattr(0, TCSANOW, &term); write(1, "\033[6n", 4); for( i = 0, ch = 0; ch != 'R'; i++ ) { ret = read(0, &ch, 1); if ( !ret ) { tcsetattr(0, TCSANOW, &restore); //fprintf(stderr, "getpos: error reading response!\n"); return 1; } buf[i] = ch; //printf("buf[%d]: \t%c \t%d\n", i, ch, ch); } if (i < 2) { tcsetattr(0, TCSANOW, &restore); //printf("i < 2\n"); return(1); } for( i -= 2, pow = 1; buf[i] != ';'; i--, pow *= 10) *x = *x + ( buf[i] - '0' ) * pow; for( i-- , pow = 1; buf[i] != '['; i--, pow *= 10) *y = *y + ( buf[i] - '0' ) * pow; tcsetattr(0, TCSANOW, &restore); return 0; } /*--------------------------*/ /* Set up terminal */ /*--------------------------*/ //For code simplification purposes void init_term(void) { //switch to alternate screen buffer printf("\033[?1049h"); hidecursor(); pushTerm(); resetch(); //Setup unicode setlocale(LC_ALL, ""); } void close_term(void) { showcursor(); resetTerm(); resetAnsi(0); //restore previous screen printf("\033[?1049l"); //cls gotoxy(0,0); printf("%c[2J\r", 0x1b); } /* void init_term(){ hidecursor(); pushTerm(); resetch(); //Setup unicode setlocale(LC_ALL, ""); } void close_term(){ showcursor(); resetTerm(); outputcolor(F_WHITE, B_BLACK); screencol(B_BLACK); resetAnsi(0); printf("\n"); } */ ================================================ FILE: src/rterm.h ================================================ /* ======================================================================== - HEADER - Module to control some display routines that implement ANSI functions. @author : Velorek @version : 1.0 Last modified: 15/09/2021 + Linux Term ======================================================================== */ #ifndef _RTERM_H_ #define _RTERM_H_ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include #include #include #include /*====================================================================*/ /* COLOR CONSTANTS */ /*====================================================================*/ // Background colors low intensity #define B_BLACK 40 #define B_RED 41 #define B_GREEN 42 #define B_YELLOW 43 #define B_BLUE 44 #define B_MAGENTA 45 #define B_CYAN 46 #define B_WHITE 47 // Foreground colors low intensity #define F_BLACK 30 #define F_RED 31 #define F_GREEN 32 #define F_YELLOW 33 #define F_BLUE 34 #define F_MAGENTA 35 #define F_CYAN 36 #define F_WHITE 37 //#define F_WHITE 37 #define F_GREY 90 // Background colors high intensity #define BH_BLACK 100 #define BH_RED 101 #define BH_GREEN 102 #define BH_YELLOW 103 #define BH_BLUE 104 #define BH_MAGENTA 105 #define BH_CYAN 106 #define BH_WHITE 107 // Foreground colors high intensity #define FH_BLACK 90 #define FH_RED 91 #define FH_GREEN 92 #define FH_YELLOW 93 #define FH_BLUE 94 #define FH_MAGENTA 95 #define FH_CYAN 96 #define FH_WHITE 97 #define TRUE 1 #define FALSE 0 //Keys used. #define K_ENTER 13 #define K_ENTER2 10 #define K_ESCAPE 27 #define K_UP_TRAIL "\e[A\0\0" #define K_DOWN_TRAIL "\e[B\0\0" #define K_RIGHT_TRAIL "\e[C\0\0" #define K_LEFT_TRAIL "\e[D\0\0" //#define K_UP_ARROW 'A' // K_ESCAPE + 'A' -> UP_ARROW //#define K_DOWN_ARROW 'B' // K_ESCAPE + 'B' -> DOWN_ARROW typedef int BOOL; /*====================================================================*/ /* FUNCTION PROTOTYPES */ /*====================================================================*/ void pushTerm(); int resetTerm(); int kbhit(int timeout_ms); char readch(); char getch(); void resetch(); //char getch(void); void gotoxy(int x, int y); void outputcolor(int foreground, int background); void screencol(int x); void resetAnsi(int x); int get_terminal_dimensions(int *rows, int *columns); int get_pos(int *y, int *x); void showcursor(); void hidecursor(); void init_term(); void close_term(); #endif ================================================ FILE: src/scbuf.c ================================================ /* Module to create a screen buffer * to act as an intermediate step between screen output and * the display so as to create a Text User Interface. * @author:velorek * Last modified: 1/02/2019 */ #include #include #include #include "rterm.h" #include "scbuf.h" #define ROWS_FAILSAFE 25 #define COLUMNS_FAILSAFE 80 #define FILL_CHAR 32 int sc_rows=0, sc_columns=0; int buffersize=0; /* SINGLE LINKED LIST ROUTINES */ // create new list element of type SCREENCELL from the supplied text string SCREENCELL *newelement(SCREENCELL temp) { SCREENCELL *newp; newp = (SCREENCELL *) malloc (sizeof(SCREENCELL)); newp->index = temp.index; newp->backColor = temp.backColor; newp->foreColor = temp.foreColor; newp->toUpdate = 0; newp->ch = temp.ch; newp->next = NULL; return newp; } void deleteList(SCREENCELL **head) { /* deref head_ref to get the real head */ SCREENCELL *current = *head; SCREENCELL *next = NULL; while (current != NULL) { next = current->next; free(current); current = next; } free(current); free(next); current = NULL; next = NULL; *head = NULL; } /* addfront: add new SCREENCELL to front of list */ /* example usage: start = (addfront(start, newelement("burgers")); */ SCREENCELL *addfront(SCREENCELL *head, SCREENCELL *newp) { newp -> next = head; return newp; } /* addend: add new SCREENCELL to the end of a list */ /* usage example: start = (addend(start, newelement("wine")); */ SCREENCELL *addend (SCREENCELL *head, SCREENCELL *newp) { SCREENCELL *p2; if (head == NULL) return newp; // now find the end of list for (p2 = head; p2 -> next != NULL; p2 = p2 -> next) ; p2 -> next = newp; return head; } int length(SCREENCELL **head) // this routine uses pointer-to-pointer techniques :-) { int count=0; SCREENCELL **tracer = head; while ((*tracer) != NULL) { count = count +1; tracer = &(*tracer)->next; } return count; } void reindex(SCREENCELL **head) { int count=0; SCREENCELL *p=NULL; SCREENCELL **tracer = head; while ((*tracer) != NULL) { p = *tracer; p->index=count; count = count +1; tracer = &(*tracer)->next; } } /* SCREEN BUFFER ROUTINES */ int create_screen(SCREENCELL **newScreen){ /* Get terminal dimensions and create a dynamic list with the size of the screen. if it is not possible to determine screen dimensions default is set to 80x25. Insertion happens at the end of the list. */ int i=0; SCREENCELL temp; SCREENCELL **aux = newScreen; //We attempt to get terminal dimensions. If not //successful failsafe values (80x25) apply. get_terminal_dimensions(&sc_rows, &sc_columns); if(sc_rows == 0) sc_rows = ROWS_FAILSAFE; if(sc_columns == 0) sc_columns = COLUMNS_FAILSAFE; buffersize = sc_rows * sc_columns; // Assign dummy data to Screen buffer if(*aux == NULL) { //All cells have the same init data temp.ch = FILL_CHAR; temp.backColor = B_BLACK; temp.foreColor = F_WHITE; for(i = 0; i < buffersize; i++) { //Except for their index temp.index = buffersize - 1 - i; *aux = addfront(*aux, newelement(temp)); } return 1; } else{ //Pointer is not void return 0; } } void copy_screen(SCREENCELL *destination,SCREENCELL *source){ int i=0; SCREENCELL *aux1=destination; SCREENCELL *aux2=source; //We attempt to get terminal dimensions. If not //successful failsafe values (80x25) apply. if((aux1 != NULL && aux2 != NULL) && (length(&aux1) == length(&aux2)) ) { for(i = 0; i < buffersize; i++) { aux1->backColor = aux2->backColor; aux1->foreColor = aux2->foreColor; aux1->ch = aux2->ch; aux1 = aux1->next; aux2 = aux2->next; } } } /*------------------------------------------*/ /* Update single char to screen for display */ /*------------------------------------------*/ void update_ch(int x, int y, wchar_t ch, char backcolor, char forecolor) { //Write Unicode char to screen raw resetAnsi(0); gotoxy(x+1, y+1); outputcolor(forecolor, backcolor); printf("%lc", ch); //unicode } void write_ch(SCREENCELL *newScreen, int x, int y, wchar_t ch, char backcolor, char forecolor,BOOL raw) { /* Update cell on screenbuffer. It will be shown on screen when it is updated by calling update_screen */ int i, pos; SCREENCELL *aux = newScreen; resetAnsi(0); if (aux != NULL && buffersize <= length(&aux)){ pos = (y - 1) * sc_columns + x; //This is the formula to calculate the position index in the screen buffer if(pos < buffersize) { // If it is within buffer limits, otherwise do nothing. for(i = 0; i <= pos; i++) { //Run through the buffer until reaching desired position if (aux->index == pos) break; aux = aux->next; } //Update cell info at position selected. //Manage special charaters as well. aux->ch = ch; aux->backColor = backcolor; aux->foreColor = forecolor; aux->toUpdate = 1; if(raw==TRUE) { gotoxy(x+1, y); outputcolor(forecolor, backcolor); if (ch>31) printf("%lc", ch); //unicode } } } } SCREENCELL read_cell(SCREENCELL *newScreen, int x, int y) { /* read specific character with attributes from buffer */ int i=0, pos=0; SCREENCELL *aux=newScreen; SCREENCELL ret ={}; if (aux != NULL && buffersize <= length(&aux)){ pos = (y - 1) * sc_columns + x; //this is the formula to calculate the position index in the screen buffer if(pos < buffersize) { for(i = 0; i <= pos; i++) { //run through the buffer until reaching desired position if(aux->index == pos) break; aux = aux->next; } ret.index = aux->index; ret.ch = aux->ch; //for some unknown reason these values have to be inverted ret.backColor = aux->foreColor; ret.foreColor = aux->backColor; ret.next = NULL; } } return ret; } wchar_t read_char(SCREENCELL *newScreen, int x, int y) { /* read specific character from buffer */ int i=0, pos=0; wchar_t ch = FILL_CHAR; SCREENCELL *aux=newScreen; if (aux != NULL && buffersize <= length(&aux)){ pos = (y - 1) * sc_columns + x; //this is the formula to calculate the position index in the screen buffer if(pos < buffersize) { for(i = 0; i <= pos; i++) { //run through the buffer until reaching desired position if(aux->index == pos) break; aux = aux->next; } ch = aux->ch; } } return ch; } /*------------------------------------------*/ /* Writes a string of characters to buffer. */ /*------------------------------------------*/ void write_str(SCREENCELL *newScreen, int x, int y, char *str, char backcolor, char forecolor, BOOL raw) { //Writes a string of characters to buffer. char *astr=NULL; size_t i=0; int wherex=0; SCREENCELL *aux = newScreen; if (aux!=NULL){ wherex = x; astr = str; for(i = 0; i <= strlen(str) - 1; i++) { write_ch(aux, wherex, y, astr[i], backcolor, forecolor,raw); wherex = wherex + 1; } } } /*------------------------------------------*/ /* Writes a string of characters to screen. */ /*------------------------------------------*/ void update_str(int x, int y, char *str, char backcolor, char forecolor) { //Writes a string of characters to buffer. char *astr=NULL; size_t i=0; int wherex=0; resetAnsi(0); wherex = x; astr = str; for(i = 0; i <= strlen(str) - 1; i++) { update_ch(wherex, y, astr[i], backcolor, forecolor); wherex = wherex + 1; } } /*-----------------------------------------------*/ /* Writes an integer value as a string on screen */ /*-----------------------------------------------*/ int write_num(SCREENCELL *newScreen, int x, int y, int num, char backcolor, char forecolor, BOOL raw) { //the length of the string must be passed on the function char astr[30]; char len=0; SCREENCELL *aux = newScreen; if (aux!=NULL){ sprintf(astr, "%d", num); write_str(newScreen, x, y, astr, backcolor, forecolor,raw); len = strlen(astr); } return len; } void screen_color(SCREENCELL *newScreen, char bcolor, char fcolor, wchar_t ch) { /* Changes the color of all the cells to create the effect of changing color in the background */ int i=0; SCREENCELL *aux=newScreen; resetAnsi(0); //screen should be updated twice to get round the last row glitch if (aux != NULL && buffersize <= length(&aux)){ for(i = 0; i < buffersize; i++) { aux->backColor = bcolor; aux->foreColor = fcolor; aux->ch = ch; aux->toUpdate = 1; aux = aux->next; } } } /*------------------------------------*/ /* Dumps buffer to screen for display */ /*------------------------------------*/ void dump_screen(SCREENCELL *newScreen) { /* UPDATES ALL SCREEN CELLS TO DISPLAY */ int i=0; int wherex=0, wherey=0; SCREENCELL *aux=newScreen; if (aux!=NULL && buffersize <= length(&aux)){ for(i = 0; i < buffersize; i++) { update_ch(wherex,wherey,aux->ch,aux->backColor,aux->foreColor); wherex = wherex + 1; //line counter if(wherex == sc_columns) { //new line wherex = 0; wherey = wherey + 1; } aux = aux->next; } } } void update_screen(SCREENCELL *newScreen) { /* UPDATES ALL SCREEN CELLS TO DISPLAY */ int i=0; int wherex=0, wherey=0; SCREENCELL *aux=newScreen; if (aux!=NULL && buffersize <= length(&aux)){ for(i = 0; i < buffersize; i++) { if (aux->toUpdate ==1) {aux->toUpdate = 0; update_ch(wherex,wherey,aux->ch,aux->backColor,aux->foreColor);} wherex = wherex + 1; //line counter if(wherex == sc_columns) { //new line wherex = 0; wherey = wherey + 1; } aux = aux->next; } } } void xor_update(SCREENCELL *screen1, SCREENCELL *screen2) { /* UPDATE only the cells that are different */ int i=0; int wherex=0, wherey=0; SCREENCELL *aux1=screen1; SCREENCELL *aux2=screen2; if ((aux1!=NULL && aux2!=NULL) && (length(&aux1) == length(&aux2))){ for(i = 0; i < buffersize; i++) { if (aux1->ch != aux2->ch || aux1-> backColor != aux2 -> backColor || aux1-> foreColor != aux2 -> foreColor){ update_ch(wherex,wherey,aux1->ch,aux1->backColor,aux1->foreColor); } wherex = wherex + 1; //line counter if(wherex == sc_columns) { //new line wherex = 0; wherey = wherey + 1; } aux1 = aux1->next; aux2 = aux2->next; } } } void xor_copy(SCREENCELL *screen1, SCREENCELL *screen2) { /* UPDATES ALL SCREEN CELLS TO DISPLAY */ int i = 0; int wherex = 0, wherey = 0; SCREENCELL *aux1 = screen1; SCREENCELL *aux2 = screen2; if ((aux1 != NULL && aux2 != NULL) && (length(&aux1) == length(&aux2))) { for (i = 0; i < buffersize; i++) { if (aux1->ch != aux2->ch || aux1->backColor != aux2->backColor || aux1->foreColor != aux2->foreColor) { aux1->ch = aux2->ch; aux1->backColor = aux2->backColor; aux1->foreColor = aux2->foreColor; wherex = wherex + 1; //line counter if (wherex == sc_columns) { //new line wherex = 0; wherey = wherey + 1; } aux1 = aux1->next; aux2 = aux2->next; } } } } /*------------------------------------------*/ /* Draw window area with or without border. */ /*------------------------------------------*/ void draw_window(SCREENCELL *newScreen, int x1, int y1, int x2, int y2, int backcolor, int bordercolor, int titlecolor, BOOL border, BOOL title, BOOL shadow, BOOL raw) { /* Draw a box on screen */ SCREENCELL *aux = newScreen; int i, j; wchar_t ch=FILL_CHAR; i = x1; j = y1; //shadow resetAnsi(0); if (shadow == TRUE){ for(j = y1 + 1; j <= y2 + 1; j++) for(i = x1 + 1; i <= x2 + 1; i++) { ch=read_char(aux, i,j); //dynamic shadow if ((ch=='\0') || (ch == UNICODEBAR1) || (ch<0)) ch=FILL_CHAR; //ch=FILL_CHAR; //if (ch<0) ch = (tempLine.linea[i].specialChar << 8) | tempLine.linea[i].ch; write_ch(aux, i, j, ch, B_BLACK, F_WHITE,raw); } } //window for(j = y1; j <= y2; j++) for(i = x1; i <= x2; i++) write_ch(aux, i, j, FILL_CHAR, backcolor, bordercolor,raw); //borders if(border == TRUE) { for(i = x1; i <= x2; i++) { //upper and lower borders write_ch(aux, i, y1, HOR_LINE, backcolor, bordercolor,raw); //horizontal line box-like char write_ch(aux, i, y2, HOR_LINE, backcolor, bordercolor,raw); } for(j = y1; j <= y2; j++) { //left and right borders write_ch(aux, x1, j, VER_LINE, backcolor, bordercolor,raw); //vertical line box-like char write_ch(aux, x2, j, VER_LINE, backcolor, bordercolor,raw); } write_ch(aux, x1, y1, UPPER_LEFT_CORNER, backcolor, bordercolor,raw); //upper-left corner box-like char write_ch(aux, x1, y2, LOWER_LEFT_CORNER, backcolor, bordercolor,raw); //lower-left corner box-like char write_ch(aux, x2, y1, UPPER_RIGHT_CORNER, backcolor, bordercolor,raw); //upper-right corner box-like char write_ch(aux, x2, y2, LOWER_RIGHT_CORNER, backcolor, bordercolor,raw); //lower-right corner box-like char } if (title == TRUE) { for(i = x1+1; i <= x2-1; i++) write_ch(aux, i, y1, ' ', titlecolor, titlecolor,raw); } } ================================================ FILE: src/scbuf.h ================================================ #ifndef _SCBUF_H_ #define _SCBUF_H_ #include #include #include #include #include #include #include "rterm.h" #define HOR_LINE 9472 #define VER_LINE 9474 #define UPPER_LEFT_CORNER 9484 #define LOWER_LEFT_CORNER 9492 #define UPPER_RIGHT_CORNER 9488 #define LOWER_RIGHT_CORNER 9496 #define UNICODEBAR1 0x2592 typedef struct _screencell { int index; char backColor; char foreColor; unsigned char toUpdate; //int char attrib; wchar_t ch; struct _screencell *next; } SCREENCELL; /* Adapted from Kernighan and Pike's "The Practice of Programming" pp.46 et seq. (Addison-Wesley 1999) - computerPhile */ // create new list element of type SCREENCELL from the supplied text string SCREENCELL *newelement(SCREENCELL temp); SCREENCELL *addfront(SCREENCELL *head, SCREENCELL *newp); SCREENCELL *addend (SCREENCELL *head, SCREENCELL *newp); void deleteList(SCREENCELL **head); int length(SCREENCELL **head); void reindex(SCREENCELL **head); //SCREEN BUFFER ROUTINES int create_screen(SCREENCELL **newScreen); void update_ch(int x, int y, wchar_t ch, char backcolor, char forecolor); void update_screen(SCREENCELL *newScreen); void dump_screen(SCREENCELL *newScreen); void write_ch(SCREENCELL *newScreen, int x, int y, wchar_t ch, char backcolor, char forecolor, BOOL raw); wchar_t read_char(SCREENCELL *newScreen, int x, int y); SCREENCELL read_cell(SCREENCELL *newScreen, int x, int y); void write_str(SCREENCELL *newScreen, int x, int y, char *str, char backcolor, char forecolor, BOOL raw); void update_str(int x, int y, char *str, char backcolor, char forecolor); int write_num(SCREENCELL *newScreen, int x, int y, int num, char backcolor, char forecolor, BOOL raw); void screen_color(SCREENCELL *newScreen, char bcolor, char fcolor, wchar_t ch); void copy_screen(SCREENCELL *destination,SCREENCELL *source); void xor_update(SCREENCELL *screen1, SCREENCELL *screen2); void xor_copy(SCREENCELL *screen1, SCREENCELL *screen2); void draw_window(SCREENCELL *newScreen, int x1, int y1, int x2, int y2, int backcolor, int bordercolor, int titlecolor, BOOL border, BOOL title, BOOL shadow, BOOL raw); #endif ================================================ FILE: src/t.txt ================================================ Hello there ================================================ FILE: src/tm.c ================================================ /* Module to create a milisecond timer * @author: velorek * Last modified: 10/08/2020 */ #include #include #include #include "tm.h" #include #include long long epoch_ms(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000LL + (tv.tv_usec + 500) / 1000; } int timerC(NTIMER *mytimer1){ long long difference = 0; long long res; if (mytimer1->ticks == -1) return 0; if (mytimer1->ticks == 0){ // First tick, set up values mytimer1->oldtime = epoch_ms(); mytimer1->ticks = 1; return 1; } else { // Subsequent ticks long long now = epoch_ms(); difference = now - mytimer1->oldtime; res = difference; if (res < mytimer1->ms) { return 0; } else { mytimer1->oldtime = now; mytimer1->ticks = mytimer1->ticks + 1; return 1; } } } void init_timer(NTIMER *mytimer1, int ms){ // Init routine mytimer1->ticks = 0; mytimer1->ms = ms; } void stop_timer(NTIMER *mytimer1){ // Stop routine mytimer1->ticks = -1; } void resume_timer(NTIMER *mytimer1){ // Resume routine mytimer1->ticks = 0; } ================================================ FILE: src/tm.h ================================================ /* ======================================================================== - HEADER - Module to implement a millisecond TIMER in C. @author : Velorek @version : 1.0 Last modified: 15/09/2021 + New technique + init timer ======================================================================== */ #ifndef _TM_H_ #define _TM_H_ /*====================================================================*/ /* COMPILER DIRECTIVES AND INCLUDES */ /*====================================================================*/ #include #include #include /*====================================================================*/ /* FUNCTION PROTOTYPES */ /*====================================================================*/ typedef struct nTimer{ int ms; //milliseconds long oldtime; //to calculate increment in time long ticks; //how many ticks have been made } NTIMER; /* Miliseconds timer */ int timerC(NTIMER *mytimer1); void init_timer(NTIMER *mytimer, int ms); void stop_timer(NTIMER *mytimer); void resume_timer(NTIMER *mytimer); #endif ================================================ FILE: src/ui.c ================================================ /* Module to create different widgets/gadgets * for a Text User Interface * TextBox * Window * Last modified: 15/04/2023 * @author:velorek */ #include #include "rterm.h" #include "scbuf.h" #include "ui.h" #include "tm.h" #include "keyb.h" #include "global.h" /*----------------------------*/ /* User Interface - Text Box. */ /*----------------------------*/ int textbox(SCREENCELL *newScreen,int wherex, int wherey, int displayLength, char *label, char text[MAX_TEXT], int backcolor, int labelcolor, int textcolor, BOOL raw, BOOL locked) { int charCount = 0; int exitFlag = 0; int cursorON = 1; int i; int limitCursor = 0; int positionx = 0; int posCursor = 0; int keypressed = 0; char chartrail[5]; // char accentchar[2]; char displayChar; char ch; NTIMER cursorTime; SCREENCELL *aux = newScreen; resetAnsi(0); init_timer(&cursorTime,150); positionx = wherex + strlen(label); limitCursor = wherex+strlen(label)+displayLength+1; write_str(aux,wherex, wherey, label, backcolor, labelcolor,raw); write_ch(aux,positionx, wherey, '[', backcolor, textcolor,raw); for(i = positionx + 1; i <= positionx + displayLength; i++) { write_ch(aux,i, wherey, '.', backcolor, textcolor,raw); } write_ch(aux,positionx + displayLength + 1, wherey, ']', backcolor, textcolor,raw); if (raw != TRUE) dump_screen(aux); //Reset keyboard if(kbhit(1) == 1) ch = readch(); ch = 0; do { if (locked == 0) break; keypressed = kbhit(1); if (timerC(&timer2) == TRUE) _animation(); //Cursor Animation if (keypressed == 0){ if (timerC(&cursorTime) == TRUE){ switch (cursorON) { case 1: posCursor = positionx + 1; displayChar = '.'; if (posCursor == limitCursor) { posCursor = posCursor - 1; displayChar = ch; } write_ch(aux,posCursor, wherey, displayChar, backcolor, textcolor,raw); //update_screen(aux); if (raw != TRUE) dump_screen(aux); cursorON = 0; break; case 0: posCursor = positionx + 1; if (posCursor == limitCursor) posCursor = posCursor - 1; write_ch(aux,posCursor, wherey, '|', backcolor, textcolor,raw); //update_screen(aux); if (raw != TRUE) dump_screen(aux); cursorON = 1; break; } } } //Process keys if(keypressed == 1) { ch = readch(); keypressed = 0; //Read special keys if (ch==K_ESCAPE) { read_keytrail(chartrail); } //Read accents //if (ch==SPECIAL_CHARSET_1) read_accentchar(&ch, accentchar); //if (ch==SPECIAL_CHARSET_2) read_accentchar(&ch, accentchar); if(charCount < displayLength) { if(ch > 31 && ch < 127) { write_ch(aux,positionx + 1, wherey, ch, backcolor, textcolor,raw); text[charCount] = ch; positionx++; charCount++; //update_screen(aux); if (raw != TRUE) dump_screen(aux); } } } if (ch==K_CTRL_C){ return ENDSIGNAL; } if (ch==K_BACKSPACE){ if (positionx>0 && charCount>0){ ch=0; positionx--; charCount--; text[charCount] = '\0'; write_ch(aux,positionx + 1, wherey, '.', backcolor, textcolor,raw); if (positionx < limitCursor-2) write_ch(aux,positionx + 2, wherey, '.', backcolor, textcolor,raw); //update_screen(aux); if (raw != TRUE) dump_screen(aux); //resetch(); } } if(ch == K_ENTER || ch == K_TAB || ch == K_ESCAPE) exitFlag = 1; //ENTER OR TAB FINISH LOOP } while(exitFlag != 1); text[charCount] = '\0'; //Clear field positionx = wherex + strlen(label); for(i = positionx + 1; i <= positionx + displayLength; i++) { write_ch(aux,i, wherey, '.', backcolor, textcolor,raw); } write_ch(aux,positionx + displayLength + 1, wherey, ']', backcolor, textcolor,raw); //resetch(); if (ch == K_ESCAPE) charCount = 0; return charCount; } void window(SCREENCELL *screen1, int x1, int y1, int x2, int y2, int backcolor, int bordercolor, int titlecolor, int border, int title, int shadow) { /* Chars for drawing box-like characters will be passed as negative values. When the update_screen routine is called, it will check for negative values and map these chars to Unicode characters. */ int i, j; //char ch=0x20; wchar_t ch=0x20; i = x1; j = y1; resetAnsi(0); //shadow if (shadow==1){ for(j = y1 + 1; j <= y2 + 1; j++) for(i = x1 + 1; i <= x2 + 1; i++) { ch=read_char(screen1, i,j); //dynamic shadow if ((ch=='\0') || (ch==UNICODEBAR1)) ch=0x20; write_ch(screen1, i, j, ch, B_BLACK, F_WHITE,0); } } //window for(j = y1; j <= y2; j++) for(i = x1; i <= x2; i++) write_ch(screen1, i, j, ' ', backcolor, bordercolor,0); //borders if(border == 1) { //with borders. ANSI-ASCII 106-121 for(i = x1; i <= x2; i++) { //upper and lower borders write_ch(screen1, i, y1, HOR_LINE, backcolor, bordercolor,0); //horizontal line box-like char write_ch(screen1, i, y2, HOR_LINE, backcolor, bordercolor,0); } for(j = y1; j <= y2; j++) { //left and right borders write_ch(screen1,x1, j, VER_LINE, backcolor, bordercolor,0); //vertical line box-like char write_ch(screen1,x2, j, VER_LINE, backcolor, bordercolor,0); } write_ch(screen1, x1, y1, UPPER_LEFT_CORNER, backcolor, bordercolor,0); //upper-left corner box-like char write_ch(screen1, x1, y2, LOWER_LEFT_CORNER, backcolor, bordercolor,0); //lower-left corner box-like char write_ch(screen1, x2, y1, UPPER_RIGHT_CORNER, backcolor, bordercolor,0); //upper-right corner box-like char write_ch(screen1, x2, y2, LOWER_RIGHT_CORNER, backcolor, bordercolor,0); //lower-right corner box-like char } if (title == 1) { for(i = x1; i <= x2; i++) write_ch(screen1, i, y1-1, ' ', titlecolor, titlecolor,0); } // dump_screen(screen1); } int inputWindow(char *label, char *tempFile, char *windowTitle, int offsetX, int offsetY, int length) { int window_x1 = 0, window_y1 = 0, window_x2 = 0, window_y2 = 0; int count = 0; resetAnsi(0); copy_screen(screen2,screen1); window_y1 = (new_rows / 2) - offsetY; window_y2 = (new_rows / 2); window_x1 = (new_columns / 2) - offsetX; window_x2 = (new_columns / 2) + offsetX; window(screen1,window_x1, window_y1, window_x2, window_y2, MENU_PANEL, MENU_FOREGROUND0, WINDOW_TITLEB,1,1,1); write_str(screen1, (window_x2-window_x1) /2 + window_x1 - (strlen(windowTitle)/2) , window_y1-1, windowTitle, WINDOW_TITLEB, WINDOW_TITLEF,0); dump_screen(screen1); count = textbox(screen1, window_x1 + 1, window_y1 + 1, length, label, tempFile, MENU_PANEL, MENU_FOREGROUND0, MENU_FOREGROUND0,1,1); copy_screen(screen1,screen2); dump_screen(screen1); return count; } int yesnoWindow(char *message, char *windowTitle) { int window_x1 = 0, window_y1 = 0, window_x2 = 0, window_y2 = 0; char ch = 0; int ok = 0; size_t i=0; int j = 0; int ix = 0; char tempChar=0; resetAnsi(0); copy_screen(screen2,screen1); window_y1 = (new_rows / 2) - 4; window_y2 = (new_rows / 2) + 4; window_x1 = (new_columns / 2) - 16; window_x2 = (new_columns / 2) + 16; window(screen1,window_x1, window_y1, window_x2, window_y2, MENU_PANEL, MENU_FOREGROUND0, WINDOW_TITLEB,1,0,1); dump_screen(screen1); write_str(screen1, (window_x2-window_x1) /2 + window_x1 - (strlen(windowTitle)/2) , window_y1, windowTitle, WINDOW_TITLEB, WINDOW_TITLEF,1); //'|' is used as line separator for(i = 0; i < strlen(message); i++) { tempChar = message[i]; if (tempChar != '|'){ write_ch(screen1,window_x1 + 1 + ix, window_y1 + 1 + j, tempChar, MENU_PANEL,MENU_FOREGROUND0,1); ix++; }else { j++; ix = 0; } } printf("\n"); if (listBox1 != NULL) removeList(&listBox1); listBox1 = addatend(listBox1, newitem("[ YES ]",window_x1+3,window_y2-1,-1,-1)); listBox1 = addatend(listBox1, newitem("[ NO ]",window_x1+13,window_y2-1,-1,-1)); listBox1 = addatend(listBox1, newitem("[CANCEL]",window_x1+23,window_y2-1,-1,-1)); setselectorLimit(8); ch = listBox(listBox1, window_x1+2,window_y2, &scrollData, MENU_PANEL, MENU_FOREGROUND0,MENU_SELECTOR, MENU_FOREGROUND1, 3, HORIZONTAL,1,LOCKED); ok = scrollData.itemIndex; if (listBox1 != NULL) removeList(&listBox1); ch++; copy_screen(screen1,screen2); dump_screen(screen1); resetScrollData(&scrollData); return ok; } ================================================ FILE: src/ui.h ================================================ /* ======================================================================== - HEADER - Module with different UI gadgets for LYNX. @author : Velorek @version : 1.0 Last modified: 06/02/2022 ======================================================================== */ #ifndef _UI_H_ #define _UI_H_ #define MAX_TEXT 255 #include "scbuf.h" #include int textbox(SCREENCELL *newScreen,int wherex, int wherey, int displayLength, char *label, char text[MAX_TEXT], int backcolor, int labelcolor, int textcolor, BOOL raw, BOOL locked); void window(SCREENCELL *screen1, int x1, int y1, int x2, int y2, int backcolor, int bordercolor, int titlecolor, int border, int title, int shadow); int inputWindow(char *label, char *tempFile, char *windowTitle, int offsetX, int offsetY, int length); int yesnoWindow(char *message, char *windowTitle); #endif