Add "error windows", stop crashing when editing empty list of books
authorFelix Bauckholt <felixbauckholt@gmail.com>
Sun, 31 Jan 2016 02:20:38 +0000 (21:20 -0500)
committerFelix Bauckholt <felixbauckholt@gmail.com>
Sat, 6 Feb 2016 21:17:44 +0000 (16:17 -0500)
librarian
library/exceptions.py
library/interface/browser.py
library/interface/form.py

index 5335954..bbbbd18 100755 (executable)
--- a/librarian
+++ b/librarian
@@ -31,7 +31,17 @@ def menutest(s, l):
     hb.refresh()
     w = curses.newwin(15,40,(rows-10)//2, (cols-40)//2)
 
-    menu(w, l)
+    try:
+        menu(w, l)
+    except SystemExit: pass
+    except:
+        text = """An unexpected error occured.
+You can contact the librarian (librarian@csclub.uwaterloo.ca),
+but given the history of the library system, it seems unlikely
+that somebody will be around to care.
+The program will now quit."""
+        form.error_form(text, stdscr, hb)
+        raise
 
     curses.curs_set(1)
 
index a82df8a..5ae429b 100644 (file)
@@ -15,3 +15,6 @@ class PermissionsError(LibrarianException):
     @property
     def error_msg(self):
         return "Need privilege level {}".format(self.permission_string)
+
+class NoHighlightedEntry(LibrarianException):
+    error_msg = "No highlighted entry"
index 7a610a8..e1eef10 100644 (file)
@@ -1,6 +1,7 @@
 import curses
 import library.database as db
-from library.interface.form import BookForm,CategoryForm
+from library.interface.form import BookForm,CategoryForm,error_form,catch_error
+from library.exceptions import NoHighlightedEntry
 
 class browserWindow:
     # These are actually class variables, not member variables? :<
@@ -118,12 +119,18 @@ class browserWindow:
 
     def mvHighlight(self,delta):
         new = self.hl+delta
-        new = max(new,0)
         new = min(new,len(self.entries)-1)
+        new = max(new,0)
         self.unHighlight()
         self.hl = new
         self.highlight()
 
+    def highlightedEntry(self):
+        try:
+            return self.entries[self.hl]
+        except:
+            raise NoHighlightedEntry()
+
     def scroll(self,delta):
         self.unHighlight()
         self.topline += delta
@@ -277,6 +284,7 @@ class trashBrowser(browserWindow):
         bf.event_loop()
         bf.clear()
 
+    @catch_error
     def restoreSelected(self):
         books = []
         for sel,book in zip(self.selected, self.entries):
@@ -284,6 +292,7 @@ class trashBrowser(browserWindow):
                 books.append(book)
         db.restoreBooks(books)
 
+    @catch_error
     def delSelected(self):
         books = []
         for sel,book in zip(self.selected, self.entries):
@@ -294,10 +303,11 @@ class trashBrowser(browserWindow):
     def refreshBooks(self):
         self.load_data(db.getRemovedBooks())
 
+    @catch_error
     def handleInput(self,ch):
         browserWindow.handleInput(self,ch)
         if ch == 10:
-            book = self.entries[self.hl]
+            book = self.highlightedEntry()
             self.viewSelection(book)
             self.refresh()
         if ch==114: #restore books
@@ -332,6 +342,7 @@ class bookBrowser(browserWindow):
 
 
     # redefinable functions
+    @catch_error
     def updateSelection(self,book):
         bookid = book['id']
 
@@ -341,9 +352,9 @@ class bookBrowser(browserWindow):
         bf.caption='Update Book '+str(bookid)
         bf.blabel='update'
         newbook = bf.event_loop()
+        bf.clear()
         if len(newbook)!=0:
             db.updateBook(newbook,bookid)
-        bf.clear()
 
     def viewSelection(self,book):
         bookid = book['id']
@@ -363,7 +374,9 @@ class bookBrowser(browserWindow):
         cs.refreshCategories()
         cs.eventLoop()
         cs.clear()
+        self.refreshBooks()
 
+    @catch_error
     def delSelected(self):
         books = []
         for sel,book in zip(self.selected, self.entries):
@@ -390,22 +403,23 @@ class bookBrowser(browserWindow):
         self.refreshBooks = lambda : self.load_data(db.get_onshelf_books())
         self.refreshBooks()
 
+    @catch_error
     def handleInput(self,ch):
         browserWindow.handleInput(self,ch)
         if ch == 117: #update on 'u'
-            book = self.entries[self.hl]
+            book = self.highlightedEntry()
             self.updateSelection(book)
             self.entries[self.hl]=db.get_book(book['id'])
             self.refresh()
         elif ch == 10:
-            book = self.entries[self.hl]
+            book = self.highlightedEntry()
             self.viewSelection(book)
             self.refresh()
-        elif ch == 99:
-            book = self.entries[self.hl]
+        elif ch == 99: #c
+            book = self.highlightedEntry()
             self.categorizeSelection(book)
             self.refresh()
-        if ch==100:
+        if ch == 100:
             count=0
             for s in self.selected[0:self.hl-1]:
                 if s:
@@ -425,22 +439,24 @@ class categoryBrowser(browserWindow):
         self.load_data(db.getCategories())
         self.sortByColumn('category')
 
+    @catch_error
     def addCategory(self):
         w = curses.newwin(1,1,10,10)
         cf = CategoryForm(w,self.hb)
         self.centreChild(w)
         cat = cf.event_loop()
-        db.addCategory(cat)
         cf.clear()
+        db.addCategory(cat)
 
     def viewCategory(self):
         w = curses.newwin(3,5)
         b = bookBrowser(w,self.hb)
         self.centreChild(w)
-        b.refreshBooksInCategory(self.entries[self.hl])
+        b.refreshBooksInCategory(self.highlightedEntry())
         b.eventLoop()
         b.clear()
 
+    @catch_error
     def delSelected(self):
         categories = []
         for sel,cat in zip(self.selected, self.entries):
@@ -496,7 +512,7 @@ class categorySelector(browserWindow):
             j+=1
         self.selected = self.original[:]
 
-
+    @catch_error
     def addCategory(self):
         w = curses.newwin(1,1,10,10)
         cf = CategoryForm(w,self.hb)
@@ -506,6 +522,7 @@ class categorySelector(browserWindow):
             db.addCategory(c)
         cf.clear()
 
+    @catch_error
     def updateCategories(self):
         # first removed the deselected ones
         uncats = []
@@ -558,8 +575,8 @@ class columnSelector(browserWindow):
         ch = self.w.getch()
         while ch != 27 and ch != 113:
             ch = self.handleInput(ch)
-            if ch==10:
-                col = self.entries[self.hl]
+            if ch == 10:
+                col = self.highlightedEntry()
                 return col['column']
             self.w.refresh()
             ch = self.w.getch()
index 619f3ae..85c09f0 100644 (file)
@@ -1,4 +1,5 @@
 import curses
+from library.exceptions import LibrarianException
 
 
 class TextEntry:
@@ -316,3 +317,54 @@ class CategoryForm(FormWindow):
 
     def _return_values(self):
         return self.entries[0].value
+
+class DummyTextEntry(TextEntry):
+    def redraw(self): pass
+
+class ErrorForm(FormWindow):
+    caption = "Error"
+    blabel = "OK"
+    def __init__(self,window,helpbar,errortext,width=50):
+        self.labels = errortext.split("\n")
+        super().__init__(window, helpbar, width=width)
+        self.bt = 1
+
+    def _make_entries(self):
+        self.entries = []
+        for e in range(len(self.labels)):
+            self.entries.append(DummyTextEntry(self.w))
+
+    def redraw(self):
+        self.w.box()
+        self.w.addstr(0,(self.mx-len(self.caption))//2,self.caption)
+        r=0
+        for l in self.labels:
+            c = 2
+            self.w.addstr(r+self.top,c,l)
+            self.entries[r].redraw()
+            r+=1
+        self.w.addstr(self.brow,self.bcol[1], "<"+self.blabel+">")
+        self.w.chgat(self.brow, self.bcol[1], len(self.blabel)+2, curses.A_REVERSE)
+        self.w.refresh()
+
+    def _mv_focus(self,delta): pass
+
+def error_form(text, w, hb):
+    width = max([len(l) for l in text.split("\n")]) + 4
+    child=curses.newwin(1,1)
+    f = ErrorForm(child, hb, text, width)
+    (my,mx)=w.getmaxyx()
+    (r,c)=child.getmaxyx()
+    child.mvwin((my-r)//2,(mx-c)//2)
+    w.refresh()
+    f.event_loop()
+    f.clear()
+
+def catch_error(fn):
+    def wrapper_fun(self, *args, **kwd):
+        try:
+            return fn(self, *args, **kwd)
+        except LibrarianException as e:
+            error_form(str(e), self.w, self.hb)
+            self.refresh()
+    return wrapper_fun