def open_db(self): ''' acquire db file, or quit ''' if not os.path.isfile(dbfile): mbib_dir = os.environ.get('mbib_dir', '') empty_db = os.path.join(mbib_dir, db_template) if os.path.isfile(empty_db): while True: answer = input( "Database file %s not found. Create? (y/n) " % dbfile).lower()[:] if answer in "yn": break if answer == 'n': raise SystemExit("No database - exiting") # copy empty database file to configured path shutil.copy(empty_db, dbfile) else: raise SystemExit("Can find neither %s nor %s" % (dbfile, empty_db)) elif config['preferences'].getboolean('backup_db_file'): backup_db = "%s.bak" % dbfile if not os.path.exists(backup_db) or os.stat( backup_db).st_mtime < os.stat(dbfile).st_mtime: shutil.copy(dbfile, backup_db) # at this point, the database should be in place hub.register('sqlite', SqliteDB(dbfile)) hub.register('dbfile', dbfile) self._db = hub.sqlite
class BranchEdit(dialog.SimpleEdit): ''' we can keep this simple. ''' title_prefix = "Rename folder" prompt = "New name:" label_width = len(prompt) def setup(self): self.__super.setup() self.node = hub.tree.focus_element() self.current_name, weg = hub.get_node_text(self.node) self.title = "%s '%s'" % (self.title_prefix, self.current_name) def prevalidate(self): ''' make sure we are in the right kind of folder ''' node = hub.tree.focus_element() return hub.is_branch( node) and node[1] not in hub.coredb.special_branch_ids def show_invalid(self): hub.show_errors('This folder is protected and cannot be renamed') def process(self): ''' read out any changed data and send them off for processing. ''' new_name = self.get_edit_text() if self.current_name != new_name: hub.update_branch(self.node, self.current_name, new_name) hub.register('branch_edit', lambda *a, **kw: BranchEdit(*a, **kw).show())
class ConfirmFlatten(NodeConfirmation): outer_height = 13 processor_name = "flatten_folder" branch_question = "Erase all nested sub-folders and move their references to folder '%s'?" hub.register('confirm_flatten', lambda *a, **kw: ConfirmFlatten(*a, **kw).show())
class ConfirmErase(NodeConfirmation): #outer_height = 13 processor_name = "erase_node" ref_question = "Delete %s from ALL folders?" hub.register('confirm_erase', lambda *a, **kw: ConfirmErase(*a, **kw).show())
class ConfirmDeleteOthers(NodeConfirmation): processor_name = "delete_other_instances" ref_question = "Delete %s from all other folders?" branch_question = "Delete references in %s from all other folders?" hub.register('confirm_delete_others', lambda *a, **kw: ConfirmDeleteOthers(*a, **kw).show())
class ConfirmDelete(NodeConfirmation): processor_name = "delete_node" ref_question = "Delete %s from this folder?" branch_question = "Delete %s?" hub.register('confirm_delete', lambda *a, **kw: ConfirmDelete(*a, **kw).show())
class PubmedInput(MbibInput): title = "Import from Pubmed" prompt = "Paste pmids or give filename:" multiline = True processor_name = "import_pubmed" processor_args = dict(node="node", raw_info="edit_text") hub.register('pubmed_input', lambda *a: PubmedInput().show())
class SelectedToBibtex(SelectedToHtml): ''' shove selected references into an HTML file ''' title = "Export selected references to BibTex" processor_name = "export_bibtex" extension = ".bib" hub.register('bibtex_selected', lambda *a: SelectedToBibtex().show())
class PdfInput(CiteInput): ''' shortcut for citing a reference by exact bibtexkey we use 'like', however, to make the search case-insensitive. ''' title = "Show a PDF file" processor_name = "show_pdf_bibtexkey" hub.register('pdf_key_input', lambda *a: PdfInput().show())
class CiteInput(MbibInput): ''' shortcut for citing a reference by exact bibtexkey ''' title = "Cite reference in OOo" prompt = "BibTeX key:" processor_name = "cite_by_key" processor_args = dict(bibtexkey="edit_text") hub.register('cite_key_input', lambda *a: CiteInput().show())
class HtmlFileInput(MbibFileInput): ''' just specify a folder name. There seems to be no file browser widget; we will simply use os.path.abspath to evaluate the string given here. ''' title = "Export HTML file" prompt = "File name:" extension = '.html' processor_name = "export_html" hub.register('html_filename', lambda *a: HtmlFileInput().show())
class SelectedToHtml(MbibFileInput): ''' shove selected references into an HTML file ''' title = "Export selected references to HTML" processor_name = "export_html" processor_args = dict(file_name="edit_text") extension = ".html" _initial_text = lambda self: "selected" + self.extension hub.register('html_selected', lambda *a: SelectedToHtml().show())
class BibtexInput(PubmedInput): ''' paste bibtex code for import ''' title = "Import references from BibTex" prompt = "paste BibTex or give filename:" processor_name = "import_bibtex" outer_width = 70 outer_height = 30 hub.register('bibtex_input', lambda *a: BibtexInput().show())
class BibFileInput(MbibFileInput): ''' just specify a file name. There seems to be no file browser widget; we will simply use os.path.abspath to evaluate the string given here. We could of course use a tkinter dialog. ''' title = "Export BibTex file" prompt = "File name:" extension = ".bib" processor_name = "export_bibtex" hub.register('bibtex_filename', lambda *a: BibFileInput().show())
class EditSearch(SearchForm): def setup(self): self.__super.setup() stored = hub.saved_search() if stored is not None: self.stored_data = stored self.title = "Edit search" else: self.title = "New search (no stored search available)" hub.register('edit_search', lambda *a: EditSearch().show())
class SearchForm(RefAdd): ''' we again just need an empty form; the differences kick in only in downstream. I might prune the fields here a little - 'tis just too much, ain't it. ''' title = "Search for references" sequence = """ bibtexkey reftype purpose title keywords comment author journal year abstract pmid doi url volume number pages booktitle editor publisher """ sequence = sequence.strip().split() label_width = max([len(s) for s in sequence]) def setup(self): self.__super.setup() self.title = "New search" def prevalidate(self): ''' search can be accessed from anywhere ''' return True def process(self): ''' read out any changed data and send them off for processing. ''' hub.search_references(self.get_data()) hub.register('new_search', lambda *a: SearchForm().show())
def __init__(self, dtree, *a, **kw): ''' get hold of that blasted directory tree manually. also, export nicely wrapped methods to broker, so that they can also be invoked from within dialogs without much trouble. ''' self._dtree = dtree self.current_focus_element = None self.last_mouse_press = -3600 self.__super.__init__(*a, **kw) # let uth keep the nodes we visit in history. # if we do go back in history, let us put the nodes # to the 'right' into the future. # # visited nodes are the ones we actually do work on, such # as viewing details. Does this include viewing folders? self._history = [] self._future = [] for key in self.exposed.strip().split(): hub.register(key, getattr(self, key))
class BranchFilter(dialog.SimpleEdit): ''' filter branches recursively. Let's see how well that works ... I guess the idea is to filter all branches, top to bottom, that match the searched for phrase at any level? Show all precursors and descendants of each matching folder? Also, how do we deactivate this again? ''' title = "Filter folders by name" prompt = "Search string: " def process(self): hub.filter_folders(self.get_edit_text()) hub.register('filter_folders_dialog', lambda *a, **kw: BranchFilter(*a, **kw).show())
w = max([len(mc[1]) for mc in [_f for _f in self.menu_choices if _f]] + [len(self.title)]) self.outer_width = w + 14 # need enough space for shortcuts def process_choice(self, button, value): ''' just hand it off to the hub. ''' self.dismiss() if value: hub.process_action(value) hub.register('show_selection_menu', lambda *a: SelectionMenu().show()) class BibMenu(ResetStatusMixin, dialog.Menu): ''' well, I can see this growing quickly - with special cases for files, folders, and special folders ... ''' # outer_height = 12 this is dynamically determined in .setup outer_width = 60 # context menus differ for refs and folders, as well as by special folders. # the applicable menu is configured using nested dicts. None inserts a menu # separator. # We try to keep things consistent by defining some standard items first. _items = dict(
# finally, insert all references directly into parent, omitting duplicates remaining = set(nested_references) - set(direct_references) for ref in remaining: self._db.insert('reflink', dict(ref_id=ref, branch_id=node_id, selected=0)) # finish up self._db.commit() hub.clear_cache() hub.tree.refresh() # we should expose some methods globally, to make menu responses easier. _db = RefDb() hub.register('coredb', _db) _export = ''' bibtexkey_for_node clear_cache extend_refs filter_folders flatten_folder get_branch_title get_child_nodes get_menu_keys get_named_node get_node_display_info get_node_text get_nodes_above get_parent_node
#!/usr/bin/env python3 import os batch_arg = os.getenv('mbib_batch') # print("batch_arg", batch_arg) if batch_arg is None: from bibapp import BibApp from hub import hub hub.is_batch = False _app = BibApp() hub.register('app', _app) hub.register('tree', _app.tree) hub.register('exit', _app.exit) hub.app() else: from batchmode import BatchMode BatchMode(batch_arg)()
key, tpl = thing action, help_text = tpl self.set_widget(key, urwid.Text(help_text)) if i + 1 < len(actions): self.add_divider() def exit_buttons(self): ''' hook for creating dialog exit buttons. again, we need to figure out how to get hold of values. ''' return [("OK", "ok")] hub.register('show_help', lambda *args: HelpDialog().show()) class ErrorDialog(dialog.MessageBox): ''' just pass a list of errors and display them. Why do I have this here, under help? Also, this isn't so great - it can easily overflow, and the we can't scroll. We should rather be adding widgets to the body. Well, let's wait an see. show_errors does take a 'height' argument that can be adjusted when needed. ''' title = 'Error' message_style = 'error' outer_height = 10
class DoiInput(PubmedInput): title = "Import from DOI" prompt = "Paste DOIs or give filename:" processor_name = "import_doi" hub.register('doi_input', lambda *a: DoiInput().show())