::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 = 14 form_width = 55 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, 35, "Type: "); formwin~mvaddstr(6, 2, "Email: "); formwin~mvaddstr(7, 2, "Email Type: "); 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, 45, 15) if phone = .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, 7, 14, 15) if email = .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(8, 2, "Contact ID ["result"] added successfully!") formwin~refresh() call SysWait 0.5 end else do formwin~mvaddstr(8, 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) 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