Esempio n. 1
0
    def __init__(self, parent):
        super().__init__(parent)

        self.columnconfigure(1, weight=1)
        self.rowconfigure(4, weight=1)

        Separator(self, orient=HORIZONTAL).grid(row=0, columnspan=4, sticky=E+W, pady=5)

        self.pin_field = EntryWithHintText(self, hint='Type PIN, hit enter', width=20)
        self.pin_field.grid(row=1, column=0, sticky=W, padx=5)
        self.pin_field.bind('<Return>', self.pin_entered)

        self.code_field = EntryWithHintText(self, hint='Scan Barcode')
        self.code_field.grid(row=1, column=1, sticky=E+W)
        self.code_field.bind('<Return>', self.code_entered)

        note = " Note: If you set up your barcode scanner to prepend '%B' to scans, hitting enter after the PIN " \
               "is not required."
        Label(self, text=note).grid(row=2, columnspan=2, sticky=S+W, pady=5)

        Separator(self, orient=HORIZONTAL).grid(row=3, columnspan=4, sticky=E+W, pady=5)

        Label(self, text='Scan Output', anchor=CENTER).grid(row=4, columnspan=4, sticky=E+W)

        self.output_text = ScrolledText(self, bg='#F0F0F0', borderwidth=2, relief=GROOVE)
        self.output_text.config(state=DISABLED)
        self.output_text.grid(row=5, columnspan=4, sticky=N+E+W+S)
Esempio n. 2
0
    def __init__(self, parent):
        super().__init__(parent)

        self.columnconfigure(0, weight=1)
        self.rowconfigure(4, weight=1)

        Separator(self, orient=HORIZONTAL).grid(row=0,
                                                columnspan=4,
                                                sticky=E + W,
                                                pady=5)

        self.swipe_field = EntryWithHintText(
            self,
            hint="Type PIN, then swipe card. If there's no PIN, just swipe.")
        self.swipe_field.grid(row=1, column=0, sticky=E + W)
        self.swipe_field.bind('<Return>', self.card_swiped)

        Separator(self, orient=HORIZONTAL).grid(row=2,
                                                columnspan=4,
                                                sticky=E + W,
                                                pady=5)

        Label(self, text='Swipe Output', anchor=CENTER).grid(row=3,
                                                             columnspan=4,
                                                             sticky=E + W)

        self.output_text = ScrolledText(self,
                                        bg='#F0F0F0',
                                        borderwidth=2,
                                        relief=GROOVE)
        self.output_text.config(state=DISABLED)
        self.output_text.grid(row=4, columnspan=4, sticky=N + E + W + S)
Esempio n. 3
0
    def __init__(self, parent):
        super().__init__(parent)

        self._settings = self.winfo_toplevel().get_settings()

        top_frame = Frame(self)
        top_frame.columnconfigure(1, weight=1)
        top_frame.grid(row=0, sticky=N + E + W + S)

        middle_frame = Frame(self)
        middle_frame.columnconfigure(1, weight=1)
        middle_frame.grid(row=1, sticky=NSEW)

        Label(middle_frame, text='Email Addresses').grid(row=0,
                                                         column=0,
                                                         padx=5,
                                                         pady=5,
                                                         sticky=NW)
        self.email_list = Listbox(middle_frame, width=50)
        self.email_list.grid(row=1)
        self.email_list.bind('<<ListboxSelect>>', self.email_clicked)
        self.email_copy = []

        Label(middle_frame, text='Selected email address').grid(row=0,
                                                                column=1,
                                                                columnspan=5,
                                                                padx=5,
                                                                pady=5,
                                                                sticky=NW)
        edit_frame = Frame(middle_frame)
        edit_frame.grid(row=1, column=1, sticky=NSEW, padx=10)
        Label(edit_frame, text='asdf').grid(column=1)
        Label(edit_frame, text='asdf').grid(column=1)
        Label(edit_frame, text='asdf').grid(column=1)
        Label(edit_frame, text='asdf').grid(column=1)

        bottom_frame = Frame(self)
        bottom_frame.grid(row=2, sticky=S + E, padx=10, pady=10)
        self.rowconfigure(1, weight=1)
        self.columnconfigure(0, weight=1)

        Label(top_frame, text='ChromeDriver Executable: ').grid(row=0,
                                                                column=0,
                                                                pady=5,
                                                                padx=5,
                                                                sticky=W)
        self.chromedriver_filename = EntryWithHintText(
            top_frame, hint='Path to ChromeDriver executable.')
        self.chromedriver_filename.grid(row=0, column=1, sticky=E + W)
        Button(top_frame, text='Select',
               command=self.choose_file).grid(row=0, column=3, padx=5)

        Button(bottom_frame, text='Revert',
               command=self.load_settings).grid(row=0, column=0)
        Button(bottom_frame, text='Apply').grid(row=0, column=1)

        self.load_settings()
Esempio n. 4
0
class SwipeFrame(Frame):
    def __init__(self, parent):
        super().__init__(parent)

        self.columnconfigure(0, weight=1)
        self.rowconfigure(4, weight=1)

        Separator(self, orient=HORIZONTAL).grid(row=0,
                                                columnspan=4,
                                                sticky=E + W,
                                                pady=5)

        self.swipe_field = EntryWithHintText(
            self,
            hint="Type PIN, then swipe card. If there's no PIN, just swipe.")
        self.swipe_field.grid(row=1, column=0, sticky=E + W)
        self.swipe_field.bind('<Return>', self.card_swiped)

        Separator(self, orient=HORIZONTAL).grid(row=2,
                                                columnspan=4,
                                                sticky=E + W,
                                                pady=5)

        Label(self, text='Swipe Output', anchor=CENTER).grid(row=3,
                                                             columnspan=4,
                                                             sticky=E + W)

        self.output_text = ScrolledText(self,
                                        bg='#F0F0F0',
                                        borderwidth=2,
                                        relief=GROOVE)
        self.output_text.config(state=DISABLED)
        self.output_text.grid(row=4, columnspan=4, sticky=N + E + W + S)

    def card_swiped(self, event=None):
        field_data = self.swipe_field.get()

        # card not swiped
        if "%B" not in field_data:
            return
        else:
            pin, data = field_data.split("%B")

        if data.count('?') != 2 or data.count('^') != 2:
            self.swipe_field.delete(len(pin), END)
        else:
            sec1, sec2, sec3 = data.split('^')
            if len(sec1) != 19:
                t1, t2, t3 = sec3.split('?')
                if len(t1) == 8:
                    card_no = sec1[0:16]
                else:
                    card_no = sec1[0] + sec3[14:16] + sec1[6:15] + sec3[16:20]
            else:
                card_no = sec1[0:20]

            output_line = '{},{}\n'.format(
                ''.join(card_no[i:i + 4] for i in range(0, len(card_no), 4)),
                pin)
            self.output_text.config(state=NORMAL)
            self.output_text.insert('end-1c', output_line)
            self.output_text.see(END)
            self.output_text.config(state=DISABLED)
            self.swipe_field.delete(0, END)
Esempio n. 5
0
class SettingsFrame(Frame):
    def __init__(self, parent):
        super().__init__(parent)

        self._settings = self.winfo_toplevel().get_settings()

        top_frame = Frame(self)
        top_frame.columnconfigure(1, weight=1)
        top_frame.grid(row=0, sticky=N + E + W + S)

        middle_frame = Frame(self)
        middle_frame.columnconfigure(1, weight=1)
        middle_frame.grid(row=1, sticky=NSEW)

        Label(middle_frame, text='Email Addresses').grid(row=0,
                                                         column=0,
                                                         padx=5,
                                                         pady=5,
                                                         sticky=NW)
        self.email_list = Listbox(middle_frame, width=50)
        self.email_list.grid(row=1)
        self.email_list.bind('<<ListboxSelect>>', self.email_clicked)
        self.email_copy = []

        Label(middle_frame, text='Selected email address').grid(row=0,
                                                                column=1,
                                                                columnspan=5,
                                                                padx=5,
                                                                pady=5,
                                                                sticky=NW)
        edit_frame = Frame(middle_frame)
        edit_frame.grid(row=1, column=1, sticky=NSEW, padx=10)
        Label(edit_frame, text='asdf').grid(column=1)
        Label(edit_frame, text='asdf').grid(column=1)
        Label(edit_frame, text='asdf').grid(column=1)
        Label(edit_frame, text='asdf').grid(column=1)

        bottom_frame = Frame(self)
        bottom_frame.grid(row=2, sticky=S + E, padx=10, pady=10)
        self.rowconfigure(1, weight=1)
        self.columnconfigure(0, weight=1)

        Label(top_frame, text='ChromeDriver Executable: ').grid(row=0,
                                                                column=0,
                                                                pady=5,
                                                                padx=5,
                                                                sticky=W)
        self.chromedriver_filename = EntryWithHintText(
            top_frame, hint='Path to ChromeDriver executable.')
        self.chromedriver_filename.grid(row=0, column=1, sticky=E + W)
        Button(top_frame, text='Select',
               command=self.choose_file).grid(row=0, column=3, padx=5)

        Button(bottom_frame, text='Revert',
               command=self.load_settings).grid(row=0, column=0)
        Button(bottom_frame, text='Apply').grid(row=0, column=1)

        self.load_settings()

    def choose_file(self):
        filename = askopenfilename(title='Select chromedriver.exe',
                                   filetypes=[("", "*.exe")])
        if filename != '':
            self.chromedriver_filename.set(filename)

    def load_settings(self):
        self.chromedriver_filename.set(
            self._settings.get('Settings', 'chromedriver_path'))
        for email in (e for e in self._settings.sections()
                      if e.startswith('Email')):
            section = self._settings[email]
            section.setdefault('email_address', 'New Email')
            section.setdefault('imap_active', 'True')
            section.setdefault('imap_host', 'imap.gmail.com')
            section.setdefault('imap_port', '993')
            section.setdefault('imap_ssl', 'True')
            section.setdefault('imap_username', '*****@*****.**')
            section.setdefault('imap_password', '')
            section.setdefault('phonenum', '')

            self.email_copy.append(dict(section))
            self.email_list.insert(END, section['email_address'])

    def email_clicked(self, event):
        try:
            selected = self.email_list.curselection()[0]
        except IndexError:
            return

        print(self.email_copy[selected]['email_address'])
Esempio n. 6
0
class SwipeFrame(Frame):
    def __init__(self, parent):
        super().__init__(parent)

        self.columnconfigure(0, weight=1)
        self.rowconfigure(4, weight=1)

        Separator(self, orient=HORIZONTAL).grid(row=0,
                                                columnspan=4,
                                                sticky=E + W,
                                                pady=5)

        self.swipe_field = EntryWithHintText(
            self,
            hint="Type PIN, then swipe card. If there's no PIN, just swipe.")
        self.swipe_field.grid(row=1, column=0, sticky=E + W)
        self.swipe_field.bind('<Return>', self.card_swiped)

        Separator(self, orient=HORIZONTAL).grid(row=2,
                                                columnspan=4,
                                                sticky=E + W,
                                                pady=5)

        Label(self, text='Swipe Output', anchor=CENTER).grid(row=3,
                                                             columnspan=4,
                                                             sticky=E + W)

        self.output_text = ScrolledText(self,
                                        bg='#F0F0F0',
                                        borderwidth=2,
                                        relief=GROOVE)
        self.output_text.config(state=DISABLED)
        self.output_text.grid(row=4, columnspan=4, sticky=N + E + W + S)

    def card_swiped(self, event=None):
        field_data = self.swipe_field.get()

        # card not swiped
        if "%B" not in field_data:
            return
        else:
            pin, data = field_data.split("%B")

        if data.count('?') != 2 or data.count('^') != 2:
            self.swipe_field.delete(len(pin), END)
        else:
            sec1, sec2, sec3 = data.split('^')
            if len(sec1) != 19:
                t1, t2, t3 = sec3.split('?')
                if len(t1) == 8 or len(t1) == 10:
                    card_no = sec1[0:16]
                else:
                    card_no = sec1[0] + sec3[14:16] + sec1[6:15] + sec3[16:20]
            else:
                card_no = sec1[0:20]

            if self.luhn_checksum(card_no) == 0:
                ck = ''
            else:
                ck = ' CHECKSUM FAILED - Check card numbers from this brand.'

            output_line = '{},{}{}\n'.format(
                ''.join(card_no[i:i + 4] for i in range(0, len(card_no), 4)),
                pin, ck)
            self.output_text.config(state=NORMAL)
            self.output_text.insert('end-1c', output_line)
            self.output_text.see(END)
            self.output_text.config(state=DISABLED)
            self.swipe_field.delete(0, END)

    @staticmethod
    def luhn_checksum(card_number):
        def digits_of(n):
            return [int(d) for d in str(n)]

        digits = digits_of(card_number)
        odd_digits = digits[-1::-2]
        even_digits = digits[-2::-2]
        checksum = 0
        checksum += sum(odd_digits)
        for d in even_digits:
            checksum += sum(digits_of(d * 2))
        return checksum % 10
Esempio n. 7
0
class BarcodeFrame(Frame):
    def __init__(self, parent):
        super().__init__(parent)

        self.columnconfigure(1, weight=1)
        self.rowconfigure(4, weight=1)

        Separator(self, orient=HORIZONTAL).grid(row=0, columnspan=4, sticky=E+W, pady=5)

        self.pin_field = EntryWithHintText(self, hint='Type PIN, hit enter', width=20)
        self.pin_field.grid(row=1, column=0, sticky=W, padx=5)
        self.pin_field.bind('<Return>', self.pin_entered)

        self.code_field = EntryWithHintText(self, hint='Scan Barcode')
        self.code_field.grid(row=1, column=1, sticky=E+W)
        self.code_field.bind('<Return>', self.code_entered)

        note = " Note: If you set up your barcode scanner to prepend '%B' to scans, hitting enter after the PIN " \
               "is not required."
        Label(self, text=note).grid(row=2, columnspan=2, sticky=S+W, pady=5)

        Separator(self, orient=HORIZONTAL).grid(row=3, columnspan=4, sticky=E+W, pady=5)

        Label(self, text='Scan Output', anchor=CENTER).grid(row=4, columnspan=4, sticky=E+W)

        self.output_text = ScrolledText(self, bg='#F0F0F0', borderwidth=2, relief=GROOVE)
        self.output_text.config(state=DISABLED)
        self.output_text.grid(row=5, columnspan=4, sticky=N+E+W+S)

    def pin_entered(self, event=None):
        if '%B' in self.pin_field.get():
            pin, code = self.pin_field.get().split('%B')
            self.detect(pin, code)
        else:
            self.code_field.focus()

    def code_entered(self, event=None):
        self.detect(self.pin_field.get(), self.code_field.get())

    def detect(self, pin, code):
        code = code.replace('%B', '')

        # kohl's. others?
        if len(code) == 30:
            code = code[-19:]
        # cabela's
        elif re.search("^\d{10}[a-zA-Z]{6}$", code):
            pin = code[-6:]
            code = code[:9]
        elif len(code) != 19 and len(code) != 16:
            self.code_field.delete(0, END)
            return

        output_line = '{},{}\n'.format(''.join(code[i:i+4] for i in range(0,len(code), 4)), pin)
        self.output_text.config(state=NORMAL)
        self.output_text.insert('end-1c', output_line)
        self.output_text.see(END)
        self.output_text.config(state=DISABLED)

        self.code_field.delete(0, END)
        self.pin_field.delete(0, END)
        self.pin_field.focus()
Esempio n. 8
0
    def __init__(self, parent):
        super().__init__(parent)
        self.modified = False
        self._settings = self.winfo_toplevel().get_settings()

        top_frame = Frame(self)
        top_frame.columnconfigure(1, weight=1)
        top_frame.grid(row=0, sticky=N + E + W + S)

        middle_frame = Frame(self)
        middle_frame.columnconfigure(1, weight=1)
        middle_frame.grid(row=1, sticky=NSEW)

        Label(middle_frame, text='Email Addresses').grid(row=0,
                                                         column=0,
                                                         padx=5,
                                                         pady=5,
                                                         sticky=NW)
        add_delete_frame = Frame(middle_frame)
        add_delete_frame.grid(row=0, column=0, sticky=E)
        self.del_email_button = Button(add_delete_frame,
                                       text='-',
                                       width=3,
                                       command=self.delete_email)
        self.del_email_button.grid(row=0, column=0, sticky=W)
        self.add_email_button = Button(add_delete_frame,
                                       text='+',
                                       width=3,
                                       command=self.add_email)
        self.add_email_button.grid(row=0, column=1, sticky=E)
        self.email_list = Listbox(middle_frame, width=50, exportselection=0)
        self.email_list.grid(row=1, sticky=NSEW)
        self.email_list.bind('<<ListboxSelect>>', self.email_clicked)
        self.email_copy = []

        Label(
            middle_frame,
            text=
            '* Modified email address. Changes are not saved until Save button is clicked.'
        ).grid(row=2, column=0, padx=5, pady=5, sticky=W)

        Label(middle_frame, text='Selected email address').grid(row=0,
                                                                column=1,
                                                                columnspan=5,
                                                                padx=5,
                                                                pady=5,
                                                                sticky=NW)

        edit_frame = Frame(middle_frame)
        edit_frame.grid(row=1, column=1, sticky=NSEW, padx=10)
        Label(edit_frame, text='Enabled: ').grid(row=0, column=0, sticky=E)
        self.email_enabled_variable = BooleanVar()
        self.email_enabled_checkbox = Checkbutton(
            edit_frame,
            state=DISABLED,
            command=self.email_enable_checked,
            variable=self.email_enabled_variable)
        self.email_enabled_checkbox.grid(row=0, column=1, sticky=W)
        Label(edit_frame, text='Email Address: ').grid(row=1,
                                                       column=0,
                                                       sticky=E)
        self.email_address_entry = Entry(edit_frame,
                                         text='',
                                         width=30,
                                         state=DISABLED)
        self.email_address_entry.grid(row=1, column=1)
        Label(edit_frame, text='Username: '******'',
                                    width=30,
                                    state=DISABLED)
        self.username_entry.grid(row=2, column=1)
        Label(edit_frame, text='Password: '******'*',
                                    text='',
                                    width=20,
                                    state=DISABLED)
        self.password_entry.grid(row=2, column=3)
        Label(edit_frame, text='IMAP Host: ').grid(row=3, column=0, sticky=E)
        self.imap_host_entry = Entry(edit_frame,
                                     text='',
                                     width=30,
                                     state=DISABLED)
        self.imap_host_entry.grid(row=3, column=1, sticky=W)
        Label(edit_frame, text='IMAP Port: ').grid(row=3, column=2, sticky=E)
        self.imap_port_entry = Entry(edit_frame,
                                     text='',
                                     width=10,
                                     state=DISABLED)
        self.imap_port_entry.grid(row=3, column=3, sticky=W)
        Label(edit_frame, text='SSL Enabled: ').grid(row=4, column=0, sticky=E)
        self.ssl_enabled_variable = BooleanVar()
        self.ssl_enabled_checkbox = Checkbutton(
            edit_frame, state=DISABLED, variable=self.ssl_enabled_variable)
        self.ssl_enabled_checkbox.grid(row=4, column=1, sticky=W)
        Label(edit_frame, text='Phone number: ').grid(row=4,
                                                      column=2,
                                                      sticky=E)
        self.phone_number_entry = Entry(edit_frame,
                                        text='',
                                        width=15,
                                        state=DISABLED)
        self.phone_number_entry.grid(row=4, column=3, sticky=W)
        Label(
            edit_frame,
            text=
            'Note: Phone number is used for gift card retrieval challenges from some sources.'
        ).grid(row=5, column=0, columnspan=10, sticky=W)

        self.revert_button = Button(edit_frame,
                                    text='Revert Email',
                                    state=DISABLED,
                                    command=self.revert_email)
        self.revert_button.grid(row=6,
                                column=0,
                                columnspan=10,
                                sticky=W,
                                pady=5)

        bottom_frame = Frame(self)
        bottom_frame.grid(row=2, sticky=S + E, columnspan=10, padx=3, pady=3)
        # self.revert_button = Button(bottom_frame, text='Revert', state=DISABLED, command=self.revert_email)
        # self.revert_button.grid(row=0, column=0)
        self.save_button = Button(bottom_frame,
                                  text='Save',
                                  state=NORMAL,
                                  command=self.save_settings)
        self.save_button.grid(row=0, column=1, padx=15, pady=15)

        self.rowconfigure(1, weight=1)
        self.columnconfigure(0, weight=1)

        Label(top_frame, text='ChromeDriver Executable: ').grid(row=0,
                                                                column=0,
                                                                pady=5,
                                                                padx=5,
                                                                sticky=W)
        self.chromedriver_filename = EntryWithHintText(
            top_frame, hint='Path to ChromeDriver executable.')
        self.chromedriver_filename.grid(row=0, column=1, sticky=E + W)
        Button(top_frame, text='Select',
               command=self.choose_file).grid(row=0, column=3, padx=5)

        self.email_fields = [
            [self.email_address_entry, 'email_address',
             StringVar()], [self.username_entry, 'imap_username',
                            StringVar()],
            [self.password_entry, 'imap_password',
             StringVar()], [self.imap_host_entry, 'imap_host',
                            StringVar()],
            [self.imap_port_entry, 'imap_port',
             StringVar()], [self.phone_number_entry, 'phonenum',
                            StringVar()]
        ]

        for i, f in enumerate(self.email_fields):
            f[0].configure(textvariable=f[2])
            self.email_fields[i].append(f[2].trace(
                'w', lambda a, b, c, i=i: self.field_changed(i)))

        self.bind('<Visibility>', self.visible)
        self.load_settings()
Esempio n. 9
0
class SettingsFrame(Frame):
    def __init__(self, parent):
        super().__init__(parent)
        self.modified = False
        self._settings = self.winfo_toplevel().get_settings()

        top_frame = Frame(self)
        top_frame.columnconfigure(1, weight=1)
        top_frame.grid(row=0, sticky=N + E + W + S)

        middle_frame = Frame(self)
        middle_frame.columnconfigure(1, weight=1)
        middle_frame.grid(row=1, sticky=NSEW)

        Label(middle_frame, text='Email Addresses').grid(row=0,
                                                         column=0,
                                                         padx=5,
                                                         pady=5,
                                                         sticky=NW)
        add_delete_frame = Frame(middle_frame)
        add_delete_frame.grid(row=0, column=0, sticky=E)
        self.del_email_button = Button(add_delete_frame,
                                       text='-',
                                       width=3,
                                       command=self.delete_email)
        self.del_email_button.grid(row=0, column=0, sticky=W)
        self.add_email_button = Button(add_delete_frame,
                                       text='+',
                                       width=3,
                                       command=self.add_email)
        self.add_email_button.grid(row=0, column=1, sticky=E)
        self.email_list = Listbox(middle_frame, width=50, exportselection=0)
        self.email_list.grid(row=1, sticky=NSEW)
        self.email_list.bind('<<ListboxSelect>>', self.email_clicked)
        self.email_copy = []

        Label(
            middle_frame,
            text=
            '* Modified email address. Changes are not saved until Save button is clicked.'
        ).grid(row=2, column=0, padx=5, pady=5, sticky=W)

        Label(middle_frame, text='Selected email address').grid(row=0,
                                                                column=1,
                                                                columnspan=5,
                                                                padx=5,
                                                                pady=5,
                                                                sticky=NW)

        edit_frame = Frame(middle_frame)
        edit_frame.grid(row=1, column=1, sticky=NSEW, padx=10)
        Label(edit_frame, text='Enabled: ').grid(row=0, column=0, sticky=E)
        self.email_enabled_variable = BooleanVar()
        self.email_enabled_checkbox = Checkbutton(
            edit_frame,
            state=DISABLED,
            command=self.email_enable_checked,
            variable=self.email_enabled_variable)
        self.email_enabled_checkbox.grid(row=0, column=1, sticky=W)
        Label(edit_frame, text='Email Address: ').grid(row=1,
                                                       column=0,
                                                       sticky=E)
        self.email_address_entry = Entry(edit_frame,
                                         text='',
                                         width=30,
                                         state=DISABLED)
        self.email_address_entry.grid(row=1, column=1)
        Label(edit_frame, text='Username: '******'',
                                    width=30,
                                    state=DISABLED)
        self.username_entry.grid(row=2, column=1)
        Label(edit_frame, text='Password: '******'*',
                                    text='',
                                    width=20,
                                    state=DISABLED)
        self.password_entry.grid(row=2, column=3)
        Label(edit_frame, text='IMAP Host: ').grid(row=3, column=0, sticky=E)
        self.imap_host_entry = Entry(edit_frame,
                                     text='',
                                     width=30,
                                     state=DISABLED)
        self.imap_host_entry.grid(row=3, column=1, sticky=W)
        Label(edit_frame, text='IMAP Port: ').grid(row=3, column=2, sticky=E)
        self.imap_port_entry = Entry(edit_frame,
                                     text='',
                                     width=10,
                                     state=DISABLED)
        self.imap_port_entry.grid(row=3, column=3, sticky=W)
        Label(edit_frame, text='SSL Enabled: ').grid(row=4, column=0, sticky=E)
        self.ssl_enabled_variable = BooleanVar()
        self.ssl_enabled_checkbox = Checkbutton(
            edit_frame, state=DISABLED, variable=self.ssl_enabled_variable)
        self.ssl_enabled_checkbox.grid(row=4, column=1, sticky=W)
        Label(edit_frame, text='Phone number: ').grid(row=4,
                                                      column=2,
                                                      sticky=E)
        self.phone_number_entry = Entry(edit_frame,
                                        text='',
                                        width=15,
                                        state=DISABLED)
        self.phone_number_entry.grid(row=4, column=3, sticky=W)
        Label(
            edit_frame,
            text=
            'Note: Phone number is used for gift card retrieval challenges from some sources.'
        ).grid(row=5, column=0, columnspan=10, sticky=W)

        self.revert_button = Button(edit_frame,
                                    text='Revert Email',
                                    state=DISABLED,
                                    command=self.revert_email)
        self.revert_button.grid(row=6,
                                column=0,
                                columnspan=10,
                                sticky=W,
                                pady=5)

        bottom_frame = Frame(self)
        bottom_frame.grid(row=2, sticky=S + E, columnspan=10, padx=3, pady=3)
        # self.revert_button = Button(bottom_frame, text='Revert', state=DISABLED, command=self.revert_email)
        # self.revert_button.grid(row=0, column=0)
        self.save_button = Button(bottom_frame,
                                  text='Save',
                                  state=NORMAL,
                                  command=self.save_settings)
        self.save_button.grid(row=0, column=1, padx=15, pady=15)

        self.rowconfigure(1, weight=1)
        self.columnconfigure(0, weight=1)

        Label(top_frame, text='ChromeDriver Executable: ').grid(row=0,
                                                                column=0,
                                                                pady=5,
                                                                padx=5,
                                                                sticky=W)
        self.chromedriver_filename = EntryWithHintText(
            top_frame, hint='Path to ChromeDriver executable.')
        self.chromedriver_filename.grid(row=0, column=1, sticky=E + W)
        Button(top_frame, text='Select',
               command=self.choose_file).grid(row=0, column=3, padx=5)

        self.email_fields = [
            [self.email_address_entry, 'email_address',
             StringVar()], [self.username_entry, 'imap_username',
                            StringVar()],
            [self.password_entry, 'imap_password',
             StringVar()], [self.imap_host_entry, 'imap_host',
                            StringVar()],
            [self.imap_port_entry, 'imap_port',
             StringVar()], [self.phone_number_entry, 'phonenum',
                            StringVar()]
        ]

        for i, f in enumerate(self.email_fields):
            f[0].configure(textvariable=f[2])
            self.email_fields[i].append(f[2].trace(
                'w', lambda a, b, c, i=i: self.field_changed(i)))

        self.bind('<Visibility>', self.visible)
        self.load_settings()

    def visible(self, Event):
        try:
            selected = self.email_list.curselection()[0]
            return
        except IndexError:
            pass

        if len(self.email_list.get(0, END)) > 0:
            self.email_list.selection_set(0, 0)
            self.email_clicked(None)

    def choose_file(self):
        filename = askopenfilename(title='Select chromedriver.exe',
                                   filetypes=[("", "*.exe")])
        if filename != '':
            self.chromedriver_filename.set(filename)

    def email_enable_checked(self):
        try:
            selected = self.email_list.curselection()[0]
        except IndexError:
            return

        self.email_copy[selected]['modified'] = True
        self.email_copy[selected][
            'imap_active'] = self.email_enabled_variable.get()
        self.revert_button.configure(state=NORMAL)
        self.update_email_list()

    def ssl_enable_checked(self):
        try:
            selected = self.email_list.curselection()[0]
        except IndexError:
            return

        self.email_copy[selected]['modified'] = True
        self.email_copy[selected]['imap_ssl'] = self.ssl_enabled_variable.get()
        self.revert_button.configure(state=NORMAL)
        self.update_email_list()

    def delete_email(self):
        try:
            selected = self.email_list.curselection()[0]
        except IndexError:
            return

        self.email_copy[selected]['deleted'] = True
        if selected == len(self.email_list.get(0, END)) - 1:
            self.email_list.selection_set(0, 0)
        else:
            self.email_list.selection_set(selected + 1, selected + 2)

        self.revert_button.configure(state=NORMAL)
        self.update_email_list()

    def add_email(self):
        new_email = dict()
        self.set_email_defaults(new_email)
        new_email['modified'] = True
        new_email['deleted'] = False
        new_email['email_address'] = "New Email Address"
        self.email_copy.append(new_email)
        self.update_email_list()

    def field_changed(self, i):
        try:
            selected = self.email_list.curselection()[0]
        except IndexError:
            return

        self.email_copy[selected]['modified'] = True
        self.email_copy[selected][self.email_fields[i]
                                  [1]] = self.email_fields[i][2].get()
        self.revert_button.configure(state=NORMAL)
        self.update_email_list()

    def revert_email(self):
        try:
            selected = self.email_list.curselection()[0]
        except IndexError:
            return

        section = 'Email' + str(selected + 1)
        try:
            self.email_copy[selected] = dict(self._settings[section])
            self.email_copy[selected]['modified'] = False
            self.email_copy[selected]['deleted'] = False
        except KeyError:
            # email is newly added, delete it.
            self.email_copy[selected]['deleted'] = True

        self.update_email_list()
        # self.email_clicked(None)

    def save_settings(self):
        self._settings['Settings'][
            'chromedriver_path'] = self.chromedriver_filename.get()
        # delete all emails from settings dict
        for email in (e for e in self._settings.sections()
                      if e.startswith('Email')):
            del self._settings[email]

        num_deleted = 0
        # remove deleted emails from copied list
        for i, e in enumerate(list(self.email_copy)):
            if e['deleted']:
                del self.email_copy[i - num_deleted]
                num_deleted = num_deleted + 1

            # if e['modified']:
            #     section = 'Email' + str(i+1)
            #     self._settings[section] = dict(e)
            #     del self._settings[section]['modified']
            #     e['modified'] = False
            #     e['deleted'] = False

        # put remaining emails back in settings, renumbered
        for i, e in enumerate(self.email_copy):
            section = 'Email' + str(i + 1)
            e['modified'] = False
            e['deleted'] = False
            self._settings[section] = dict(e)
            del self._settings[section]['modified']
            del self._settings[section]['deleted']

        self.winfo_toplevel().save_settings()
        self.revert_button.configure(state=DISABLED)
        self.update_email_list()

    def update_email_list(self):
        try:
            selected = self.email_list.curselection()[0]
        except IndexError:
            selected = 0

        self.email_list.delete(0, END)
        for e in self.email_copy:
            text = ''
            if e['modified']:
                text = '* '
            if e['deleted']:
                text = text + 'DELETED '
            text = text + e['email_address']
            self.email_list.insert(END, text)

        self.email_list.select_set(selected)
        self.email_list.event_generate('<<ListboxSelect>>')

    def set_email_defaults(self, e):
        e.setdefault('imap_username', '*****@*****.**')
        e.setdefault('email_address', e['imap_username'])
        e.setdefault('imap_active', 'True')
        e.setdefault('imap_host', 'imap.gmail.com')
        e.setdefault('imap_port', '993')
        e.setdefault('imap_ssl', 'True')
        e.setdefault('imap_password', '')
        e.setdefault('phonenum', '')

    def load_settings(self):
        self.chromedriver_filename.set(
            self._settings.get('Settings', 'chromedriver_path'))
        self.email_list.delete(0, END)
        for email in (e for e in self._settings.sections()
                      if e.startswith('Email')):
            section = self._settings[email]
            self.set_email_defaults(section)

            self.email_copy.append(dict(section))
            self.email_copy[-1]['modified'] = False
            self.email_copy[-1]['deleted'] = False
            self.email_list.insert(END, section['email_address'])

    def email_clicked(self, event):
        # disable all traces
        for f in self.email_fields:
            f[2].trace_vdelete('w', f[3])
            f[3] = None

        try:
            selected = self.email_list.curselection()[0]
        except IndexError:
            selected = 0

        email_copy = self.email_copy[selected]
        # enable all fields
        self.email_enabled_checkbox.configure(state=NORMAL)
        self.email_enabled_variable.set(email_copy['imap_active'])
        self.ssl_enabled_checkbox.configure(state=NORMAL)
        self.ssl_enabled_variable.set(email_copy['imap_ssl'])
        if email_copy['modified']:
            self.revert_button.configure(state=NORMAL)
        else:
            self.revert_button.configure(state=DISABLED)
#        self.save_button.configure(state=NORMAL)

        for f in self.email_fields:
            f[0].configure(state=NORMAL)
            f[2].set(email_copy[f[1]])

        # re-enable traces
        for i, f in enumerate(self.email_fields):
            f[3] = f[2].trace('w', lambda a, b, c, i=i: self.field_changed(i))