class FolderProvider: def __init__( self ): self._business = BusinessLogic() self._business.initDatabase() self._folder_id_iid_ref: Dict = { 0: '' } # key: id, value: iid self._folderImage = ImageFactory.getInstance().imgFolder def provideFolders( self, tree: NotesTree ) -> Dict: #key: id, value: iid folder_id_iid_ref: Dict = { 0: '' } self._provideFolders( tree, folder_id_iid_ref, 0, '' ) return folder_id_iid_ref def _provideFolders( self, tree:NotesTree, folder_id_iid_ref:Dict, parent_id:int, parent_iid:str ): folders: List[Tuple] = self._business.getFolders( parent_id ) for f in folders: id, parent_id, text = f iid = tree.addFolder( parent_iid, id, text, self._folderImage ) folder_id_iid_ref[id] = iid # each id could have subfolders: self._provideFolders( tree, folder_id_iid_ref, id, iid )
class Controller: def __init__(self, mainframe: MainFrame): self._mainframe = mainframe self._tree: NotesTree = mainframe.tree self._edi: NoteEditor = mainframe.edi self._edi.setCtrlSCallback(self.onCtrlS) self._status = mainframe.statusbar self._tree.setTreeCallback(self._treeCallback) self._mainframe.setToolActionCallback(self.toolActionCallback) self._folder_id_iid_ref: Dict = {0: ''} #key: id, value: iid self._note_id_iid_ref: Dict = {} #key: id, value: iid self._imgFolder: PhotoImage = None #= PhotoImage( file="/home/martin/Projects/python/notes/images/folder_16.png" ) self._imgNote: PhotoImage = None #= PhotoImage( file="/home/martin/Projects/python/notes/images/note_16.png" ) self._business = BusinessLogic() self._options = Options() def startWork(self): if self._options.getOption("download_db") == "1": #get us the last saved database self._business.downloadDatabase() self._business.initDatabase() self._imgFolder = ImageFactory.getInstance().imgFolder self._imgNote = ImageFactory.getInstance().imgNote # set tree's folders: self._setFolders(parent_id=0, parent_iid='') self._setNotes() def isNoteModified(self) -> bool: return self._edi.isModified() def endWork(self): if self._options.getOption("download_db") == "1": #only upload db if downloaded previously self._business.uploadDatabase() print("Database successfully uploaded to server.") def _setFolders(self, parent_id: int, parent_iid: str): folders: List[Tuple] = self._business.getFolders(parent_id) for f in folders: id, parent_id, text = f iid = self._tree.addFolder(parent_iid, id, text, self._imgFolder) self._folder_id_iid_ref[id] = iid # each id could have subfolders: self._setFolders(id, iid) def _setNotes(self): headers = self._business.getHeaders() for h in headers: id, parent_id, header = h iid_parent = self._folder_id_iid_ref[parent_id] self._note_id_iid_ref[id] = self._tree.addNote( iid_parent, id, header, self._imgNote) def _treeCallback(self, action: TreeAction, treeItems: List[TreeItem]) -> None: # itemspec: FOLDER or NOTE nItems: int = len(treeItems) if action == TreeAction.DELETE: if nItems == 1: self.deleteItem(treeItems[0]) else: self.deleteItems(treeItems) elif action == TreeAction.SELECT: treeItem = treeItems[0] if nItems > 1 or treeItem.isNote == False: return self._handleOneNoteSelection(treeItem) elif action == TreeAction.MOVE: self._moveAction(treeItems[0]) elif action == TreeAction.INSERT_FOLDER: self.newFolderAction(treeItems[0]) elif action == TreeAction.RENAME: self.renameAction(treeItems[0]) def _handleOneNoteSelection(self, treeItem: TreeItem) -> None: #before changing the displayed note, check if there are unsaved changes: if self._edi.isModified(): note: Note = self._edi.getNote() if messagebox.askyesno( "Unsaved Changes", "Note '" + note.header + "' has been modified.\nSave changes?"): self.saveNoteLocalAction() #show selected note: note: Note = self._business.getNote(treeItem.id) self._edi.setNote(note) def _moveAction(self, treeItem: TreeItem): def _onOk(ok: bool): folder: TreeItem = folderDlg.getSelectedFolder() if treeItem.isNote: self._business.changeNoteParent(treeItem.id, folder.id) self._tree.moveItem(treeItem.iid, folder.iid) else: self._business.changeFolderParent(treeItem.id, folder.id) self._tree.moveItem(treeItem.iid, folder.iid) folderDlg = FolderDialog(self._mainframe) folderDlg.setOkCancelCallback(_onOk) def _syncIdIIdReferences(self): #note id/iid references: idlist: List[int] = self._business.getAllNoteIds() self._note_id_iid_ref = [ x for x in self._note_id_iid_ref if x in set(idlist) ] #folder id/iid references: idlist = self._business.getAllFolderIds() self._folder_id_iid_ref = [ x for x in self._folder_id_iid_ref if x in set(idlist) ] def deleteItems(self, treeItems: List[TreeItem]): yes = messagebox.askyesno("Confirmation Prompt", "Really delete the selected items?", icon='warning') if not yes: return for item in treeItems: self.deleteItem(item, False) def deleteItem(self, treeItem: TreeItem, ask: bool = True): yes: bool = True if ask: q = "Really delete this folder and all its content?" if treeItem.isNote == False else \ "Really delete this note?" yes = messagebox.askyesno("Confirmation Prompt", q, icon='warning') if yes: self._business.deleteItem(treeItem.id, NOTE if treeItem.isNote else FOLDER) self._tree.remove(treeItem.iid) if treeItem.isNote: self._note_id_iid_ref.pop(treeItem.id) self._edi.clear() else: #The deleted folder might have contained subfolders and notes. #We have to synchronize _folder_id_iid_ref and _note_id_iid_ref. self._syncIdIIdReferences() def toolActionCallback(self, action: ToolAction) -> None: if action == ToolAction.NEW_TOPFOLDER: self.newTopFolderAction() elif action == ToolAction.NEW_NOTE: #print( "Controller.toolActionCallback - ToolAction.NEW_NOTE" ) self.newNoteAction() elif action == ToolAction.SAVE_LOCAL: self.saveNoteLocalAction() elif action == ToolAction.SAVE_REMOTE: self.saveRemoteAction() elif action == ToolAction.FONT_BOLD: self._edi.triggerStyleAction(StyleAction.BOLD) elif action == ToolAction.FONT_ITALIC: self._edi.triggerStyleAction(StyleAction.ITALIC) elif action == ToolAction.FONT_RED: self._edi.triggerStyleAction(StyleAction.RED_FOREGROUND) elif action == ToolAction.FONT_BLUE: self._edi.triggerStyleAction(StyleAction.BLUE_FOREGROUND) elif action == ToolAction.FONT_GREEN: self._edi.triggerStyleAction(StyleAction.GREEN_FOREGROUND) elif action == ToolAction.FONT_BLACK: self._edi.triggerStyleAction(StyleAction.BLACK_FOREGROUND) elif action == ToolAction.MARK_AS_LINK: self._edi.triggerStyleAction(StyleAction.MARK_AS_LINK) def onCtrlS(self): self.saveNoteLocalAction() def newTopFolderAction(self): s = simpledialog.askstring("New top level folder", "Name of new folder:", initialvalue="") if s: id: int = self._business.createTopLevelFolder(s) self._tree.addFolder('', id, s, self._imgFolder, True) def newFolderAction(self, treeItem: TreeItem): s = simpledialog.askstring("New folder", "Name of new folder:", initialvalue="") if s: id: int = self._business.createFolder(s, treeItem.id, True) self._tree.addFolder(treeItem.iid, id, s, self._imgFolder, True) def newNoteAction(self): note: Note = self._edi.getNote() if self._edi.isModified(): if messagebox.askyesno( "Unsaved Changes", "There are unsaved changes. Do you want them to be saved?" ): if self.saveNoteLocalAction() != SaveResult.OK: return self._edi.setNote(Note()) #print( "Controller._tree.unsetSelection" ) self._tree.unsetSelection() def renameAction(self, treeItem: TreeItem) -> None: s = simpledialog.askstring("Rename", "Enter new name:", initialvalue=treeItem.label) if s: try: self._business.renameFolder(treeItem.id, s) self._tree.item(treeItem.iid, text=s) treeItem.label = s except Exception as ex: messagebox.showerror("Error renaming Item", ex) def saveNoteLocalAction(self) -> SaveResult: """ First check if this is the first time the note is saved. If so, check if a folder is selected. If not, give a proper hint. Save the note """ note: Note = self._edi.getNote() if len(note.header) == 0 and len(note.text) == 0: if not messagebox.askyesno( "No Heading specified", "Do you really wish to save this note without caption?\n" "No text will be shown in the tree afterwards."): return SaveResult.NEED_CAPTION if note.id <= 0: #new note, not yet saved iid_parent = '' iid, id, text, itemspec = self._tree.getSelectedTreeItemAsTuple() if iid == '0' or iid == '': # no tree item selected if not messagebox.askyesno( "No Folder Selected", "Do you really wish to save this note outside any folder?" ): return SaveResult.NEED_FOLDER else: # a tree item is selected - is it a note or a folder? if not itemspec == FOLDER: # another note is selected (and opened). Get that note's folder. folder_id, foldername = self._business.getNoteFolder(id) iid_parent = self._folder_id_iid_ref[folder_id] note.parent_id = folder_id else: # a folder is selected. Make sure the new note is to be saved in this folder. iid_parent = iid note.parent_id = id foldername = text if not messagebox.askyesno( "Save Note", "Save this note in folder\n" + foldername + "?"): return SaveResult.CANCEL self._business.insertNote(note) iid_note = self._tree.addNote(iid_parent, note.id, note.header, self._imgNote) self._note_id_iid_ref[note.id] = iid_note self._tree.openFolder(iid_parent) self._tree.setSelection(iid_note) self._edi.resetModified() return SaveResult.OK else: #update an existing note self._business.updateNote(note) self._edi.resetModified() #maybe the header has changed, so let the tree update its label item_iid = self._note_id_iid_ref[note.id] self._tree.updateLabel(item_iid, note.header) return SaveResult.OK def saveRemoteAction(self): # first save changes local #self.saveNoteLocalAction() self._business.uploadDatabase()