::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) /* 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 ::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", "[R]emove Contact", "[E]dit Contact", "[S]earch", "[L]ist All", "[Q]uit") menu_keys = .array~of("a", "r", "e", "s", "l", "q") /* Display menu items */ .environment~selected = 1 self~DrawMenu(menuwin, menu_items, .environment~selected, win) menuwin~refresh RETURN ::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 ::method addContactPanel expose win db menuwin menu_items /* Create a form panel */ max_y = win~lines max_x = win~cols form_height = 14 form_width = 50 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(6, 2, "Email: ") 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 return /* User canceled */ lastName = self~getInputField(formwin, 4, 14, 30) if lastName = .nil then return phone = self~getInputField(formwin, 5, 14, 15) if phone = .nil then return email = self~getInputField(formwin, 6, 14, 30) if email = .nil then return /* Add to database */ contactDict = .Directory~new() contactDict["FIRST_NAME"] = firstName contactDict["LAST_NAME"] = lastName contactDict["PHONE"] = phone contactDict["EMAIL"] = email result = db~addContact(contactDict) /* Display result message */ if result > 0 then do formwin~mvaddstr(8, 2, "Contact ID ["result"] added successfully!") end else do formwin~mvaddstr(8, 2, "Failed to add contact.") end formwin~refresh() call SysWait 1.5 formwin~erase() formwin~refresh() /* Redraw the main menu */ self~setupMainMenu(win) return ::method listAllContactsPanel expose win db menuwin /* Create a list panel */ max_y = win~lines max_x = win~cols list_height = 20 list_width = 70 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, 0, "All Contacts", .true) /* Display column headers */ listwin~attron(listwin~A_BOLD) listwin~mvaddstr(3, 2, "ID") listwin~mvaddstr(3, 6, "First Name") listwin~mvaddstr(3, 22, "Last Name") listwin~mvaddstr(4, 2, "-- ---------- ---------") /* listwin~mvaddstr(2, 38, "Phone") listwin~mvaddstr(2, 54, "Email") */ listwin~attroff(listwin~A_BOLD) /* Get all contacts */ contacts = db~getAllContacts() /* Display contacts */ if contacts~items > 0 then do do i = 1 to contacts~items contact = contacts[i] listwin~mvaddstr(i+4, 2, contact['ID']) listwin~mvaddstr(i+4, 6, contact['FIRST_NAME']) listwin~mvaddstr(i+4, 22, contact['LAST_NAME']) /* listwin~mvaddstr(i+3, 38, contact['phone']) listwin~mvaddstr(i+3, 54, contact['email']) */ /* Break if we run out of screen space */ if i > list_height-6 then leave end end else do listwin~mvaddstr(5, 5, "No contacts found.") end listwin~mvaddstr(list_height-2, 2, "Press any key to return to main menu") listwin~refresh() /* Wait for any key */ listwin~getch() listwin~erase() listwin~refresh() /* Redraw the main menu */ win~refresh self~setupMainMenu(win) return ::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, 3, "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 searchwin~erase() searchwin~refresh() self~DrawMenu(menuwin, menu_items, .environment~selected, win) menuwin~refresh() return end /* Perform search */ searchwin~erase() searchwin~refresh() /* Display results in a new window */ self~displaySearchResults(term) return ::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 */ 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 = 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 */ call SysWait 0.25 .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 call SysWait 0.25 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 menuwin~mvaddstr(19 - 3, 5, "I would launch the ADD panel "); menuwin~refresh call SysWait 0.25 self~addContactPanel() END when key_char = 'r' then do menuwin~mvaddstr(19 - 3, 5, "I would launch the REMOVE panel "); menuwin~refresh call SysWait 0.25 END when key_char = 'e' then do menuwin~mvaddstr(19 - 3, 5, "I would launch the EDIT panel "); menuwin~refresh call SysWait 0.25 END when key_char = 's' then do menuwin~mvaddstr(19 - 3, 5, "I would launch the SEARCH panel "); menuwin~refresh call SysWait 0.25 self~searchContactPanel() END when key_char = 'l' then do menuwin~mvaddstr(19 - 3, 5, "I would launch the LIST panel ") menuwin~refresh call SysWait 0.25 self~listAllContactsPanel() END when key_char = 'q' then do menuwin~mvaddstr(19 - 3, 5, "Exiting the application... ") menuwin~refresh call SysWait 0.25 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