From 1e2fb72e90a086d0556f90fcc553e604c2dd3e6e Mon Sep 17 00:00:00 2001 From: mysticbbs Date: Mon, 23 Jul 2012 23:01:52 -0400 Subject: [PATCH] New menu editor, completely revamped menu system, and more. --- mystic/HISTORY.txt | 100 +++ mystic/bbs_ansi_menubox.pas | 46 +- mystic/bbs_cfg_main.pas | 4 +- mystic/bbs_cfg_menuedit.pas | 950 ++++++++++++++++++-------- mystic/bbs_core.pas | 4 +- mystic/bbs_filebase.pas | 5 +- mystic/bbs_io.pas | 93 +-- mystic/bbs_menus.pas | 1249 +++++++++++++++++++---------------- mystic/bbs_user.pas | 4 +- mystic/mide.pas | 3 - mystic/mpl_compile.pas | 3 - mystic/mystic.pas | 2 +- mystic/records.pas | 87 ++- 13 files changed, 1615 insertions(+), 935 deletions(-) diff --git a/mystic/HISTORY.txt b/mystic/HISTORY.txt index 31befb0..3e807db 100644 --- a/mystic/HISTORY.txt +++ b/mystic/HISTORY.txt @@ -4302,3 +4302,103 @@ + MakeTheme now only looks at the "mysticbbs" environment variable if mystic.dat is not found in the same directory. + + + The "Transfer of XXX" file prompt has been enhanced to attempt to + always strip the filepath from the file, in case of a strange situation + where it somehow gets into the prompt info code. + + + Display files that are not found in a theme directory now have the option + to "fallback" to the default display file directory, if Theme fallback + is enabled for the theme. + + + Added new ANSI menu editor to replace the old outdated crap. The new + editor has references to all of the menu commands as well as the special + functional hotkeys. + + + Revamped the entire menu system. Menu files must be converted if you are + any version pre 1.10 alpha 15 using the CvtMenus program. Simply copy + this into your folder with all of the *.mnu files are run it one time ONLY + + After you are done you will want to review your system because the new + system is not a direct convertion of file format. The method that Mystic + links menu commands has changed. The conversion program does its best to + analyze your menu files and create a menu that functions the same, but + there may be issues - particularly in lightbar grid menus I would guess + since they can be quite complex. + + + Each menu option on a menu now as its own "execution list" of menu + commands. This basically replaces the old "kludge" stacking of commands + to execute back to back with a single hotkey. It is a much more clean + approach this way, and it allows for more options! + + + Each menu command now has the option to redraw the menu or not after its + command list executes. + + + Menus can now be internally generated from 1 to 4 columns (up from 3) + + + Menus now contain a description configurable within the menu flags. A + new MCI code |MD goes along with it, and will display the description + of the current menu. + + + Menus now have an optional "user status" field in the menu flags. If this + is set to a non-blank value, the user's action in node listings will be + updated to this value. If the value is left blank, Mystic will use the + default "Browsing menus" prompt from the theme prompts. + + + Menus now have a "theme fallback" option similar to the recent change to + the display file system. If a menu is not found in its theme menu + directory, then if the option is enabled, Mystic will look for it in the + default menu directory configured in the System Configuration. When + combined with the similar display changes, it makes it very easy to build + additional themes off of the default. In your new themes, the stuff that + you haven't themed can simply fall back to your default theme now. + + + Merged the "Hot Key" and "Long Key" options from the old menu system into + one single hotkey field. + + + Added an "Input char" option to menu flags that defines how the user's + input is echoed back to them at a menu prompt. Valid options are + Uppercase, Lowercase, and hidden. + + + Lightbar menus now allow extended input while the lightbar continues to + function! This works for both standard lightbar and lightbar grid menus. + + For example, you can set the hotkey to "OWNAGE" and have full lightbar + menu. Mystic will still recognize and execute the commands linked to + OWNAGE if the user types it. In addition, it has a smart input so that + it only allows valid characters to be entered. Mystic will use the X/Y + location of the cursor after the menu prompt is displayed, so it is best + to set where you might want to see the user's input (if its not hidden) + using the menu footer/prompt. + + + Lightbar grid menus now allow the following events to be handled in + addition to arrow keys: + + Page Up + Page Down + Escape + Tab + + + In a lightbar grid menu, each menu command now has the ability to execute + on a grid-specific event. The following are available options: + + Selected + Up + Down + Left + Right + Tab + Escape + PgUp + PgDn + + + All displayed menu items now have an option for display type. If it set + to Access it will only be displayed if the user has access. If it is set + to Always, it will always been displayed, and if it is set to Never, it + will never be displayed. + + + Lightbar and Lightbar/Grid menus now display menu header and prompts if + they are not set to a blank value. They were ignored in the past, but + this provides some better flexibility. If you have lightbar menus you + may need to validate that the header/footers in your menu flags are blank + if you do not want this feature. diff --git a/mystic/bbs_ansi_menubox.pas b/mystic/bbs_ansi_menubox.pas index 1b38e43..2855a7a 100644 --- a/mystic/bbs_ansi_menubox.pas +++ b/mystic/bbs_ansi_menubox.pas @@ -10,6 +10,7 @@ Uses Procedure WriteXY (X, Y, A: Byte; S: String); Procedure WriteXYPipe (X, Y, A, SZ : Byte; S: String); Function InXY (X, Y, Field, Max, Mode: Byte; Default: String) : String; +Function InBox (Header, Text, Def: String; Len, MaxLen: Byte) : String; Procedure VerticalLine (X, Y1, Y2 : Byte); Function ShowMsgBox (BoxType : Byte; Str : String) : Boolean; @@ -90,7 +91,8 @@ Uses m_Strings, BBS_Core, BBS_IO, - BBS_Common; + BBS_Common, + BBS_Ansi_MenuInput; Procedure WriteXY (X, Y, A: Byte; S: String); Begin @@ -152,6 +154,46 @@ Begin Session.io.BufFlush; End; +Function InBox (Header, Text, Def: String; Len, MaxLen: Byte) : String; +Var + Box : TAnsiMenuBox; + Input : TAnsiMenuInput; + Offset : Byte; + Str : String; + WinSize : Byte; +Begin + If Len > Length(Text) Then + Offset := Len + Else + Offset := Length(Text); + + WinSize := (80 - Offset + 2) DIV 2; + + Box := TAnsiMenuBox.Create; + Input := TAnsiMenuInput.Create; + + Box.Header := ' ' + Header + ' '; + +//Input.Attr := 15 + 4 * 16; +//Input.FillAttr := 7 + 4 * 16; + Input.LoChars := #13#27; + + Box.Open (WinSize, 10, WinSize + Offset + 3, 15); + + WriteXY (WinSize + 2, 12, 112, Text); + + Str := Input.GetStr(WinSize + 2, 13, Len, MaxLen, 1, Def); + + Box.Close; + + If Input.ExitCode = #27 Then Str := ''; + + Input.Free; + Box.Free; + + Result := Str; +End; + Function InXY (X, Y, Field, Max, Mode: Byte; Default: String) : String; Begin Session.io.AnsiGotoXY (X, Y); @@ -251,7 +293,7 @@ Begin BoxAttr2 := 8 + 7 * 16; BoxAttr3 := 15 + 7 * 16; BoxAttr4 := 8 + 7 * 16; - HeadAttr := 0 + 7 * 16; + HeadAttr := 15 + 1 * 16; HeadType := 0; HideImage := NIL; WasOpened := False; diff --git a/mystic/bbs_cfg_main.pas b/mystic/bbs_cfg_main.pas index 9b4cf3f..853060a 100644 --- a/mystic/bbs_cfg_main.pas +++ b/mystic/bbs_cfg_main.pas @@ -44,6 +44,7 @@ Begin 'F' : Configuration_FileBaseEditor; 'G' : Configuration_GroupEditor(True); 'L' : Configuration_SecurityEditor(True); + 'M' : Configuration_MenuEditor; 'P' : Configuration_ProtocolEditor; 'R' : Configuration_GroupEditor(False); 'U' : Configuration_UserEditor; @@ -122,7 +123,6 @@ Var Session.io.AnsiClear; Case Mode of - 'M' : Menu_Editor; 'E' : Event_Editor; 'V' : Vote_Editor; End; @@ -288,12 +288,12 @@ Begin 'B' : Configuration_MessageBaseEditor; 'F' : Configuration_FileBaseEditor; 'G' : Configuration_GroupEditor(True); + 'M' : Configuration_MenuEditor; 'P' : Configuration_ProtocolEditor; 'R' : Configuration_GroupEditor(False); 'S' : Configuration_SecurityEditor(True); 'T' : Configuration_ThemeEditor(False); 'U' : Configuration_UserEditor; - 'M', 'E', 'V' : ExecuteOldConfiguration(Res); 'X' : Break; diff --git a/mystic/bbs_cfg_menuedit.pas b/mystic/bbs_cfg_menuedit.pas index 338e7fd..12380b1 100644 --- a/mystic/bbs_cfg_menuedit.pas +++ b/mystic/bbs_cfg_menuedit.pas @@ -1,304 +1,706 @@ -Unit bbs_cfg_menuedit; +Unit bbs_cfg_MenuEdit; {$I M_OPS.PAS} Interface -Uses - DOS, - m_Strings, - m_FileIO, - bbs_Common, - bbs_Core, - bbs_User, - bbs_Menus; - -Procedure Menu_Editor; +Procedure Configuration_MenuEditor; Implementation +Uses + DOS, + m_Types, + m_Output, + m_Strings, + m_QuickSort, + m_FileIO, + bbs_Ansi_MenuBox, + bbs_Ansi_MenuForm, + BBS_Core, + BBS_Common, + BBS_MenuData, + bbs_cfg_Theme, + bbs_cfg_Common; + +Type + CmdRec = Record + Name : String[2]; + Desc : String[30]; + End; + +Const + Num_Cmds = 96; + MenuCmds : Array[1..Num_Cmds] of CmdRec = ( + // AUTOSIG MENU COMMANDS + ( Name: 'AE'; Desc: 'Autosig editor' ), + ( Name: 'AT'; Desc: 'Toggle autosig on/off' ), + ( Name: 'AV'; Desc: 'View autosig' ), + + // BBS LIST MENU COMMANDS + ( Name: 'BA'; Desc: 'BBS list add' ), + ( Name: 'BL'; Desc: 'BBS list view extended' ), + ( Name: 'BS'; Desc: 'BBS list view' ), + + // DOOR EXECUTION MENU COMMANDS + ( Name: 'D-'; Desc: 'Exec door (no dropfile)' ), + ( Name: 'D3'; Desc: 'Exec door (door32.sys)' ), + ( Name: 'DC'; Desc: 'Exec door (CHAIN.TXT)' ), + ( Name: 'DD'; Desc: 'Exec door (DORINFO1.DEF)' ), + ( Name: 'DG'; Desc: 'Exec door (DOOR.SYS)' ), + + // FILE BASE MENU COMMANDS + ( Name: 'FA'; Desc: 'File area change' ), + ( Name: 'FD'; Desc: 'Download file' ), + ( Name: 'FF'; Desc: 'Download filelist' ), + ( Name: 'FG'; Desc: 'File group change' ), + ( Name: 'FL'; Desc: 'List files' ), + ( Name: 'FN'; Desc: 'New file scan' ), + ( Name: 'FP'; Desc: 'Set new filescan date' ), + ( Name: 'FS'; Desc: 'Search for files' ), + ( Name: 'FU'; Desc: 'Upload files' ), + ( Name: 'FV'; Desc: 'View archive' ), + ( Name: 'FZ'; Desc: 'Toggle newscan bases' ), + ( Name: 'F1'; Desc: '(SYSOP) Mass upload' ), + ( Name: 'F2'; Desc: '(SYSOP) Directory editor' ), + ( Name: 'F3'; Desc: 'Send file by location' ), + + // GENERAL MENU COMMANDS + ( Name: 'G1'; Desc: 'Show BBS history' ), + ( Name: 'GA'; Desc: 'ANSI art gallery' ), + ( Name: 'GD'; Desc: 'Display a file' ), + ( Name: 'GE'; Desc: 'Edit user settings' ), + ( Name: 'GH'; Desc: 'Hangup' ), + ( Name: 'GI'; Desc: 'Hangup immediately' ), + ( Name: 'GL'; Desc: 'Show last callers' ), + ( Name: 'GO'; Desc: 'Go to new menu' ), + ( Name: 'GN'; Desc: 'Show one-liners' ), + ( Name: 'GP'; Desc: 'Page Sysop for chat' ), + ( Name: 'GR'; Desc: 'Return from gosub menu' ), + ( Name: 'GS'; Desc: 'Gosub to new menu' ), + ( Name: 'GT'; Desc: 'Display a line of text' ), + ( Name: 'GU'; Desc: 'Display user list' ), + ( Name: 'GX'; Desc: 'Execute MPL program' ), + ( Name: 'G?'; Desc: 'Open ANSI help browser' ), + + // MESSAGE BASE MENU COMMANDS + ( Name: 'MA'; Desc: 'Message area change' ), + ( Name: 'MC'; Desc: 'Check e-mail' ), + ( Name: 'MD'; Desc: 'Set msg newscan date' ), + ( Name: 'MG'; Desc: 'Message group change' ), + ( Name: 'MM'; Desc: 'Send mass e-mail' ), + ( Name: 'MN'; Desc: 'Message new scan' ), + ( Name: 'MP'; Desc: 'Post a message' ), + ( Name: 'MQ'; Desc: 'Message quick scan' ), + ( Name: 'MR'; Desc: 'Read messages' ), + ( Name: 'MS'; Desc: 'Global message search' ), + ( Name: 'MV'; Desc: 'View sent e-mail' ), + ( Name: 'MW'; Desc: 'Send new e-mail' ), + ( Name: 'MX'; Desc: 'Post text file to base' ), + ( Name: 'MZ'; Desc: 'Toggle new scan bases' ), + + // NODE MENU COMMANDS + ( Name: 'NA'; Desc: 'Set node action' ), + ( Name: 'NC'; Desc: 'Enter teleconference chat' ), + ( Name: 'NP'; Desc: 'Page user for private chat' ), + ( Name: 'NS'; Desc: 'Send node message' ), + ( Name: 'NW'; DEsc: 'Show whos online' ), + + // OFFLINE MAIL MENU COMMANDS + ( Name: 'OS'; Desc: 'Set QWK scanned bases' ), + ( Name: 'OD'; Desc: 'Download QWK packet' ), + ( Name: 'OU'; Desc: 'Upload REP packet' ), + + // DOWNLOAD QUEUE MENU COMMANDS + ( Name: 'QA'; Desc: 'Add file to batch queue' ), + ( Name: 'QC'; Desc: 'Clear batch queue' ), + ( Name: 'QD'; Desc: 'Delete from batch queue' ), + ( Name: 'QL'; Desc: 'List batch queue' ), + + // TIME BANK MENU COMMANDS + ( Name: 'TD'; Desc: 'Deposit to time bank' ), + ( Name: 'TW'; Desc: 'Withdraw from time bank' ), + + // VOTING BOOTH MENU COMMANDS + ( Name: 'VA'; Desc: 'Create voting poll' ), + ( Name: 'VN'; Desc: 'Vote on new polls' ), + ( Name: 'VR'; Desc: 'See poll results' ), + ( Name: 'VV'; Desc: 'Vote on a poll' ), + + // MATRIX LOGIN MENU COMMANDS + ( Name: 'XA'; Desc: 'Matrix apply for access' ), + ( Name: 'XC'; Desc: 'Matrix check for access' ), + ( Name: 'XL'; Desc: 'Matrix login' ), + ( Name: 'XP'; Desc: 'Matrix page sysop' ), + + // OTHER MENU COMMANDS + ( Name: '-D'; Desc: 'Set access flags (set 2)' ), + ( Name: '-F'; Desc: 'Set access flags (set 1)' ), + ( Name: '-K'; Desc: 'Add keys to input buffer' ), + ( Name: '-N'; Desc: 'Ask Yes/No (default No)' ), + ( Name: '-P'; Desc: 'Prompt for a password' ), + ( Name: '-S'; Desc: 'Add text to Sysop log' ), + ( Name: '-Y'; Desc: 'Ask Yes/No (default Yes)' ), + + // SYSOP/EDITORS MENU COMMANDS + ( Name: '*#'; Desc: '(SYSOP) Menu editor' ), + ( Name: '*A'; Desc: '(SYSOP) Archive editor' ), + ( Name: '*E'; Desc: '(SYSOP) Event editor' ), + ( Name: '*F'; Desc: '(SYSOP) File base editor' ), + ( Name: '*G'; Desc: '(SYSOP) Message group editor' ), + ( Name: '*L'; Desc: '(SYSOP) Security level editor'), + ( Name: '*B'; Desc: '(SYSOP) Message base editor' ), + ( Name: '*P'; Desc: '(SYSOP) Protocol editor' ), + ( Name: '*R'; Desc: '(SYSOP) File group editor' ), + ( Name: '*S'; Desc: '(SYSOP) System configuration' ), + ( Name: '*U'; Desc: '(SYSOP) User editor' ), + ( Name: '*V'; Desc: '(SYSOP) Voting booth editor' ) + ); {123456789012345678901234567890} + Var - MenuFile : Text; + Menu : TMenuData; + MenuName : String; + Changed : Boolean; -Procedure Menu_Editor; - -Procedure ModifyMenu; -var a,b{,c} : byte; -{ tempcmd : menucmdrec;} +Procedure ViewMenu; +Var + OldData : TMenuData; + TmpImage : TConsoleImageRec; Begin - Session.io.OutRaw ('Menu to Edit: '); - Session.Menu.MenuName := Session.io.GetInput(mysMaxMenuNameLen, mysMaxMenuNameLen, 11, ''); + Screen.GetScreenImage (1, 1, 79, 24, TmpImage); - If Session.Menu.LoadMenu(False, False, False) <> 1 Then Exit; + Session.io.OutFull('|07|16|CL'); + Session.io.BufFlush; - Repeat - Session.io.OutFullLn ('|CL|14Menu Command List|CR|03'); - Session.io.OutFullLn ('|15## Hot-Key Cmd Text ## Hot-Key Cmd Text'); - Session.io.OutFullLn ('|09-- -------- --- --------------------- -- -------- --- ---------------------|03'); + OldData := Session.Menu.Data; - For A := 1 to Session.Menu.CmdNum Do Begin - Session.io.OutRaw (strPadR(strI2S(A), 3, ' ') + strPadR(Session.Menu.MenuList[A].HotKey, 9, ' ') + - strPadR(Session.Menu.MenuList[A].Command, 4, ' ') + strPadR(Session.Menu.MenuList[A].Text, 21, ' ') + ' '); - If (A = Session.Menu.CmdNum) or (A Mod 2 = 0) Then Session.io.OutRawLn(''); - End; + Session.Menu.Data := Menu; - Session.io.OutFull ('|CR|09(E)dit, (I)nsert, (D)elete, (F)lags, (V)iew, (Q)uit: '); - Case Session.io.OneKey('EIDFVQ', True) of - 'D' : begin - Session.io.OutRaw('Delete which? '); - a := strS2I(Session.io.GetInput(2, 2, 11, '')); - if (a > 0) and (a <= Session.Menu.CmdNum) then begin - for b := a to Session.Menu.CmdNum do - Session.Menu.Menulist[b] := Session.Menu.Menulist[b+1]; - dec (Session.Menu.cmdnum); - end; - end; - 'I' : if Session.Menu.CmdNum < mysMaxMenuCmds Then Begin - Session.io.OutRaw ('Insert before which (1-' + strI2S(Session.Menu.CmdNum + 1) + '): '); - A := strS2I(Session.io.GetInput(2, 2, 11, '')); - If (A > 0) And (A <= Session.Menu.CmdNum + 1) Then Begin - Inc (Session.Menu.CmdNum); - For B := Session.Menu.CmdNum DownTo A + 1 Do - Session.Menu.MenuList[B] := Session.Menu.MenuList[B - 1]; - Session.Menu.MenuList[A].Text := '[XXX] New Command'; - Session.Menu.MenuList[A].HotKey := 'XXX'; - Session.Menu.MenuList[A].LongKey := 'XXX'; - Session.Menu.MenuList[A].Access := ''; - Session.Menu.MenuList[A].Command := ''; - Session.Menu.MenuList[A].X := 0; - Session.Menu.MenuList[A].Y := 0; - Session.Menu.MenuList[A].Textlo := ''; - Session.Menu.MenuList[A].Texthi := ''; - End; - End; - 'F' : Begin - repeat - Session.io.OutFullLn ('|CL|14Menu Flags (' + Session.Menu.MenuName + ')|CR|03'); - Session.io.OutRawLn ('A. Menu Header : ' + strPadR(Session.Menu.Menu.header, 59, ' ')); - Session.io.OutRawLn ('B. Menu Prompt : ' + strPadR(Session.Menu.menu.prompt, 59, ' ')); - Session.io.OutRawLn ('C. Display Cols : ' + strI2S(Session.Menu.Menu.DispCols)); - Session.io.OutRawLn ('D. ACS : ' + Session.Menu.menu.access); - Session.io.OutRawLn ('E. Password : ' + Session.Menu.menu.password); - Session.io.OutRawLn ('F. Display File : ' + Session.Menu.Menu.dispFile); - Session.io.OutRawLn ('G. Fallback Menu : ' + Session.Menu.Menu.Fallback); - Session.io.OutRaw ('H. Menu Type : '); + Session.Menu.ExecuteMenu(False, False, True, False); - Case Session.Menu.Menu.MenuType of - 0 : Session.io.OutRawLn ('Standard'); - 1 : Session.io.OutRawLn ('Lightbar'); - 2 : Session.io.OutRawLn ('Lightbar Grid'); - End; + Session.Menu.Data := OldData; - Session.io.OutRawLn ('I. Finish X/Y : ' + strPadR(strI2S(Session.Menu.menu.donex), 3, ' ') + strI2S(Session.Menu.menu.doney)); - Session.io.OutRawLn ('J. Use Global MNU: ' + Session.io.OutYN(Session.Menu.Menu.Global=1)); - Session.io.OutRaw ('K. Input Type : '); - - Case Session.Menu.Menu.InputType of - 0 : Session.io.OutRawLn ('User setting'); - 1 : Session.io.OutRawLn ('Hotkey'); - 2 : Session.io.OutRawLn ('Longkey'); - End; - - Session.io.OutFull ('|CR|09(V)iew or (Q)uit: '); - Case Session.io.OneKey('ABCDEFGHIJKQV', True) of - 'A' : Session.Menu.Menu.Header := Session.io.InXY(20, 3, 60, 255, 11, Session.Menu.Menu.Header); - 'B' : Session.Menu.Menu.Prompt := Session.io.InXY(20, 4, 60, 255, 11, Session.Menu.Menu.Prompt); - 'C' : Begin - Session.Menu.Menu.DispCols := strS2I(Session.io.InXY(20, 5, 1, 1, 12, strI2S(Session.Menu.Menu.DispCols))); - If Session.Menu.Menu.DispCols < 1 Then Session.Menu.Menu.DispCols := 1; - If Session.Menu.Menu.DispCols > 3 Then Session.Menu.Menu.DispCols := 3; - End; - 'D' : Session.Menu.Menu.AcCeSs := Session.io.InXY(20, 6, 30, 30, 11, Session.Menu.Menu.AcCeSs); - 'E' : Session.Menu.Menu.Password := Session.io.InXY(20, 7, 15, 15, 12, Session.Menu.Menu.Password); - 'F' : Session.Menu.Menu.dispFile := Session.io.InXY(20, 8, 20, 20, 11, Session.Menu.Menu.dispFile); - 'G' : Session.Menu.Menu.Fallback := Session.io.InXY(20, 9, mysMaxMenuNameLen, mysMaxMenuNameLen, 11, Session.Menu.Menu.Fallback); - 'H' : If Session.Menu.Menu.MenuType = 2 Then Session.Menu.Menu.MenuType := 0 Else Inc(Session.Menu.Menu.MenuType); - 'I' : Begin - Session.Menu.Menu.donex := strS2I(Session.io.InXY(20, 11, 2, 2, 12, strI2S(Session.Menu.Menu.donex))); - Session.Menu.Menu.doney := strS2I(Session.io.InXY(23, 11, 2, 2, 12, strI2S(Session.Menu.Menu.doney))); - End; - 'J' : If Session.Menu.Menu.Global = 1 Then dec(Session.Menu.Menu.global) else Session.Menu.Menu.global := 1; - 'K' : If Session.Menu.Menu.InputType = 2 Then Session.Menu.Menu.InputType := 0 Else Inc(Session.Menu.Menu.InputType); - 'Q' : Break; - 'V' : Session.Menu.ExecuteMenu (False, False, True); - End; - Until False; - End; - 'E' : Begin - Session.io.OutRaw ('Edit which? '); - a := strS2I(Session.io.GetInput(2, 2, 11, '')); - If (a > 0) and (a <= Session.Menu.CmdNum) then Begin - Repeat - Session.io.OutFullLn ('|CL|14Menu command ' + strI2S(a) + ' of ' + strI2S(Session.Menu.CmdNum) + '|CR|03'); - Session.io.OutRawln ('A. Text : ' + Session.Menu.MenuList[A].text); - Session.io.OutRawln ('B. Hot Key : ' + Session.Menu.MenuList[A].HotKey); - Session.io.OutRawLn ('C. Long Key: ' + Session.Menu.MenuList[A].LongKey); - Session.io.OutRawln ('D. ACS : ' + Session.Menu.MenuList[A].access); - Session.io.OutRawln ('E. Command : ' + Session.Menu.MenuList[A].command); - Session.io.OutRawln ('F. Data : ' + Session.Menu.MenuList[A].data); - Session.io.OutFullLn ('|CRG. Lightbar X/Y : ' + strPadR(strI2S(Session.Menu.MenuList[a].x), 3, ' ') + strI2S(Session.Menu.MenuList[a].y)); - Session.io.OutRawln ('H. Lightbar Text : ' + Session.Menu.MenuList[a].textlo); - Session.io.OutRawln ('I. Lightbar High : ' + Session.Menu.MenuList[a].texthi); - Session.io.OutRawln (''); - Session.io.OutRawln ('J. Lightbar Up : ' + strI2S(Session.Menu.MenuList[a].cUP)); - Session.io.OutRawln ('K. Lightbar Down : ' + strI2S(Session.Menu.MenuList[a].cDOWN)); - Session.io.OutRawln ('L. Lightbar Left : ' + strI2S(Session.Menu.MenuList[a].cLEFT)); - Session.io.OutRawln ('M. Lightbar Right: ' + strI2S(Session.Menu.MenuList[a].cRIGHT)); - - Session.io.OutFull ('|CR|09([) Previous, (]) Next, (Q)uit: '); - case session.io.onekey('[]ABCDEFGHIJKLMQ', True) of - '[' : If A > 1 Then Dec(A); - ']' : If A < Session.Menu.CmdNum Then Inc(A); - 'A' : Session.Menu.MenuList[A].Text := Session.io.InXY(14, 3, 60, 79, 11, Session.Menu.MenuList[A].Text); - 'B' : Session.Menu.MenuList[A].HotKey := Session.io.InXY(14, 4, 8, 8, 12, Session.Menu.MenuList[A].HotKey); - 'C' : Session.Menu.MenuList[A].LongKey := Session.io.InXY(14, 5, 8, 8, 12, Session.Menu.MenuList[A].LongKey); - 'D' : Session.Menu.MenuList[A].Access := Session.io.InXY(14, 6, 30, 30, 11, Session.Menu.MenuList[A].Access); - 'E' : Repeat - Session.io.OutFull ('|09Menu Command (?/List): '); - Session.Menu.MenuList[A].command := Session.io.GetInput(2, 2, 12, ''); - If Session.Menu.MenuList[A].Command = '?' Then - session.io.OutFile ('menucmds', True, 0) - Else - Break; - Until False; - 'F' : Session.Menu.MenuList[A].Data := Session.io.InXY(14, 8, 60, 79, 11, Session.Menu.MenuList[a].data); - 'G' : Begin - Session.Menu.MenuList[A].X := strS2I(Session.io.InXY(20, 10, 2, 2, 12, strI2S(Session.Menu.MenuList[A].X))); - Session.Menu.MenuList[A].Y := strS2I(Session.io.InXY(23, 10, 2, 2, 12, strI2S(Session.Menu.MenuList[A].Y))); - End; - 'H' : Session.Menu.MenuList[A].Textlo := Session.io.InXY(20, 11, 59, 79, 11, Session.Menu.MenuList[A].Textlo); - 'I' : Session.Menu.MenuList[A].Texthi := Session.io.InXY(20, 12, 59, 79, 11, Session.Menu.MenuList[A].Texthi); - 'J' : Session.Menu.MenuList[A].cUP := strS2I(Session.io.InXY(20, 14, 2, 2, 12, strI2S(Session.Menu.MenuList[A].cUP))); - 'K' : Session.Menu.MenuList[A].cDOWN := strS2I(Session.io.InXY(20, 15, 2, 2, 12, strI2S(Session.Menu.MenuList[A].cDOWN))); - 'L' : Session.Menu.MenuList[A].cLEFT := strS2I(Session.io.InXY(20, 16, 2, 2, 12, strI2S(Session.Menu.MenuList[A].cLEFT))); - 'M' : Session.Menu.MenuList[A].cRIGHT := strS2I(Session.io.InXY(20, 17, 2, 2, 12, strI2S(Session.Menu.MenuList[A].cRIGHT))); - 'Q' : Break; - end; - until false; - End; - End; -(* - 'P' : begin - Session.io.OutRaw('Move which? '); - a := strS2I(Session.io.GetInput(2, 2, 11, '')); - Session.io.OutRaw('Move before which (1-' + strI2S(Session.Menu.CmdNum+1) + '): '); - b := strS2I(Session.io.GetInput(2, 2, 11, '')); - end; -*) - 'Q' : break; - 'V' : Session.Menu.ExecuteMenu(False, False, True); - - end; - Until false; - - Session.io.OutFullLn ('|14Saving...'); - assign (menufile, Session.Theme.menupath + Session.Menu.menuname + '.mnu'); - rewrite (menufile); - writeln (menufile, Session.Menu.Menu.header); - writeln (menufile, Session.Menu.Menu.prompt); - writeln (menufile, Session.Menu.Menu.dispcols); - writeln (menufile, Session.Menu.Menu.access); - writeln (menufile, Session.Menu.Menu.password); - writeln (menufile, Session.Menu.Menu.dispfile); - WriteLn (MenuFile, Session.Menu.Menu.Fallback); - writeln (menufile, Session.Menu.Menu.MenuType); - WriteLn (MenuFile, Session.Menu.Menu.InputType); - WriteLn (MenuFile, Session.Menu.Menu.DoneX); - WriteLn (MenuFile, Session.Menu.Menu.DoneY); - WriteLn (MenuFile, Session.Menu.Menu.Global); - for a := 1 to Session.Menu.CmdNum do begin - writeln (menufile, Session.Menu.MenuList[a].text); - writeln (menufile, Session.Menu.MenuList[a].HotKey); - WriteLn (MenuFile, Session.Menu.MenuList[A].LongKey); - writeln (menufile, Session.Menu.MenuList[a].access); - writeln (menufile, Session.Menu.MenuList[a].command); - writeln (menufile, Session.Menu.MenuList[a].data); - writeln (menufile, Session.Menu.MenuList[a].x); - writeln (menufile, Session.Menu.MenuList[a].y); - writeln (menufile, Session.Menu.MenuList[a].cUP); - WriteLn (MenuFile, Session.Menu.MenuList[A].cDOWN); - WriteLn (MenuFile, Session.Menu.MenuList[A].cLEFT); - WriteLn (MenuFile, Session.Menu.MenuList[A].cRIGHT); - writeln (menufile, Session.Menu.MenuList[a].textlo); - writeln (menufile, Session.Menu.MenuList[a].texthi); - end; - close (menufile); + Session.io.RemoteRestore(TmpImage); End; +Function GetCommandDesc (Str: String) : String; Var - Old : String[8]; - OldLang : RecTheme; - DirInfo: SearchRec; - A : Byte; {format dir output} + Count : Byte; Begin - If session.Theme.filename = '' then exit; + Result := 'Unknown Command'; + For Count := 1 to Num_Cmds Do + If Str = MenuCmds[Count].Name Then Begin + Result := MenuCmds[Count].Desc; + Break; + End; +End; - Old := Session.Menu.MenuName; - OldLang := Session.Theme; - Session.SystemLog ('*MENU EDITOR*'); +Function GetCommand (Str: String) : String; +Var + List : TAnsiMenuList; + Count : Byte; +Begin + List := TAnsiMenuList.Create; - Session.io.OutFull ('|CL'); - Session.User.GetLanguage; + For Count := 1 to Num_Cmds Do Begin + List.Add (MenuCmds[Count].Name + ' ' + MenuCmds[Count].Desc, 0); + If Str = MenuCmds[Count].Name Then + List.Picked := Count; + End; - Repeat - Session.io.OutFullLn ('|CL|14Menu Editor (Language: ' + Session.Theme.Desc + ')|CR'); - Session.io.OutFullLn ('|08Directory of ' + Session.Theme.MenuPath + '*.MNU|CR|03'); + List.Open (21, 4, 59, 19); + List.Close; - a := 0; - FindFirst (Session.Theme.MenuPath + '*.mnu', Archive, DirInfo); - While DosError = 0 Do Begin - inc (a); - Session.io.OutRaw (strPadR(DirInfo.Name, 25, ' ')); - FindNext (DirInfo); - if (a = 3) or (DosError <> 0) then begin - Session.io.OutRawln(''); - a := 0 - end; + If List.ExitCode = #13 Then Begin + Changed := Str = MenuCmds[List.Picked].Name; + Str := MenuCmds[List.Picked].Name; + End; - End; + List.Free; - Session.io.OutFull ('|CR|09(E)dit, (I)nsert, (D)elete, (Q)uit? '); - Case session.io.OneKey('EIDQ', True) of - 'E' : ModifyMenu; - 'I' : Begin; - Session.io.OutRaw ('Menu Name: '); - Session.menu.MenuName := Session.io.GetInput(mysMaxMenuNameLen, mysMaxMenuNameLen, 11, ''); - If Session.Menu.MenuName <> '' Then Begin - Assign (MenuFile, Session.Theme.MenuPath + Session.Menu.MenuName + '.mnu'); - {$I-} Reset(MenuFile); {$I+} - If IoResult = 0 Then - Session.io.OutRawLn ('Menu already exists') - Else Begin - Rewrite (MenuFile); - WriteLn (MenuFile, 'New Menu'); - WriteLn (MenuFile, 'Command: '); - WriteLn (MenuFile, '2'); - WriteLn (MenuFile, ''); - WriteLn (MenuFile, ''); - WriteLn (MenuFile, ''); - WriteLn (MenuFile, 'main'); - WriteLn (MenuFile, '0'); - WriteLn (MenuFile, '0'); - WriteLn (MenuFile, '0'); - WriteLn (MenuFile, '0'); - WriteLn (MenuFile, '1'); - Close (MenuFile); - End; - End; - End; - 'D' : Begin - Session.io.OutRaw ('Menu to delete: '); - Session.Menu.MenuName := Session.io.GetInput(mysMaxMenuNameLen, mysMaxMenuNameLen, 11, ''); - FileErase(Session.Theme.MenuPath + Session.Menu.MenuName + '.mnu'); - End; - 'Q' : Break; - End; - Until False; - Session.Menu.MenuName := Old; - Session.Theme := OldLang; - Close (Session.PromptFile); - Assign (Session.PromptFile, Config.DataPath + Session.Theme.FileName + '.thm'); - Reset (Session.PromptFile); + Result := Str; +End; + +Procedure GetExtendedKey (Var Key: String); +Var + List : TAnsiMenuList; +Begin + List := TAnsiMenuList.Create; + + List.Add ('FIRSTCMD', 0); + List.Add ('EVERY', 0); + List.Add ('AFTER', 0); + List.Add ('LINEFEED', 0); + List.Add ('TIMER', 0); + List.Add ('UP', 0); + List.Add ('DOWN', 0); + List.Add ('LEFT', 0); + List.Add ('RIGHT', 0); + List.Add ('ENTER', 0); + List.Add ('TAB', 0); + List.Add ('ESCAPE', 0); + List.Add ('HOME', 0); + List.Add ('END', 0); + List.Add ('PAGEUP', 0); + List.Add ('PAGEDOWN', 0); + + List.Open (35, 4, 46, 21); + List.Close; + + If List.ExitCode <> #27 Then Begin + Changed := List.List[List.Picked]^.Name <> Key; + Key := List.List[List.Picked]^.Name; + End; + + List.Free; +End; + +Procedure EditCommand (Num, CmdNum: Word); +Var + Box : TAnsiMenuBox; + Form : TAnsiMenuForm; + Topic : String; + CmdStr : String; +Begin + Topic := '|03(|09Menu Cmd Editor|03) |01-|09> |15'; + Box := TAnsiMenuBox.Create; + Form := TAnsiMenuForm.Create; + + Box.Header := ' Menu (' + MenuName + '): Command #' + strI2S(Num) + ' '; + + Box.Open (13, 9, 68, 16); + + VerticalLine (24, 11, 14); + + Form.AddNone ('C', ' Command ', 15, 11, 9, Topic + 'Menu command function'); + Form.AddStr ('A', ' Access ' , 16, 12, 26, 12, 8, 30, 30, @Menu.Item[Num]^.CmdData[CmdNum]^.Access, Topic + 'Access level to run this command'); + Form.AddStr ('D', ' Data ' , 18, 13, 26, 13, 6, 40, 80, @Menu.Item[Num]^.CmdData[CmdNum]^.Data, Topic + 'Menu command optional data'); + Form.AddTog ('G', ' Execute ', 15, 14, 26, 14, 9, 6, 0, 8, 'Selected Up Down Left Right Tab Escape PgUp PgDn', @Menu.Item[Num]^.CmdData[CmdNum]^.JumpID, Topic + '(Grid) Execute command on what Grid event?'); + + Repeat + CmdStr := '(' + Menu.Item[Num]^.CmdData[CmdNum]^.MenuCmd + ') ' + GetCommandDesc(Menu.Item[Num]^.CmdData[CmdNum]^.MenuCmd); + WriteXY (26, 11, 113, strPadR(CmdStr, 40, ' ')); + + Case Form.Execute of + #27 : Break; + 'C' : Begin + Menu.Item[Num]^.CmdData[CmdNum]^.MenuCmd := GetCommand(Menu.Item[Num]^.CmdData[CmdNum]^.MenuCmd); + Changed := True; + End; + End; + + Changed := Changed or Form.Changed; + Until False; + + Changed := Changed or Form.Changed; + + Box.Close; + + Box.Free; + Form.Free; +End; + +Procedure EditItem (Num: Word); +Const + Status1 = '(TAB) to edit menu commands'; + Status2 = '(TAB) Switch (/) Commands'; +Var + Box : TAnsiMenuBox; + List : TAnsiMenuList; + Form : TAnsiMenuForm; + Topic : String; + + Procedure MakeList; + Var + Count : Word; + Begin + List.Clear; + + For Count := 1 to Menu.Item[Num]^.Commands Do + With Menu.Item[Num]^.CmdData[Count]^ Do Begin + List.Add(strPadR('(' + MenuCmd + ') ' + GetCommandDesc(MenuCmd), 26, ' ') + ' ' + + strPadR(Access, 12, ' ') + ' ' + Data, 0); + End; + + List.Add ('', 0); + End; + +Begin + Box := TAnsiMenuBox.Create; + List := TAnsiMenuList.Create; + Form := TAnsiMenuForm.Create; + + Form.LoExitChars := #09#12#27; + List.LoChars := #09#13#27#47; + List.LoAttr := 113; + List.NoInput := True; + List.NoWindow := True; + Box.Header := ' Command #' + strI2S(Num) + ' (' + MenuName + ') '; + Topic := '|03(|09Menu Editor|03) |01-|09> |15'; + + Box.Open (3, 2, 77, 21); + + VerticalLine (20, 4, 10); + VerticalLine (71, 3, 11); + + WriteXY (5, 12, 112, 'Command Access Data'); + WriteXY (5, 13, 112, strRep('Ä', 71)); + WriteXY (5, 20, 112, strPadC(Status1, 72, ' ')); + WriteXY (5, 19, 112, strRep('Ä', 71)); + + MakeList; + + List.Open (4, 13, 77, 19); + List.Picked := 0; + List.Update; + + Form.AddPipe ('D', ' Display Text ' , 6, 4, 22, 4, 14, 40, 160, @Menu.Item[Num]^.Text, Topic + 'Text displayed on generated menus'); + Form.AddPipe ('O', ' LightBar Low ' , 6, 5, 22, 5, 14, 40, 160, @Menu.Item[Num]^.TextLo, Topic + 'Normal text in lightbar menu'); + Form.AddPipe ('I', ' LightBar High ', 5, 6, 22, 6, 15, 40, 160, @Menu.Item[Num]^.TextHi, Topic + 'Highlighted text in lightbar menu'); + Form.AddCaps ('H', ' Hot Key ' , 11, 7, 22, 7, 9, 12, mysMaxMenuInput, @Menu.Item[Num]^.HotKey, Topic + 'Key to run this command (CTRL-L/Extended Key List)'); + Form.AddStr ('A', ' Access ' , 12, 8, 22, 8, 8, 30, 30, @Menu.Item[Num]^.Access, Topic + 'ACS level required to access this command'); + Form.AddTog ('N', ' Display Type ' , 6, 9, 22, 9, 14, 6, 0, 2, 'Access Always Never', @Menu.Item[Num]^.ShowType, Topic + 'How should this command be displayed?'); + Form.AddByte ('X', 'X' , 16, 10, 22, 10, 1, 2, 0, 80, @Menu.Item[Num]^.X, Topic + 'X coordinate of lightbar'); + Form.AddByte ('Y', 'Y' , 18, 10, 25, 10, 1, 2, 0, 50, @Menu.Item[Num]^.Y, Topic + 'Y coordinate of lightbar'); + Form.AddByte ('U', ' Up ' , 67, 3, 73, 3, 4, 3, 0, 255, @Menu.Item[Num]^.JumpUp, Topic + '(Grid) Item # to jump to when UP is pressed'); + Form.AddByte ('D', ' Down ' , 65, 4, 73, 4, 6, 3, 0, 255, @Menu.Item[Num]^.JumpDown, Topic + '(Grid) Item # to jump to when DOWN is pressed'); + Form.AddByte ('L', ' Left ' , 65, 5, 73, 5, 6, 3, 0, 255, @Menu.Item[Num]^.JumpLeft, Topic + '(Grid) Item # to jump to when LEFT is pressed'); + Form.AddByte ('R', ' Right ' , 64, 6, 73, 6, 7, 3, 0, 255, @Menu.Item[Num]^.JumpRight, Topic + '(Grid) Item # to jump to when RIGHT is pressed'); + Form.AddByte ('E', ' Escape ' , 63, 7, 73, 7, 8, 3, 0, 255, @Menu.Item[Num]^.JumpEscape, Topic + '(Grid) Item # to jump to when ESCAPE is pressed'); + Form.AddByte ('T', ' Tab ' , 66, 8, 73, 8, 5, 3, 0, 255, @Menu.Item[Num]^.JumpTab, Topic + '(Grid) Item # to jump to when TAB is pressed'); + Form.AddByte ('P', ' PageUp ' , 63, 9, 73, 9, 8, 3, 0, 255, @Menu.Item[Num]^.JumpPgUp, Topic + '(Grid) Item # to jump to when PGUP is pressed'); + Form.AddByte ('G', ' PageDn ' , 63, 10, 73, 10, 8, 3, 0, 255, @Menu.Item[Num]^.JumpPgDn, Topic + '(Grid) Item # to jump to when PGDN is pressed'); + Form.AddBol ('W', ' Redraw ' , 63, 11, 73, 11, 8, 3, @Menu.Item[Num]^.ReDraw, Topic + 'Redraw menu after running this command?'); + + Repeat + Case Form.Execute of + #09 : Begin + Repeat + MakeList; + + WriteXY (5, 20, 112, strPadC(Status2, 72, ' ')); + + List.NoInput := False; + List.Open (4, 13, 77, 19); + + Case List.ExitCode of + '/' : Case GetCommandOption(10, 'A-Add|D-Delete|') of + 'A' : Begin + Menu.InsertCommand(Num, List.Picked); + Changed := True; + End; + 'D' : If List.Picked <> List.ListMax Then Begin + Menu.DeleteCommand(Num, List.Picked); + Changed := True; + End; + End; + #09 : Begin + List.Picked := 0; + List.Update; + Break; + End; + #13 : If List.Picked <> List.ListMax Then EditCommand(Num, List.Picked); + #27 : Break; + End; + Until False; + + WriteXY (5, 20, 112, strPadC(Status1, 72, ' ')); + + If List.ExitCode = #27 Then Break; + End; + #12 : GetExtendedKey(Menu.Item[Num]^.HotKey); + #27 : Break; + End; + + Changed := Changed or Form.Changed; + Until False; + + Changed := Changed or Form.Changed; + + Box.Close; + + Form.Free; + List.Free; + Box.Free; +End; + +Procedure EditFlags; +Var + Box : TAnsiMenuBox; + Form : TAnsiMenuForm; + Topic : String; +Begin + Box := TAnsiMenuBox.Create; + Form := TAnsiMenuForm.Create; + + Box.Header := ' Menu Flags (' + MenuName + ') '; + Topic := '|03(|09Menu Flags|03) |01-|09> |15';; + + Box.Open (6, 5, 75, 20); + + VerticalLine (22, 7, 19); + + Form.AddStr ('D', ' Description ' , 9, 7, 24, 7, 13, 30, 30, @Menu.Info.Description, Topic + 'Description of menu'); + Form.AddStr ('A', ' Access ' , 14, 8, 24, 8, 8, 30, 30, @Menu.Info.Access, Topic + 'Security requirements to access this menu'); + Form.AddTog ('T', ' Menu Type ' , 11, 9, 24, 9, 11, 13, 0, 2, 'Standard Lightbar Lightbar/Grid', @Menu.Info.MenuType, Topic + 'Type of menu'); + Form.AddTog ('I', ' Input Type ' , 10, 10, 24, 10, 12, 12, 0, 2, 'User_Defined HotKey LongKey', @Menu.Info.InputType, Topic + 'Input type for this menu'); + Form.AddTog ('C', ' Input Chars ' , 9, 11, 24, 11, 13, 9, 0, 2, 'Uppercase Lowercase Hidden', @Menu.Info.CharType, Topic + 'Input format display'); + Form.AddBol ('G', ' Use Global ' , 10, 12, 24, 12, 12, 3, @Menu.Info.Global, Topic + 'Include global menu options in this menu?'); + Form.AddStr ('N', ' Node Status ' , 9, 13, 24, 13, 13, 30, 30, @Menu.Info.NodeStatus, Topic + 'Node/User status set when this menu is loaded'); + Form.AddStr ('F', ' Display File ', 8, 14, 24, 14, 14, 20, 20, @Menu.Info.DispFile, Topic + 'Display file shown instead of generated menu'); + Form.AddTog ('L', ' Display Cols ', 8, 15, 24, 15, 14, 1, 1, 4, '1 2 3 4', @Menu.Info.DispCols, Topic + 'Number of columns in generated menu'); + Form.AddPipe ('H', ' Menu Header ' , 9, 16, 24, 16, 13, 50, 160, @Menu.Info.Header, Topic + 'Menu header displayed in generated menu'); + Form.AddPipe ('P', ' Menu Prompt ' , 9, 17, 24, 17, 13, 50, 160, @Menu.Info.Footer, Topic + 'Menu prompt displayed in generated menu'); + Form.AddByte ('X', ' X ' , 19, 18, 24, 18, 3, 2, 0, 80, @Menu.Info.DoneX, Topic + 'Locate to X coordinate after lightbar menu'); + Form.AddByte ('Y', ' Y ' , 19, 19, 24, 19, 3, 2, 0, 50, @Menu.Info.DoneY, Topic + 'Locate to Y coordinate after lightbar menu'); + + Form.Execute; + + Changed := Changed Or Form.Changed; + + Box.Close; + + Box.Free; + Form.Free; +End; + +Procedure EditMenu; +Var + Box : TAnsiMenuBox; + List : TAnsiMenuList; + Count : Word; + CopyItem : Word; + Str : String; +Begin + Menu := TMenuData.Create; + + Menu.Load (False, Session.Theme.MenuPath + MenuName + '.mnu'); + + Box := TAnsiMenuBox.Create; + List := TAnsiMenuList.Create; + + List.NoWindow := True; + List.LoChars := #13#27#47; + Box.Header := ' Command list (' + MenuName + ') '; + + Box.Open (9, 5, 72, 21); + + WriteXY (11, 6, 112, 'Hot Key Text Exec'); + WriteXY (11, 7, 112, strRep('Ä', 60)); + WriteXY (11, 19, 112, strRep('Ä', 60)); + WriteXY (29, 20, 112, cfgCommandList); + + CopyItem := 0; + + Repeat + List.Clear; + + For Count := 1 to Menu.NumItems Do Begin + Str := strStripMCI(Menu.Item[Count]^.Text); + + If (Str = '') And (Menu.Item[Count]^.TextLo <> '') Then + Str := strStripMCI(Menu.Item[Count]^.TextLo); + + List.Add (strPadR(Menu.Item[Count]^.HotKey, 15, ' ') + + strPadR(Str, 43, ' ') + + strPadL(strI2S(Menu.Item[Count]^.Commands), 2, ' '), 0); + End; + + List.Add ('', 0); + + List.Open (9, 7, 72, 19); + + Case List.ExitCode of + '/' : Case GetCommandOption(10, 'F-Flags|A-Add|D-Delete|C-Copy|P-Paste|V-View|') of + 'A' : Begin + Menu.InsertItem(List.Picked); + Changed := True; + End; + 'C' : If List.Picked <> List.ListMax Then + CopyItem := List.Picked; + 'D' : If List.Picked <> List.ListMax Then Begin + Menu.DeleteItem(List.Picked); + Changed := True; + End; + 'F' : EditFlags; + 'P' : If (CopyItem > 0) And (CopyItem < List.ListMax) And (Menu.Item[CopyItem] <> NIL) Then Begin + Menu.CopyItem(CopyItem, List.Picked); + Changed := True; + End; + 'V' : ViewMenu; + End; + #13 : If List.Picked <> List.ListMax Then + EditItem (List.Picked); + #27 : Break; + End; + Until False; + + Box.Close; + + List.Free; + Box.Free; + + If Changed Then + If ShowMsgBox(1, 'Save changes to ' + MenuName + '?') Then + If Not Menu.Save(Session.Theme.MenuPath + MenuName + '.mnu') Then + ShowMsgBox(0, 'Unable to save menu'); + + Menu.Free; +End; + +Function GetMenuName (OldName: String) : String; +Var + Box : TAnsiMenuBox; + List : TAnsiMenuList; + MF : Text; + + Procedure MakeList; + Var + Dir : SearchRec; + Sort : TQuickSort; + Count : Word; + Desc : String; + Begin + Sort := TQuickSort.Create; + + FindFirst (Session.Theme.MenuPath + '*.mnu', Archive, Dir); + + While DosError = 0 Do Begin + Sort.Add(JustFileName(Dir.Name), 0); + FindNext (Dir); + End; + + FindClose(Dir); + + Sort.Sort(1, Sort.Total, qAscending); + + List.Clear; + + For Count := 1 to Sort.Total Do Begin + Assign (MF, Session.Theme.MenuPath + Sort.Data[Count]^.Name + '.mnu'); + + {$I-} Reset (MF); {$I+} + + If IoResult = 0 Then Begin + ReadLn (MF, Desc); + Close (MF); + End Else + Desc := ''; + + List.Add(strPadR(Sort.Data[Count]^.Name, 22, ' ') + Desc, 0); + + If Sort.Data[Count]^.Name = OldName Then + List.Picked := List.ListMax; + End; + + Sort.Free; + End; + + Procedure CopyMenu (Orig: String); + Var + Str : String; + Begin + Str := InBox('Copy menu', 'New menu name: ', '', 20, 20); + + If Str = '' Then Exit; + + Str := Session.Theme.MenuPath + Str + '.mnu'; + + If FileExist(Str) Then + If ShowMsgBox(1, JustFile(Str) + ' already exists. Overwrite?') Then + FileErase(Str); + + FileCopy(Session.Theme.MenuPath + Orig + '.mnu', Str); + End; + + Procedure InsertMenu; + Var + Str : String; + OK : Boolean; + Begin + Str := InBox('Insert Menu', 'New menu name: ', '', 20, 20); + + If Str = '' Then Exit; + + OK := Not FileExist(Session.Theme.MenuPath + Str + '.mnu'); + + If Not OK Then + OK := ShowMsgBox(1, Str + ' already exists. Overwrite?'); + + If OK Then Begin + Menu := TMenuData.Create; + + Menu.CreateNewMenu(Session.Theme.MenuPath + Str + '.mnu'); + + Menu.Free; + End; + End; + +Begin + Result := ''; + + Box := TAnsiMenuBox.Create; + List := TAnsiMenuList.Create; + + List.NoWindow := True; + List.LoChars := #13#27#47; + Box.Header := ' Menu Editor (' + Session.Theme.Desc + ') '; + + Box.Open (12, 5, 68, 21); + + WriteXY (14, 6, 112, 'Menu Name Description'); + WriteXY (14, 7, 112, strRep('Ä', 53)); + WriteXY (14, 19, 112, strRep('Ä', 53)); + WriteXY (29, 20, 112, cfgCommandList); + + Repeat + Changed := False; + + MakeList; + + List.Open (12, 7, 68, 19); + + Case List.ExitCode of + '/' : Case GetCommandOption(10, 'I-Insert|D-Delete|C-Copy|') of + 'C' : If List.ListMax > 0 Then + CopyMenu(strWordGet(1, List.List[List.Picked]^.Name, ' ')); + 'I' : InsertMenu; + 'D' : If List.ListMax > 0 Then + If ShowMsgBox(1, 'Delete menu: ' + strWordGet(1, List.List[List.Picked]^.Name, ' ')) Then + FileErase (Session.Theme.MenuPath + strWordGet(1, List.List[List.Picked]^.Name, ' ') + '.mnu'); + End; + #13 : Begin + If List.ListMax <> 0 Then + Result := strWordGet(1, List.List[List.Picked]^.Name, ' '); + Break; + End; + #27 : Break; + End; + Until False; + + Box.Close; + + List.Free; + Box.Free; +End; + +Procedure Configuration_MenuEditor; +Var + Saved : String; +Begin + Saved := ''; + MenuName := Configuration_ThemeEditor(True); + + If MenuName = '' Then Exit; + + Repeat + MenuName := GetMenuName(Saved); + Saved := MenuName; + + If MenuName = '' Then Exit; + + EditMenu; + Until False; End; End. diff --git a/mystic/bbs_core.pas b/mystic/bbs_core.pas index e00e5d4..741c6d7 100644 --- a/mystic/bbs_core.pas +++ b/mystic/bbs_core.pas @@ -25,7 +25,7 @@ Type User : TBBSUser; Msgs : TMsgBase; FileBase : TFileBase; - Menu : TMenuSystem; + Menu : TMenuEngine; IO : TBBSIO; Client : TSocketClass; EventFile : File of EventRec; @@ -131,7 +131,7 @@ Begin IO := TBBSIO.Create(Pointer(Self)); Msgs := TMsgBase.Create(Pointer(Self)); FileBase := TFileBase.Create(Pointer(Self)); - Menu := TMenuSystem.Create(Pointer(Self)); + Menu := TMenuEngine.Create(Pointer(Self)); End; Destructor TBBSCore.Destroy; diff --git a/mystic/bbs_filebase.pas b/mystic/bbs_filebase.pas index 12f9597..499663a 100644 --- a/mystic/bbs_filebase.pas +++ b/mystic/bbs_filebase.pas @@ -411,6 +411,8 @@ Begin Session.io.OutRawLn (''); + Session.io.PromptInfo[1] := JustFile(Data); + If dszSearch(JustFile(Data)) Then Begin Result := True; Session.io.OutFullLn (Session.GetPrompt(385)); @@ -2862,7 +2864,8 @@ Begin Session.io.OutRawLn (''); For A := 1 to BatchNum Do Begin - Session.io.PromptInfo[1] := Batch[A].FileName; + Session.io.PromptInfo[1] := JustFile(Batch[A].FileName); + If dszSearch (Batch[A].FileName) Then Begin Session.SystemLog ('Download: ' + Batch[A].FileName); diff --git a/mystic/bbs_io.pas b/mystic/bbs_io.pas index d2f58d8..e8f83ac 100644 --- a/mystic/bbs_io.pas +++ b/mystic/bbs_io.pas @@ -84,7 +84,7 @@ Type Procedure OutBS (Num : Byte; Del: Boolean); Procedure OutFull (Str : String); Procedure OutFullLn (Str : String); - Procedure OutFile (FName : String; DoPause: Boolean; Speed: Byte); + Function OutFile (FName : String; DoPause: Boolean; Speed: Byte) : Boolean; Function OutYN (Y : Boolean) : String; Function OutON (O : Boolean) : String; Procedure PauseScreen; @@ -192,16 +192,9 @@ Var Begin {$IFDEF WINDOWS} If OutBufPos > 0 Then Begin - If Not TBBSCore(Core).LocalMode Then Begin + If Not TBBSCore(Core).LocalMode Then Res := TBBSCore(Core).Client.WriteBuf(OutBuffer, OutBufPos); - (* Moved to m_sockets_class 1.10a14 - While (Res = -1) and (WSAGetLastError = EWOULDBLOCK) Do Begin - WaitMS(10); - Res := TBBSCore(Core).Client.WriteBuf(OutBuffer, OutBufPos); - End;*) - End; - OutBufPos := 0; End; {$ENDIF} @@ -533,6 +526,10 @@ Begin End; 'M' : Case Code[2] of 'B' : LastMCIValue := TBBSCore(Core).Msgs.MBase.Name; + 'D' : If Session.Menu.Data <> NIL Then + LastMCIValue := Session.Menu.Data.Info.Description + Else + LastMCIValue := ''; 'E' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.Emails); 'G' : LastMCIValue := TBBSCore(Core).Msgs.Group.Name; 'L' : LastMCIValue := OutON(TBBSCore(Core).User.ThisUser.UseLBIndex); @@ -694,9 +691,6 @@ Procedure TBBSIO.OutFull (Str : String); Var A : Byte; B : Byte; - D : DirStr; - N : NameStr; - E : ExtStr; Begin A := 1; @@ -720,8 +714,7 @@ Begin While (Str[B] <> ' ') and (Str[B] <> '|') and (B <= Length(Str)) Do Inc (B); - FSplit (strStripLOW(Copy(Str, A + 1, B - A - 1)), D, N, E); - OutFile (TBBSCore(Core).Theme.TextPath + N + E, True, 0); + OutFile (JustFile(strStripLOW(Copy(Str, A + 1, B - A - 1))), True, 0); A := B; @@ -919,7 +912,7 @@ Begin If O Then OutON := 'On' Else OutON := 'Off'; {++lang} End; -Procedure TBBSIO.OutFile (FName : String; DoPause: Boolean; Speed: Byte); +Function TBBSIO.OutFile (FName : String; DoPause: Boolean; Speed: Byte) : Boolean; Var Buffer : Array[1..4096] of Char; BufPos : LongInt; @@ -932,6 +925,25 @@ Var Ch : Char; Done : Boolean; + Function CheckFileInPath (Path: String) : Boolean; + Var + Temp : String; + Begin + Result := False; + Temp := Path + FName; + + If (Graphics = 1) and (FileExist(Temp + '.ans')) Then Begin + Ext := '.ans'; + FName := Temp; + Result := True; + End Else + If FileExist(Temp + '.asc') Then Begin + Ext := '.asc'; + FName := Temp; + Result := True; + End; + End; + Function GetChar : Char; Begin If BufPos = BufSize Then Begin @@ -951,37 +963,42 @@ Var End; Begin - If Pos(PathChar, FName) = 0 Then - FName := TBBSCore(Core).Theme.TextPath + FName; + Result := False; + NoFile := True; - If Pos('.', FName) > 0 Then - Ext := '' - Else - If (Graphics = 1) and (FileExist(FName + '.ans')) Then - Ext := '.ans' - Else - Ext := '.asc'; + If (Pos(PathSep, FName) > 0) or (Pos('.', FName) > 0) Then Begin + If Not FileExist(FName) Then + If Not CheckFileInPath('') Then Exit; + End Else Begin + If Not CheckFileInPath(Session.Theme.TextPath) Then + If Session.Theme.Flags AND thmFallBack <> 0 Then Begin + If Not CheckFileInPath(Config.TextPath) Then Exit; + End Else + Exit; - If FileExist(FName + Copy(Ext, 1, 3) + '1') Then Begin - Repeat - BufPos := Random(9); - If BufPos = 0 Then - Code := Ext[Length(Ext)] - Else - Code := strI2S(BufPos); - Until FileExist(FName + Copy(Ext, 1, 3) + Code); + If FileExist(FName + Copy(Ext, 1, 3) + '1') Then Begin + Repeat + BufPos := Random(9); - Ext := Copy(Ext, 1, 3) + Code; + If BufPos = 0 Then + Code := Ext[Length(Ext)] + Else + Code := strI2S(BufPos); + + Until FileExist(FName + Copy(Ext, 1, 3) + Code); + + Ext := Copy(Ext, 1, 3) + Code; + End; End; Assign (dFile, FName + Ext); + {$I-} Reset(dFile, 1); {$I+} - If IoResult <> 0 Then Begin - NoFile := True; - Exit; - End; + + If IoResult <> 0 Then Exit; NoFile := False; + Result := True; Old := AllowPause; AllowPause := DoPause; PausePtr := 1; @@ -1032,7 +1049,7 @@ Begin Str := Str + GetChar; End; - OutFile (TBBSCore(Core).Theme.TextPath + strStripLOW(Str), True, 0); + OutFile (JustFile(strStripLOW(Str)), True, 0); Continue; End; diff --git a/mystic/bbs_menus.pas b/mystic/bbs_menus.pas index 0e8a655..f89e8b1 100644 --- a/mystic/bbs_menus.pas +++ b/mystic/bbs_menus.pas @@ -1,79 +1,91 @@ -Unit bbs_Menus; +Unit BBS_Menus; {$I M_OPS.PAS} Interface Uses - m_Strings, - bbs_Common, - bbs_Doors; + BBS_Common, + BBS_MenuData, + MPL_Execute; Type - TMenuSystem = Class - LBMenuPos : Byte; - CmdNum : Byte; - Menu : RecMenuFlags; - MenuList : Array[1..mysMaxMenuCmds] of RecMenuCommand; - MenuOld : String[mysMaxMenuNameLen]; - MenuName : String[mysMaxMenuNameLen]; - MenuStack : Array[1..8] of String[mysMaxMenuNameLen]; - StackNum : Byte; + TMenuEngine = Class + Owner : Pointer; + Data : TMenuData; + Stack : Array[1..mysMaxMenuStack] of String[mysMaxMenuNameLen]; + StackPos : Byte; + MenuName : String[20]; + MenuOld : String[20]; + ExtKeys : String; + UseHotKeys : Boolean; + ReDraw : Boolean; + NextReDraw : Boolean; + SetAction : Boolean; + UseLongKey : Boolean; + UseTimer : Boolean; + ViewOnly : Boolean; - Constructor Create (Var Owner: Pointer); + Constructor Create (O: Pointer); Destructor Destroy; Override; - - Function StripSecurity (Str : String) : String; - Function ReplaceSecurity (Str : String) : String; - Procedure ToggleAccessFlags (Data: String; Var Flags: AccessFlagType); - Function LoadMenu (CheckSec: Boolean; RunCmd: Boolean; Global: Boolean) : Byte; - Procedure ExecuteMenu (FallBack, Global, View: Boolean); - Function ExecuteCommand (Cmd: String; Data: String) : Boolean; {True if menu is to be reloaded} + Function StripSecurity (Str: String) : String; + Function ReplaceSecurity (Str: String; SecLevel: Byte) : String; + Procedure ToggleAccessFlags (Cmd: String; Var Flags: AccessFlagType); + Function LoadMenu (Forced: Boolean) : Boolean; + Procedure ExecuteMenu (Load, Forced, View, Action: Boolean); + Function ExecuteCommandList (Num, JumpID: LongInt) : Byte; + Function ExecuteByHotkey (Key: String) : Boolean; + Function ExecuteCommand (Cmd, CmdData: String) : Boolean; + Function ShowMenu : Boolean; + Procedure GenerateMenu; + Procedure DoStandardMenu; + Procedure DoLightBarMenu; End; Implementation Uses - bbs_Core, - bbs_MsgBase, - bbs_FileBase, - bbs_General, - bbs_User, - bbs_NodeChat, - bbs_NodeInfo, - bbs_UserChat, - bbs_ansi_Help, - MPL_Execute, - bbs_cfg_MenuEdit, - bbs_cfg_FileBase, - bbs_cfg_UserEdit, - bbs_cfg_MsgBase, - bbs_cfg_SecLevel, - bbs_cfg_Groups, - bbs_cfg_Events, - bbs_cfg_Vote, - bbs_Cfg_Main; + m_Strings, + BBS_Core, + BBS_NodeInfo, + BBS_General, + BBS_Doors, + BBS_NodeChat, + BBS_UserChat, + BBS_Ansi_Help, + BBS_Cfg_Main, + BBS_Cfg_Events, + BBS_Cfg_UserEdit, + BBS_Cfg_Vote; -Constructor TMenuSystem.Create (Var Owner: Pointer); +Constructor TMenuEngine.Create (O: Pointer); Begin Inherited Create; - StackNum := 0; + StackPos := 0; + MenuName := ''; + MenuOld := ''; + Owner := O; + Data := TMenuData.Create; + Redraw := True; + NextRedraw := True; End; -Destructor TMenuSystem.Destroy; +Destructor TMenuEngine.Destroy; Begin + Data.Free; + Inherited Destroy; End; -Function TMenuSystem.StripSecurity (Str : String) : String; +Function TMenuEngine.StripSecurity (Str : String) : String; Begin Delete (Str, Pos('@S', Str), 2); Result := Str; End; -Function TMenuSystem.ReplaceSecurity (Str : String) : String; +Function TMenuEngine.ReplaceSecurity (Str: String; SecLevel: Byte) : String; Var A : Byte; Begin @@ -81,59 +93,54 @@ Begin If A > 0 Then Begin Delete (Str, A, 2); - Insert (strI2S(Session.User.ThisUser.Security), Str, A); + Insert (strI2S(SecLevel), Str, A); End; Result := Str; End; -Procedure TMenuSystem.ToggleAccessFlags (Data: String; Var Flags: AccessFlagType); +Procedure TMenuEngine.ToggleAccessFlags (Cmd: String; Var Flags: AccessFlagType); Var - A : Byte; + Count : Byte; Begin - A := 1; + Count := 1; - While A <= Length(Data) Do Begin - If (Data[A] in ['+','-','!']) and (Data[A+1] in ['A'..'Z']) Then Begin - Case Data[A] of - '+' : Flags := Flags + [Ord(Data[A+1]) - 64]; - '-' : Flags := Flags - [Ord(Data[A+1]) - 64]; - '!' : If Ord(Data[2]) - 64 in Flags Then - Flags := Flags - [Ord(Data[A+1]) - 64] + While Count <= Length(Cmd) Do Begin + If (Cmd[Count] in ['+','-','!']) and (Cmd[Count + 1] in ['A'..'Z']) Then Begin + Case Cmd[Count] of + '+' : Flags := Flags + [Ord(Cmd[Count + 1]) - 64]; + '-' : Flags := Flags - [Ord(Cmd[Count + 1]) - 64]; + '!' : If Ord(Cmd[2]) - 64 in Flags Then + Flags := Flags - [Ord(Cmd[Count + 1]) - 64] Else - Flags := Flags + [Ord(Data[A+1]) - 64]; + Flags := Flags + [Ord(Cmd[Count + 1]) - 64]; End; - Inc (A); + Inc (Count); End; - Inc(A); - End; - {$IFNDEF UNIX} - UpdateStatusLine(StatusPTR, ''); - {$ENDIF} + Inc (Count); + End; End; -Function TMenuSystem.ExecuteCommand (Cmd: String; Data: String) : Boolean; +Function TMenuEngine.ExecuteCommand (Cmd, CmdData: String) : Boolean; Var - A : Integer; - Help : TAnsiMenuHelp; + Loop1 : LongInt; + Help : TAnsiMenuHelp; Begin Result := False; - {$IFDEF LOGGING} Session.SystemLog('Exec MenuCmd: ' + Cmd + ' ' + Data); {$ENDIF} - - If Length(Cmd) <> 2 Then Exit; + If Cmd[0] <> #2 Then Exit; Case Cmd[1] of '-' : Case Cmd[2] of - 'D' : ToggleAccessFlags(Data, Session.User.ThisUser.AF2); - 'F' : ToggleAccessFlags(Data, Session.User.ThisUser.AF1); - 'N' : Session.User.AcsOkFlag := Session.io.GetYN(Data, False); - 'P' : Session.User.AcsOkFlag := Session.io.GetPW(Copy(Data, 1, Pos(';', Data) - 1), Session.GetPrompt(417), - strUpper(Copy(Data, Pos(';', Data) + 1, Length(Data)))); - 'S' : Session.SystemLog(Data); - 'Y' : Session.User.AcsOkFlag := Session.io.GetYN(Data, True); + 'D' : ToggleAccessFlags(CmdData, Session.User.ThisUser.AF2); + 'F' : ToggleAccessFlags(CmdData, Session.User.ThisUser.AF1); + 'N' : Session.User.AcsOkFlag := Session.io.GetYN(CmdData, False); + 'P' : Session.User.AcsOkFlag := Session.io.GetPW(Copy(CmdData, 1, Pos(';', CmdData) - 1), Session.GetPrompt(417), + strUpper(Copy(CmdData, Pos(';', CmdData) + 1, Length(CmdData)))); + 'S' : Session.SystemLog(CmdData); + 'Y' : Session.User.AcsOkFlag := Session.io.GetYN(CmdData, True); End; 'A' : Case Cmd[2] of 'E' : AutoSig_Edit; @@ -141,14 +148,14 @@ Begin 'V' : AutoSig_View; End; 'D' : Case Cmd[2] of - '-' : ExecuteDoor (0, Data); - 'C' : ExecuteDoor (3, Data); - 'D' : ExecuteDoor (1, Data); - 'G' : ExecuteDoor (2, Data); - '3' : ExecuteDoor (4, Data); + '-' : ExecuteDoor (0, CmdData); + 'C' : ExecuteDoor (3, CmdData); + 'D' : ExecuteDoor (1, CmdData); + 'G' : ExecuteDoor (2, CmdData); + '3' : ExecuteDoor (4, CmdData); End; 'F' : Case Cmd[2] of - 'A' : Session.FileBase.ChangeFileArea(Data); + 'A' : Session.FileBase.ChangeFileArea(CmdData); 'D' : Begin Session.io.OutFile ('download', True, 0); @@ -157,10 +164,10 @@ Begin Else Session.FileBase.DownloadFile; End; - 'F' : Session.FileBase.DownloadFileList (strUpper(Data)); - 'G' : Session.FileBase.FileGroupChange (Data, True, True); - 'L' : Session.FileBase.ListFiles (1, strUpper(Data)); - 'N' : Session.FileBase.NewFileScan(UpCase(Data[1])); + 'F' : Session.FileBase.DownloadFileList (strUpper(CmdData)); + 'G' : Session.FileBase.FileGroupChange (CmdData, True, True); + 'L' : Session.FileBase.ListFiles (1, strUpper(CmdData)); + 'N' : Session.FileBase.NewFileScan(UpCase(CmdData[1])); 'P' : Session.FileBase.SetFileScanDate; 'S' : Session.FileBase.FileSearch; 'U' : Session.FileBase.UploadFile; @@ -168,18 +175,18 @@ Begin 'Z' : Session.FileBase.ToggleFileNewScan; '1' : Session.FileBase.MassUpload; '2' : Session.FileBase.DirectoryEditor(False, ''); - '3' : Session.FileBase.SendFile (Data); + '3' : Session.FileBase.SendFile (CmdData); End; 'B' : Case Cmd[2] of - 'A' : Add_BBS_List (Data); - 'L' : View_BBS_List (True, Data); - 'S' : View_BBS_List (False, Data); + 'A' : Add_BBS_List (CmdData); + 'L' : View_BBS_List (True, CmdData); + 'S' : View_BBS_List (False, CmdData); End; 'G' : Case Cmd[2] of - '1' : ShowBBSHistory(strS2I(Data)); - 'A' : View_Directory(Data, 0); - 'D' : Session.io.OutFile (Data, True, 0); - 'E' : Session.User.Edit_User_Settings(strS2I(Data)); + '1' : ShowBBSHistory(strS2I(CmdData)); + 'A' : View_Directory(CmdData, 0); + 'D' : Session.io.OutFile (CmdData, True, 0); + 'E' : Session.User.Edit_User_Settings(strS2I(CmdData)); 'H', 'I' : Begin If Cmd[2] = 'H' Then Begin @@ -196,76 +203,76 @@ Begin 'L' : ShowLastCallers; 'O' : Begin MenuOld := MenuName; - MenuName := Data; + MenuName := CmdData; Result := True; End; - 'N' : ShowOneLiners (Data); - 'P' : {$IFNDEF UNIX} PageForSysopChat (Pos('/F', strUpper(Data)) > 0) {$ENDIF}; + 'N' : ShowOneLiners (CmdData); + 'P' : {$IFNDEF UNIX} PageForSysopChat (Pos('/F', strUpper(CmdData)) > 0) {$ENDIF}; 'R' : Begin - If StackNum > 0 Then Begin + If StackPos > 0 Then Begin MenuOld := MenuName; - MenuName := MenuStack[StackNum]; + MenuName := Stack[StackPos]; Result := True; - Dec (StackNum); + Dec (StackPos); End; End; 'S' : Begin MenuOld := MenuName; - If StackNum = 8 Then Begin - For A := 1 to 7 Do - MenuStack[A + 1] := MenuStack[A]; + If StackPos = 8 Then Begin + For Loop1 := 1 to 7 Do + Stack[Loop1 + 1] := Stack[Loop1]; - Dec (StackNum); + Dec (StackPos); End; - Inc (StackNum); + Inc (StackPos); - MenuStack[StackNum] := MenuName; - MenuName := Data; + Stack[StackPos] := MenuName; + MenuName := CmdData; Result := True; End; - 'T' : Session.io.OutFull (Data); - 'U' : ShowUserList (strUpper(Data)); - 'X' : Result := ExecuteMPL(NIL, Data) = 2; + 'T' : Session.io.OutFull (CmdData); + 'U' : ShowUserList (strUpper(CmdData)); + 'X' : Result := ExecuteMPL(NIL, CmdData) = 2; '?' : Begin // online ANSI help system (BBSHTML) prototype Help := TAnsiMenuHelp.Create; - Help.OpenHelp (Session.Theme.TextPath + Data + ';ansihelp;INDEX'); + Help.OpenHelp (Session.Theme.TextPath + CmdData + ';ansihelp;INDEX'); Help.Free; End; End; 'M' : Case Cmd[2] of - 'A' : Session.Msgs.ChangeArea(Data); + 'A' : Session.Msgs.ChangeArea(CmdData); 'C' : Session.Msgs.CheckEMail; 'D' : Session.Msgs.SetMessagePointers; - 'G' : Session.Msgs.MessageGroupChange (Data, True, True); + 'G' : Session.Msgs.MessageGroupChange (CmdData, True, True); 'M' : Session.Msgs.SendMassEmail; - 'N' : Session.Msgs.MessageNewScan (strUpper(Data)); - 'P' : Session.Msgs.PostMessage (False, Data); - 'Q' : Session.Msgs.MessageQuickScan(strUpper(Data)); + 'N' : Session.Msgs.MessageNewScan (strUpper(CmdData)); + 'P' : Session.Msgs.PostMessage (False, CmdData); + 'Q' : Session.Msgs.MessageQuickScan(strUpper(CmdData)); 'R' : Begin - If Data = '' Then Data := ' '; + If CmdData = '' Then CmdData := ' '; - Session.Msgs.ReadMessages(UpCase(Data[1]), ''); + Session.Msgs.ReadMessages(UpCase(CmdData[1]), ''); End; - 'S' : Session.Msgs.GlobalMessageSearch(UpCase(Data[1])); + 'S' : Session.Msgs.GlobalMessageSearch(UpCase(CmdData[1])); 'V' : Session.Msgs.ViewSentEmail; - 'W' : Session.Msgs.PostMessage (True, Data); - 'X' : Session.Msgs.PostTextFile(Data, False); + 'W' : Session.Msgs.PostMessage (True, CmdData); + 'X' : Session.Msgs.PostTextFile(CmdData, False); 'Z' : Session.Msgs.ToggleNewScan(False); End; 'N' : Case Cmd[2] of - 'A' : Set_Node_Action (Data); + 'A' : Set_Node_Action (CmdData); 'C' : Node_Chat; 'P' : PageUserForChat; - 'S' : Send_Node_Message (3, Data, 0); + 'S' : Send_Node_Message (3, CmdData, 0); 'W' : Show_Whos_Online; End; 'O' : Case Cmd[2] of 'S' : Session.Msgs.ToggleNewScan(True); - 'D' : Session.Msgs.DownloadQWK(Data); + 'D' : Session.Msgs.DownloadQWK(CmdData); 'U' : Session.Msgs.UploadREP; End; 'Q' : Case Cmd[2] of @@ -281,8 +288,8 @@ Begin 'V' : Case Cmd[2] of 'A' : Add_Booth; 'N' : Voting_Booth_New; - 'R' : Voting_Result (strS2I(Data)); - 'V' : Voting_Booth (False, strS2I(Data)); + 'R' : Voting_Result (strS2I(CmdData)); + 'V' : Voting_Booth (False, strS2I(CmdData)); End; 'X' : Case Cmd[2] of 'A' : Begin @@ -307,14 +314,14 @@ Begin Result := True; End; 'P' : {$IFNDEF UNIX} If Session.User.GetMatrixUser Then - PageForSysopChat (Pos('/F', strUpper(Data)) > 0) {$ENDIF}; + PageForSysopChat (Pos('/F', strUpper(CmdData)) > 0) {$ENDIF}; End; '*' : Begin If Not Session.io.GetPW (Session.GetPrompt(493), Session.GetPrompt(417), Config.SysopPW) Then Exit; Case Cmd[2] of '#' : Begin - Menu_Editor; + Configuration_ExecuteEditor('M'); Result := True; End; 'A' : Configuration_ExecuteEditor('A'); @@ -333,521 +340,621 @@ Begin End; End; -Function TMenuSystem.LoadMenu (CheckSec: Boolean; RunCmd: Boolean; Global: Boolean) : Byte; -{ 0 = Menu not found: Load fallback menu - 1 = Menu loaded. - 2 = Re-load menu: ie GO in FIRSTCMD } +Function TMenuEngine.ExecuteCommandList (Num, JumpID: LongInt) : Byte; +// 0 = no commands ran, 1 = commands ran, 2 = load new menu Var - MenuFile : Text; - Buffer : Array[1..2048] of Char; - Temp : String; + Count : LongInt; Begin Result := 0; - {$IFDEF LOGGING} Session.SystemLog('Load menu: ' + MenuName); {$ENDIF} + If ViewOnly Then Exit; - Assign (MenuFile, Session.Theme.MenuPath + MenuName + '.mnu'); - {$I-} Reset (MenuFile); {$I+} + If Not TBBSCore(Owner).User.Access(Data.Item[Num]^.Access) Then Exit; - If IoResult <> 0 Then Begin - If Not Global Then Begin - Session.io.OutFullLn ('|CR|14Menu not found, loading fallback.'); - Session.SystemLog ('Menu: ' + MenuName + ' not found'); + Redraw := Boolean(Data.Item[Num]^.Redraw); + + For Count := 1 to Data.Item[Num]^.Commands Do Begin + If JumpID <> -1 Then + If JumpID <> Data.Item[Num]^.CmdData[Count]^.JumpID Then Continue; + + If TBBSCore(Owner).User.Access(Data.Item[Num]^.CmdData[Count]^.Access) Then Begin + Result := 1; + If ExecuteCommand(Data.Item[Num]^.CmdData[Count]^.MenuCmd, Data.Item[Num]^.CmdData[Count]^.Data) Then Begin + Result := 2; + Exit; + End; End; - - Exit; End; +End; - SetTextBuf (MenuFile, Buffer, SizeOf(Buffer)); +Function TMenuEngine.ExecuteByHotkey (Key: String) : Boolean; +Var + Count : LongInt; +Begin + Result := False; + Key := strUpper(Key); - If CheckSec Then Begin - ReadLn (MenuFile, Temp); {Header} - ReadLn (MenuFile, Temp); {Prompt} - ReadLn (MenuFile, Temp); {Display columns} - ReadLn (MenuFile, Temp); {ACS} - - If Not Session.User.Access(Temp) Then Begin - Close (MenuFile); - - MenuName := MenuOld; - Result := 2; - - If Not Global Then - Session.io.OutFullLn (Session.GetPrompt(149)); + For Count := 1 to Data.NumItems Do Begin + If Data.Item[Count] = Nil Then Begin + Result := True; Exit; End; - ReadLn (MenuFile, Temp); - - If Temp <> '' Then - If Not Session.io.GetPW(Session.GetPrompt(150), Session.GetPrompt(417), Temp) Then Begin - Close (MenuFile); - - Result := 2; - MenuName := MenuOld; - + If Data.Item[Count]^.HotKey = Key Then + If ExecuteCommandList(Count, -1) = 2 Then Begin + Result := True; Exit; End; End; - - Reset (MenuFile); - - ReadLn (MenuFile, Menu.Header); - ReadLn (MenuFile, Menu.Prompt); - ReadLn (MenuFile, Menu.DispCols); - ReadLn (MenuFile, Menu.Access); - ReadLn (MenuFile, Menu.Password); - ReadLn (MenuFile, Menu.DispFile); - ReadLn (MenuFile, Menu.FallBack); - ReadLn (MenuFile, Menu.MenuType); - ReadLn (MenuFile, Menu.InputType); - ReadLn (MenuFile, Menu.DoneX); - ReadLn (MenuFile, Menu.DoneY); - ReadLn (MenuFile, Menu.Global); - - If Not Global Then CmdNum := 0; - - While (CmdNum < mysMaxMenuCmds) And (Not Eof(MenuFile)) Do Begin - Inc (CmdNum); - - ReadLn (MenuFile, MenuList[CmdNum].Text); - ReadLn (MenuFile, MenuList[CmdNum].HotKey); - ReadLn (MenuFile, MenuList[CmdNum].LongKey); - ReadLn (MenuFile, MenuList[CmdNum].Access); - ReadLn (MenuFile, MenuList[CmdNum].Command); - ReadLn (MenuFile, MenuList[CmdNum].Data); - ReadLn (MenuFile, MenuList[CmdNum].X); - ReadLn (MenuFile, MenuList[CmdNum].Y); - ReadLn (MenuFile, MenuList[CmdNum].cUP); - ReadLn (MenuFile, MenuList[CmdNum].cDOWN); - ReadLn (MenuFile, MenuList[CmdNum].cLEFT); - ReadLn (MenuFile, MenuList[CmdNum].cRIGHT); - ReadLn (MenuFile, MenuList[CmdNum].TextLo); - ReadLn (MenuFile, MenuList[CmdNum].TextHi); - - If (RunCmd) and (MenuList[CmdNum].HotKey = 'FIRSTCMD') Then Begin - If Session.User.Access(MenuList[CmdNum].Access) Then - If ExecuteCommand (MenuList[CmdNum].Command, MenuList[CmdNum].Data) Then Begin - Result := 2; - Close (MenuFile); - Exit; - End; - Dec (CmdNum); - End; - End; - Close (MenuFile); - - LBMenuPos := 0; - Result := 1; End; -Procedure TMenuSystem.ExecuteMenu (FallBack, Global, View: Boolean); -{If fallback is false, Run_Menu will not try to load any fallback menus. -if the MenuName variable doesn't exist} +Function TMenuEngine.ShowMenu : Boolean; +Begin + With TBBSCore(Owner) Do Begin + Result := Not io.OutFile (ReplaceSecurity(Data.Info.DispFile, User.ThisUser.Security), False, 0); + + If Result And (Pos('@S', Data.Info.DispFile) > 0) Then + Result := Not io.OutFile(StripSecurity(Data.Info.DispFile), False, 0); + End; +End; + +Procedure TMenuEngine.GenerateMenu; Var - Keys : String[mysMaxMenuCmds]; - ExtKeys : String[mysMaxMenuCmds]; - HotKeys : Boolean; - Done : Boolean; - - Function ExecuteAfterCommands : Boolean; - Var - A : Byte; - Begin - ExecuteAfterCommands := False; - - For A := 1 to CmdNum Do - If (MenuList[A].HotKey = 'AFTER') And Session.User.Access(MenuList[A].Access) Then - If ExecuteCommand(MenuList[A].Command, MenuList[A].Data) Then Begin - ExecuteAfterCommands := True; - Done := True; - Exit; - End; - End; - - Function ValidLightBar (Pos : Byte) : Boolean; - Begin - ValidLightBar := False; - - If Pos = 0 Then Exit; - - ValidLightBar := (MenuList[Pos].HotKey <> 'EVERY') and - (MenuList[Pos].HotKey <> 'AFTER') and - (MenuList[Pos].TextLo <> '') and - (MenuList[Pos].TextHi <> ''); -{ we need to add LINEFEED?! } - End; - - Procedure Do_LightBar_Menu; - Var - A : Byte; - Ch : Char; - TempPos : Byte; - TempStr : String; - Begin - If View Then Begin - Done := False; - LBMenuPos := 0; + Format : Byte; + Listed : Word; + Count : LongInt; +Begin + If ShowMenu Then Begin + Case Data.Info.DispCols of + 1 : Format := 79; + 2 : Format := 39; + 3 : Format := 26; + 4 : Format := 19; End; - Session.io.OutFile (ReplaceSecurity(Menu.DispFile), True, 0); + If Data.Info.Header <> '' Then + TBBSCore(Owner).io.OutFullLn (Data.Info.Header); - If Session.io.NoFile and (Pos('@S', Menu.DispFile) > 0) Then - Session.io.OutFile (StripSecurity(Menu.DispFile), True, 0); + Listed := 0; - For A := 1 to CmdNum Do - If ValidLightBar(A) Then Begin - If LBMenuPos = 0 Then LBMenuPos := A; - Session.io.AnsiGotoXY (MenuList[A].X, MenuList[A].Y); - Session.io.OutFull (MenuList[A].TextLo); + For Count := 1 to Data.NumItems Do Begin + If (Data.Item[Count]^.ShowType = 2) or + (Data.Item[Count]^.Text = '') or + (Data.Item[Count]^.HotKey = 'EVERY') or + (Data.Item[Count]^.HotKey = 'AFTER') or + (Data.Item[Count]^.HotKey = 'FIRSTCMD') or + ((Data.Item[Count]^.ShowType = 0) And (Not TBBSCore(Owner).User.Access(Data.Item[Count]^.Access))) + Then Continue; + + If Data.Item[Count]^.HotKey = 'LINEFEED' Then Begin + If Listed MOD Data.Info.DispCols <> 0 Then Session.io.OutRawLn(''); + + Session.io.OutFull(Data.Item[Count]^.Text); + + While Listed Mod Data.Info.DispCols <> 0 Do Inc(Listed); + End Else Begin + Inc (Listed); + + If Format = 79 Then + TBBSCore(Owner).io.OutFull(Data.Item[Count]^.Text) + Else + TBBSCore(Owner).io.OutFull(strPadR(Data.Item[Count]^.Text, Format + Length(Data.Item[Count]^.Text) - strMCILen(Data.Item[Count]^.Text), ' ')); + + If Listed MOD Data.Info.DispCols = 0 Then + TBBSCore(Owner).io.OutFullLn (''); End; + End; - Session.io.AllowArrow := True; + If Listed MOD Data.Info.DispCols <> 0 Then + TBBSCore(Owner).io.OutFullLn (''); - If ExecuteAfterCommands Then Exit; + TBBSCore(Owner).io.BufFlush; + End; - Session.io.PurgeInputBuffer; + If ExecuteByHotKey('AFTER') Then Exit; - Repeat - Session.io.AnsiGotoXY (MenuList[LBMenuPos].X, MenuList[LBMenuPos].Y); - Session.io.OutFull (MenuList[LBMenuPos].TextHi); + If Data.Info.Footer <> '' Then + TBBSCore(Owner).io.OutFull(Data.Info.Footer); + TBBSCore(Owner).io.BufFlush; +End; + +Procedure TMenuEngine.DoStandardMenu; +Var + Ch : Char; + Temp : String[mysMaxMenuInput]; + Count : LongInt; + Found : Boolean; + + Procedure Translate; + Begin + Case Ch of + #09 : Temp := 'TAB'; + #27 : Temp := 'ESCAPE'; + #71 : Temp := 'HOME'; + #72 : Temp := 'UP'; + #73 : Temp := 'PAGEUP'; + #75 : Temp := 'LEFT'; + #77 : Temp := 'RIGHT'; + #79 : Temp := 'END'; + #80 : Temp := 'DOWN'; + #81 : Temp := 'PAGEDOWN' + End; + End; + + Procedure AddChar; + Begin + Temp := Temp + UpCase(Ch); + + Case Data.Info.CharType of + 0 : TBBSCore(Owner).io.OutRaw(UpCase(Ch)); + 1 : TBBSCore(Owner).io.OutRaw(LoCase(Ch)); + 2 : {hidden}; + End; + End; + +Begin + While Not TBBSCore(Owner).ShutDown Do Begin + If Not ViewOnly Then + If ExecuteByHotKey('EVERY') Then Exit; + + If ReDraw Then GenerateMenu; + + TBBSCore(Owner).io.AllowArrow := True; + + If SetAction Then + If Data.Info.NodeStatus <> '' Then + Set_Node_Action(Data.Info.NodeStatus) + Else + Set_Node_Action(TBBSCore(Owner).GetPrompt(346)); + + Temp := ''; + + While Not TBBSCore(Owner).ShutDown Do Begin Ch := Session.io.GetKey; + If TBBSCore(Owner).ShutDown Then Exit; + Case Ch of + #08 : If Length(Temp) > 0 Then Begin + Dec (Temp[0]); + + TBBSCore(Owner).io.OutBS(1, True); + End; + #09, + #27 : If Pos(Ch, ExtKeys) > 0 Then Begin + Translate; + + Break; + End; #13 : Begin - TempStr := MenuList[LBMenuPos].HotKey; + If Temp = '' Then Temp := 'ENTER'; - For A := 1 To CmdNum Do - If MenuList[A].HotKey = TempStr Then - If Session.User.Access(MenuList[A].Access) Then Begin - Session.io.AnsiGotoXY (Menu.DoneX, Menu.DoneY); - If View Then Exit; - If ExecuteCommand(MenuList[A].Command, MenuList[A].Data) Then - Done := True; - End; - Exit; + Break; End; - #72, - #75 : Begin {Up, Left} - Session.io.AnsiGotoXY (MenuList[LBMenuPos].X, MenuList[LBMenuPos].Y); - Session.io.OutFull (MenuList[LBMenuPos].TextLo); - - If Menu.MenuType = 1 Then Begin - TempPos := LBMenuPos; - Repeat - Dec (TempPos); - If ValidLightBar(TempPos) Then Begin - LBMenuPos := TempPos; - Break; - End; - Until TempPos <= 1; - End Else - Case Ch of - #72 : If ValidLightBar(MenuList[LBMenuPos].cUP) Then - LBMenuPos := MenuList[LBMenuPos].cUP; - #75 : If ValidLightBar(MenuList[LBMenuPos].cLEFT) Then - LBMenuPos := MenuList[LBMenuPos].cLEFT; - End; - End; - #80, - #77 : Begin {Down, Right} - Session.io.AnsiGotoXY (MenuList[LBMenuPos].X, MenuList[LBMenuPos].Y); - Session.io.OutFull (MenuList[LBMenuPos].TextLo); - - If Menu.MenuType = 1 Then Begin - If LBMenuPos < CmdNum Then Begin - TempPos := LBMenuPos; - Repeat - Inc (TempPos); - If ValidLightBar(TempPos) Then Begin - LBMenuPos := TempPos; - Break; - End; - Until TempPos >= CmdNum; - End; - End Else Begin - Case Ch of - #77 : If ValidLightBar(MenuList[LBMenuPos].cRIGHT) Then - LBMenuPos := MenuList[LBMenuPos].cRIGHT; - #80 : If ValidLightBar(MenuList[LBMenuPos].cDOWN) Then - LBMenuPos := MenuList[LBMenuPos].cDOWN; - End; + #32.. + #126: If Length(Temp) < mysMaxMenuInput Then Begin + If TBBSCore(Owner).io.IsArrow And (Pos(Ch, ExtKeys) > 0) Then Begin + Translate; + Break; End; + + If UseHotKeys Then Begin + Count := 0; + + Repeat + Inc (Count); + Found := Data.Item[Count]^.HotKey = Temp + UpCase(Ch); + Until Found or (Count >= Data.NumItems); + + If Found And (TBBSCore(Owner).User.Access(Data.Item[Count]^.Access)) Then Begin + AddChar; + Break; + End Else + If UseLongKey Then + If ((Temp[1] = '/') And (Temp[0] > #0)) or ((Temp[0] = #0) And (Ch = '/')) Then + AddChar; + End Else + AddChar; End; - Else - If Pos(UpCase(Ch), Keys) > 0 Then begin - For A := 1 to CmdNum Do Begin - If ((Ch = #27) and (MenuList[A].HotKey = 'ESCAPE')) or - ((Ch = #9) and (MenuList[A].HotKey = 'TAB')) or - (UpCase(Ch) = MenuList[A].HotKey) Then - If Session.User.Access(MenuList[A].Access) Then Begin - Session.io.AnsiGotoXY (Menu.DoneX, Menu.DoneY); - If View Then Exit; - If ExecuteCommand(MenuList[A].Command, MenuList[A].Data) Then - Done := True; - End; - End; - Exit; - End; End; - Until Done; + End; + + If Data.Info.CharType <> 2 Then + TBBSCore(Owner).io.OutRawLn(''); + + If ViewOnly Then Exit; + + If Not TBBSCore(Owner).ShutDown Then + If ExecuteByHotKey(Temp) Then + Exit; + End; +End; + +Procedure TMenuEngine.DoLightBarMenu; +Var + TempStr : String; + PromptX : Byte; + PromptY : Byte; + PromptA : Byte; + + Function ValidLightBar (BarPos: Word) : Boolean; + Begin + Result := False; + + If BarPos = 0 Then Exit; + + Result := (Data.Item[BarPos]^.HotKey <> 'EVERY') And + (Data.Item[BarPos]^.HotKey <> 'AFTER') And + (Data.Item[BarPos]^.HotKey <> 'FIRSTCMD') And + (Data.Item[BarPos]^.TextLo <> '') And + (Data.Item[BarPos]^.TextHi <> '') And + (Data.Item[BarPos]^.ShowType <> 2) And + ( (((Data.Item[BarPos]^.ShowType = 0) And (TBBSCore(Owner).User.Access(Data.Item[BarPos]^.Access)) Or (Data.Item[BarPos]^.ShowType = 1))) + ); End; - Procedure Do_Internal_Menu; + Procedure DrawBar (Num: Word; High: Boolean); Var - Format : Byte; - A : Byte; - Listed : Byte; - Temp : String[8]; - Ch : Char; - Found : Boolean; + Str : String; Begin - Session.io.OutFile (ReplaceSecurity(Menu.DispFile), True, 0); + If Num = 0 Then Exit; - If Session.io.NoFile and (Pos('@S', Menu.DispFile) > 0) Then - Session.io.OutFile (StripSecurity(Menu.DispFile), True, 0); + If High Then + Str := Data.Item[Num]^.TextHi + Else + Str := Data.Item[Num]^.TextLo; - If Session.io.NoFile Then Begin - Case Menu.DispCols of - 1 : Format := 79; - 2 : Format := 39; - 3 : Format := 26; - End; + If Str = '' Then Exit; - Session.io.OutFullLn (Menu.Header); + TBBSCore(Owner).io.AnsiGotoXY(Data.Item[Num]^.X, Data.Item[Num]^.Y); + TBBSCore(Owner).io.OutFull(Str); + End; - Listed := 0; - - For A := 1 to CmdNum Do Begin - If MenuList[A].Text <> '' Then - If (MenuList[A].HotKey <> 'EVERY') and (MenuList[A].HotKey <> 'AFTER') Then - If Session.User.Access(MenuList[A].Access) Then Begin - If MenuList[A].HotKey = 'LINEFEED' Then Begin - If Listed MOD Menu.DispCols <> 0 Then Session.io.OutRawLn(''); - Session.io.OutFull(MenuList[A].Text); - While Listed Mod Menu.DispCols <> 0 Do Inc(Listed); - End Else Begin - Inc (Listed); - If Format <> 79 Then - Session.io.OutFull (strPadR(MenuList[A].Text, Format + (Length(MenuList[A].Text) - strMCILen(MenuList[A].Text)), ' ')) - Else - Session.io.OutFull (MenuList[A].Text); - While Screen.CursorX < Format Do Session.io.BufAddChar(' '); - If Listed Mod Menu.DispCols = 0 Then Session.io.OutRawLn (''); - End; - End; - End; - - If Listed Mod Menu.DispCols <> 0 Then Session.io.OutRawLn (''); + Procedure AddChar (Ch: Char); + Var + SavedAttr : Byte; + Str : String = ''; + Offset : Byte; + Begin + If Data.Info.CharType = 2 Then Begin // hidden + TempStr := TempStr + UpCase(Ch); + Exit; End; - If ExecuteAfterCommands Then Exit; + SavedAttr := Screen.TextAttr; // tbbscore - If Menu.Prompt <> '' Then Session.io.OutFull (Menu.Prompt); + If Ch = #08 Then + Offset := Length(TempStr) + 1 + Else + Offset := Length(TempStr); - Session.io.PurgeInputBuffer; + TBBSCore(Owner).io.BufAddStr(#27 + '[s'); + TBBSCore(Owner).io.AnsiGotoXY(PromptX + Offset, PromptY); + TBBSCore(Owner).io.Attr2Ansi(PromptA); - Listed := 0; - Session.io.AllowArrow := True; + If Ch = #08 Then + Str := Str + #8#32#8 + Else Begin + Case Data.Info.CharType of + 0 : Ch := UpCase(Ch); + 1 : Ch := LoCase(Ch); + End; - If HotKeys Then Begin - Repeat - Temp := UpCase(Session.io.GetKey); - - If Session.io.IsArrow Then Begin - If Pos(Temp, ExtKeys) > 0 Then Break; - End Else - If Pos(Temp, Keys) > 0 Then - If Temp = '/' Then Begin - Session.io.BufAddChar (Temp[1]); - Repeat - Ch := UpCase(Session.io.GetKey); - - Case Ch of - #08 : If Length(Temp) > 0 Then Begin - Dec (Temp[0]); - Session.io.OutBS(1, True); - End; - #13 : Begin - Session.io.OutRawLn(Ch); - Exit; - End; - #32.. - #126: Begin - Found := False; - For A := 1 to CmdNum Do - If Pos (Temp + Ch, MenuList[A].HotKey) > 0 Then Begin - If Not Found Then Begin - Temp := Temp + Ch; - Found := True; - Session.io.BufAddChar (Ch); - End; - If Temp = MenuList[A].HotKey Then - If Session.User.Access(MenuList[A].Access) Then Begin - If View Then Exit; - If Listed = 0 Then Session.io.OutRawLn(''); - Listed := A; - ExecuteCommand (MenuList[A].Command, MenuList[A].Data); - Done := True; - End; - End; - If Done Then Exit; - End; - End; - Until Temp = ''; - End Else - Break; - Until False; - - If Ord(Temp[1]) > 32 Then Session.io.OutRawLn(Temp) Else Session.io.OutRawLn(''); - -{ needs to ignore LINEFEED? } - - For A := 1 to CmdNum Do - If ((Temp = #27) and (MenuList[A].HotKey = 'ESCAPE')) or - ((Temp = #13) and (MenuList[A].HotKey = 'ENTER')) or - ((Temp = #9) and (MenuList[A].HotKey = 'TAB')) or - (Session.io.IsArrow and (Temp = #71) and (MenuList[A].HotKey = 'HOME')) or - (Session.io.IsArrow and (Temp = #72) and (MenuList[A].HotKey = 'UP')) or - (Session.io.IsArrow and (Temp = #73) and (MenuList[A].HotKey = 'PAGEUP')) or - (Session.io.IsArrow and (Temp = #75) and (MenuList[A].HotKey = 'LEFT')) or - (Session.io.IsArrow and (Temp = #77) and (MenuList[A].HotKey = 'RIGHT')) or - (Session.io.IsArrow and (Temp = #79) and (MenuList[A].HotKey = 'END')) or - (Session.io.IsArrow and (Temp = #80) and (MenuList[A].HotKey = 'DOWN')) or - (Session.io.IsArrow and (Temp = #81) and (MenuList[A].HotKey = 'PAGEDOWN')) or - (Not Session.io.IsArrow and (Temp = MenuList[A].HotKey)) Then - - If Session.User.Access(MenuList[A].Access) Then - If ExecuteCommand (MenuList[A].Command, MenuList[A].Data) Then Begin - Done := True; - Exit; - End; - End Else Begin { non hotkey input } - Temp := Session.io.GetInput (8, 8, 2, ''); - - If Temp = '' Then Temp := 'ENTER'; - { temporary support for ENTER in non hotkey mode } - - For A := 1 to CmdNum Do - If Temp = MenuList[A].LongKey Then - If Session.User.Access(MenuList[A].Access) Then Begin - If View Then Exit; - If ExecuteCommand (MenuList[A].Command, MenuList[A].Data) Then Begin - Done := True; - Exit; - End; - End; + Str := Str + Ch; + TempStr := TempStr + UpCase(Ch); End; + + TBBSCore(Owner).io.BufAddStr(Str); + TBBSCore(Owner).io.Attr2Ansi(SavedAttr); + TBBSCore(Owner).io.BufAddStr(#27 + '[u'); + TBBSCore(Owner).io.BufFlush; End; Var - A : Byte; - MR : RecMenuFlags; + Count : Word; + CursorPos : Word; + TempPos : Word; + Ch : Char; + Found : Boolean; + ValidKey : Boolean; Begin - If View Then Begin - Keys := #13; + CursorPos := 0; + + While Not TBBSCore(Owner).ShutDown Do Begin + If Not ViewOnly Then + ExecuteByHotKey('EVERY'); + + If SetAction Then + If Data.Info.NodeStatus <> '' Then + Set_Node_Action(Data.Info.NodeStatus) + Else + Set_Node_Action(TBBSCore(Owner).GetPrompt(346)); + + If ReDraw Then Begin + ShowMenu; + + If Data.Info.Header <> '' Then + TBBSCore(Owner).io.OutFullLn(Data.Info.Header); + + If Data.Info.Footer <> '' Then + TBBSCore(Owner).io.OutFull(Data.Info.Footer); + + PromptX := Screen.CursorX; //tbbscore + PromptY := Screen.CursorY; //tbbscore + PromptA := Screen.TextAttr; //tbbscore + + TBBSCore(Owner).io.BufFlush; + End; + + For Count := 1 to Data.NumItems Do + If ValidLightBar(Count) Then Begin + If CursorPos = 0 Then CursorPos := Count; + DrawBar (Count, False); + End; + + TBBSCore(Owner).io.AllowArrow := True; + + If Not ViewOnly Then + ExecuteByHotKey('AFTER'); + + DrawBar (CursorPos, True); + + TempStr := ''; + + While Not TBBSCore(Owner).ShutDown Do Begin + Ch := Session.io.GetKey; + + If TBBSCore(Owner).ShutDown Then Exit; + + If TBBSCore(Owner).io.IsArrow Then Begin + Case Ch of + #72, + #75 : Case Data.Info.MenuType of + 1 : Begin + TempPos := CursorPos; + + While TempPos > 1 Do Begin + Dec (TempPos); + If ValidLightBar(TempPos) Then Begin + DrawBar (CursorPos, False); + DrawBar (TempPos, True); + CursorPos := TempPos; + Break; + End; + End; + End; + 2 : Case Ch of + #72 : If ValidLightBar(Data.Item[CursorPos]^.JumpUp) Then Begin + ExecuteCommandList(CursorPos, 1); + DrawBar (CursorPos, False); + CursorPos := Data.Item[CursorPos]^.JumpUp; + DrawBar (CursorPos, True); + End; + #75 : If ValidLightBar(Data.Item[CursorPos]^.JumpLeft) Then Begin + ExecuteCommandList(CursorPos, 3); + DrawBar (CursorPos, False); + CursorPos := Data.Item[CursorPos]^.JumpLeft; + DrawBar (CursorPos, True); + End; + End; + End; + #77, + #80 : Case Data.Info.MenuType of + 1 : Begin + TempPos := CursorPos; + + While TempPos < Data.NumItems Do Begin + Inc (TempPos); + If ValidLightBar(TempPos) Then Begin + DrawBar (CursorPos, False); + DrawBar (TempPos, True); + CursorPos := TempPos; + Break; + End; + End; + End; + 2 : Case Ch of + #77 : If ValidLightBar(Data.Item[CursorPos]^.JumpRight) Then Begin + ExecuteCommandList (CursorPos, 4); + DrawBar (CursorPos, False); + CursorPos := Data.Item[CursorPos]^.JumpRight; + DrawBar (CursorPos, True); + End; + #80 : If ValidLightBar(Data.Item[CursorPos]^.JumpDown) Then Begin + ExecuteCommandList (CursorPos, 2); + DrawBar (CursorPos, False); + CursorPos := Data.Item[CursorPos]^.JumpDown; + DrawBar (CursorPos, True); + End; + End; + End; + #73 : If Data.Info.MenuType = 2 Then + Case ExecuteCommandList(CursorPos, 7) of + 0 : ; + 1 : Break; + 2 : Exit; + End; + #81 : If Data.Info.MenuType = 2 Then + Case ExecuteCommandList(CursorPos, 8) of + 0 : ; + 1 : Break; + 2 : Exit; + End; + End; + End Else + Case Ch of + #08 : If Length(TempStr) > 0 Then Begin + Dec (TempStr[0]); + AddChar(#8); + End; + #09 : If Data.Info.MenuType = 2 Then + Case ExecuteCommandList(CursorPos, 5) of + 0 : ; + 1 : Break; + 2 : Exit; + End; + #13 : Begin + TBBSCore(Owner).io.AnsiGotoXY(Data.Info.DoneX, Data.Info.DoneY); + + If ViewOnly Then Exit; + + If Data.Info.MenuType = 1 Then + Found := ExecuteCommandList(CursorPos, -1) = 2 + Else + Found := ExecuteCommandList(CursorPos, 0) = 2; + + If Found Then Exit Else Break; + End; + #27 : If Data.Info.MenuType = 2 Then + Case ExecuteCommandList(CursorPos, 6) of + 0 : ; + 1 : Break; + 2 : Exit; + End; + Else + If Length(TempStr) < mysMaxMenuInput Then Begin + Found := False; + ValidKey := False; + Count := 0; + + Repeat + Inc (Count); + + Found := Data.Item[Count]^.HotKey = TempStr + UpCase(Ch); + + If Not ValidKey Then + ValidKey := TempStr + UpCase(Ch) = Copy(Data.Item[Count]^.HotKey, 1, Length(TempStr + Ch)); + + Until Found or (Count >= Data.NumItems); + + If Found And (TBBSCore(Owner).User.Access(Data.Item[Count]^.Access)) Then Begin + If Length(TempStr) > 0 Then AddChar (Ch); + + If ValidLightBar(Count) Then Begin + DrawBar(CursorPos, False); + CursorPos := Count; + DrawBar(CursorPos, True); + End; + + TBBSCore(Owner).io.AnsiGotoXY(Data.Info.DoneX, Data.Info.DoneY); + + If Data.Info.MenuType = 1 Then + Found := ExecuteCommandList(CursorPos, -1) = 2 + Else + Found := ExecuteCommandList(CursorPos, 0) = 2; + + If Found Then Exit Else Break; + End Else + If ValidKey Then AddChar(Ch); + End; + End; + End; + End; +End; + +Function TMenuEngine.LoadMenu (Forced: Boolean) : Boolean; +Begin + Result := True; + + If Not Data.Load (False, TBBSCore(Owner).Theme.MenuPath + MenuName + '.mnu') Then Begin + Result := False; + + If TBBSCore(Owner).Theme.Flags AND thmFallback <> 0 Then + Result := Data.Load (False, Config.MenuPath + MenuName + '.mnu'); + + If Not Result Then Begin + If Forced Then Begin + Session.io.OutFullLn ('|CRError Loading ' + MenuName + '.mnu'); + Session.SystemLog ('Error Loading Menu: ' + MenuName); + + Halt(1); + End; + + Exit; + End; + End; +End; + +Procedure TMenuEngine.ExecuteMenu (Load, Forced, View, Action: Boolean); +Var + Count : LongInt; +Begin + SetAction := Action; + ViewOnly := View; + + If ViewOnly Then Begin + Case Data.Info.MenuType of + 0 : DoStandardMenu; + 1, + 2 : If TBBSCore(Owner).io.Graphics > 0 Then + DoLightBarMenu + Else + DoStandardMenu; + End; - If (Menu.MenuType > 0) and (Session.io.Graphics = 1) Then Begin - Do_LightBar_Menu; - Session.io.AnsiGotoXY (Menu.DoneX, Menu.DoneY); - End Else - Do_Internal_Menu; Exit; End; - Repeat - Case LoadMenu(True, True, False) of - 0 : Begin - { 1. Try Menu.FallBack } - { 2. Try Config.dFallMNU } - { 3. Give error and halt } - If Not FallBack Then Exit; + If Load Then + If Not LoadMenu(Forced) Then Exit; - If (MenuName = Config.MatrixMenu) or (MenuName = Config.DefFallMenu) Then Begin - Session.io.OutFullLn ('|CRError Loading ' + MenuName + '.mnu'); - Session.SystemLog ('Error Loading Menu: ' + MenuName); - Halt(1); - End; + If Not TBBSCore(Owner).User.Access(Data.Info.Access) Then Begin + MenuName := MenuOld; - If (Menu.FallBack <> '') and (MenuName <> Menu.FallBack) Then - MenuName := Menu.Fallback - Else - MenuName := Config.DefFallMenu; - Exit; - End; - 1 : Break; - 2 : Exit; - End; - Until False; - - If Global and (Menu.Global = 1) Then Begin - Keys := MenuName; - MR := Menu; - MenuName := 'global'; - - LoadMenu(True, True, True); - - MenuName := Keys; - Menu := MR; + TBBSCore(Owner).io.OutFull(TBBSCore(Owner).GetPrompt(149)); + Exit; End; - If Menu.InputType = 0 Then - HotKeys := Session.User.ThisUser.HotKeys + If Data.Info.Global Then + If Not Data.Load (True, TBBSCore(Owner).Theme.MenuPath + 'global.mnu') Then + If TBBSCore(Owner).Theme.Flags AND thmFallback <> 0 Then + Data.Load (True, Config.MenuPath + 'global.mnu'); + + If Data.Info.InputType = 0 Then + UseHotKeys := TBBSCore(Owner).User.ThisUser.HotKeys Else - HotKeys := Not Boolean(Menu.InputType - 1); + UseHotKeys := Not Boolean(Data.Info.InputType - 1); - Repeat - Done := False; + // Run FIRSTCMD commands and setup valid extended keys - Set_Node_Action (Session.GetPrompt(346)); + ExtKeys := ''; + UseTimer := False; + ReDraw := NextReDraw; + NextReDraw := True; + UseLongKey := False; - Keys := #13; - ExtKeys := ''; + For Count := 1 to Data.NumItems Do Begin + If (Data.Item[Count]^.HotKey = 'EVERY') or + Not TBBSCore(Owner).User.Access(Data.Item[Count]^.Access) Then + Continue; - For A := 1 to CmdNum Do - If Session.User.Access(MenuList[A].Access) Then - If MenuList[A].HotKey = 'EVERY' Then Begin - If ExecuteCommand (MenuList[A].Command, MenuList[A].Data) Then Exit; - End Else - If MenuList[A].HotKey = 'TAB' Then - Keys := Keys + #9 - Else - If MenuList[A].HotKey = 'ESCAPE' Then - Keys := Keys + #27 - Else - If MenuList[A].HotKey = 'HOME' Then - ExtKeys := ExtKeys + #71 - Else - If MenuList[A].HotKey = 'UP' Then - ExtKeys := ExtKeys + #72 - Else - If MenuList[A].HotKey = 'PAGEUP' Then - ExtKeys := ExtKeys + #73 - Else - If MenuList[A].HotKey = 'LEFT' Then - ExtKeys := ExtKeys + #75 - Else - If MenuList[A].HotKey = 'RIGHT' Then - ExtKeys := ExtKeys + #77 - Else - If MenuList[A].HotKey = 'END' Then - ExtKeys := ExtKeys + #79 - Else - If MenuList[A].HotKey = 'DOWN' Then - ExtKeys := ExtKeys + #80 - Else - If MenuList[A].HotKey = 'PAGEDOWN' Then - ExtKeys := ExtKeys + #81 - Else - Keys := Keys + MenuList[A].HotKey[1]; + If Data.Item[Count]^.HotKey = 'FIRSTCMD' Then Begin + If ExecuteCommandList(Count, -1) = 2 Then Exit; + End Else + If Data.Item[Count]^.HotKey = 'TAB' Then ExtKeys := ExtKeys + #09 Else + If Data.Item[Count]^.HotKey = 'ESCAPE' Then ExtKeys := ExtKeys + #27 Else + If Data.Item[Count]^.HotKey = 'UP' Then ExtKeys := ExtKeys + #72 Else + If Data.Item[Count]^.HotKey = 'PAGEUP' Then ExtKeys := ExtKeys + #73 Else + If Data.Item[Count]^.HotKey = 'LEFT' Then ExtKeys := ExtKeys + #75 Else + If Data.Item[Count]^.HotKey = 'RIGHT' Then ExtKeys := ExtKeys + #77 Else + If Data.Item[Count]^.HotKey = 'DOWN' Then ExtKeys := ExtKeys + #80 Else + If Data.Item[Count]^.HotKey = 'PAGEDOWN' Then ExtKeys := ExtKeys + #81 Else + If Data.Item[Count]^.HotKey = 'HOME' Then ExtKeys := ExtKeys + #71 Else + If Data.Item[Count]^.HotKey = 'END' Then ExtKeys := ExtKeys + #79 Else + If Data.Item[Count]^.HotKey = 'TIMER' Then UseTimer := True Else + If Byte(Data.Item[Count]^.HotKey[0]) > 1 Then UseLongKey := True; + End; - If (Menu.MenuType > 0) and (Session.io.Graphics = 1) Then - Do_LightBar_Menu - Else - Do_Internal_Menu; - Until Done; + Case Data.Info.MenuType of + 0 : DoStandardMenu; + 1, + 2 : If TBBSCore(Owner).io.Graphics > 0 Then + DoLightBarMenu + Else + DoStandardMenu; + End; End; End. diff --git a/mystic/bbs_user.pas b/mystic/bbs_user.pas index b3fe4b8..3b9f9b2 100644 --- a/mystic/bbs_user.pas +++ b/mystic/bbs_user.pas @@ -951,7 +951,7 @@ Begin Session.Menu.MenuName := 'newinfo'; - Session.Menu.ExecuteMenu (False, False, False); + Session.Menu.ExecuteMenu (False, False, False, True); Session.io.OutFullLn (Session.GetPrompt(21)); @@ -1227,7 +1227,7 @@ Begin If Config.UseMatrix Then Begin Repeat Session.Menu.MenuName := Config.MatrixMenu; - Session.Menu.ExecuteMenu (True, False, False); + Session.Menu.ExecuteMenu (True, False, False, True); Until MatrixOK or Session.ShutDown; End; diff --git a/mystic/mide.pas b/mystic/mide.pas index 8201f9b..29ee416 100644 --- a/mystic/mide.pas +++ b/mystic/mide.pas @@ -619,10 +619,7 @@ Var Box : TMenuBox; InKey : TMenuInput; Str : String; - Save : Boolean; Begin - Save := True; - Box := TMenuBox.Create(Console); InKey := TMenuInput.Create(Console); diff --git a/mystic/mpl_compile.pas b/mystic/mpl_compile.pas index 20d28ae..533a52c 100644 --- a/mystic/mpl_compile.pas +++ b/mystic/mpl_compile.pas @@ -787,7 +787,6 @@ End; Function TParserEngine.ParseElement (VN: Word; TypeCheck: Boolean; VT: TIdentTypes) : TIdentTypes; Var Element : String; - RecNum : Word; Count : Word; Found : Boolean; Offset : LongInt; @@ -1659,8 +1658,6 @@ Function TParserEngine.ParseVariableInfo (Param: Boolean; IsRec: Boolean; Var In End; Procedure ParseVarType; - Var - Count : LongInt; Begin GetIdent(False); diff --git a/mystic/mystic.pas b/mystic/mystic.pas index 62b0f8a..3b94f06 100644 --- a/mystic/mystic.pas +++ b/mystic/mystic.pas @@ -461,6 +461,6 @@ Begin Session.Menu.MenuName := Config.DefStartMenu; Repeat - Session.Menu.ExecuteMenu (True, True, False); + Session.Menu.ExecuteMenu (True, True, False, True); Until False; End. diff --git a/mystic/records.pas b/mystic/records.pas index 97c93d5..3f06462 100644 --- a/mystic/records.pas +++ b/mystic/records.pas @@ -46,7 +46,10 @@ Const mysMaxBatchQueue = 50; // max files per queue mysMaxVoteQuestion = 20; // Max number of voting questions mysMaxMenuNameLen = 20; // menu name size - mysMaxMenuCmds = 75; // Maximum menu commands per menu + mysMaxMenuItems = 75; // Maximum menu items per menu + mysMaxMenuCmds = 20; // Max menu commands per item + mysMaxMenuInput = 12; + mysMaxMenuStack = 8; mysMaxThemeText = 493; // Total prompts in theme file fn_SemFileEcho = 'echomail.now'; @@ -512,43 +515,55 @@ Type Hidden : Boolean; End; -(* Mystic BBS stores it's menu files as text files. They *) -(* have been stored this way to make it possible to edit them with a text *) -(* editor (which is sometimes easier then using the menu editor). The *) -(* following records do not need to be used, but provide one way of *) -(* reading a menu into a record. *) - - RecMenuFlags = Record - Header : String[255]; - Prompt : String[255]; - DispCols : Byte; - Access : String[30]; - Password : String[15]; - DispFile : String[20]; - FallBack : String[20]; - MenuType : Byte; { 0 = standard, 1 = lightbar, 2 = lightbar grid } - InputType : Byte; { 0 = user setting, 1 = longkey, 2 = hotkey } - DoneX : Byte; - DoneY : Byte; - Global : Byte; { 0 = no, 1 = yes } + PtrMenuCmd = ^RecMenuCmd; + RecMenuCmd = Packed Record + MenuCmd : String[2]; + Access : String[mysMaxAcsSize]; + Data : String[80]; + JumpID : Byte; End; - PtrMenuCommand = ^RecMenuCommand; - RecMenuCommand = Record - Text : String[79]; - TextLo : String[79]; - TextHi : String[79]; - HotKey : String[8]; - LongKey : String[8]; - Access : string[30]; - Command : String[2]; - Data : String[79]; - X : Byte; - Y : Byte; - cUp : Byte; - cDown : Byte; - cLeft : Byte; - cRight : Byte; + PtrMenuItem = ^RecMenuItem; + RecMenuItem = Packed Record + Text : String[160]; + TextLo : String[160]; + TextHi : String[160]; + HotKey : String[mysMaxMenuInput]; + Access : String[mysMaxAcsSize]; + ShowType : Byte; + ReDraw : Byte; + JumpUp : Byte; + JumpDown : Byte; + JumpLeft : Byte; + JumpRight : Byte; + JumpEscape : Byte; + JumpTab : Byte; + JumpPgUp : Byte; + JumpPgDn : Byte; + JumpHome : Byte; + JumpEnd : Byte; + CmdData : Array[1..mysMaxMenuCmds] of PtrMenuCmd; + Commands : Byte; + X : Byte; + Y : Byte; + Timer : Word; + End; + + RecMenuInfo = Packed Record + Description : String[30]; + Access : String[mysMaxAcsSize]; + DispFile : String[20]; + Password : String[20]; + NodeStatus : String[30]; + Header : String[160]; + Footer : String[160]; + DoneX : Byte; + DoneY : Byte; + MenuType : Byte; + InputType : Byte; + CharType : Byte; + DispCols : Byte; + Global : Boolean; End; RecPercent = Record