552 lines
18 KiB
OpenEdge ABL
552 lines
18 KiB
OpenEdge ABL
::requires 'ooSQLite.cls'
|
|
::requires "rxunixsys" LIBRARY
|
|
::requires 'ncurses.cls'
|
|
::requires 'app/utils.rex'
|
|
|
|
::class AddressBookUI public
|
|
|
|
::method init
|
|
expose win mainMenu db
|
|
use arg db
|
|
return
|
|
|
|
::method initialize
|
|
expose win mainMenu db
|
|
|
|
win = self~DrawMainPanel(0, .true) /* default colour, draw border box */
|
|
self~setupMainMenu(win)
|
|
return
|
|
|
|
::METHOD DrawMainPanel
|
|
use arg clrset, with_box
|
|
win = .window~new() /* The first invocation must be with no arguments */
|
|
win~curs_set(0) /* Hide the cursor by default */
|
|
win~keypad(1) /* Enable function keys and arrow keys */
|
|
win~raw
|
|
win~noecho
|
|
win~clear
|
|
win~cbreak
|
|
|
|
self~SetPanelColor(win, clrset)
|
|
|
|
if with_box = .true then DO
|
|
win~box(0, 0) /* Draw box with default ACS characters */
|
|
win~refresh
|
|
END
|
|
return win
|
|
|
|
::METHOD DrawSubPanel
|
|
use arg height, width, starty, startx, clrset, title, with_box
|
|
new_win = .window~new(height, width, starty, startx)
|
|
new_win~curs_set(0)
|
|
|
|
/* Set the panel color */
|
|
self~SetPanelColor(new_win, clrset)
|
|
|
|
/* Display title */
|
|
new_win~attron(new_win~A_BOLD)
|
|
new_win~mvaddstr(starty-(starty-2), (width - title~length) % 2, title)
|
|
new_win~attroff(new_win~A_BOLD)
|
|
new_win~refresh
|
|
if with_box = .true then DO
|
|
new_win~box(0,0)
|
|
new_win~refresh
|
|
END
|
|
return new_win
|
|
|
|
::METHOD SetPanelColor
|
|
use arg panel, clrset
|
|
|
|
panel~start_color()
|
|
SELECT
|
|
/* white text on blue background */
|
|
when clrset = 1 then panel~init_pair(2, panel~COLOR_WHITE, panel~COLOR_BLUE)
|
|
/* white text on grey background */
|
|
when clrset = 2 then panel~init_pair(3, panel~COLOR_WHITE, panel~COLOR_GREEN)
|
|
/* yellow text on red background */
|
|
when clrset = 3 then panel~init_pair(4, panel~COLOR_YELLOW, panel~COLOR_RED)
|
|
/* black text on white background */
|
|
when clrset = 4 then panel~init_pair(5, panel~COLOR_BLACK, panel~COLOR_WHITE)
|
|
/* black text on green background */
|
|
when clrset = 5 then panel~init_pair(6, panel~COLOR_BLACK, panel~COLOR_GREEN)
|
|
/* black text on cyan background */
|
|
when clrset = 6 then panel~init_pair(7, panel~COLOR_BLACK, panel~COLOR_CYAN)
|
|
/* Yellow text on blue background */
|
|
when clrset = 7 then panel~init_pair(8, panel~COLOR_YELLOW, panel~COLOR_BLUE)
|
|
/* Cyan text on black background */
|
|
when clrset = 8 then panel~init_pair(9, panel~COLOR_CYAN, panel~COLOR_BLACK)
|
|
/* blacken everything to give the appearance of disappearance */
|
|
when clrset = 9 then panel~init_pair(10, panel~COLOR_BLACK, panel~COLOR_BLACK)
|
|
/* Default to white text on black background */
|
|
OTHERWISE DO
|
|
clrset=0
|
|
panel~init_pair(1, panel~COLOR_WHITE, panel~COLOR_BLACK)
|
|
END
|
|
END
|
|
panel~bkgd(panel~color_pair(clrset+1))
|
|
panel~refresh
|
|
RETURN
|
|
|
|
/***********************
|
|
* SETUP MAIN MENU *
|
|
***********************/
|
|
::method setupMainMenu
|
|
expose mainMenu menuwin menu_items menu_keys selected
|
|
use arg win
|
|
|
|
max_y = win~lines
|
|
max_x = win~cols
|
|
|
|
menu_height = 21
|
|
menu_width = 40
|
|
start_y = (max_y - menu_height) % 2
|
|
start_x = (max_x - menu_width) % 2
|
|
clrset=1
|
|
title = "Rexx Address Book"
|
|
with_box = .true
|
|
|
|
menuwin = self~DrawSubPanel(menu_height, menu_width, start_y, start_x, clrset, title, with_box)
|
|
menu_items = .array~of("[A]dd Contact", "[D]elete Contact", "[E]dit Contact", "[S]earch", "[L]ist All", "[Q]uit")
|
|
menu_keys = .array~of("a", "d", "e", "s", "l", "q")
|
|
|
|
.environment~selected = 1
|
|
self~DrawMenu(menuwin, menu_items, .environment~selected, win)
|
|
menuwin~refresh
|
|
RETURN
|
|
|
|
/***********************
|
|
* DRAW MENU *
|
|
***********************/
|
|
::method DrawMenu
|
|
expose win menu_items menu_keys selected mainwin
|
|
use arg menuwin, items, selected, mainwin
|
|
|
|
do i = 1 to items~items
|
|
if i = .environment~selected then do
|
|
menuwin~attron(menuwin~attron(menuwin~A_REVERSE))
|
|
menuwin~attron(menuwin~A_BOLD)
|
|
menuwin~mvaddstr(i+3, 2, items[i])
|
|
menuwin~attroff(menuwin~A_REVERSE)
|
|
menuwin~attroff(menuwin~A_BOLD)
|
|
end
|
|
else do
|
|
menuwin~mvaddstr(i+3, 2, items[i])
|
|
end
|
|
end
|
|
menuwin~refresh
|
|
return
|
|
|
|
/***********************
|
|
* DROP WINDOW *
|
|
***********************/
|
|
::method dropWindow
|
|
expose win menuwin
|
|
use arg window
|
|
self~SetPanelColor(window, 9)
|
|
window~erase()
|
|
window~delwin()
|
|
win~refresh
|
|
self~setupMainMenu(win)
|
|
menuwin~refresh()
|
|
RETURN
|
|
|
|
/**********************
|
|
* Add new contact *
|
|
**********************/
|
|
::method addContactPanel
|
|
expose win db menuwin formwin
|
|
|
|
/* Create a form panel */
|
|
max_y = win~lines
|
|
max_x = win~cols
|
|
form_height = 15
|
|
form_width = 60
|
|
start_y = (max_y - form_height) % 2
|
|
start_x = (max_x - form_width) % 2
|
|
formwin = self~DrawSubPanel(form_height, form_width, start_y, start_x, 0, "Add New Contact", .true)
|
|
|
|
/* Create form fields */
|
|
formwin~mvaddstr(3, 2, "First Name: ")
|
|
formwin~mvaddstr(4, 2, "Last Name: ")
|
|
formwin~mvaddstr(5, 2, "Phone: "); formwin~mvaddstr(5, 42, "Type: ");
|
|
formwin~mvaddstr(6, 2, "Email: "); formwin~mvaddstr(6, 42, "Type: ");
|
|
formwin~mvaddstr(7, 2, "Street: ")
|
|
formwin~mvaddstr(8, 2, "City: "); formwin~mvaddstr(8, 28, "State: "); formwin~mvaddstr(8, 40, "PostCode: ")
|
|
formwin~mvaddstr(form_height-2, 2, "[Enter] to save, [Esc] to cancel")
|
|
formwin~refresh()
|
|
|
|
/* Input fields */
|
|
firstName = self~getInputField(formwin, 3, 14, 30)
|
|
if firstName = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
lastName = self~getInputField(formwin, 4, 14, 30)
|
|
if lastName = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
phone = self~getInputField(formwin, 5, 14, 15)
|
|
if phone = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
phone_type = self~getInputField(formwin, 5, 48, 15)
|
|
if phone_type = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
email = self~getInputField(formwin, 6, 14, 30)
|
|
if email = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
email_type = self~getInputField(formwin, 6, 48, 15)
|
|
if email_type = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
|
|
street = self~getInputField(formwin, 7, 10, 25)
|
|
if street = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
city = self~getInputField(formwin, 8, 10, 15)
|
|
if city = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
state = self~getInputField(formwin, 8, 35, 2)
|
|
if state = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
post = self~getInputField(formwin, 8, 50, 10)
|
|
if post = .nil then do
|
|
self~dropWindow(formwin)
|
|
RETURN
|
|
END
|
|
|
|
if firstname = "" | lastname = "" then do
|
|
formwin~mvaddstr(8, 2, "First and Last names are required.")
|
|
formwin~refresh()
|
|
call SysWait 0.5
|
|
END /* don't add contact */
|
|
else DO
|
|
/* Add to database */
|
|
contactDict = .Directory~new()
|
|
contactDict["FIRST_NAME"] = firstName
|
|
contactDict["LAST_NAME"] = lastName
|
|
contactDict["PHONE_NUMBER"] = phone
|
|
contactDict["PHONE_TYPE"] = phone_type
|
|
contactDict["EMAIL_ADDRESS"] = email
|
|
contactDict["EMAIL_TYPE"] = email_type
|
|
result = db~addContact(contactDict)
|
|
|
|
/* Display result message */
|
|
if result > 0 then do /* should be a result id number */
|
|
formwin~mvaddstr(10, 2, "Contact ID ["result"] added successfully!")
|
|
formwin~refresh()
|
|
call SysWait 0.5
|
|
end
|
|
else do
|
|
formwin~mvaddstr(10, 2, "Failed to add contact.")
|
|
formwin~refresh()
|
|
call SysWait 0.5
|
|
end
|
|
end /* add contact */
|
|
self~dropWindow(formwin)
|
|
return
|
|
|
|
/****************************
|
|
* Delete Contact Panel *
|
|
****************************/
|
|
::METHOD deleteContactPanel
|
|
expose win db menuwin delwin
|
|
|
|
/* Create a form panel */
|
|
max_y = win~lines
|
|
max_x = win~cols
|
|
form_height = 14
|
|
form_width = 35
|
|
start_y = (max_y - form_height) % 2
|
|
start_x = (max_x - form_width) % 2
|
|
delwin = self~DrawSubPanel(form_height, form_width, start_y, start_x, 0, "Delete A Contact", .true)
|
|
|
|
delwin~mvaddstr(3, 2, "Contact ID: ")
|
|
delwin~mvaddstr(form_height-2, 2, "[Enter] to save, [Esc] to cancel")
|
|
delwin~refresh()
|
|
|
|
contactId = self~getInputField(delwin, 3, 14, 10)
|
|
if contactId = .nil then do
|
|
self~dropWindow(delwin)
|
|
RETURN
|
|
END
|
|
|
|
result = db~deleteContact(contactId)
|
|
|
|
if result = 0 then do /* should be a result id number */
|
|
delwin~mvaddstr(8, 2, "Contact deleted successfully!")
|
|
delwin~refresh()
|
|
call SysWait 0.5
|
|
end
|
|
else do
|
|
delwin~mvaddstr(8, 2, "Failed to add contact.")
|
|
delwin~refresh()
|
|
call SysWait 0.5
|
|
end
|
|
self~dropWindow(delwin)
|
|
RETURN
|
|
|
|
/****************************
|
|
* List all contacts *
|
|
****************************/
|
|
::method listAllContactsPanel
|
|
expose win db menuwin
|
|
|
|
/* Create a list panel */
|
|
max_y = win~lines
|
|
max_x = win~cols
|
|
list_height = max_y - 5
|
|
list_width = max_x - 23
|
|
start_y = (max_y - list_height) % 2
|
|
start_x = (max_x - list_width) % 2
|
|
|
|
listwin = self~DrawSubPanel(list_height, list_width, start_y, start_x, 1, "All Contacts", .true)
|
|
listwin~scrollok(.true)
|
|
listwin~Setscrreg(4,18)
|
|
|
|
/* Display column headers */
|
|
listwin~attron(listwin~A_BOLD)
|
|
listwin~mvaddstr(4, 2, "ID")
|
|
listwin~mvaddstr(4, 6, "First Name")
|
|
listwin~mvaddstr(4, 18, "Last Name")
|
|
listwin~mvaddstr(4, 30, "Phone")
|
|
listwin~mvaddstr(4, 50, "Email")
|
|
listwin~mvaddstr(5, 2, "-- ---------- --------- --------------- -------------------------")
|
|
listwin~attroff(listwin~A_BOLD)
|
|
|
|
contacts = db~getAllContacts()
|
|
|
|
/* Display contacts */
|
|
if contacts~items > 0 then do
|
|
do i = 1 to contacts~items
|
|
contact = contacts[i]
|
|
listwin~mvaddstr(i+5, 2, contact['ID'])
|
|
listwin~mvaddstr(i+5, 6, contact['FIRST_NAME'])
|
|
listwin~mvaddstr(i+5, 18, contact['LAST_NAME'])
|
|
listwin~mvaddstr(i+5, 30, contact['PHONE_NUMBER'])
|
|
listwin~mvaddstr(i+5, 50, contact['EMAIL_ADDRESS'])
|
|
/* Break if we run out of screen space */
|
|
if i > list_height-7 then LEAVE
|
|
end
|
|
end
|
|
else do
|
|
listwin~mvaddstr(6, 5, "No contacts found.")
|
|
end
|
|
|
|
listwin~getch()
|
|
self~dropWindow(listwin)
|
|
RETURN
|
|
Return
|
|
|
|
/****************************
|
|
* Search for contact *
|
|
****************************/
|
|
::method searchContactPanel
|
|
expose win db menuwin menu_items
|
|
|
|
/* Create a search panel */
|
|
max_y = win~lines
|
|
max_x = win~cols
|
|
search_height = 6
|
|
search_width = 50
|
|
start_y = (max_y - search_height) % 2
|
|
start_x = (max_x - search_width) % 2
|
|
searchwin = self~DrawSubPanel(search_height, search_width, start_y, start_x, 7, "Search Contacts", .true)
|
|
|
|
searchwin~mvaddstr(3, 2, "Search term: ")
|
|
searchwin~refresh()
|
|
|
|
/* Get search term */
|
|
term = self~getInputField(searchwin, 3, 14, 30)
|
|
|
|
/* If canceled, return to main menu */
|
|
if term = .nil then do
|
|
self~DropWindow(searchwin)
|
|
return
|
|
end
|
|
|
|
/* Perform search */
|
|
searchwin~erase()
|
|
searchwin~refresh()
|
|
|
|
/* Display results in a new window */
|
|
self~dropWindow(searchwin)
|
|
self~displaySearchResults(term)
|
|
return
|
|
|
|
/**************************
|
|
* DISPLAY SEARCH RESULTS *
|
|
**************************/
|
|
::METHOD displaySearchResults
|
|
expose win menuwin db
|
|
use arg term
|
|
/* TODO: COMPLETE ME! */
|
|
|
|
self~setupMainMenu(win)
|
|
menuwin~refresh()
|
|
|
|
RETURN
|
|
|
|
/************************
|
|
* Get Input From Field *
|
|
************************/
|
|
::method getInputField
|
|
use arg win, y, x, maxlen
|
|
|
|
win~move(y, x)
|
|
win~curs_set(1) /* Show cursor */
|
|
win~keypad(1) /* Enable function keys and arrow keys */
|
|
win~echo() /* Show typed characters */
|
|
/* win~raw */
|
|
|
|
buffer = ""
|
|
|
|
do forever
|
|
key = win~getch()
|
|
select
|
|
when key = D2C(27) then do /* ESC key */
|
|
win~curs_set(0) /* Hide cursor */
|
|
win~noecho() /* Stop showing typed characters */
|
|
win~mvaddstr(8, 2, "Decimal: "C2D(key) )
|
|
return .nil /* Return nil to indicate cancellation */
|
|
end
|
|
when key = D2C(10) | key = D2C(13) then do /* Enter key */
|
|
win~curs_set(0) /* Hide cursor */
|
|
win~noecho() /* Stop showing typed characters */
|
|
return buffer /* Return the entered text */
|
|
end
|
|
when key = D2C(8) | key = D2C(127) then do /* Backspace */
|
|
if buffer~length > 0 then do
|
|
buffer = buffer~left(buffer~length - 1)
|
|
win~move(y, x)
|
|
win~addstr(buffer || " ") /* Erase the last character */
|
|
win~move(y, x + buffer~length - 1)
|
|
end
|
|
end
|
|
otherwise do
|
|
if buffer~length < maxlen then do
|
|
ch = key
|
|
buffer = buffer || ch
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return buffer
|
|
|
|
/***************/
|
|
/** MAIN LOOP **/
|
|
/***************/
|
|
::method mainLoop
|
|
expose win mainMenu menuwin selected menu_items menu_keys
|
|
menuwin~refresh
|
|
running = .true
|
|
|
|
do while running
|
|
key = win~getch
|
|
|
|
old_selected = .environment~selected
|
|
|
|
select
|
|
when key = menuwin~KEY_UP then do
|
|
if .environment~selected > 1 then .environment~selected = .environment~selected - 1
|
|
self~DrawMenu(menuwin, menu_items, .environment~selected, win)
|
|
menuwin~refresh
|
|
end
|
|
when key = menuwin~KEY_DOWN then do
|
|
if .environment~selected < menu_items~items then .environment~selected = .environment~selected + 1
|
|
self~DrawMenu(menuwin, menu_items, .environment~selected, win)
|
|
menuwin~refresh
|
|
end
|
|
when key = D2C(81) | key = D2C(113) then do /* Q for quit */
|
|
.environment~selected = self~findInArray(menu_keys, key)
|
|
self~ProcessSelection(menuwin, menu_keys[.environment~selected])
|
|
RETURN
|
|
END
|
|
when key = D2C(10) | key = D2C(13) then do /* Enter key - numeric codes only */
|
|
menuwin~mvaddstr(19 - 1, 18, "Letter selection ["||menu_keys[.environment~selected]||"]")
|
|
menuwin~refresh
|
|
self~ProcessSelection(menuwin, menu_keys[.environment~selected])
|
|
return
|
|
end
|
|
otherwise do
|
|
key = lower(key)
|
|
poz = self~findInArray(menu_keys, key)
|
|
if poz > 0 then do
|
|
.environment~selected = poz
|
|
self~DrawMenu(menuwin, menu_items, .environment~selected, win)
|
|
menuwin~mvaddstr(19 - 1, 18, "Letter selection ["||key||"]")
|
|
menuwin~refresh
|
|
self~ProcessSelection(menuwin, key)
|
|
end /* if pos > 0 */
|
|
end /* otherwise */
|
|
end /* select */
|
|
/* Only redraw if selection changed */
|
|
if old_selected \= .environment~selected then do
|
|
self~DrawMenu(menuwin, menu_items, .environment~selected, win)
|
|
end
|
|
end /* do while running */
|
|
return
|
|
|
|
/***********************
|
|
* PROCESS SELECTION *
|
|
***********************/
|
|
::METHOD ProcessSelection
|
|
expose menu_items menu_keys
|
|
use arg menuwin, key_char
|
|
select
|
|
when key_char = 'a' then do
|
|
self~addContactPanel()
|
|
END
|
|
when key_char = 'd' then do
|
|
self~deleteContactPanel()
|
|
END
|
|
when key_char = 'e' then do
|
|
menuwin~mvaddstr(19 - 3, 5, "TODO: Create an Edit Panel ");
|
|
menuwin~refresh
|
|
END
|
|
when key_char = 's' then do
|
|
self~searchContactPanel()
|
|
END
|
|
when key_char = 'l' then do
|
|
self~listAllContactsPanel()
|
|
END
|
|
when key_char = 'q' then do
|
|
menuwin~mvaddstr(19 - 3, 5, "Exiting the application... ")
|
|
menuwin~refresh
|
|
menuwin~endwin
|
|
.environment['STOPNOW'] = 1
|
|
END
|
|
otherwise nop
|
|
end
|
|
return
|
|
|
|
::METHOD findInArray
|
|
use arg array, item
|
|
do i = 1 to array~items
|
|
if array[i] = item then return i
|
|
end
|
|
return 0 /* Not found */
|
|
|
|
::method cleanup
|
|
expose win menuwin
|
|
/* Clean up ncurses */
|
|
menuwin~endwin
|
|
win~endwin
|
|
exit 0
|
|
return
|
|
|
|
|