Esempio n. 1
0
    def __init__(self, master, resizer=True, *args, **kwargs):
        Frame.__init__(self, master, *args, **kwargs)

        self.master = master  # root or toplevel

        self.sizer = Sizer(self.master)

        self.grid_columnconfigure(0, weight=1)
        # With custom window border, you can't use the otherwise
        #   desirable option bd=2, relief='sunken' for a border on statusbar
        #   because edge grabber for resizing is below statusbar
        #   so border looks wrong there. Instead put a Separator
        #   above the statusbar frame.
        # relief = Frame(self, bd=2, relief='sunken')
        relief = Frame(self, bd=0)
        relief.grid(column=0, row=0, sticky='news')
        relief.grid_columnconfigure(0, weight=1)

        self.status_label = LabelStatusbar(relief, cursor='arrow', anchor='w')
        self.tooltip_label = LabelStatusbar(relief,
                                            bd=2,
                                            relief='sunken',
                                            anchor='e')

        if resizer is True:
            self.sizer.place(relx=1.0, x=-3, rely=1.0, anchor='se')
            self.sizer.bind('<Button-1>', self.sizer.get_pos)
        self.status_label.grid(column=0, row=0, sticky='w')
Esempio n. 2
0
    def make_widgets(self):

        self.title('{}{} ({})'.format('Notes for Conclusion #',
                                      self.finding_id, self.birth_name))

        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)
        header = Frame(self)
        header.grid_columnconfigure(1, weight=1)
        header.grid_rowconfigure(0, weight=1)

        self.notes_dialog_header = Frame(header)

        content = Frame(self)

        left_panel = Frame(content)

        topiclab = Label(left_panel, text='Note Subtopics')

        self.toc = Listbox(left_panel,
                           self.sorted_subtopics,
                           view_height=424,
                           view_width=180,
                           scrollbar=False)

        self.new_index = len(self.subtopics)
        self.toc.insert(self.new_index, self.new_subtopic_label_text)
        self.toc.focus_set()
        self.toc.selection_set(0)

        self.toc.store_new_subtopic_name = self.store_new_subtopic_name
        self.toc.pass_ctrl_click = self.open_links_dialog
        self.toc.pass_delete_key = self.delete_note

        self.selected_subtopic = Label(content, text='New Note')

        self.note_input = ScrolledText(content)

        note_submit = Button(content,
                             text='Submit Changes',
                             command=self.save_changes)

        order = Button(content,
                       text='Change Order of Subtopics',
                       command=self.reorder_notes)

        radframe = LabelFrame(content, text='Make selected note...')

        self.public = Radiobutton(radframe,
                                  text='...public',
                                  anchor='w',
                                  variable=self.privacy,
                                  value=0,
                                  command=self.save_privacy_setting)

        self.private = Radiobutton(radframe,
                                   text='...private',
                                   anchor='w',
                                   variable=self.privacy,
                                   value=1,
                                   command=self.save_privacy_setting)

        close = Button(content, text='DONE', command=self.close_roles_dialog)

        # notes_statusbar = StatusbarTooltips(self)

        # visited = (
        # (above,
        # 'this is a notes_statusbar message',
        # 'this is a tooltip'),
        # (below,
        # 'this is also a notes_statusbar message',
        # 'this is another tooltip'))

        # run_statusbar_tooltips(
        # visited,
        # notes_statusbar.status_label,
        # notes_statusbar.tooltip_label)

        # grid in self
        header.grid(column=0, row=0, columnspan=2, pady=12, sticky='we')
        content.grid(column=0, row=1, sticky='news', padx=(0, 6))
        # notes_statusbar.grid(column=0, row=2, sticky='ew')

        # grid in header
        self.notes_dialog_header.grid(column=1, row=0, sticky='ew')
        self.notes_dialog_header.grid_columnconfigure(0, weight=1)

        # grid in content
        left_panel.grid(column=0, row=1, sticky='news', rowspan=2, pady=24)
        self.selected_subtopic.grid(column=1, row=1, sticky='w')
        self.note_input.grid(column=1,
                             row=2,
                             columnspan=3,
                             sticky='nsew',
                             padx=(0, 24),
                             pady=12)
        note_submit.grid(column=3, row=3, sticky='se')
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        content.grid_rowconfigure(1, weight=1)
        order.grid(column=1, row=4)
        radframe.grid(column=2, row=5, sticky='n', pady=24)
        close.grid(column=3, row=6, sticky='se', padx=24, pady=(0, 24))

        # grid in left_panel
        topiclab.grid(column=0, row=0)
        self.toc.grid(column=0, row=1)
        for child in self.toc.listbox_content.winfo_children():
            child.bind('<<ListboxSelected>>', self.switch_note)

        self.subtopic_widgets = self.toc.listbox_content.winfo_children()

        # grid in radframe
        self.public.grid(column=0, row=0, sticky='news', padx=24)
        self.private.grid(column=0, row=1, sticky='news', padx=24)

        # rcm_widgets = (self.subtopic_input, self.note_input.text)
        # make_rc_menus(
        # rcm_widgets,
        # self.rc_menu,
        # note_dlg_msg,
        # header_parent=self.notes_dialog_header,
        # header=self.header,
        # which_dlg='notes')

        config_generic(self)
        center_window(self)
Esempio n. 3
0
class Combobox(FrameHilited3):
    hive = []

    def __init__(self,
                 master,
                 root,
                 callback=None,
                 height=480,
                 values=[],
                 scrollbar_size=24,
                 *args,
                 **kwargs):
        FrameHilited3.__init__(self, master, *args, **kwargs)
        '''
            This is a replacement for ttk.Combobox.
        '''

        self.master = master
        self.callback = callback
        self.root = root
        self.height = height
        self.values = values
        self.scrollbar_size = scrollbar_size

        self.formats = make_formats_dict()

        self.buttons = []
        self.selected = None
        self.result_string = ''

        self.entered = None
        self.lenval = len(self.values)
        self.owt = None
        self.scrollbar_clicked = False
        self.typed = None

        self.screen_height = self.winfo_screenheight()
        self.config(bd=0)

        # simulate <<ComboboxSelected>>:
        self.var = tk.StringVar()
        self.var.trace_add('write',
                           lambda *args, **kwargs: self.combobox_selected())

        # simulate ttk.Combobox.current()
        self.current = 0

        self.make_widgets()
        self.master.bind_all('<ButtonRelease-1>', self.close_dropdown, add='+')

        # self.root.bind('<Configure>', self.hide_all_drops) # DO NOT DELETE
        # Above binding closes dropdown if Windows title bar is clicked, it
        #   has no other purpose. But it causes minor glitches e.g. if a
        #   dropdown button is highlighted and focused, the Entry has to be
        #   clicked twice to put it back into the alternating drop/undrop
        #   cycle as expected. Without this binding, the click on the title
        #   bar lowers the dropdown below the root window which is good
        #   enough for now. To get around it, use the custom_window_border.py.

        # expose only unique methods of Entry e.g. not self.config (self is a Frame and
        #    the Entry, Toplevel, Canvas, and window have to be configured together) so
        #    to size the entry use instance.config_drop_width(72)
        self.insert = self.entry.insert
        self.delete = self.entry.delete
        self.get = self.entry.get

    def make_widgets(self):
        self.entry = Entry(self, textvariable=self.var)
        self.arrow = LabelHilited(self, text='\u25BC', width=2)
        # self.arrow = ComboboxArrow(self, text='\u25BC', width=2)

        self.entry.grid(column=0, row=0)
        self.arrow.grid(column=1, row=0)

        self.update_idletasks()
        self.width = self.winfo_reqwidth()

        self.drop = ToplevelHilited(self, bd=0)
        self.drop.bind('<Destroy>', self.clear_reference_to_dropdown)
        self.drop.withdraw()
        Combobox.hive.append(self.drop)
        for widg in (self.master, self.drop):
            widg.bind('<Escape>', self.hide_all_drops, add='+')

        self.drop.grid_columnconfigure(0, weight=1)
        self.drop.grid_rowconfigure(0, weight=1)

        self.canvas = CanvasHilited(self.drop)
        self.canvas.grid(column=0, row=0, sticky='news')

        self.scrollv_combo = Scrollbar(self.drop,
                                       hideable=True,
                                       command=self.canvas.yview)
        self.canvas.config(yscrollcommand=self.scrollv_combo.set)
        self.content = Frame(self.canvas)

        self.content.grid_columnconfigure(0, weight=1)
        self.content.grid_rowconfigure('all', weight=1)

        self.scrollv_combo.grid(column=1, row=0, sticky='ns')

        self.entry.bind('<KeyPress>', self.open_or_close_dropdown)
        self.entry.bind('<Tab>', self.open_or_close_dropdown)

        for widg in (self.entry, self.arrow):
            widg.bind('<Button-1>', self.open_or_close_dropdown, add='+')

        self.arrow.bind('<Button-1>', self.focus_entry_on_arrow_click, add='+')

        for frm in (self, self.content):
            frm.bind('<FocusIn>', self.arrow.highlight)
            frm.bind('<FocusOut>', self.arrow.unhighlight)

        self.drop.bind('<FocusIn>', self.focus_dropdown)
        self.drop.bind('<Unmap>', self.unhighlight_all_drop_items)

        self.current_combo_parts = [
            self, self.entry, self.arrow, self.scrollv_combo
        ]
        for part in self.current_combo_parts:
            part.bind('<Enter>', self.unbind_combo_parts)
            part.bind('<Leave>', self.rebind_combo_parts)

        self.config_values(self.values)

        config_generic(self.drop)

    def unbind_combo_parts(self, evt):
        self.master.unbind_all('<ButtonRelease-1>')

    def rebind_combo_parts(self, evt):
        self.master.bind_all('<ButtonRelease-1>', self.close_dropdown, add='+')

    def unhighlight_all_drop_items(self, evt):
        for child in self.content.winfo_children():
            child.config(bg=self.formats['highlight_bg'])

    def clear_reference_to_dropdown(self, evt):
        dropdown = evt.widget
        if dropdown in Combobox.hive:
            idx = Combobox.hive.index(dropdown)
            del Combobox.hive[idx]
            dropdown = None

    def config_values(self, values):
        '''
            The vertical scrollbar, when there is one, overlaps the 
            dropdown button highlight but both still work. To change
            this, the button width can be changed when the scrollbar
            appears and disappears.
        '''

        # a sample button is made to get its height, then destroyed
        b = ButtonFlatHilited(self.content, text='Sample')
        one_height = b.winfo_reqheight()
        b.destroy()
        self.fit_height = one_height * len(values)

        self.values = values
        self.lenval = len(self.values)

        for button in self.buttons:
            button.destroy()
        self.buttons = []

        host_width = self.winfo_reqwidth()
        self.window = self.canvas.create_window(0,
                                                0,
                                                anchor='nw',
                                                window=self.content,
                                                width=host_width)
        self.canvas.config(scrollregion=(0, 0, host_width, self.fit_height))
        c = 0
        for item in values:
            bt = ButtonFlatHilited(self.content, text=item, anchor='w')
            bt.grid(column=0, row=c, sticky='ew')
            for event in ('<Button-1>', '<Return>', '<space>'):
                bt.bind(event, self.get_clicked, add='+')
            bt.bind('<Enter>', self.highlight)
            bt.bind('<Leave>', self.unhighlight)
            bt.bind('<Tab>', self.tab_out_of_dropdown_fwd)
            bt.bind('<Shift-Tab>', self.tab_out_of_dropdown_back)
            bt.bind('<KeyPress>', self.traverse_on_arrow)
            bt.bind('<FocusOut>', self.unhighlight)
            bt.bind('<FocusOut>', self.get_tip_widg, add='+')
            bt.bind('<FocusIn>', self.get_tip_widg)
            bt.bind('<Enter>', self.get_tip_widg, add='+')
            bt.bind('<Leave>', self.get_tip_widg, add='+')
            self.buttons.append(bt)
            c += 1
        for b in self.buttons:
            b.config(command=self.callback)

    def get_tip_widg(self, evt):
        '''
            '10' is FocusOut, '9' is FocusIn
        '''
        if self.winfo_reqwidth() <= evt.widget.winfo_reqwidth():
            widg = evt.widget
            evt_type = evt.type
            if evt_type in ('7', '9'):
                self.show_overwidth_tip(widg)
            elif evt_type in ('8', '10'):
                self.hide_overwidth_tip()

    def show_overwidth_tip(self, widg):
        '''
            Instead of a horizontal scrollbar, if a dropdown item doesn't all
            show in the space allotted, the full text will appear in a tooltip
            on highlight. Most of this code is borrowed from Michael Foord.
        '''
        text = widg.cget('text')
        if self.owt:
            return
        x, y, cx, cy = widg.bbox()
        x = x + widg.winfo_rootx() + 32
        y = y + cy + widg.winfo_rooty() + 32
        self.owt = ToplevelHilited(self)
        self.owt.wm_overrideredirect(1)
        l = LabelTip2(self.owt, text=text)
        l.pack(ipadx=6, ipady=3)
        self.owt.wm_geometry('+{}+{}'.format(x, y))

    def hide_overwidth_tip(self):
        tip = self.owt
        self.owt = None
        if tip:
            tip.destroy()

    def highlight_arrow(self, evt):
        self.arrow.config(bg=self.formats['head_bg'])

    def unhighlight_arrow(self, evt):
        self.arrow.config(bg=self.formats['highlight_bg'])

    def focus_entry_on_arrow_click(self, evt):
        self.focus_set()
        self.entry.select_range(0, 'end')

    def hide_other_drops(self):
        for dropdown in Combobox.hive:
            if dropdown != self.drop:
                dropdown.withdraw()

    def hide_all_drops(self, evt=None):
        for dropdown in Combobox.hive:
            dropdown.withdraw()

    def close_dropdown(self, evt):
        '''
            Runs only on ButtonRelease-1.

            In the case of a destroyable combobox in a dialog, after the
            combobox is destroyed, this event will cause an error because
            the dropdown no longer exists. I think this is harmless so I
            added the try/except to pass on it instead of figuring out how
            to prevent the error.
        '''
        widg = evt.widget
        if widg == self.scrollv_combo:
            self.scrollbar_clicked = True
        try:
            self.drop.withdraw()
        except tk.TclError:
            pass

    def config_drop_width(self, new_width):
        self.entry.config(width=new_width)
        self.update_idletasks()
        self.width = self.winfo_reqwidth()
        self.drop.geometry('{}x{}'.format(self.width, self.height))
        self.scrollregion_width = new_width
        self.canvas.itemconfigure(self.window, width=self.width)
        self.canvas.configure(scrollregion=(0, 0, new_width, self.fit_height))

    def open_or_close_dropdown(self, evt=None):
        if evt is None:  # dropdown item clicked--no evt bec. of Button command option
            if self.callback:
                self.callback(self.selected)
            self.drop.withdraw()
            return
        if len(self.buttons) == 0:
            return
        evt_type = evt.type
        evt_sym = evt.keysym
        if evt_sym == 'Tab':
            self.drop.withdraw()
            return
        elif evt_sym == 'Escape':
            self.hide_all_drops()
            return
        first = None
        last = None
        if len(self.buttons) != 0:
            first = self.buttons[0]
            last = self.buttons[len(self.buttons) - 1]
        # self.drop.winfo_ismapped() gets the wrong value
        #   if the scrollbar was the last thing clicked
        #   so drop_is_open has to be used also.
        if evt_type == '4':
            if self.drop.winfo_ismapped() == 1:
                drop_is_open = True
            elif self.drop.winfo_ismapped() == 0:
                drop_is_open = False
            if self.scrollbar_clicked is True:
                drop_is_open = True
                self.scrollbar_clicked = False
            if drop_is_open is True:
                self.drop.withdraw()
                drop_is_open = False
                return
            elif drop_is_open is False:
                pass
        elif evt_type == '2':
            if evt_sym not in ('Up', 'Down'):
                return
            elif first is None or last is None:
                pass
            elif evt_sym == 'Down':
                first.config(bg=self.formats['bg'])
                first.focus_set()
                self.canvas.yview_moveto(0.0)
            elif evt_sym == 'Up':
                last.config(bg=self.formats['bg'])
                last.focus_set()
                self.canvas.yview_moveto(1.0)

        self.update_idletasks()
        x = self.winfo_rootx()
        y = self.winfo_rooty()
        combo_height = self.winfo_reqheight()

        self.fit_height = self.content.winfo_reqheight()
        self.drop.wm_overrideredirect(1)
        fly_up = self.get_vertical_pos(combo_height, evt)
        if fly_up[0] is False:
            y = y + combo_height
        else:
            y = fly_up[1]

        self.drop.geometry('{}x{}+{}+{}'.format(self.width, self.height, x, y))
        self.drop.deiconify()
        self.hide_other_drops()

    def get_vertical_pos(self, combo_height, evt):
        fly_up = False
        vert_pos = evt.y_root - evt.y
        clearance = self.screen_height - (vert_pos + combo_height)
        if clearance < self.height:
            fly_up = True

        return (fly_up, vert_pos - self.height)

    def highlight(self, evt):
        for widg in self.buttons:
            widg.config(bg=self.formats['highlight_bg'])
        widget = evt.widget
        widget.config(bg=self.formats['bg'])
        self.selected = widget
        widget.focus_set()

    def unhighlight(self, evt):
        x, y = self.winfo_pointerxy()
        hovered = self.winfo_containing(x, y)
        if hovered in self.buttons:
            evt.widget.config(bg=self.formats['highlight_bg'])

    def hide_drops_on_title_bar_click(self, evt):
        x, y = self.winfo_pointerxy()
        hovered = self.winfo_containing(x, y)

    def focus_dropdown(self, evt):
        for widg in self.buttons:
            widg.config(takefocus=1)

    def handle_tab_out_of_dropdown(self, go):

        for widg in self.buttons:
            widg.config(takefocus=0)

        self.entry.delete(0, 'end')
        self.entry.insert(0, self.selected.cget('text'))
        self.drop.withdraw()
        self.entry.focus_set()
        if go == 'fwd':
            goto = self.entry.tk_focusNext()
        elif go == 'back':
            goto = self.entry.tk_focusPrev()
        goto.focus_set()

    def tab_out_of_dropdown_fwd(self, evt):
        self.selected = evt.widget
        self.handle_tab_out_of_dropdown('fwd')

    def tab_out_of_dropdown_back(self, evt):
        self.selected = evt.widget
        self.handle_tab_out_of_dropdown('back')

    def get_clicked(self, evt):

        self.selected = evt.widget
        self.current = self.selected.grid_info()['row']
        self.entry.delete(0, 'end')
        self.entry.insert(0, self.selected.cget('text'))
        self.entry.select_range(0, 'end')
        self.open_or_close_dropdown()

    def get_typed(self):
        self.typed = self.var.get()

    def highlight_on_traverse(self, evt, next_item=None, prev_item=None):

        evt_type = evt.type
        evt_sym = evt.keysym  # 2 is key press, 4 is button press

        for widg in self.buttons:
            widg.config(bg=self.formats['highlight_bg'])
        if evt_type == '4':
            self.selected = evt.widget
        elif evt_type == '2' and evt_sym == 'Down':
            self.selected = next_item
        elif evt_type == '2' and evt_sym == 'Up':
            self.selected = prev_item

        self.selected.config(bg=self.formats['bg'])
        self.widg_height = int(self.fit_height / self.lenval)
        widg_screenpos = self.selected.winfo_rooty()
        widg_listpos = self.selected.winfo_y()
        win_top = self.drop.winfo_rooty()
        win_bottom = win_top + self.height
        win_ratio = self.height / self.fit_height
        list_ratio = widg_listpos / self.fit_height
        widg_ratio = self.widg_height / self.fit_height
        up_ratio = list_ratio - win_ratio + widg_ratio

        if widg_screenpos > win_bottom - 0.75 * self.widg_height:
            self.canvas.yview_moveto(float(list_ratio))
        elif widg_screenpos < win_top:
            self.canvas.yview_moveto(float(up_ratio))
        self.selected.focus_set()

    def traverse_on_arrow(self, evt):
        if evt.keysym not in ('Up', 'Down'):
            return
        widg = evt.widget
        sym = evt.keysym
        self.widg_height = int(self.fit_height / self.lenval)
        self.trigger_down = self.height - self.widg_height * 3
        self.trigger_up = self.height - self.widg_height * 2
        self.update_idletasks()
        next_item = widg.tk_focusNext()
        prev_item = widg.tk_focusPrev()
        rel_ht = widg.winfo_y()

        if sym == 'Down':
            if next_item in self.buttons:
                self.highlight_on_traverse(evt, next_item=next_item)
            else:
                next_item = self.buttons[0]
                next_item.focus_set()
                next_item.config(bg=self.formats['bg'])
                self.canvas.yview_moveto(0.0)

        elif sym == 'Up':
            if prev_item in self.buttons:
                self.highlight_on_traverse(evt, prev_item=prev_item)
            else:
                prev_item = self.buttons[self.lenval - 1]
                prev_item.focus_set()
                prev_item.config(bg=self.formats['bg'])
                self.canvas.yview_moveto(1.0)

    def colorize(self):
        # the widgets that don't respond to events are working
        # the scrollbar, which has its own colorize method, is working
        # the arrow label has its own highlight methods, it's working
        self.config(bg=self.formats['bg'])
        self.entry.config(bg=self.formats['highlight_bg'])
        self.drop.config(bg=self.formats['highlight_bg'])
        self.content.config(bg=self.formats['highlight_bg'])
        # The dropdown buttons respond to so many events that it might be
        #   a sort of minor miracle to make them colorize instantly. For
        #   now it's enough that they colorize on reload and they are not
        #   on top, they're only seen on dropdown.

    def callback(self):
        '''
            A function specified on instantiation.
        '''
        print('this will not print if overridden (callback)')

    def combobox_selected(self):
        '''
            A function specified on instantiation will run when
            the selection is made. Similar to ttk's <<ComboboxSelected>>
            but instead of binding to a virtual event.
        '''
        print('this will not print if overridden (combobox_selected)')
Esempio n. 4
0
class NotesDialog(Toplevel):
    def __init__(self,
                 master,
                 current_person,
                 finding_id=None,
                 header=None,
                 pressed=None,
                 *args,
                 **kwargs):
        Toplevel.__init__(self, master, *args, **kwargs)

        self.birth_name = get_name_with_id(current_person)
        self.title('{} ({})'.format('Notes for an Event', self.birth_name))

        self.current_person = current_person
        self.root = master
        self.finding_id = finding_id
        self.header = header
        self.pressed = pressed

        # self.self.new_index = None
        self.current_note_text = ''
        self.current_subtopic = ''
        self.current_note_id = None
        self.new_subtopic_label_text = 'New Note'
        self.current_subtopic_index = None
        self.new_subtopic_name = ''

        self.current_file = get_current_file()[0]

        self.bind('<Escape>', self.close_roles_dialog)
        self.subtopics = []

        self.privacy = tk.IntVar()

        self.refresh_notes_per_finding()
        self.rc_menu = RightClickMenu(self.root)
        self.make_widgets()
        self.protocol('WM_DELETE_WINDOW', self.close_roles_dialog)

    def make_widgets(self):

        self.title('{}{} ({})'.format('Notes for Conclusion #',
                                      self.finding_id, self.birth_name))

        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)
        header = Frame(self)
        header.grid_columnconfigure(1, weight=1)
        header.grid_rowconfigure(0, weight=1)

        self.notes_dialog_header = Frame(header)

        content = Frame(self)

        left_panel = Frame(content)

        topiclab = Label(left_panel, text='Note Subtopics')

        self.toc = Listbox(left_panel,
                           self.sorted_subtopics,
                           view_height=424,
                           view_width=180,
                           scrollbar=False)

        self.new_index = len(self.subtopics)
        self.toc.insert(self.new_index, self.new_subtopic_label_text)
        self.toc.focus_set()
        self.toc.selection_set(0)

        self.toc.store_new_subtopic_name = self.store_new_subtopic_name
        self.toc.pass_ctrl_click = self.open_links_dialog
        self.toc.pass_delete_key = self.delete_note

        self.selected_subtopic = Label(content, text='New Note')

        self.note_input = ScrolledText(content)

        note_submit = Button(content,
                             text='Submit Changes',
                             command=self.save_changes)

        order = Button(content,
                       text='Change Order of Subtopics',
                       command=self.reorder_notes)

        radframe = LabelFrame(content, text='Make selected note...')

        self.public = Radiobutton(radframe,
                                  text='...public',
                                  anchor='w',
                                  variable=self.privacy,
                                  value=0,
                                  command=self.save_privacy_setting)

        self.private = Radiobutton(radframe,
                                   text='...private',
                                   anchor='w',
                                   variable=self.privacy,
                                   value=1,
                                   command=self.save_privacy_setting)

        close = Button(content, text='DONE', command=self.close_roles_dialog)

        # notes_statusbar = StatusbarTooltips(self)

        # visited = (
        # (above,
        # 'this is a notes_statusbar message',
        # 'this is a tooltip'),
        # (below,
        # 'this is also a notes_statusbar message',
        # 'this is another tooltip'))

        # run_statusbar_tooltips(
        # visited,
        # notes_statusbar.status_label,
        # notes_statusbar.tooltip_label)

        # grid in self
        header.grid(column=0, row=0, columnspan=2, pady=12, sticky='we')
        content.grid(column=0, row=1, sticky='news', padx=(0, 6))
        # notes_statusbar.grid(column=0, row=2, sticky='ew')

        # grid in header
        self.notes_dialog_header.grid(column=1, row=0, sticky='ew')
        self.notes_dialog_header.grid_columnconfigure(0, weight=1)

        # grid in content
        left_panel.grid(column=0, row=1, sticky='news', rowspan=2, pady=24)
        self.selected_subtopic.grid(column=1, row=1, sticky='w')
        self.note_input.grid(column=1,
                             row=2,
                             columnspan=3,
                             sticky='nsew',
                             padx=(0, 24),
                             pady=12)
        note_submit.grid(column=3, row=3, sticky='se')
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        content.grid_rowconfigure(1, weight=1)
        order.grid(column=1, row=4)
        radframe.grid(column=2, row=5, sticky='n', pady=24)
        close.grid(column=3, row=6, sticky='se', padx=24, pady=(0, 24))

        # grid in left_panel
        topiclab.grid(column=0, row=0)
        self.toc.grid(column=0, row=1)
        for child in self.toc.listbox_content.winfo_children():
            child.bind('<<ListboxSelected>>', self.switch_note)

        self.subtopic_widgets = self.toc.listbox_content.winfo_children()

        # grid in radframe
        self.public.grid(column=0, row=0, sticky='news', padx=24)
        self.private.grid(column=0, row=1, sticky='news', padx=24)

        # rcm_widgets = (self.subtopic_input, self.note_input.text)
        # make_rc_menus(
        # rcm_widgets,
        # self.rc_menu,
        # note_dlg_msg,
        # header_parent=self.notes_dialog_header,
        # header=self.header,
        # which_dlg='notes')

        config_generic(self)
        center_window(self)

    def open_links_dialog(self):
        links = Toplevel(self.root)
        links.title('Link Notes to Multiple Entities')
        instrux = LabelH3(
            links,
            text='The current note can be linked to any number of Entities\n'
            'such as Persons, Places, Assertions, Conclusions, or Sources.')
        instrux.grid(padx=24, pady=24)

    def save_changes(self):

        self.save_new_subtopic()

        conn = sqlite3.connect(self.current_file)
        conn.execute('PRAGMA foreign_keys = 1')
        cur = conn.cursor()

        new_note_text = self.note_input.text.get(1.0, 'end-1c')
        subtopic = self.current_subtopic
        cur.execute(update_note, (new_note_text, subtopic))
        conn.commit()
        cur.close()
        conn.close()

        self.master.current_note_text = new_note_text
        self.refresh()

    def save_new_subtopic(self):
        if self.selected_subtopic.cget('text') != self.new_subtopic_label_text:
            return
        self.subtopic_dialog = Toplevel(self.root)
        self.subtopic_dialog.title('New Note: Subtopic Input Dialog')
        headlab = LabelH3(self.subtopic_dialog,
                          text='Unique subtopic name for new note:')
        self.subtopic_input = Entry(self.subtopic_dialog, width=64)
        self.subtopic_input.focus_set()
        buttonbox = Frame(self.subtopic_dialog)
        self.subtopic_dialog.grid_columnconfigure(0, weight=1)
        new_note_ok = Button(buttonbox,
                             text='Submit Note and Subtitle',
                             command=self.submit_new_note)
        new_note_cancel = Button(buttonbox,
                                 text='Cancel',
                                 command=self.close_subtopic_dialog)

        headlab.grid(column=0, row=0, pady=(24, 0), columnspan=2)
        self.subtopic_input.grid(column=0,
                                 row=1,
                                 padx=24,
                                 pady=24,
                                 columnspan=2)
        buttonbox.grid(column=1, row=2, sticky='we', padx=24, pady=24)
        new_note_ok.grid(column=0, row=0, sticky='e', padx=12)
        new_note_cancel.grid(column=1, row=0, sticky='e', padx=12)

        self.subtopic_dialog.bind('<Escape>', self.close_subtopic_dialog)
        self.subtopic_dialog.protocol('WM_DELETE_WINDOW',
                                      self.close_subtopic_dialog)

    def submit_new_note(self):
        conn = sqlite3.connect(self.current_file)
        conn.execute('PRAGMA foreign_keys = 1')
        cur = conn.cursor()

        new_note_text = self.note_input.text.get(1.0, 'end-1c')
        new_subtopic = self.subtopic_input.get()
        cur.execute(select_count_subtopic, (new_subtopic, ))
        count = cur.fetchone()[0]
        if count > 0:
            non_unique_subtopic = ErrorMessage(
                self, message="Each subtopic can be\nused once in a tree.")
            non_unique_subtopic.title('Non-unique Note Title Error')
            self.subtopic_input.focus_set()
            return

        if len(new_subtopic) == 0:
            blank_subtopic = ErrorMessage(self,
                                          message="Blank notes are OK\n"
                                          "but not blank note titles.")
            blank_subtopic.title('Blank Note Title Error')
            self.subtopic_input.focus_set()
            return
        cur.execute(insert_note, (new_note_text, new_subtopic))
        conn.commit()
        cur.execute("SELECT seq FROM SQLITE_SEQUENCE WHERE name = 'note'")
        new_note_id = cur.fetchone()[0]

        cur.execute(insert_findings_notes,
                    (self.finding_id, new_note_id, self.new_index))
        conn.commit()
        cur.close()
        conn.close()

        self.master.current_note_text = new_note_text
        self.refresh_notes_per_finding()
        self.toc.insert(self.new_index, new_subtopic)
        items = self.toc.listbox_content.winfo_children()
        for child in items:
            child.bind('<<ListboxSelected>>', self.switch_note)
        self.toc.resize_scrollbar()
        self.toc.selection_set(self.new_index)
        self.switch_note()
        self.pressed.config(text=' ... ')
        self.new_index = len(self.subtopics)
        self.close_subtopic_dialog()
        self.refresh()

    def refresh_notes_per_finding(self):
        conn = sqlite3.connect(self.current_file)
        cur = conn.cursor()
        cur.execute(select_notes_refresh, (self.finding_id, ))
        notes = cur.fetchall()
        all_subtopics = []
        if len(notes) != 0:
            for tup in notes:
                all_subtopics.append([tup[1], tup[3]])
        all_subtopics.sort(key=lambda i: i[1])
        self.sorted_subtopics = []
        for lst in all_subtopics:
            self.sorted_subtopics.append(lst[0])

        self.subtopics = self.sorted_subtopics
        self.notesdict = {}
        for tup in notes:
            self.notesdict[tup[0]] = [tup[1], tup[2]]
        cur.close()
        conn.close()

    def close_roles_dialog(self, evt=None):
        self.destroy()
        self.master.focus_set()

    def close_subtopic_dialog(self, evt=None):
        self.subtopic_dialog.destroy()

    def save_privacy_setting(self):
        privacy_setting = self.privacy.get()
        conn = sqlite3.connect(self.current_file)
        conn.execute('PRAGMA foreign_keys = 1')
        cur = conn.cursor()
        cur.execute(update_note_private,
                    (privacy_setting, self.current_note_id))
        conn.commit()
        cur.close()
        conn.close()

    def get_selected_subtopic(self):
        # "is not None" has to be explicit here
        if self.toc.curselection() is not None:
            self.current_subtopic_index = self.toc.curselection()
        else:
            return
        self.current_subtopic = self.toc.get(self.current_subtopic_index)
        if len(self.notesdict) != 0:
            for k, v in self.notesdict.items():
                if v[0] == self.current_subtopic:
                    break
            self.current_note_id = k
            self.current_note_text = v[1]

    def store_new_subtopic_name(self):
        conn = sqlite3.connect(self.current_file)
        conn.execute('PRAGMA foreign_keys = 1')
        cur = conn.cursor()
        cur.execute(update_note_subtopic,
                    (self.toc.new_subtopic_name, self.current_note_id))
        conn.commit()
        cur.close()
        conn.close()

        self.subtopic_widgets = self.toc.listbox_content.winfo_children()
        for child in self.subtopic_widgets:
            child.bind('<<ListboxSelected>>', self.switch_note)
            if child.winfo_reqwidth() > self.toc.view_width:
                create_tooltip(child, self.toc.new_subtopic_name)

    def switch_note(self, evt=None):

        self.get_selected_subtopic()
        if len(self.current_subtopic) == 0:
            return
        self.selected_subtopic.config(text=self.current_subtopic)
        if self.current_subtopic == self.new_subtopic_label_text:
            self.note_input.text.delete(1.0, 'end')
            return
        self.note_input.text.delete(1.0, 'end')
        self.note_input.text.insert(1.0, self.current_note_text)

        conn = sqlite3.connect(self.current_file)
        cur = conn.cursor()
        cur.execute(select_private_note,
                    (self.current_subtopic, self.finding_id))
        privacy_setting = cur.fetchone()

        if privacy_setting:
            privacy_setting = privacy_setting[0]

        cur.close()
        conn.close()

        if privacy_setting is None:
            return
        elif privacy_setting == 0:
            self.public.select()
        elif privacy_setting == 1:
            self.private.select()

    def delete_note(self):
        '''
            Since any given note can be re-used more than once by linking it 
            to any number of findings, delete note from note table only if 
            the note_id no longer exists at all in the findings_notes table; 
            not every time a note_id is deleted from the findings_notes table. 
        '''
        def ok_delete():
            run_delete()
            cancel_delete()

        def cancel_delete():
            self.focus_set()
            deleter.destroy()

        def run_delete():

            note_count_per_finding = 0

            self.toc.delete(selected)
            self.note_input.text.delete(1.0, 'end')
            self.selected_subtopic.config(text='')
            cur.execute(delete_findings_notes, (deletable, self.finding_id))
            conn.commit()

            if delete_var.get() == 1:
                cur.execute(select_count_findings_notes_note_id, (deletable, ))
                linked_notes = cur.fetchone()[0]

                if linked_notes == 0:
                    cur.execute(delete_note, (deletable, ))
                    conn.commit()
                cur.execute(select_count_findings_notes_finding_id,
                            (self.finding_id, ))
                note_count_per_finding = cur.fetchone()[0]

            cur.close()
            conn.close()

            self.toc.selection_clear()

            if note_count_per_finding == 0:
                self.pressed.config(text='     ')

            self.refresh()

        selected = self.toc.curselection()
        subtopic = self.toc.get(selected)
        if subtopic is None:
            return

        conn = sqlite3.connect(self.current_file)
        conn.execute('PRAGMA foreign_keys = 1')
        cur = conn.cursor()
        cur.execute(select_note_id, (subtopic, ))
        deletable = cur.fetchone()
        if deletable is None:
            return
        elif deletable is not None:
            deletable = deletable[0]

        delete_var = tk.IntVar()

        deleter = Toplevel(self)
        deleter.title('Delete Note Dialog')

        instrux = LabelH3(
            deleter, text='Any note can be linked to any number of entities.')

        radio1 = Radiobutton(deleter,
                             text='Delete this instance of this note.',
                             value=0,
                             variable=delete_var)

        radio2 = Radiobutton(deleter,
                             text='Delete all instances of this note.',
                             value=1,
                             variable=delete_var)

        instrux.grid(column=0, row=0, columnspan=2, padx=24, pady=24)
        radio1.grid(column=0, row=1)
        radio2.grid(column=1, row=1)

        buttonbox = Frame(deleter)
        buttonbox.grid(column=1, row=2, sticky='e')
        b1 = Button(buttonbox, text='OK', command=ok_delete)
        b2 = Button(buttonbox, text='Cancel', command=cancel_delete)
        b1.grid(column=0, row=0)
        b2.grid(column=1, row=0)

    def refresh(self):

        self.refresh_notes_per_finding()
        self.toc.make_listbox_content()
        self.subtopic_widgets = self.toc.listbox_content.winfo_children()
        for child in self.subtopic_widgets:
            child.bind('<<ListboxSelected>>', self.switch_note)

    def reorder_notes(self):
        '''
        '''
        if self.toc.size() < 2:
            return

        self.order_dlg = Toplevel(self)
        self.order_dlg.grab_set()
        self.order_dlg.protocol('WM_DELETE_WINDOW', self.ignore_changes)
        self.order_dlg.bind('<Return>', self.save_close_reorder_dlg)
        self.order_dlg.bind('<Escape>', self.ignore_changes)
        self.order_dlg.grid_columnconfigure(0, weight=1)
        self.order_dlg.title('Reorder Subtopics')

        instrux = ('Tab or Ctrl+Tab selects movable subtopic.\n'
                   'Arrow keys change subtopic order up or down.')

        top = LabelH3(self.order_dlg, text=instrux, anchor='center')

        self.labels = Frame(self.order_dlg)

        e = 0
        for subtopic in self.subtopics:
            lab = LabelMovable(self.labels, text=subtopic, anchor='w')
            if e == 0:
                first = lab
            e += 1
            lab.grid(column=0, row=e, padx=3, sticky='ew')
        first.focus_set()

        close2 = Button(self.order_dlg,
                        text='OK',
                        command=self.save_close_reorder_dlg)

        top.grid(column=0, row=0, pady=(24, 0), padx=24, columnspan=2)
        self.labels.grid(column=0, row=1, columnspan=2, padx=24, pady=24)
        self.labels.grid_columnconfigure(0, weight=1)
        close2.grid(column=1, row=2, sticky='se', padx=12, pady=(0, 12))

        center_window(self.order_dlg)

    def ignore_changes(self, evt=None):
        self.order_dlg.grab_release()
        self.order_dlg.destroy()
        self.focus_set()

    def save_close_reorder_dlg(self, evt=None):
        '''
            Replace the values list.
        '''

        q = 0
        new_order = []
        save_order = []
        for child in self.labels.winfo_children():
            text = child.cget('text')
            new_order.append(text)
            save_order.append([text, q])
            q += 1

        conn = sqlite3.connect(self.current_file)
        conn.execute('PRAGMA foreign_keys = 1')
        cur = conn.cursor()

        for lst in save_order:
            cur.execute(select_note_id, (lst[0], ))
            note_id = cur.fetchone()[0]
            cur.execute(update_findings_notes,
                        (lst[1], self.finding_id, note_id))
            conn.commit()
        cur.close()
        conn.close()
        self.subtopics = self.toc.items = new_order
        self.refresh()
        self.order_dlg.grab_release()
        self.order_dlg.destroy()
        self.focus_set()