04550b90eebe6090d136c28fa30f998eaf7e2358
[library/.git] / browser.py
1 import sys
2 import curses
3 import dbLayer as db
4 from form import bookForm,categoryForm
5
6 class browserWindow:
7     hl=0
8     topline = 0
9     entries = []
10     selected = []
11     commands = [(' q', 'quit')]
12     # column definitions are in (label, weight, specified width) triples
13     columnDefs = [('something',1,None)]
14     mx = my = 0
15
16
17     def clear(self):
18         self.w.erase()
19         self.w.refresh()
20
21     def __init__(self,window,helpbar):
22         self.w = window
23         self.hb = helpbar
24         self.updateGeometry()
25
26     def sortByColumn(self, col):
27         self.entries.sort(key=lambda k: k.get(col)) # key=dict.get(col))
28         self.selected = map(lambda x: False, self.selected)
29
30     def updateGeometry(self):
31         (self.my,self.mx)=self.w.getmaxyx()
32         self.pageSize = self.my-3
33         self.calcColWidths()
34
35     def calcColWidths(self):
36         total_weights = 0
37         available_space = self.mx - len(self.columnDefs)
38         cols = []
39         for label,weight,value in self.columnDefs:
40             if value!=None:
41                 available_space -= value
42             else:
43                 total_weights+=weight
44
45         for label,weight,value in self.columnDefs:
46             if value!=None:
47                 cols.append((label,value))
48             else:
49                 cols.append((label,available_space*weight/total_weights))
50         self.columns=cols
51
52     def refresh(self):
53         self.hb.commands = self.commands
54         self.hb.refresh()
55         self.displayHeader()
56         for r in range(0,self.pageSize):
57             self.displayRow(r)
58         self.w.refresh()
59         self.highlight()
60
61     def displayHeader(self):
62         cursor = 1
63         for header,width in self.columns:
64             self.w.addnstr(0,cursor,header+" "*width,width)
65             self.w.addstr(1,cursor,"-"*width)
66             cursor += width+1
67
68     def displayRow(self,row):
69         if self.topline+row < len(self.entries):
70             entry = self.entries[self.topline+row]
71             cursor = 1
72             if self.selected[self.topline+row]:
73                 self.w.addstr(row+2, 0, "*")
74             else:
75                 self.w.addstr(row+2, 0, " ")
76             for k,width in self.columns:
77                 if k.lower() in entry:
78                     self.w.addnstr(row+2,cursor,str(entry[k.lower()])+" "*width,width)
79                 cursor += width+1
80         else:
81             self.w.addstr(row+2,0," "*self.mx)
82
83     def highlight(self):
84         row = self.hl-self.topline+2
85         if row > 1 and row < self.my:
86             self.w.chgat(row,0,self.mx,curses.A_REVERSE)
87
88     def unHighlight(self):
89         row = self.hl-self.topline+2
90         if row > 1 and row < self.my:
91             self.w.chgat(row,0,self.mx,curses.A_NORMAL)
92
93     def mvHighlight(self,delta):
94         new = self.hl+delta
95         new = max(new,0)
96         new = min(new,len(self.entries)-1)
97         self.unHighlight()
98         self.hl = new
99         self.highlight()
100     
101     def scroll(self,delta):
102         self.unHighlight()
103         self.topline += delta
104         self.topline = max(self.topline,0)
105         self.topline = min(self.topline,len(self.entries)-1)
106         self.refresh()
107
108     def eventLoop(self):
109         self.w.keypad(1)
110         self.refresh()
111
112         ch = self.w.getch()
113         while ch != 27 and ch != 113:
114             ch = self.handleInput(ch)
115             if ch==113:
116                 return {}
117             self.w.refresh()
118             ch = self.w.getch()
119
120     def handleInput(self,ch):
121         if ch == curses.KEY_UP or ch == 107 or ch == 16:
122             if self.hl == self.topline:
123                 self.scroll(-self.pageSize/2-1)
124             self.mvHighlight(-1)
125         elif ch == curses.KEY_DOWN or ch == 106 or ch == 14:
126             if self.hl == self.topline+self.pageSize-1:
127                 self.scroll(+self.pageSize/2+1)
128             self.mvHighlight(+1)
129         elif ch == curses.KEY_PPAGE:
130             self.scroll(-self.pageSize)
131             self.mvHighlight(-self.pageSize)
132         elif ch == curses.KEY_NPAGE:
133             self.scroll(+self.pageSize)
134             self.mvHighlight(+self.pageSize)
135         elif ch == 32:
136             self.selected[self.hl] = not self.selected[self.hl]
137             self.displayRow(self.hl-self.topline)
138             self.highlight()
139
140
141
142 class bookBrowser(browserWindow):
143     columnDefs = [('ID',0,3),
144                   ('ISBN',0,13),
145                   ('Authors',30,None),
146                   ('Title',60,None)]
147     
148     commands = [(' u', 'update'), (' d', 'delete selected'), (' q', 'quit')]
149     
150     # redefinable functions
151     def updateSelection(self,book):
152         bookid = book['id']
153         
154         w=curses.newwin(1,1,20,20)
155         bf=bookForm(w,self.hb,book)
156         bf.caption='Update Book '+str(bookid)
157         bf.blabel='update'
158         newbook = bf.eventLoop()
159         if len(newbook)!=0:
160             db.updateBook(newbook,bookid)
161         bf.clear()
162
163     def viewSelection(self,book):
164         bookid = book['id']
165         w=curses.newwin(1,1,20,20)
166         bf = bookForm(w,self.hb,book)
167         bf.caption='Viewing Book '+str(bookid)
168         bf.blabel='done'
169         bf.eventLoop()
170         bf.clear()
171
172     def categorizeSelection(self,book):
173         w = curses.newwin(40,20,20,20)
174         cs = categorySelector(w,self.hb)
175         cs.book = book
176         cs.refreshCategories()
177         cs.eventLoop()
178         cs.clear()
179     
180     def delSelected(self):
181         books = []
182         for sel,book in zip(self.selected, self.entries):
183             if sel:
184                 books.append(book)
185         db.removeBooks(books)
186
187     def refreshBooks(self):
188         self.entries = db.getBooks()
189         self.selected = map(lambda x:False, self.entries)
190
191     def handleInput(self,ch):
192         browserWindow.handleInput(self,ch)
193         if ch == 117: #update on 'u'
194             book = self.entries[self.hl]
195             self.updateSelection(book)
196             self.entries[self.hl]=db.getBookByID(book['id'])
197             self.refresh()
198         elif ch == 10:
199             book = self.entries[self.hl]
200             self.viewSelection(book)
201             self.refresh()
202         elif ch == 99:
203             book = self.entries[self.hl]
204             self.categorizeSelection(book)
205             self.refresh()
206         if ch==100:
207             count=0
208             for s in self.selected[0:self.hl-1]:
209                 if s:
210                     count+=1
211             self.delSelected()
212             self.refreshBooks()
213             self.refresh()
214             self.scroll(-count)
215             self.mvHighlight(-count)
216         return ch
217
218 class categoryBrowser(browserWindow):
219     columnDefs = [('Category',100,None)]
220     commands = [(' a', 'add category'), (' d', 'delete selected'), (' q', 'quit')]
221
222
223     def refreshCategories(self):
224         self.entries = db.getCategories()
225         self.sortByColumn('category')
226         self.selected = map(lambda x:False, self.entries)
227
228     def addCategory(self):
229         w = curses.newwin(1,1,10,10)
230         cf = categoryForm(w,self.hb)
231         cats = cf.eventLoop()
232         for c in cats:
233             db.addCategory(c)
234         cf.clear()
235
236     def delSelected(self):
237         categories = []
238         for sel,cat in zip(self.selected, self.entries):
239             if sel:
240                 categories.append(cat)
241         db.deleteCategories(categories)
242
243     def handleInput(self,ch):
244         browserWindow.handleInput(self,ch)
245         if ch==97:
246             self.addCategory()
247             self.refreshCategories()
248             self.refresh()
249         if ch==100:
250             count=0
251             for s in self.selected[0:self.hl-1]:
252                 if s:
253                     count+=1
254             self.delSelected()
255             self.refreshCategories()
256             self.refresh()
257             self.scroll(-count)
258             self.mvHighlight(-count)
259         return ch
260
261 class categorySelector(browserWindow):
262     columnDefs = [('Category',100,None)]
263     commands = [(' a', 'add category'), (' c', 'commit'), (' q', 'quit')]
264     book = {'id':''}
265     original=[]
266
267
268     def refreshCategories(self):
269         self.entries = db.getCategories()
270         self.sortByColumn('category')
271         self.refreshSelected()
272
273     def refreshSelected(self):
274         self.original = map(lambda x:False, self.entries)
275         cats = db.getBookCategories(self.book)
276         cats.sort()
277         cats.sort(key=lambda k: k.get('category')) # key=dict.get(col))
278         i = 0
279         j = 0
280         for cat in self.entries:
281             if i == len(cats):
282                 break
283             if cat['id']==cats[i]['cat_id']:
284                 self.original[j] = True;
285                 i+=1
286             j+=1
287         self.selected = self.original[:]
288
289
290     def addCategory(self):
291         w = curses.newwin(1,1,10,10)
292         cf = categoryForm(w,self.hb)
293         cats = cf.eventLoop()
294         for c in cats:
295             db.addCategory(c)
296         cf.clear()
297
298     def updateCategories(self):
299         # first removed the deselected ones
300         uncats = []
301         cats = []
302         for old, new, category in zip(self.original, self.selected, self.entries):
303             if old and (not new):
304                 uncats.append(category)
305             if (not old) and new:
306                 cats.append(category)
307         db.uncategorizeBook(self.book, uncats)
308         # add the newly selected categories
309         db.categorizeBook(self.book, cats)
310
311
312     def handleInput(self,ch):
313         browserWindow.handleInput(self,ch)
314         if ch==97:
315             self.addCategory()
316             self.refreshCategories()
317             self.refresh()
318         if ch==99:
319             self.updateCategories()
320             return 113
321