#!/usr/bin/env python ## CURSES BASIC-MAIL GUI # This is the Basic-Mail program, a basic email client written in Python # with a curses interface. This program reads mail messages in from a file # and allows you to view them and create others, all in the highest-grade # GUI that the cutting-edge terminals allow! # # To Run: Go to the Basic-Mail directory and type "python Basic-Mail.py", # without the quotes. If it doesn't work, make sure that the size # of the console window is at least 80 x 30 pixels. # # Use the arrow keys to navigate within the inbox and the composition # window, and the highlighted letters and numbers for the menus and # tabs. # # ALSO: You MUST change the directory variable in variables.py to whatever path # the files are stored in on your computer. e.g /home/rdolan/Basic-Mail/ ## ## Filename: basic-mail.py ## # This is the main file, where the interface is created and where the user # interacts with the program. ## ## KNOWN ISSUES AND BUGS.. ## # 1: Deletion does not work properly in the composition window. # 2: The program does not write back to the file and save your changes. # 3: Address Book, Accounts, and Reply do not work, but I'm keeping them in # because the more populated menus look prettier. # 4: Closing tabs has limited functionality; you cannot close the inbox, ever, # and closing one screen leaves it on the tab to the left of it. ## ## VERSION HISTORY.. ## ## Version 1.0 - May 1st, 2006 # Message composition works very well, and you can view multiple messages! ## ## Version 0.9 - April 30th, 2006 # Inbox and message screen working, reads in from file successfully ## ## Version 0.8 - April 28th, 2006 # Organized code in more files and got tab closure working. ## ## Version 0.75 - April 20th, 2006 # The code is now much more organized and the functions better explained. ## ## Version 0.7 - April 12th, 2006 # Includes working pop-ups and tab displays, though you cannot open and close # tabs. ## ## Version 0.6 - April 8th, 2006 # Has working menus and tabs, though tab displaying is not quite worked out. ## ## Version 0.5 - April 5th, 2006 # The Chat GUI stripped down to its basic functionality. ## ## A more detailed version history is available in versions.txt ## ## IMPORTS ## import curses, curses.ascii, curses.panel # Curses imports import traceback, os, sys, select, string # Random imports from variables import * # Imports all variables and initializes windows from chat import * # Imports chat functions # Imports interface functions from various files from draw import * from menu import * from tabs import * from pop_up import * from files import * ## Write function windows. ## USER-INTERACTION CODE ## # "try" is used in case of GUI failure. try: mail = readmail() # Reads mail from file into an array draw_inbox(mail,inbox_line) # Draws inbox screen, # = message highlighted. draw_borders(stdscr, 80, 30) # Draws the screen border draw_screen(active_tab) # Draws the title, menu bar, and tab bar. curses.panel.update_panels() menu_win_location = '' # No open menus compose_start = 1 # Compose window close # Begin Interacting With User. live = 1 while(live): curses.curs_set(0) # Makes Cursor invisible, 0-2 stdscr.move(0,0) # Moves cursor input = stdscr.getch() # Takes input # Decides what to do with the input # Checks for open menus if(menu_win_location): count = 0 # Checks if the user has selected one of the menu options. for number in menu[menu_win_location]: if curses.ascii.unctrl(input) == \ string.lower(number[menu[menu_win_location+1][count]]) or \ curses.ascii.unctrl(input) == \ string.upper(number[menu[menu_win_location+1][count]]): # If the user has chosen to quit. if menu_win_location == 2\ and curses.ascii.unctrl(input) == 'q' or \ curses.ascii.unctrl(input) == 'Q': live = create_pop_up(0) # If the user has chosen to open a window if menu_win_location == 6 or menu_win_location == 8: option_count = 0 # Loops across the View menu and checks to see # if one of the options has been selected for option in menu[6]: if curses.ascii.unctrl(input) == \ string.lower(option[menu[7][count]]) or \ curses.ascii.unctrl(input) == \ string.upper(option[menu[7][count]]): # Opens or creates appropriate tab if len(tabs[0]) == 5: useless = create_pop_up(2) else: active_tab = open_tab(option) if option == "Address Book": active_tab.replace(addressbook_win) tabs[2].append(addressbook_win) elif option == "Compose": active_tab.replace(compose_win) tabs[2].append(compose_win) option_count += 1 if menu_win_location == 8 and\ curses.ascii.unctrl(input) == 'n' or\ curses.ascii.unctrl(input) == 'N': if len(tabs[0]) == 5: useless = create_pop_up(2) else: active_tab = open_tab(option) active_tab.replace(compose_win) tabs[2].append(compose_win) # If the user has chosen to close a tab if menu_win_location == 2\ and curses.ascii.unctrl(input) == 'c' or \ curses.ascii.unctrl(input) == 'C': i = 10 # Default: Do not adjust tab numbering # If only one tab open, gives an error message if len(tabs[0]) == 1: create_pop_up(1) # If there is more than one tab open elif active_tab == tab1: # Deletes tab from tabs array del tabs[0][0] del tabs[2][0] i = 0 # Tab to adjust to the right of # Sets a new active and visible tab and window active_tab.replace(tabs[2][0]) #active_tab = tab1 draw_screen(active_tab) elif active_tab == tab2: # Deletes tab from tabs array if len(tabs[0]) == 2: # If tab is at the end tabs[0].pop() else: del tabs[0][1] i = 1 # Tab to adjust to the right of # Sets a new active and visible tab and window active_tab = tab1 tab1.top() draw_screen(active_tab) elif active_tab == tab3: # Deletes tab from tabs array if len(tabs[0]) == 3: # If tab is at the end tabs[0].pop() else: del tabs[0][2] i = 2 # Tab to adjust to the right of # Sets a new active and visible tab and window active_tab = tab2 tab2.top() draw_screen(active_tab) elif active_tab == tab4: # Deletes tab from tabs array if len(tabs[0]) == 4: # If tab is at the end tabs[0].pop() else: del tabs[0][3] i = 3 # Tab to adjust to the right of # Sets a new active and visible tab and window active_tab = tab3 tab3.top() draw_screen(active_tab) elif active_tab == tab5: # Deletes tab from tabs array if len(tabs[0]) == 5: # If tab is at the end tabs[0].pop() else: del tabs[0][4] i = 4 # Tab to adjust to the right of # Sets a new active and visible tab and window active_tab = tab4 tab4.top() draw_screen(active_tab) # Adjust numbering of tabs to the right of closed tab # Also adjusts windows if(i != 10): # Checks if user calls for adjustment while(i < len(tabs[0])): # Adjusts numbering tabs[0][i] = tabs[0][i][:-2] + str(i+1) + '/' # Adjusts windows if i == 0: win2.overwrite(win1) win2.erase() if i == 1: win3.overwrite(win2) win3.erase() if i == 2: win4.overwrite(win3) win4.erase() if i == 3: win5.overwrite(win4) win5.erase() if i == 4: # Always last window, no worries mate! win5.erase() i += 1 count += 1 menu_win_location = 0 menu_win.hide() curses.panel.update_panels() curses.doupdate() draw_screen(active_tab) # Uses inbox controls if inbox is open if active_tab.window() == inbox_win: # Checks for up arrow if curses.ascii.ascii(input) == 65: if inbox_line == 0: inbox_line = len(mail) - 1 else: inbox_line -= 1 draw_inbox(mail,inbox_line) inbox_win.refresh() # Checks for down arrow if curses.ascii.ascii(input) == 66: if inbox_line == len(mail) - 1: inbox_line = 0 else: inbox_line += 1 draw_inbox(mail,inbox_line) inbox_win.refresh() if curses.ascii.ascii(input) == 10: # Opens a tab named the subject of the message if len(tabs[0]) == 5: useless = create_pop_up(2) else: active_tab = open_tab(mail[inbox_line][2]) # Opens a message window if messages_open != 4: if messages_open == 0: active_tab.replace(message_1_win) tabs[2].append(message_1_win) message_1_win = draw_message(message_1_win,\ mail,inbox_line) elif messages_open == 1: active_tab.replace(message_2_win) tabs[2].append(message_2_win) message_2_win = draw_message(message_2_win,\ mail,inbox_line) elif messages_open == 2: active_tab.replace(message_3_win) tabs[2].append(message_3_win) message_3_win = draw_message(message_3_win,\ mail,inbox_line) elif messages_open == 3: active_tab.replace(message_4_win) tabs[2].append(message_4_win) message_4_win = draw_message(message_4_win,\ mail,inbox_line) curses.panel.update_panels() messages_open += 1 # Uses compose controls if composing window is open if active_tab.window() == compose_win: mail = write_message(compose_win,mail) active_tab.replace(inbox_win) draw_screen(active_tab) draw_inbox(mail,inbox_line) inbox_win.refresh() # Checks if user selected a menu count = 0 for number in menu[0]: # Checks if the input selects any of the menu options # In both lower case and upper case if curses.ascii.unctrl(input) == \ string.lower(number[menu[1][count]]) or \ curses.ascii.unctrl(input) == \ string.upper(number[menu[1][count]]): draw_screen(active_tab) select_bar(menu_bar,menu,count,10,10,0) menu_win = create_menu(menu,count) menu_win_location = (count * 2) + 2 # Tells which menu is open count += 1 # Checks if the user selected one of the tabs count = 0 # Resets count for checking tabs for number in tabs[0]: # Checks if the input selects any of the tabs if curses.ascii.unctrl(input) == number[tabs[1][count]]: active_tab = select_tab(count) count += 1 stdscr.keypad(0) curses.nocbreak() curses.echo() curses.endwin() # Closes Curses Window print "Thank you for trying the Basic-Mail client!" except: # Returns terminal to a normal state if error stdscr.keypad(0) curses.nocbreak() curses.echo() curses.endwin() # Closes Curses Window print "Thank you! The interface has broken and you have found a bug!"