REM
REM   ***************************************************
REM   *  MSEDIT - Mateusz's Saucy Editor                *
REM   *  Author: Mateusz Viste <mateusz.viste@mail.ru>  *
REM   *  Compiled with FreeBASIC v0.18.5                *
REM   ***************************************************
REM
REM This program is free software; you can redistribute it and/or modify it
REM under the terms of the GNU General Public License as published by the
REM Free Software Foundation; either version 2 of the License, or (at your
REM option) any later version.
REM
REM This program is distributed in the hope that it will be useful, but
REM WITHOUT ANY WARRANTY; without even the implied warranty of
REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
REM Public License for more details.
REM
REM You should have received a copy of the GNU General Public License along
REM with this program; if not, write to the Free Software Foundation, Inc.,
REM 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#INCLUDE "vbcompat.bi"

DECLARE SUB RefreshMenu()
DECLARE SUB RefreshScrollBar()
DECLARE SUB RefreshStatusBar()
DECLARE SUB RefreshTextArea()
DECLARE SUB CmdLineHelp()
DECLARE SUB RefreshKontur()
DECLARE SUB UpdateTime()
DECLARE SUB FlushKeyb()
DECLARE SUB KeyPressed(KeyID AS STRING)
DECLARE SUB LoadFile(TextFile AS STRING, DstSlot AS BYTE)
DECLARE SUB About()
DECLARE SUB MenuProcess(KeyPressed AS STRING)
DECLARE SUB QuitEditor()
DECLARE SUB NotYetImplemented()
DECLARE SUB DrawShadow(x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER)
DECLARE SUB SaveFile()
DECLARE SUB NewFile()
DECLARE SUB CloseFile()
DECLARE SUB TooManySlots()
DECLARE SUB SlotCopy(SlotSrc AS BYTE, SlotDst AS BYTE)
DECLARE SUB AdjustBuffer()
DECLARE SUB OpenFile()
DECLARE SUB SaveAs()
DECLARE SUB KeybHelp()
DECLARE SUB CompileAndRun()
DECLARE FUNCTION ExtINKEY() AS STRING
DECLARE FUNCTION LoadedSlots() AS BYTE

TYPE BOOL AS BYTE        ' Creating the BOOL type, as it
CONST TRUE AS BOOL = 1   ' is not supported natively by
CONST FALSE AS BOOL = 0  ' the FreeBASIC compiler.

TYPE SlotProperties
  FileName AS STRING = ""
  LoadIt AS BOOL = FALSE    ' Tells edit to actually load the file
  FileCursorX AS LONGINT = 1
  FileCursorY AS LONGINT = 1
  CursorX AS INTEGER = 1
  CursorY AS INTEGER = 1
  TotalLines AS ULONGINT = 1
  EndOfLineType AS BYTE = 0    ' 0=DOS(CR+LF-13,10)  1=Linux(LF-10)  2=Mac(CR-13)
  HasBeenChanged AS BOOL = FALSE
END TYPE
REDIM SHARED FileData(0 TO 9, 1 TO 100) AS STRING

DIM SHARED Slot(0 TO 9) AS SlotProperties   ' Slot 0 is a tmp/work slot
DIM SHARED AS BYTE ActiveSlot = 1, TmpByte
DIM SHARED AS DOUBLE LastTimeRefresh, LastScreenRefresh
DIM SHARED AS STRING CurrentTime, LastKey, OpenDialog
DIM SHARED MenuState(1 TO 3) AS BYTE
DIM SHARED NextOp(1 TO 3) AS STRING
DIM SHARED AS BOOL ShiftKey, CtrlKey, AltKey, FBmode, ResetMenu
DIM SHARED AS ULONGINT MaxLinesBuff = 100
DIM AS BYTE x    ' private variable

CONST pVer AS STRING = "0.11"
CONST pDate AS STRING = "2008"

SCREEN 0            ' Init text mode
WIDTH 80            ' (tries to) set 80 columns mode
IF LOWORD(WIDTH()) < 80 THEN PRINT "MSEDIT needs at least a 80 columns text mode to work.": END
SCREEN , 1, 0       ' Work on page #1, display page #0

FOR TmpByte = 1 TO 9
  IF MID(COMMAND(TmpByte), 1, 1) <> "/" AND LEN(COMMAND(TmpByte)) > 0 THEN
    Slot(TmpByte).FileName = COMMAND(TmpByte)
    IF FileExists(Slot(TmpByte).FileName) THEN Slot(TmpByte).LoadIt = TRUE
  END IF
  IF MID(COMMAND(TmpByte), 1, 1) = "/" AND UCASE(COMMAND(TmpByte)) <> "/FB" THEN
      CmdLineHelp()
    ELSEIF UCASE(COMMAND(TmpByte)) = "/FB" THEN
      FBmode = TRUE
  END IF
NEXT TmpByte

COLOR 7, 1
CLS

DO
  IF ABS(Timer - LastTimeRefresh) > 5 THEN UpdateTime()
 REM  * Refresh all the screen *
  IF ABS(Timer - LastScreenRefresh) > 0.025 THEN  ' Refresh screen at 40 FPS
    LOCATE ,,0        ' Hide the blinking cursor
    IF LEN(OpenDialog) > 0 THEN MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
    RefreshTextArea()
    RefreshStatusBar()
    RefreshScrollBar()
    RefreshKontur()
    RefreshMenu()    ' Must be refreshed last (menus are allowed to overwrite other screen's areas)
    PCOPY 1, 0       ' Copy page #1 (work page) to #0 (displayed one)
    REM Position cursor and make it visible again, if no menu is active:
    IF MenuState(1) = 0 THEN LOCATE Slot(ActiveSlot).CursorY + 2, Slot(ActiveSlot).CursorX + 1, 1
    LastScreenRefresh = TIMER
    IF LEN(OpenDialog) > 0 THEN  ' Check for pending dialogs to open
      MenuState(1) = 0 : MenuState(2) = 0 : MenuState(3) = 0
      SELECT CASE OpenDialog
        CASE "ABOUT"
          OpenDialog = ""
          About()
        CASE "OpenFile"
          OpenDialog = ""
          OpenFile()
        CASE "NotYetImplemented"
          OpenDialog = ""
          NotYetImplemented()
        CASE "TooManySlots"
          OpenDialog = ""
          TooManySlots()
        CASE "KeybHelp"
          OpenDialog = ""
          KeybHelp()
        CASE "SaveAs"
          OpenDialog = ""
          SaveAs()
        CASE "SaveFile"
          OpenDialog = ""
          SaveFile()
        CASE "CompileAndRun"
          OpenDialog = ""
          CompileAndRun()
      END SELECT
    END IF
  END IF
 REM  * Screen refreshed *
  SLEEP 1, 1          ' Give away some CPU cycles
 REM  * Check if there are any files to load *
  FOR x = 1 TO 9
    IF Slot(x).LoadIt = TRUE THEN LoadFile(Slot(x).FileName, x): Slot(x).LoadIt = FALSE
  NEXT x
 REM  * Check key inputs *
  LastKey = ExtINKEY()
 REM  * Menu stuff *
  IF AltKey = FALSE AND MenuState(3) = 2 THEN MenuState(3) = 0
  IF AltKey = TRUE AND MenuState(1) = 0 THEN MenuState(1) = -1: MenuState(2) = 0: MenuState(3) = 0
  IF AltKey = TRUE AND MenuState(1) <> 0 AND MenuState(2) <> 0 AND MenuState(3) = 0 THEN MenuState(3) = 1
  IF AltKey = FALSE AND MenuState(1) <> 0 AND MenuState(2) <> 0 AND MenuState(3) = 1 THEN MenuState(2) = 0: MenuState(3) = 0
  IF MenuState(1) = -1 AND AltKey = FALSE THEN MenuState(1) = 1 : MenuState(2) = 0: MenuState(3) = 0
  IF MenuState(1) <> 0 AND MenuState(2) = 0 AND AltKey = TRUE THEN MenuState(3) = 1
  IF MenuState(3) = 1 AND AltKey = FALSE THEN MenuState(1) = 0: MenuState(2) = 0 : MenuState(3) = 0
 REM  * Interprete key inputs *
  IF LastKey = CHR(255) + ";" THEN OpenDialog = "KeybHelp": LastKey = "" ' F1 = Keyb. Help
  IF LastKey = CHR(255) + "<" THEN OpenDialog = "ABOUT": LastKey = ""    ' F2 = About
  IF MenuState(1) <> 0 OR MenuState(2) <> 0 OR MenuState(3) <> 0 THEN MenuProcess(LastKey): LastKey = ""
  IF LastKey = CHR(13) THEN KeyPressed("ENTER"): LastKey = ""
  IF LastKey = CHR(255) + "H" THEN KeyPressed("UP"): LastKey = ""
  IF LastKey = CHR(255) + "P" THEN KeyPressed("DOWN"): LastKey = ""
  IF LastKey = CHR(255) + "K" THEN KeyPressed("LEFT"): LastKey = ""
  IF LastKey = CHR(255) + "M" THEN KeyPressed("RIGHT"): LastKey = ""
  IF LastKey = CHR(255) + "I" THEN KeyPressed("PAGEUP"): LastKey = ""
  IF LastKey = CHR(255) + "Q" THEN KeyPressed("PAGEDOWN"): LastKey = ""
  IF LastKey = CHR(255) + "G" THEN KeyPressed("HOME"): LastKey = ""
  IF LastKey = CHR(255) + "O" THEN KeyPressed("END"): LastKey = ""
  IF LastKey = CHR(255) + "R" THEN KeyPressed("INSERT"): LastKey = ""
  IF LastKey = CHR(255) + "S" THEN KeyPressed("DELETE"): LastKey = ""
  IF LastKey = CHR(255, 145) THEN KeyPressed("LINEDOWN"): LastKey = ""
  IF LastKey = CHR(255, 141) THEN KeyPressed("LINEUP"): LastKey = ""
  IF LastKey = CHR(8) THEN KeyPressed("BACKSPACE"): LastKey = ""
  IF LastKey = CHR(9) THEN KeyPressed("TAB"): LastKey = ""
  IF LEN(LastKey) = 1 AND ASC(LastKey) >= 32 THEN KeyPressed(LastKey): LastKey = ""
 REM  * Check queued operations *
  IF LEN(NextOp(1)) > 0 THEN KeyPressed(NextOp(1)) : NextOp(1) = ""
  IF LEN(NextOp(2)) > 0 THEN KeyPressed(NextOp(2)) : NextOp(2) = ""
  IF LEN(NextOp(3)) > 0 THEN KeyPressed(NextOp(3)) : NextOp(3) = ""
 REM  * Check for an empty last line. If not found, add one *
  IF LEN(FileData(ActiveSlot, Slot(ActiveSlot).TotalLines)) > 0 THEN
    Slot(ActiveSlot).TotalLines += 1
    FileData(ActiveSlot, Slot(ActiveSlot).TotalLines) = ""
  END IF
 REM  * Check slots (must be at least one) and reorder if needed *
  IF LoadedSlots() = 0 THEN NewFile()
  FOR TmpByte = 1 TO 8
    IF Slot(TmpByte).FileName = "" THEN
      Slot(0) = Slot(TmpByte)
      SlotCopy(TmpByte, 0)
      Slot(TmpByte) = Slot(TmpByte + 1)
      SlotCopy(TmpByte + 1, TmpByte)
      Slot(TmpByte + 1) = Slot(0)
      SlotCopy(0, TmpByte + 1)
      IF ActiveSlot = TmpByte + 1 THEN ActiveSlot = TmpByte
    END IF
  NEXT TmpByte
 REM  * Check if the file buffer needs to be bigger *
  AdjustBuffer()
LOOP


REM  * * *  END OF THE MAIN PROGRAM  *  SUBS AND FUNCTIONS BELOW  * * *


SUB RefreshMenu()
  DIM AS INTEGER x
  LOCATE 1, 1
  COLOR 0, 7
  IF FBmode = FALSE THEN
      PRINT "   File  Edit  Search  View  Options  Help                                      ";
    ELSE
      PRINT "   File  Edit  Search  View  Options  Help  FBC                                 ";
  END IF
  SELECT CASE MenuState(1)
    CASE 1
      COLOR 7, 0
      LOCATE 1, 3: PRINT " File ";
    CASE 2
      COLOR 7, 0
      LOCATE 1, 9: PRINT " Edit ";
    CASE 3
      COLOR 7, 0
      LOCATE 1, 15: PRINT " Search ";
    CASE 4
      COLOR 7, 0
      LOCATE 1, 23: PRINT " View ";
    CASE 5
      COLOR 7, 0
      LOCATE 1, 29: PRINT " Options ";
    CASE 6
      COLOR 7, 0
      LOCATE 1, 38: PRINT " Help ";
    CASE 7
      COLOR 7, 0
      LOCATE 1, 44: PRINT " FBC  ";
  END SELECT
  IF MenuState(2) = 0 AND MenuState(1) <> 0 THEN
    IF MenuState(1) = 1 THEN COLOR 15, 0 ELSE COLOR 15, 7
    LOCATE 1, 4: PRINT "F";
    IF MenuState(1) = 2 THEN COLOR 15, 0 ELSE COLOR 15, 7
    LOCATE 1, 10: PRINT "E";
    IF MenuState(1) = 3 THEN COLOR 15, 0 ELSE COLOR 15, 7
    LOCATE 1, 16: PRINT "S";
    IF MenuState(1) = 4 THEN COLOR 15, 0 ELSE COLOR 15, 7
    LOCATE 1, 24: PRINT "V";
    IF MenuState(1) = 5 THEN COLOR 15, 0 ELSE COLOR 15, 7
    LOCATE 1, 30: PRINT "O";
    IF MenuState(1) = 6 THEN COLOR 15, 0 ELSE COLOR 15, 7
    LOCATE 1, 39: PRINT "H";
    IF FBmode = TRUE THEN
      IF MenuState(1) = 7 THEN COLOR 15, 0 ELSE COLOR 15, 7
      LOCATE 1, 47: PRINT "C";
    END IF
  END IF
 REM  * Display submenus, if any *
  IF MenuState(2) > 0 THEN
    COLOR 0, 7
    SELECT CASE MenuState(1)
      CASE 1  ' File menu
        LOCATE 2, 2: PRINT "Ú"; STRING(16, "Ä"); "¿";
        FOR x = 1 TO 7
          LOCATE 2 + x, 2: PRINT "³"; SPACE(16); "³";
        NEXT x
        LOCATE 10, 2: PRINT "À"; STRING(16, "Ä"); "Ù";
        IF MenuState(2) = 1 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 3, 3: PRINT " New            ";
        COLOR 15: LOCATE 3, 4: PRINT "N";
        IF MenuState(2) = 2 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 4, 3: PRINT " Open...        ";
        COLOR 15: LOCATE 4, 4: PRINT "O";
        IF MenuState(2) = 3 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 5, 3: PRINT " Save           ";
        COLOR 15: LOCATE 5, 4: PRINT "S";
        IF MenuState(2) = 4 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 6, 3: PRINT " Save as...     ";
        COLOR 15: LOCATE 6, 9: PRINT "A";
        IF MenuState(2) = 5 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 7, 3: PRINT " Close          ";
        COLOR 15: LOCATE 7, 4: PRINT "C";
        IF MenuState(2) = 6 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 8, 3: PRINT " Print...       ";
        COLOR 15: LOCATE 8, 4: PRINT "P";
        IF MenuState(2) = 7 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 9, 3: PRINT " Exit           ";
        COLOR 15: LOCATE 9, 5: PRINT "x";
        DrawShadow(3, 3, 20, 11)
      CASE 2  ' Edit menu
        LOCATE 2, 8: PRINT "Ú"; STRING(18, "Ä"); "¿";
        FOR x = 1 TO 4
          LOCATE 2 + x, 8: PRINT "³"; SPACE(18); "³";
        NEXT x
        LOCATE 7, 8: PRINT "À"; STRING(18, "Ä"); "Ù";
        IF MenuState(2) = 1 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 3, 9: PRINT " Cut       CTRL+X ";
        COLOR 15: LOCATE 3, 12: PRINT "t";
        IF MenuState(2) = 2 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 4, 9: PRINT " Copy      CTRL+C ";
        COLOR 15: LOCATE 4, 10: PRINT "C";
        IF MenuState(2) = 3 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 5, 9: PRINT " Paste     CTRL+V ";
        COLOR 15: LOCATE 5, 10: PRINT "P";
        IF MenuState(2) = 4 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 6, 9: PRINT " Clear     DEL    ";
        COLOR 15: LOCATE 6, 12: PRINT "e";
        DrawShadow(9, 3, 28, 8)
      CASE 3  ' Search menu
        LOCATE 2, 14: PRINT "Ú"; STRING(29, "Ä"); "¿";
        FOR x = 1 TO 3
          LOCATE 2 + x, 14: PRINT "³"; SPACE(29); "³";
        NEXT x
        LOCATE 6, 14: PRINT "À"; STRING(29, "Ä"); "Ù";
        IF MenuState(2) = 1 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 3, 15: PRINT " Find...                     ";
        COLOR 15: LOCATE 3, 16: PRINT "F";
        IF MenuState(2) = 2 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 4, 15: PRINT " Repeat Last Find...      F3 ";
        COLOR 15: LOCATE 4, 23: PRINT "L";
        IF MenuState(2) = 3 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 5, 15: PRINT " Replace...                  ";
        COLOR 15: LOCATE 5, 16: PRINT "R";
        DrawShadow(15, 3, 45, 7)
      CASE 4  ' View menu
        LOCATE 2, 22: PRINT "Ú"; STRING(25, "Ä"); "¿";
        FOR x = 1 TO LoadedSlots()
          LOCATE 2 + x, 22: PRINT "³"; SPACE(25); "³";
          IF MenuState(2) = x THEN COLOR 7, 0 ELSE COLOR 0, 7
          LOCATE 2 + x, 23: PRINT "   " + MID(Slot(x).Filename + SPACE(15), 1, 15) + " ALT+" + STR(x) + " ";
          COLOR 15: LOCATE 2 + x, 24: PRINT STR(x);
          COLOR 0, 7
        NEXT x
        LOCATE 2 + x, 22: PRINT "À"; STRING(25, "Ä"); "Ù";
        DrawShadow(23, 3, 49, 3 + x)
      CASE 5  ' Options menu
        LOCATE 2, 28: PRINT "Ú"; STRING(17, "Ä"); "¿";
        FOR x = 1 TO 2
          LOCATE 2 + x, 28: PRINT "³"; SPACE(17); "³";
        NEXT x
        LOCATE 5, 28: PRINT "À"; STRING(17, "Ä"); "Ù";
        IF MenuState(2) = 1 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 3, 29: PRINT " Settings...     ";
        COLOR 15: LOCATE 3, 30: PRINT "S";
        IF MenuState(2) = 2 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 4, 29: PRINT " Colors...       ";
        COLOR 15: LOCATE 4, 30: PRINT "C";
        DrawShadow(29, 3, 47, 6)
      CASE 6  ' Help menu
        LOCATE 2, 37: PRINT "Ú"; STRING(13, "Ä"); "¿";
        FOR x = 1 TO 2
          LOCATE 2 + x, 37: PRINT "³"; SPACE(13); "³";
        NEXT x
        LOCATE 5, 37: PRINT "À"; STRING(13, "Ä"); "Ù";
        IF MenuState(2) = 1 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 3, 38: PRINT " Commands... ";
        COLOR 15: LOCATE 3, 39: PRINT "C";
        IF MenuState(2) = 2 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 4, 38: PRINT " About...    ";
        COLOR 15: LOCATE 4, 39: PRINT "A";
        DrawShadow(38, 3, 52, 6)
      CASE 7  ' FBC menu
        LOCATE 2, 43: PRINT "Ú"; STRING(15, "Ä"); "¿";
        FOR x = 1 TO 4
          LOCATE 2 + x, 43: PRINT "³"; SPACE(15); "³";
        NEXT x
        LOCATE 7, 43: PRINT "À"; STRING(15, "Ä"); "Ù";
        IF MenuState(2) = 1 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 3, 44: PRINT " Compile       ";
        COLOR 15: LOCATE 3, 45: PRINT "C";
        IF MenuState(2) = 2 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 4, 44: PRINT " Compile & Run ";
        COLOR 15: LOCATE 4, 55: PRINT "R";
        IF MenuState(2) = 3 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 5, 44: PRINT " Parameters    ";
        COLOR 15: LOCATE 5, 45: PRINT "P";
        IF MenuState(2) = 4 THEN COLOR 7, 0 ELSE COLOR 0, 7
        LOCATE 6, 44: PRINT " Help          ";
        COLOR 15: LOCATE 6, 45: PRINT "H";
        DrawShadow(44, 3, 60, 8)
    END SELECT
  END IF
 REM  * Display the current time *
  LOCATE 1, 80 - LEN(CurrentTime)
  COLOR 0, 7
  PRINT CurrentTime;
' *** DEBUG START
'  LOCATE 1, 50: PRINT "1="; MenuState(1); "  2="; MenuState(2); "  3="; MenuState(3);
' *** DEBUG END
END SUB

SUB RefreshScrollBar()
  DIM AS INTEGER x, ScrollSize, ScrollPos
 REM  * Display static stuff *
  COLOR 7, 0
  LOCATE 3, 80: PRINT CHR(24);
  FOR x = 4 TO 23
    LOCATE x, 80: PRINT CHR(176);
  NEXT x
  LOCATE 24, 80: PRINT CHR(25);
 REM  * Calculate scroll's size *
  ScrollSize = INT((22 / Slot(ActiveSlot).TotalLines) * 20)
  IF ScrollSize > 20 THEN ScrollSize = 20
  IF ScrollSize < 1 THEN ScrollSize = 1
 REM  * Calculate scroll's position *
  ScrollPos = 20 * Slot(ActiveSlot).FileCursorY \ Slot(ActiveSlot).TotalLines
  IF ScrollPos + (ScrollSize - ScrollSize \ 2) > 21 THEN ScrollPos = 21 - (ScrollSize - ScrollSize \ 2)
  IF ScrollPos - ScrollSize \ 2 < 1 THEN ScrollPos = 1 + ScrollSize \ 2
 REM  * Display the scroll *
  COLOR 0, 7
  FOR x = ScrollPos - ScrollSize \ 2 + 1 TO ScrollPos + (ScrollSize - ScrollSize \ 2)
    LOCATE x + 2, 80: PRINT " ";
  NEXT x
END SUB

SUB RefreshStatusBar()
  COLOR 0, 7
  LOCATE 25, 1: PRINT " F1=Help                                             "; CHR(179); "  Line:      Col:         ";
  LOCATE 25, 62: PRINT "" & Slot(ActiveSlot).FileCursorY;
  LOCATE 25, 72: PRINT "" & Slot(ActiveSlot).FileCursorX;
END SUB

SUB RefreshTextArea()
  DIM AS ULONGINT FirstLine, LastLine, FirstColumn, x
  COLOR 7, 1
  FirstLine = Slot(ActiveSlot).FileCursorY - Slot(ActiveSlot).CursorY + 1
  LastLine = FirstLine + 21
  IF LastLine > Slot(ActiveSlot).TotalLines THEN LastLine = Slot(ActiveSlot).TotalLines
  FirstColumn = Slot(ActiveSlot).FileCursorX - Slot(ActiveSlot).CursorX + 1
  FOR x = FirstLine TO LastLine
    LOCATE x - FirstLine + 3, 2
    PRINT LEFT(MID(FileData(ActiveSlot, x), FirstColumn, 78) + SPACE(78), 78);
  NEXT x
  FOR x = LastLine - FirstLine + 1 TO 21
    LOCATE x + 3, 2
    PRINT SPACE(78);
  NEXT x
END SUB

SUB CmdLineHelp()
  PRINT "Mateusz's Saucy Editor v"; pVer; " Copyright (C) Mateusz Viste "; pDate
  PRINT
  PRINT "Usage:  MSEDIT [file1.txt] [file2.txt] ... [file8.txt] [/fb]"
  PRINT
  PRINT " /fb - activates the FBC mode (IDE for the FreeBASIC compiler)"
  PRINT
  END
END SUB

SUB RefreshKontur()
  DIM AS INTEGER x
  COLOR 7, 0
  LOCATE 2, 1: PRINT "Ú"; STRING(78, "Ä"); "¿";
  FOR x = 3 TO 24
    LOCATE x, 1: PRINT CHR(179);
  NEXT x
  COLOR 0, 7
  LOCATE 2, 40 - ((LEN(Slot(ActiveSlot).FileName) + 2) \ 2): PRINT " "; Slot(ActiveSlot).FileName; " ";
END SUB

SUB UpdateTime()
  CurrentTime = HOUR(NOW) & ":" & RIGHT("0" & MINUTE(NOW), 2)
  LastTimeRefresh = Timer
END SUB

SUB FlushKeyb()
  DO: LOOP UNTIL LEN(INKEY) = 0
END SUB

SUB KeyPressed(KeyID AS STRING)
  DIM AS LONGINT x
  IF KeyID = "TAB" THEN
    KeyID = SPACE(9 - ((Slot(ActiveSlot).FileCursorX + 8) MOD 8))
    IF KeyID = SPACE(9) THEN KeyID = " "
  END IF
  SELECT CASE KeyID
    CASE "ENTER"
      Slot(ActiveSlot).TotalLines += 1
      FOR x = Slot(ActiveSlot).TotalLines TO Slot(ActiveSlot).FileCursorY + 1 STEP -1
        FileData(ActiveSlot, x) = FileData(ActiveSlot, x - 1)
      NEXT x
      IF Slot(ActiveSlot).FileCursorX = 1 THEN
          FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) = ""
        ELSE
          FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) = MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY), 1, Slot(ActiveSlot).FileCursorX - 1)
          FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY + 1) = MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY + 1), Slot(ActiveSlot).FileCursorX)
      END IF
      REM Auto-indentation:
      Slot(ActiveSlot).CursorX = LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) - LEN(LTRIM(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY))) + 1
      IF Slot(ActiveSlot).CursorX > 78 THEN Slot(ActiveSlot).CursorX = 78
      Slot(ActiveSlot).FileCursorX = LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) - LEN(LTRIM(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY))) + 1
      REM The line below removes any spaces at the end of the current line:
      FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) = RTRIM(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY))
      Slot(ActiveSlot).CursorY += 1
      Slot(ActiveSlot).FileCursorY += 1
      IF Slot(ActiveSlot).CursorY > 22 THEN Slot(ActiveSlot).CursorY = 22
    CASE "UP"
      IF Slot(ActiveSlot).FileCursorY > 1 THEN
        Slot(ActiveSlot).FileCursorY -= 1
        IF Slot(ActiveSlot).CursorY > 1 THEN Slot(ActiveSlot).CursorY -= 1
      END IF
    CASE "DOWN"
      IF Slot(ActiveSlot).FileCursorY < Slot(ActiveSlot).TotalLines THEN
        Slot(ActiveSlot).FileCursorY += 1
        IF Slot(ActiveSlot).CursorY < 22 THEN Slot(ActiveSlot).CursorY += 1
      END IF
    CASE "LEFT"
      IF Slot(ActiveSlot).FileCursorX > 1 THEN
        Slot(ActiveSlot).FileCursorX -= 1
        IF Slot(ActiveSlot).CursorX > 1 THEN Slot(ActiveSlot).CursorX -= 1
      END IF
    CASE "RIGHT"
      Slot(ActiveSlot).FileCursorX += 1
      IF Slot(ActiveSlot).CursorX < 78 THEN Slot(ActiveSlot).CursorX += 1
    CASE "PAGEUP"
      IF Slot(ActiveSlot).FileCursorY > 1 THEN
        Slot(ActiveSlot).FileCursorY -= 21
        IF Slot(ActiveSlot).FileCursorY < 1 THEN Slot(ActiveSlot).FileCursorY = 1
        IF Slot(ActiveSlot).FileCursorY = 1 THEN Slot(ActiveSlot).CursorY = 1
      END IF
    CASE "PAGEDOWN"
      Slot(ActiveSlot).FileCursorY += 21
      IF Slot(ActiveSlot).FileCursorY > Slot(ActiveSlot).TotalLines THEN
        Slot(ActiveSlot).FileCursorY = Slot(ActiveSlot).TotalLines
        Slot(ActiveSlot).CursorY = 22
      END IF
      IF Slot(ActiveSlot).FileCursorY + 22 - Slot(ActiveSlot).CursorY > Slot(ActiveSlot).TotalLines THEN
        Slot(ActiveSlot).CursorY = 22 - (Slot(ActiveSlot).TotalLines - Slot(ActiveSlot).FileCursorY)
      END IF
      IF Slot(ActiveSlot).FileCursorY > Slot(ActiveSlot).TotalLines THEN Slot(ActiveSlot).FileCursorY = Slot(ActiveSlot).TotalLines
      IF Slot(ActiveSlot).CursorY > Slot(ActiveSlot).TotalLines THEN Slot(ActiveSlot).CursorY = Slot(ActiveSlot).TotalLines
    CASE "HOME"
      Slot(ActiveSlot).FileCursorX = 1
      Slot(ActiveSlot).CursorX = 1
    CASE "END"
      x = LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) - Slot(ActiveSlot).FileCursorX + 1
      Slot(ActiveSlot).FileCursorX += x
      IF Slot(ActiveSlot).CursorX + x > 78 THEN
          Slot(ActiveSlot).CursorX = 78
        ELSEIF Slot(ActiveSlot).CursorX + x < 1 THEN
          Slot(ActiveSlot).CursorX = 1
        ELSE
          Slot(ActiveSlot).CursorX += x
      END IF
    CASE "INSERT"
      ' WriteMe!
    CASE "DELETE"
      REM If on end of line (and not the last one), then glue both lines together:
      IF Slot(ActiveSlot).FileCursorX > LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) AND Slot(ActiveSlot).FileCursorY < Slot(ActiveSlot).TotalLines THEN
        FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) += SPACE(Slot(ActiveSlot).CursorX - LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) - 1)
        FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) += FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY + 1)
        FOR x = Slot(ActiveSlot).FileCursorY + 1 TO Slot(ActiveSlot).TotalLines
          FileData(ActiveSlot, x) = FileData(ActiveSlot, x + 1)
        NEXT x
        FileData(ActiveSlot, Slot(ActiveSlot).TotalLines) = ""
        Slot(ActiveSlot).TotalLines -= 1
      REM If inside a line, then just remove the character
      ELSEIF LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) > 0 AND LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) >= Slot(ActiveSlot).FileCursorX THEN
        IF Slot(ActiveSlot).FileCursorX = 1 THEN
            FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) = MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY), 2)
          ELSEIF Slot(ActiveSlot).FileCursorX = LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) THEN
            FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) = LEFT(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY), LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) - 1)
          ELSE
          FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) = MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY), 1, Slot(ActiveSlot).FileCursorX - 1) + MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY), Slot(ActiveSlot).FileCursorX + 1)
        END IF
      END IF
    CASE "BACKSPACE"
      IF Slot(ActiveSlot).FileCursorX > 1 OR Slot(ActiveSlot).FileCursorY > 1 THEN
        IF Slot(ActiveSlot).FileCursorX > 1 THEN
            Slot(ActiveSlot).FileCursorX -= 1
            IF Slot(ActiveSlot).CursorX > 1 THEN Slot(ActiveSlot).CursorX -= 1
            REM If cursor is not outside the line, then the next operation is "DEL".
            IF Slot(ActiveSlot).FileCursorX <= LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) THEN NextOp(1) = "DELETE"
          ELSE
            Slot(ActiveSlot).FileCursorY -= 1
            x = LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) - Slot(ActiveSlot).FileCursorX + 1
            Slot(ActiveSlot).CursorX += x
            IF Slot(ActiveSlot).CursorX > 78 THEN Slot(ActiveSlot).CursorX = 78
            Slot(ActiveSlot).FileCursorX = LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) + 1
            IF Slot(ActiveSlot).CursorY > 1 THEN Slot(ActiveSlot).CursorY -= 1
            NextOp(1) = "DELETE"
        END IF
      END IF
    CASE "LINEUP"
      IF Slot(ActiveSlot).CursorY < 22 THEN
          IF Slot(ActiveSlot).CursorY < Slot(ActiveSlot).FileCursorY THEN Slot(ActiveSlot).CursorY += 1
        ELSEIF Slot(ActiveSlot).FileCursorY > 22 THEN
          Slot(ActiveSlot).FileCursorY -= 1
      END IF
    CASE "LINEDOWN"
      IF Slot(ActiveSlot).CursorY > 1 THEN
          IF Slot(ActiveSlot).FileCursorY + 22 - Slot(ActiveSlot).CursorY < Slot(ActiveSlot).TotalLines THEN Slot(ActiveSlot).CursorY -= 1
        ELSEIF Slot(ActiveSlot).FileCursorY + 22 - Slot(ActiveSlot).CursorY < Slot(ActiveSlot).TotalLines THEN
          Slot(ActiveSlot).FileCursorY += 1
      END IF
    CASE CHR(27)
      REM Ignored keys: ESC
    CASE ELSE  ' If any other key, then add the char to the file
      REM If cursor is farther than the lenght of line, then add spaces to fill the gap:
      IF LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) < Slot(ActiveSlot).FileCursorX - 1 THEN FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) += SPACE(Slot(ActiveSlot).FileCursorX - LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) - 1)
      REM Add the character to the line:
      IF Slot(ActiveSlot).FileCursorX = 1 THEN
          FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) = KeyID + FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)
        ELSEIF Slot(ActiveSlot).FileCursorX = LEN(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY)) + 1 THEN
          FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) += KeyID
        ELSE
          FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY) = MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY), 1, Slot(ActiveSlot).FileCursorX - 1) + KeyID + MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY), Slot(ActiveSlot).FileCursorX)
      END IF
      Slot(ActiveSlot).FileCursorX += LEN(KeyID)
      Slot(ActiveSlot).CursorX += LEN(KeyID)
      IF Slot(ActiveSlot).CursorX > 78 THEN Slot(ActiveSlot).CursorX = 78
  END SELECT
  FlushKeyb()
END SUB

SUB LoadFile(TextFile AS STRING, DstSlot AS BYTE)
  DIM AS LONGINT x
  DIM AS UBYTE ByteBuffer
  DIM AS BYTE LoadPercent, OldLoadPercent
  DIM AS STRING LineBuffer
  OldLoadPercent = -1
  OPEN TextFile FOR BINARY AS #1
  DrawShadow(31, 11, 50, 13)
  COLOR 0, 7
  LOCATE 10, 30: PRINT "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿";
  LOCATE 11, 30: PRINT "³ Loading...       ³";
  LOCATE 12, 30: PRINT "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ";
  Slot(DstSlot).TotalLines = 1
  x = 0
  DO
    LINE INPUT #1, LineBuffer
    x += LEN(LineBuffer)
    FileData(DstSlot, Slot(DstSlot).TotalLines) = LineBuffer
    Slot(DstSlot).TotalLines += 1
    IF Slot(DstSlot).TotalLines + 500 > MaxLinesBuff THEN AdjustBuffer()
    IF LOF(1) > 0 THEN LoadPercent = 100 * x \ LOF(1)
    IF LoadPercent > OldLoadPercent THEN LOCATE 11, 43: PRINT LoadPercent; "%"; : PCOPY 1, 0
    OldLoadPercent = LoadPercent
  LOOP UNTIL EOF(1) OR Slot(DstSlot).TotalLines >= 1000000 ' Max file's size: 1 million lines
  CLOSE #1
  IF Slot(DstSlot).TotalLines = 1000000 THEN CLS: PRINT "The file has too many lines! Sorry.": END
  FlushKeyb()
END SUB

FUNCTION ExtINKEY() AS STRING
  DIM AS STRING Wynik
  Wynik = INKEY
  IF MULTIKEY(&h2A) = -1 OR MULTIKEY(&h36) = -1 THEN ShiftKey = TRUE ELSE ShiftKey = FALSE
  IF MULTIKEY(&h1D) = -1 THEN CtrlKey = TRUE ELSE CtrlKey = FALSE
  IF MULTIKEY(&h38) = -1 THEN AltKey = TRUE ELSE AltKey = FALSE
  RETURN Wynik
END FUNCTION

SUB About()
  COLOR 0, 15
  LOCATE 9, 18: PRINT SPACE(17); "About MSEDIT"; SPACE(17);
  COLOR 0, 7
  LOCATE 10, 18: PRINT SPACE(46);
  LOCATE 11, 18: PRINT SPACE(12); "Mateusz's Saucy Editor"; SPACE(12);
  LOCATE 12, 18: PRINT SPACE(46);
  LOCATE 12, 35: PRINT "Version "; pVer;
  LOCATE 13, 18: PRINT SPACE(7); "Copyright (C) Mateusz Viste "; pDate; SPACE(7);
  LOCATE 14, 18: PRINT SPACE(46);
  LOCATE 15, 18: PRINT SPACE(15); "Press any key..."; SPACE(15);
  LOCATE 16, 18: PRINT SPACE(46);
  DrawShadow(19, 10, 64, 17)
  LOCATE 15, 49  ' Put the cursor after the "Press any key" text
  PCOPY 1, 0
  SLEEP
  FlushKeyb()
END SUB

SUB MenuProcess(KeyPress AS STRING)
  REM  * If any menu is open, then do an action:
  SELECT CASE KeyPress
    CASE CHR(13)  ' Enter
      SELECT CASE MenuState(1) * 10 + MenuState(2)
        CASE 11  ' New file
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
          NewFile()
        CASE 12  ' open
          OpenDialog = "OpenFile"
        CASE 13  ' save
          OpenDialog = "SaveFile"
        CASE 14  ' save as
          OpenDialog = "SaveAs"
        CASE 15  ' close
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
          CloseFile()
        CASE 16  ' print
          OpenDialog = "NotYetImplemented"
        CASE 17  ' exit
          QuitEditor()
        CASE 21  ' cut
          OpenDialog = "NotYetImplemented"
        CASE 22  ' copy
          OpenDialog = "NotYetImplemented"
        CASE 23  ' paste
          OpenDialog = "NotYetImplemented"
        CASE 24  ' clear
          OpenDialog = "NotYetImplemented"
        CASE 31  ' find
          OpenDialog = "NotYetImplemented"
        CASE 32  ' repeat last find
          OpenDialog = "NotYetImplemented"
        CASE 33  ' replace
          OpenDialog = "NotYetImplemented"
        CASE 41
          ActiveSlot = 1
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 42
          ActiveSlot = 2
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 43
          ActiveSlot = 3
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 44
          ActiveSlot = 4
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 45
          ActiveSlot = 5
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 46
          ActiveSlot = 6
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 47
          ActiveSlot = 7
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 48
          ActiveSlot = 8
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 49
          ActiveSlot = 9
          MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
        CASE 51  ' settings
          OpenDialog = "NotYetImplemented"
        CASE 52  ' colors
          OpenDialog = "NotYetImplemented"
        CASE 61  ' keyb commands
          OpenDialog = "KeybHelp"
        CASE 62  ' about
          OpenDialog = "ABOUT"
        CASE 71  ' Compile
          OpenDialog = "NotYetImplemented"
        CASE 72  ' Compile & Run
          OpenDialog = "CompileAndRun"
        CASE 73  ' Parameters
          OpenDialog = "NotYetImplemented"
        CASE 74  ' Help
          OpenDialog = "NotYetImplemented"
        CASE 10, 20, 30, 40, 50, 60, 70, 80
          REM If menu is not unrolled yet, then unroll it when pushing ENTER:
          IF MenuState(2) = 0 THEN MenuState(2) = 1
      END SELECT
    CASE CHR(255) + "H"    ' UP
      IF MenuState(2) = 0 THEN  ' If menu isn't unrolled, then pos = 1
          MenuState(2) = 1
        ELSE
          IF MenuState(2) > 1 THEN MenuState(2) -= 1 ELSE MenuState(2) -= 2
      END IF
    CASE CHR(255) + "P"    ' DOWN
      MenuState(2) += 1
    CASE CHR(255) + "K"    ' LEFT
      IF FBmode = FALSE THEN
          IF MenuState(1) > 1 THEN MenuState(1) -= 1 ELSE MenuState(1) = 6
        ELSE
          IF MenuState(1) > 1 THEN MenuState(1) -= 1 ELSE MenuState(1) = 7
      END IF  
      IF MenuState(2) <> 0 THEN MenuState(2) = 1
    CASE CHR(255) + "M"    ' RIGHT
      IF FBmode = FALSE THEN
          IF MenuState(1) < 6 THEN MenuState(1) += 1 ELSE MenuState(1) = 1
        ELSE
          IF MenuState(1) < 7 THEN MenuState(1) += 1 ELSE MenuState(1) = 1
      END IF
      IF MenuState(2) <> 0 THEN MenuState(2) = 1
    CASE ""                ' Nothing
      REM Do nothing
    CASE CHR(27)
      MenuState(1) = 0: MenuState(2) = 0: MenuState(3) = 0
    CASE "n", "N", CHR(255, 49)
      IF MenuState(1) = 1 AND MenuState(2) > 0 THEN
        MenuState(1) = 1: MenuState(2) = 0: MenuState(3) = 1
        NewFile()
      END IF
    CASE "o", "O", CHR(255, 24)
      IF MenuState(1) = 1 AND MenuState(2) > 0 THEN
        OpenDialog = "OpenFile"
      END IF
    CASE "s", "S", CHR(255, 31)
      IF MenuState(1) = 1 AND MenuState(2) > 0 THEN
          OpenDialog = "SaveFile"
        ELSEIF MenuState(1) = 5 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
      END IF
    CASE "a", "A", CHR(255, 30)
      IF MenuState(1) = 1 AND MenuState(2) > 0 THEN
          OpenDialog = "SaveAs"
        ELSEIF MenuState(1) = 6 AND MenuState(2) > 0 THEN
          OpenDialog = "ABOUT"
      END IF
    CASE "c", "C", CHR(255, 46)
      IF MenuState(1) = 1 AND MenuState(2) > 0 THEN
          MenuState(1) = 1: MenuState(2) = 0: MenuState(3) = 1
          CloseFile()
        ELSEIF MenuState(1) = 2 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
        ELSEIF MenuState(1) = 5 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
        ELSEIF MenuState(1) = 6 AND MenuState(2) > 0 THEN
          OpenDialog = "KeybHelp"
        ELSEIF MenuState(1) = 7 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
      END IF
    CASE "p", "P", CHR(255, 25)
      IF MenuState(1) = 1 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
        ELSEIF MenuState(1) = 2 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
        ELSEIF MenuState(1) = 7 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
      END IF
    CASE "x", "X", CHR(255, 45)
      IF MenuState(1) = 1 AND MenuState(2) > 0 THEN
        QuitEditor()
      END IF
    CASE "f", "F", CHR(255, 33)
      IF MenuState(1) = 3 AND MenuState(2) > 0 THEN
        OpenDialog = "NotYetImplemented"
      END IF
    CASE "r", "R", CHR(255, 19)
      IF MenuState(1) = 3 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
        ELSEIF MenuState(1) = 7 AND MenuState(2) > 0 THEN
          OpenDialog = "NotYetImplemented"
      END IF
    CASE "l", "L", CHR(255, 28)
      IF MenuState(1) = 3 AND MenuState(2) > 0 THEN
        OpenDialog = "NotYetImplemented"
      END IF
    CASE "t", "T", CHR(255, 20)
      IF MenuState(1) = 2 AND MenuState(2) > 0 THEN
        OpenDialog = "NotYetImplemented"
      END IF
    CASE "e", "E", CHR(255, 18)
      IF MenuState(1) = 2 AND MenuState(2) > 0 THEN
        OpenDialog = "NotYetImplemented"
      END IF
    CASE "h", "H", CHR(255, 35)
      IF MenuState(1) = 7 AND MenuState(2) > 0 THEN
        OpenDialog = "NotYetImplemented"
      END IF
    CASE "1", CHR(255, 120)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 1 THEN ActiveSlot = 1
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
    CASE "2", CHR(255, 121)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 2 THEN ActiveSlot = 2
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
    CASE "3", CHR(255, 122)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 3 THEN ActiveSlot = 3
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
    CASE "4", CHR(255, 123)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 4 THEN ActiveSlot = 4
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
    CASE "5", CHR(255, 124)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 5 THEN ActiveSlot = 5
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
    CASE "6", CHR(255, 125)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 6 THEN ActiveSlot = 6
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
    CASE "7", CHR(255, 126)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 7 THEN ActiveSlot = 7
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
    CASE "8", CHR(255, 127)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 8 THEN ActiveSlot = 8
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
    CASE "9", CHR(255, 128)
      IF MenuState(1) = 4 AND MenuState(2) > 0 THEN
        IF LoadedSlots() >= 9 THEN ActiveSlot = 9
        MenuState(1) = 4: MenuState(2) = 0: MenuState(3) = 1
      END IF
  END SELECT
 REM  * If no menu is open (and ALT pressed), then open the good menu
  IF MenuState(1) = -1 OR (MenuState(1) > 0 AND MenuState(2) = 0) THEN
    SELECT CASE KeyPress
      CASE CHR(255, 33), "f", "F"  ' ALT+F
        MenuState(1) = 1 : MenuState(2) = 1 : MenuState(3) = 2
      CASE CHR(255, 18), "e", "E"  ' ALT+E
        MenuState(1) = 2 : MenuState(2) = 1 : MenuState(3) = 2
      CASE CHR(255, 31), "s", "S"  ' ALT+S
        MenuState(1) = 3 : MenuState(2) = 1 : MenuState(3) = 2
      CASE CHR(255, 47), "v", "V"  ' ALT+V
        MenuState(1) = 4 : MenuState(2) = 1 : MenuState(3) = 2
      CASE CHR(255, 24), "o", "O"  ' ALT+O
        MenuState(1) = 5 : MenuState(2) = 1 : MenuState(3) = 2
      CASE CHR(255, 35), "h", "H"  ' ALT+H
        MenuState(1) = 6 : MenuState(2) = 1 : MenuState(3) = 2
      CASE CHR(255, 46), "c", "C"  ' ALT+C
        IF FBmode = TRUE THEN MenuState(1) = 7 : MenuState(2) = 1 : MenuState(3) = 2
      CASE CHR(255, 120)           ' ALT+1
        IF LoadedSlots() >= 1 THEN ActiveSlot = 1
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 1
      CASE CHR(255, 121)           ' ALT+2
        IF LoadedSlots() >= 2 THEN ActiveSlot = 2
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 1
      CASE CHR(255, 122)           ' ALT+3
        IF LoadedSlots() >= 3 THEN ActiveSlot = 3
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 1
      CASE CHR(255, 123)           ' ALT+4
        IF LoadedSlots() >= 4 THEN ActiveSlot = 4
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 19
      CASE CHR(255, 124)           ' ALT+5
        IF LoadedSlots() >= 5 THEN ActiveSlot = 5
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 1
      CASE CHR(255, 125)           ' ALT+6
        IF LoadedSlots() >= 6 THEN ActiveSlot = 6
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 1
      CASE CHR(255, 126)           ' ALT+7
        IF LoadedSlots() >= 7 THEN ActiveSlot = 7
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 1
      CASE CHR(255, 127)           ' ALT+8
        IF LoadedSlots() >= 8 THEN ActiveSlot = 8
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 1
      CASE CHR(255, 128)           ' ALT+9
        IF LoadedSlots() >= 9 THEN ActiveSlot = 9
        MenuState(1) = 99: MenuState(2) = 0: MenuState(3) = 1
    END SELECT
  END IF
 REM Checking if MenuState(2) is in the bounds of MenuState(1)
  SELECT CASE MenuState(1)
    CASE 1
      IF MenuState(2) < 0 THEN MenuState(2) = 7
      IF MenuState(2) > 7 THEN MenuState(2) = 1
    CASE 2
      IF MenuState(2) < 0 THEN MenuState(2) = 4
      IF MenuState(2) > 4 THEN MenuState(2) = 1
    CASE 3
      IF MenuState(2) < 0 THEN MenuState(2) = 3
      IF MenuState(2) > 3 THEN MenuState(2) = 1
    CASE 4
      IF MenuState(2) < 0 THEN MenuState(2) = LoadedSlots()
      IF MenuState(2) > LoadedSlots() THEN MenuState(2) = 1
    CASE 5
      IF MenuState(2) < 0 THEN MenuState(2) = 2
      IF MenuState(2) > 2 THEN MenuState(2) = 1
    CASE 6
      IF MenuState(2) < 0 THEN MenuState(2) = 2
      IF MenuState(2) > 2 THEN MenuState(2) = 1
    CASE 7
      IF MenuState(2) < 0 THEN MenuState(2) = 4
      IF MenuState(2) > 4 THEN MenuState(2) = 1
  END SELECT
END SUB

SUB QuitEditor()
  SCREEN , 0, 0   ' Work on page #0, display page #0
  COLOR 7, 0
  CLS
  END
END SUB

SUB NotYetImplemented()
  COLOR 0, 6
  LOCATE 11, 28: PRINT "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿";
  LOCATE 12, 28: PRINT "³ Not implemented yet. ³";
  LOCATE 13, 28: PRINT "³        Sorry!        ³";
  LOCATE 14, 28: PRINT "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ";
  DrawShadow(29,12,52,15)
  LOCATE 13, 43
  PCOPY 1, 0
  SLEEP 1000
  FlushKeyb()
END SUB

SUB DrawShadow(x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER)
  DIM AS INTEGER x, y
  DIM AS UBYTE CharBuff1, CharBuff2
  FOR x = x1 TO x2 - 1
    LOCATE y2, x
    COLOR 8, 0
'    CharBuff1 = SCREEN(y2, x)  ' Doesn't work: FBC takes char from active video page only
    CharBuff1 = ASC(MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY - Slot(ActiveSlot).CursorY + y2 - 2), Slot(ActiveSlot).FileCursorX - Slot(ActiveSlot).CursorX + x - 1, 1))
    PRINT CHR(CharBuff1);
  NEXT x
  FOR y = y1 TO y2
    COLOR 8, 0
    LOCATE y, x2
'    CharBuff1 = SCREEN(y, x2)  ' Doesn't work: FBC takes char from active video page only
    CharBuff1 = ASC(MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY - Slot(ActiveSlot).CursorY + y - 2), Slot(ActiveSlot).FileCursorX - Slot(ActiveSlot).CursorX + x2 - 1, 1))
    LOCATE y, x2 + 1
'    CharBuff2 = SCREEN(y, x2 + 1)  ' Doesn't work: FBC takes char from active video page only
    CharBuff2 = ASC(MID(FileData(ActiveSlot, Slot(ActiveSlot).FileCursorY - Slot(ActiveSlot).CursorY + y - 2), Slot(ActiveSlot).FileCursorX - Slot(ActiveSlot).CursorX + x2, 1))
    LOCATE y, x2
    PRINT CHR(CharBuff1); CHR(CharBuff2);
  NEXT y
END SUB

SUB SaveFile()
  DIM AS ULONGINT x
  DIM AS BYTE SavePercent, OldSavePercent
  IF Slot(ActiveSlot).FileName <> "[UNTITLED]" THEN
    OldSavePercent = -1
    OPEN Slot(ActiveSlot).FileName FOR OUTPUT AS #1
    DrawShadow(32, 11, 49, 13)
    COLOR 0, 7
    LOCATE 10, 31: PRINT "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿";
    LOCATE 11, 31: PRINT "³ Saving...      ³";
    LOCATE 12, 31: PRINT "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ";
    FOR x = 1 TO Slot(ActiveSlot).TotalLines
      PRINT #1, FileData(ActiveSlot, x);
      IF x < Slot(ActiveSlot).TotalLines THEN
        SELECT CASE Slot(ActiveSlot).EndOfLineType
          CASE 1
            PRINT #1, CHR(10);
          CASE 2
            PRINT #1, CHR(13);
          CASE 0
            PRINT #1, CHR(13,10);
        END SELECT
      END IF
      IF Slot(ActiveSlot).TotalLines > 0 THEN SavePercent = INT(x / Slot(ActiveSlot).TotalLines * 100)
      IF SavePercent > OldSavePercent THEN LOCATE 11, 43: PRINT SavePercent; "%"; : PCOPY 1, 0
      OldSavePercent = SavePercent
    NEXT x
    CLOSE #1
  ELSE ' If the file hasn't been named yet, then switch to "Save as..."
    OpenDialog = "SaveAs"
  END IF
  FlushKeyb()
END SUB

SUB NewFile()
  DIM AS BYTE x
  x = 0
  DO: x += 1
  LOOP UNTIL Slot(x).FileName = "" OR x = 9
  IF x = 9 AND LEN(Slot(x).FileName) > 0 THEN
      OpenDialog = "TooManySlots"
    ELSE
      REM Initialize the slot and make it active:
      ActiveSlot = x
      Slot(x).FileName = "[UNTITLED]"
      FileData(x, 1) = ""  ' Clears the first line
      Slot(x).FileCursorX = 1
      Slot(x).FileCursorY = 1
      Slot(x).CursorX = 1
      Slot(x).CursorY = 1
      Slot(x).TotalLines = 1
      Slot(x).EndOfLineType = 0
      Slot(x).HasBeenChanged = FALSE
  END IF
END SUB

FUNCTION LoadedSlots() AS BYTE
  DIM AS BYTE x, Wynik
  Wynik = 0
  FOR x = 1 TO 9
    IF LEN(Slot(x).FileName) > 0 THEN Wynik += 1
  NEXT x
  RETURN Wynik
END FUNCTION

SUB CloseFile()
  Slot(ActiveSlot).FileName = ""
  IF ActiveSlot > 1 THEN ActiveSlot -= 1
END SUB

SUB TooManySlots()
  COLOR 0, 6
  LOCATE 10, 25: PRINT "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿";
  LOCATE 11, 25: PRINT "³ You can load no more than  ³";
  LOCATE 12, 25: PRINT "³ 9 files at a time.         ³";
  LOCATE 13, 25: PRINT "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ";
  DrawShadow(26, 11, 55, 14)
  LOCATE 12, 45
  PCOPY 1, 0
  SLEEP 2000
  FlushKeyb()
END SUB

SUB SlotCopy(SlotSrc AS BYTE, SlotDst AS BYTE)
  DIM x AS ULONGINT
  FOR x = 1 TO Slot(SlotSrc).TotalLines
    FileData(SlotDst, x) = FileData(SlotSrc, x)
  NEXT x
END SUB

SUB AdjustBuffer()
  DIM AS ULONGINT NewMaxLinesBuff, x
  DIM AS BYTE y, LargestSlot = 1
  NewMaxLinesBuff = INT(Slot(ActiveSlot).TotalLines * 1.5) + 1000
  FOR y = 2 TO 9
    IF Slot(y).TotalLines > Slot(LargestSlot).TotalLines THEN LargestSlot = y
  NEXT y
  IF NewMaxLinesBuff > MaxLinesBuff OR MaxLinesBuff > 3 * Slot(LargestSlot).TotalLines THEN
    'REDIM PRESERVE FileData(0 TO 9, 1 TO NewMaxLinesBuff)  ' Doesn't work
    DIM FileTemp(1 TO 9, 1 TO MaxLinesBuff) AS STRING
    FOR y = 1 TO 9
      FOR x = 1 TO Slot(y).TotalLines
        FileTemp(y, x) = FileData(y, x)
      NEXT x
    NEXT y
    REDIM FileData(0 TO 9, 1 TO NewMaxLinesBuff)
    FOR y = 1 TO 9
      FOR x = 1 TO Slot(y).TotalLines
        FileData(y, x) = FileTemp(y, x)
      NEXT x
    NEXT y
    MaxLinesBuff = NewMaxLinesBuff
  END IF
END SUB

SUB OpenFile()
  DIM AS BYTE x
  DIM AS STRING FileToLoad, LastKey
  x = LoadedSlots()
  IF x < 9 THEN
    COLOR 0, 15
    LOCATE 10, 15: PRINT "                       Open                       ";
    DrawShadow(16, 11, 65, 14)
    COLOR 0, 7
    LOCATE 11, 15: PRINT "                                                  ";
    LOCATE 12, 15: PRINT " File Name:                                       ";
    LOCATE 13, 15: PRINT "                                                  ";
    DO
      IF ASC(LastKey) >= 32 AND ASC(LastKey) < 127 AND LEN(FileToLoad) < 35 THEN FileToLoad += LastKey
      IF ASC(LastKey) = 8 THEN FileToLoad = MID(FileToLoad, 1, LEN(FileToLoad) - 1)
      LOCATE 12, 27: PRINT MID("[" + FileToLoad + STRING(37, 249), 1, 36) + "]";
      LOCATE 12, 28 + LEN(FileToLoad)
      PCOPY 1, 0
      SLEEP 200
      LastKey = INKEY
      FlushKeyb
    LOOP UNTIL LastKey = CHR(13) OR LastKey = CHR(27)
    IF FileExists(FileToLoad) AND LastKey <> CHR(27) THEN
      Slot(x + 1).FileName = FileToLoad : Slot(x + 1).LoadIt = TRUE
      ActiveSlot = x + 1
    END IF
  ELSE
    TooManySlots()
  END IF
END SUB

SUB KeybHelp()
  REM ** WriteMe!!! **
  NotYetImplemented()
END SUB

SUB SaveAs()
  DIM AS BYTE x
  DIM AS STRING FileToSave, LastKey
  COLOR 0, 15
  LOCATE 10, 15: PRINT "                      Save As                     ";
  DrawShadow(16, 11, 65, 14)
  COLOR 0, 7
  LOCATE 11, 15: PRINT "                                                  ";
  LOCATE 12, 15: PRINT " File Name:                                       ";
  LOCATE 13, 15: PRINT "                                                  ";
  DO
    IF ASC(LastKey) >= 32 AND ASC(LastKey) < 127 AND LEN(FileToSave) < 35 THEN FileToSave += LastKey
    IF ASC(LastKey) = 8 THEN FileToSave = MID(FileToSave, 1, LEN(FileToSave) - 1)
    LOCATE 12, 27: PRINT MID("[" + FileToSave + STRING(37, 249), 1, 36) + "]";
    LOCATE 12, 28 + LEN(FileToSave)
    PCOPY 1, 0
    SLEEP 200
    LastKey = INKEY
    FlushKeyb
  LOOP UNTIL LastKey = CHR(13) OR LastKey = CHR(27)
  IF FileExists(FileToSave) = 0 THEN
      IF LastKey <> CHR(27) THEN
        Slot(ActiveSlot).FileName = FileToSave
        OpenDialog = "SaveFile"
      END IF
    ELSE
      LOCATE 13, 28: PRINT "THE FILE ALREADY EXISTS!";
      PCOPY 1, 0
      SLEEP 1500
      OpenDialog = "SaveAs"
  END IF
  FlushKeyb()
END SUB


SUB CompileAndRun()
  DIM AS STRING TempString
  IF Slot(ActiveSlot).FileName = "[UNTITLED]" OR Slot(ActiveSlot).HasBeenChanged = TRUE THEN
      COLOR 0, 6
      LOCATE 11, 28: PRINT "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿";
      LOCATE 12, 28: PRINT "³ You have to save the ³";
      LOCATE 13, 28: PRINT "³ file to compile it.  ³";
      LOCATE 14, 28: PRINT "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ";
      DrawShadow(29,12,52,15)
      LOCATE 13, 43
      PCOPY 1, 0
      SLEEP 1000
      FlushKeyb()
    ELSE
      COLOR 7, 0
      CLS
      PCOPY 1, 0       ' Copy page #1 (work page) to #0 (displayed one)
      REM * WRITE ME! - Check if the file is saved
      SHELL "fbc " + Slot(ActiveSlot).FileName
      'PRINT "Press any key to run the program..."
      'PCOPY 1, 0       ' Copy page #1 (work page) to #0 (displayed one)
      'DO: TempString = INKEY: LOOP UNTIL LEN(TempString) > 0
      REM * WRITE ME! - Check if compilation went okay
      SHELL MID(Slot(ActiveSlot).FileName, 1, INSTR(Slot(ActiveSlot).FileName, ".")) + "exe"
      SLEEP 750, 0
      FlushKeyb()
      PCOPY 1, 0       ' Copy page #1 (work page) to #0 (displayed one)
  END IF
END SUB
