class CategoryManager(Frame): """Category manager for the sticky notes.""" def __init__(self, master, app, **kwargs): Frame.__init__(self, master, **kwargs) self.columnconfigure(0, weight=1) self.app = app self.style = Style(self) self.style.theme_use("clam") self.im_plus = PhotoImage(file=IM_PLUS) self.im_moins = PhotoImage(file=IM_MOINS) # --- Default category self.frame_def_cat = Frame(self) self.default_category = StringVar(self.frame_def_cat, CONFIG.get("General", "default_category").capitalize()) Label(self.frame_def_cat, text=_("Default category ")).grid(row=0, column=0, sticky="e", padx=(4, 0)) self.categories = CONFIG.options("Categories") self.categories.sort() categories = [cat.capitalize() for cat in self.categories] self.def_cat_menu = OptionMenu(self.frame_def_cat, self.default_category, CONFIG.get("General", "default_category").capitalize(), *categories) optionmenu_patch(self.def_cat_menu, self.default_category) self.def_cat_menu.grid(row=0, column=1, sticky="w", padx=4, pady=4) # --- Category colors, names ... self.frame_cat = Frame(self) self.colors = list(COLORS.keys()) self.colors.sort() self.images = [] for key in self.colors: self.images.append(PhotoImage(key, master=self, width=16, height=16)) fill(self.images[-1], COLORS[key]) self.cat_colors = {} self.cat_labels = {} self.cat_menus = {} self.cat_buttons = {} for i, cat in enumerate(self.categories): self.cat_labels[cat] = Label(self.frame_cat, text="%s" % cat.capitalize(), anchor='e') self.cat_labels[cat].grid(row=i + 2, column=0, sticky="ew", padx=2) self.cat_labels[cat].bind('<Double-Button-1>', self.change_name) self.cat_colors[cat] = StringVar(self) color = CONFIG.get("Categories", cat) self.cat_menus[cat] = OptionMenu(self.frame_cat, self.cat_colors[cat], INV_COLORS[color], *self.colors, command=lambda color, c=cat: self.change_menubutton_color(color, c), style="%s.TMenubutton" % cat) optionmenu_patch(self.cat_menus[cat], self.cat_colors[cat]) self.style.configure("%s.TMenubutton" % cat, background=color) self.cat_menus[cat].grid(row=i + 2, column=1, sticky="w", padx=4, pady=4) self.cat_buttons[cat] = Button(self.frame_cat, image=self.im_moins, command=lambda c=cat: self.del_cat(c)) self.cat_buttons[cat].grid(row=i + 2, column=2, padx=4, pady=4) self.add_cat_button = Button(self.frame_cat, image=self.im_plus, command=self.add_cat) self.add_cat_button.grid(row=i + 3, column=0, pady=(0, 4)) if len(self.categories) == 1: self.cat_buttons[self.categories[0]].configure(state="disabled") # --- placement self.frame_def_cat.grid(row=0, column=0, sticky="eswn") Separator(self, orient="horizontal").grid(row=1, columnspan=3, pady=10, sticky="ew") self.frame_cat.grid(row=2, column=0, sticky="eswn") def change_name(self, event): def ok(event): cats = [l.cget('text').lower() for l in self.cat_labels.values()] cat = name.get().strip().lower() if cat and cat not in cats: label.configure(text=cat.capitalize()) if old_cat == self.default_category.get(): self.default_category.set(cat.capitalize()) self.update_def_cat_menu() name.destroy() label = event.widget old_cat = label.cget('text') name = Entry(self, justify='center') name.insert(0, label.cget('text')) name.place(in_=label, relx=0, rely=0, anchor='nw', relwidth=1, relheight=1) name.bind('<FocusOut>', lambda e: name.destroy()) name.bind('<Escape>', lambda e: name.destroy()) name.bind('<Return>', ok) name.selection_range(0, 'end') name.focus_set() def get_color(self, category): return self.cat_colors[category].get() def get_name(self, category): return self.cat_labels[category].cget('text').lower() def change_menubutton_color(self, color, cat): """ change the color of the menubutton of the category cat when its default color is changed """ self.style.configure("%s.TMenubutton" % cat, background=COLORS[color]) def del_cat(self, category): rep = askyesnocancel(_("Question"), _("Do you want to delete all notes belonging to \ the category %(category)s? If you answer 'No', the category will be deleted but \ the notes will belong to the default category. Be careful, the change will take \ effect immediately and cannot be undone." % {"category": category})) if rep is not None: del(self.cat_colors[category]) self.cat_buttons[category].grid_forget() del(self.cat_buttons[category]) self.cat_labels[category].grid_forget() cat = self.cat_labels[category].cget('text') del(self.cat_labels[category]) self.cat_menus[category].grid_forget() del(self.cat_menus[category]) self.categories.remove(category) CONFIG.remove_option("Categories", category) if self.default_category.get() == cat: default = list(self.cat_labels.values())[0].cget('text') self.default_category.set(default) CONFIG.set("General", "default_category", default.lower()) self.update_def_cat_menu() if len(self.categories) == 1: self.cat_buttons[self.categories[0]].configure(state="disabled") if rep: self.app.delete_cat(category) self.app.update_notes() self.app.update_menu() save_config() def update_def_cat_menu(self): self.def_cat_menu.destroy() categories = [l.cget('text') for l in self.cat_labels.values()] self.def_cat_menu = OptionMenu(self.frame_def_cat, self.default_category, None, *categories) optionmenu_patch(self.def_cat_menu, self.default_category) self.def_cat_menu.grid(row=0, column=1, sticky="w", padx=4, pady=4) def add_cat(self): top = Toplevel(self) top.transient(self) top.grab_set() top.resizable(False, False) top.title(_("New Category")) def valide(event=None): cats = [l.cget('text').lower() for l in self.cat_labels.values()] cat = name.get().strip().lower() if cat and cat not in cats: i = self.add_cat_button.grid_info()['row'] self.add_cat_button.grid_configure(row=i + 1) self.cat_labels[cat] = Label(self.frame_cat, text="%s " % cat.capitalize()) self.cat_labels[cat].grid(row=i, column=0, sticky="e") self.cat_colors[cat] = StringVar(self, _("Yellow")) self.cat_menus[cat] = OptionMenu(self.frame_cat, self.cat_colors[cat], _("Yellow"), *self.colors, command=lambda color, c=cat: self.change_menubutton_color(color, c), style="%s.TMenubutton" % cat) self.style.configure("%s.TMenubutton" % cat, background=COLORS[_("Yellow")]) optionmenu_patch(self.cat_menus[cat], self.cat_colors[cat]) self.cat_menus[cat].grid(row=i, column=1, padx=4, pady=4) self.cat_buttons[cat] = Button(self.frame_cat, image=self.im_moins, command=lambda c=cat: self.del_cat(c)) self.cat_buttons[cat].grid(row=i, column=2, padx=4, pady=4) self.cat_buttons[self.categories[0]].configure(state="normal") self.categories.append(cat) self.categories.sort() self.update_def_cat_menu() top.destroy() name = Entry(top, justify="center") name.grid(row=0, column=0, columnspan=2, sticky="ew") name.bind("<Return>", valide) name.focus_set() Button(top, text="Ok", command=valide).grid(row=1, column=0, sticky="nswe") Button(top, text=_("Cancel"), command=top.destroy).grid(row=1, column=1, sticky="nswe")
class Manager(Toplevel): """Mailbox Manager.""" def __init__(self, master, pwd): """Create the mailbox manager dialog.""" Toplevel.__init__(self, master, class_="CheckMails") self.title(_("Mailbox Manager")) self.minsize(200, 10) self.pwd = pwd self.resizable(False, False) self.protocol("WM_DELETE_WINDOW", self.quit) self.im_add = PhotoImage(master=self, file=ADD) self.im_del = PhotoImage(master=self, file=DEL) self.im_edit = PhotoImage(master=self, file=EDIT) self.mailboxes = {} active = CONFIG.get("Mailboxes", "active").split(", ") inactive = CONFIG.get("Mailboxes", "inactive").split(", ") while "" in active: active.remove("") while "" in inactive: inactive.remove("") active.sort() inactive.sort() self.frame = Frame(self) self.columnconfigure(0, weight=1) self.frame.columnconfigure(1, weight=1) self.frame.grid(row=0, column=0, padx=10, pady=10, sticky="eswn") i = -1 for i, box in enumerate(active): c = Checkbutton(self.frame) c.state(('selected',)) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=box) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=box: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=box: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[box] = [c, l, b_edit, b_del] for box in inactive: i += 1 c = Checkbutton(self.frame) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=box) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=box: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=box: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[box] = [c, l, b_edit, b_del] self.b_add = Button(self.frame, image=self.im_add, command=self.mailbox_info, width=1) self.b_add.grid(row=i + 1, column=0, columnspan=4, pady=4, padx=4, sticky='w') def quit(self): """Save configuration and destroy the dialog.""" active = [] inactive = [] for box, (c, l, b1, b2) in self.mailboxes.items(): if "selected" in c.state(): active.append(box) else: inactive.append(box) CONFIG.set("Mailboxes", "active", ", ".join(active)) CONFIG.set("Mailboxes", "inactive", ", ".join(inactive)) save_config() self.destroy() def del_mailbox(self, mailbox): """Delete the mailbox.""" try: os.remove(os.path.join(LOCAL_PATH, mailbox)) except FileNotFoundError: pass c, l, b_edit, b_del = self.mailboxes[mailbox] del(self.mailboxes[mailbox]) c.grid_forget() l.grid_forget() b_edit.grid_forget() b_del.grid_forget() def mailbox_info(self, mailbox=""): """GUI to add or modify a mailbox login information.""" def on_click(event): event.widget.focus_set() event.widget.selection_range(0, 'end') event.widget.unbind('<1>') return "break" def save(event=None): name = name_entry.get().strip() if not mailbox: # new mailbox i = self.b_add.grid_info()['row'] self.b_add.grid_configure(row=i + 1) c = Checkbutton(self.frame) c.state(('selected',)) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=name) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=name: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=name: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[name] = [c, l, b_edit, b_del] elif name != mailbox: # change name of mailbox os.remove(os.path.join(LOCAL_PATH, mailbox)) c, l, b_edit, b_del = self.mailboxes[mailbox] del(self.mailboxes[mailbox]) l.configure(text=name) b_edit.configure(command=lambda m=name: self.mailbox_info(m)) b_del.configure(command=lambda m=name: self.del_mailbox(m)) self.mailboxes[name] = [c, l, b_edit, b_del] encrypt(name, self.pwd, server_entry.get().strip(), login_entry.get().strip(), password_entry.get().strip(), folder_entry.get().strip()) top.destroy() top = Toplevel(self, class_="CheckMails") top.title(_("Login information")) top.transient(self) top.resizable(False, False) name_entry = Entry(top, justify='center', width=32) server_entry = Entry(top, justify='center', width=32) login_entry = Entry(top, justify='center', width=32) password_entry = Entry(top, show='*', justify='center', width=32) folder_entry = Entry(top, justify='center', width=32) if mailbox: name_entry.insert(0, mailbox) server, login, password, folder = decrypt(mailbox, self.pwd) if server is None: top.destroy() self.del_mailbox(mailbox) return server_entry.insert(0, server) login_entry.insert(0, login) password_entry.insert(0, password) folder_entry.insert(0, folder) else: name_entry.insert(0, "Mailbox name") server_entry.insert(0, "IMAP.mailbox.com") login_entry.insert(0, "*****@*****.**") folder_entry.insert(0, "inbox") name_entry.bind("<1>", on_click) server_entry.bind("<1>", on_click) login_entry.bind("<1>", on_click) password_entry.bind("<1>", on_click) folder_entry.bind("<1>", on_click) Label(top, text=_("Mailbox name")).grid(row=0, column=0, sticky="e", pady=(10, 4), padx=(10, 1)) Label(top, text=_("IMAP server")).grid(row=1, column=0, sticky="e", pady=4, padx=(10, 1)) Label(top, text=_("Login")).grid(row=2, column=0, sticky="e", pady=4, padx=(10, 1)) Label(top, text=_("Password")).grid(row=3, column=0, sticky="e", pady=4, padx=(10, 1)) Label(top, text=_("Folder to check")).grid(row=4, column=0, sticky="e", pady=4, padx=(10, 1)) name_entry.grid(row=0, column=1, sticky="w", pady=4, padx=(1, 10)) server_entry.grid(row=1, column=1, sticky="w", pady=4, padx=(1, 10)) login_entry.grid(row=2, column=1, sticky="w", pady=4, padx=(1, 10)) password_entry.grid(row=3, column=1, sticky="w", pady=4, padx=(1, 10)) folder_entry.grid(row=4, column=1, sticky="w", pady=4, padx=(1, 10)) frame = Frame(top) frame.grid(row=5, columnspan=2, pady=(0, 6)) Button(frame, text="Ok", command=save).grid(row=0, column=0, padx=(10, 4), pady=4) Button(frame, text=_("Cancel"), command=top.destroy).grid(row=0, column=1, pady=4, padx=(10, 4)) top.grab_set() name_entry.bind("<Key-Return>", save) server_entry.bind("<Key-Return>", save) login_entry.bind("<Key-Return>", save) password_entry.bind("<Key-Return>", save) folder_entry.bind("<Key-Return>", save) name_entry.focus_set()