]> asedeno.scripts.mit.edu Git - pssh.git/blob - data/recordlist.c
Greg Parker's 2005-06-23 release.
[pssh.git] / data / recordlist.c
1 /**********
2  * Copyright (c) 2003-2005 Greg Parker.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY GREG PARKER ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  **********/
24
25 #include "includes.h"
26 #include "formutils.h"
27 #include "recordlist.h"
28
29
30 struct RecordList {
31     DmOpenRef db;
32
33     UInt16 formID;
34     UInt16 tableID;
35     UInt16 scrollbarID;
36
37     RecordListDrawProc draw;
38
39     UInt16 topVisibleIndex;
40     UInt16 selectedIndex;
41 };
42
43 static RecordList **recordListList = NULL;
44 static int recordListCount = 0;
45 static int recordListAllocated = 0;
46
47 static RecordList *RecordListForTable(TablePtr table) RECORDLIST_SEGMENT;
48 static void AddRecordList(RecordList *rl) RECORDLIST_SEGMENT;
49 static void RemoveRecordList(RecordList *rl) RECORDLIST_SEGMENT;
50 static Int16 IndexToRow(RecordList *rl, UInt16 index) RECORDLIST_SEGMENT;
51 static Int16 TotalRowCount(TablePtr table) RECORDLIST_SEGMENT;
52 static Int16 VisibleRowCount(TablePtr table) RECORDLIST_SEGMENT;
53 static void RecordListRepopulate(RecordList *rl) RECORDLIST_SEGMENT;
54 static void RecordListDraw(RecordList *rl) RECORDLIST_SEGMENT;
55 static void RecordListDrawRecord(void *t, Int16 row, Int16 column, RectanglePtr bounds) RECORDLIST_SEGMENT;
56
57 static RecordList *RecordListForTable(TablePtr table)
58 {
59     int i;
60     UInt16 tableIndex;
61     UInt16 tableID;
62     UInt16 formID;
63
64     formID = FrmGetActiveFormID();
65     tableIndex = FrmGetObjectIndexFromPtr(FrmGetActiveForm(), table);
66     tableID = FrmGetObjectId(FrmGetActiveForm(), tableIndex);
67
68     for (i = 0; i < recordListCount; i++) {
69         RecordList *r = recordListList[i];
70         if (r->formID == formID  &&  r->tableID == tableID) return r;
71     }
72
73     return NULL;
74 }
75
76
77 static void AddRecordList(RecordList *rl)
78 {
79     if (recordListCount == recordListAllocated) {
80         RecordList **newList;
81         int i;
82
83         recordListAllocated += recordListAllocated + 1;
84         newList = MemPtrNew(recordListAllocated * sizeof(RecordList *));
85         for (i = 0; i < recordListCount; i++) {
86             newList[i] = recordListList[i];
87         }
88         if (recordListList) MemPtrFree(recordListList);
89         recordListList = newList;
90     }
91
92     recordListList[recordListCount++] = rl;
93 }
94
95
96 static void RemoveRecordList(RecordList *rl)
97 {
98     int i;
99     
100     for (i = 0; i < recordListCount; i++) {
101         if (recordListList[i] == rl) {
102             recordListList[i] = recordListList[--recordListCount];
103             return;
104         }
105     }
106 }
107
108
109 // create and populate
110 RecordList *RecordListNew(DmOpenRef newDB, UInt16 newFormID, UInt16 newTableID, UInt16 newScrollbarID, RecordListDrawProc newDraw)
111 {
112     RecordList *rl = MemPtrNew(sizeof(RecordList));
113     rl->db = newDB;
114     rl->formID = newFormID;
115     rl->tableID = newTableID;
116     rl->scrollbarID = newScrollbarID;
117     rl->draw = newDraw;
118
119     // fixme read some preferences and restore last selection and visible
120     rl->topVisibleIndex = 0;
121     rl->selectedIndex = noRecord;
122
123     AddRecordList(rl);
124     return rl;
125 }
126
127
128 void RecordListFree(RecordList *rl)
129 {
130     // fixme write some preferences here
131     RemoveRecordList(rl);
132     MemPtrFree(rl);
133 }
134
135
136 UInt16 RecordListCount(RecordList *rl)
137 {
138     return DmNumRecords(rl->db);
139 }
140
141
142 static Int16 IndexToRow(RecordList *rl, UInt16 index)
143 {
144     if (index != noRecord  &&  index >= rl->topVisibleIndex  &&  
145         index < DmNumRecords(rl->db)) 
146     {
147         return index - rl->topVisibleIndex;
148     } else {
149         return -1;
150     }
151 }
152
153
154 static Int16 TotalRowCount(TablePtr table)
155 {
156     return TblGetNumberOfRows(table);
157 }
158
159
160 static Int16 VisibleRowCount(TablePtr table)
161 {
162     Coord rowHeight;
163     RectangleType bounds;
164     TblGetBounds(table, &bounds);
165     rowHeight = TblGetRowHeight(table, 0);
166     return MIN(bounds.extent.y / rowHeight, TotalRowCount(table));
167 }
168
169
170 void RecordListUpdate(RecordList *rl)
171 {
172     RecordListRepopulate(rl);
173     FrmUpdateForm(rl->formID, rl->tableID);
174 }
175
176
177 static void RecordListRepopulate(RecordList *rl)
178 {
179     TablePtr table;
180     int totalRows, row, visibleRows, totalRecords;
181     UInt16 index;
182
183     if (FrmGetActiveFormID() != rl->formID) return;
184
185     table = PrvGetObjectByID(rl->tableID);
186     totalRecords = RecordListCount(rl);
187     totalRows = TotalRowCount(table);
188     visibleRows = VisibleRowCount(table);
189
190     if (rl->topVisibleIndex + visibleRows > totalRecords) {
191         // top visible leaves a gap at the bottom - move it up
192         rl->topVisibleIndex = MAX(totalRecords - visibleRows, 0);
193     }
194
195     index = rl->topVisibleIndex;
196     
197     for (row = 0; row < visibleRows; row++) {
198         MemHandle 
199             recordH = DmQueryNextInCategory(rl->db, &index, dmAllCategories);
200         if (!recordH) {
201             // no record for this row or any further rows
202             break;
203         } else {
204             // install record into row
205             // UInt32 uniqueID; 
206             // DmRecordInfo(ConnectionDB, index, NULL, &uniqueID, NULL);
207
208             TblSetItemStyle(table, row, 0, customTableItem);
209
210             TblSetRowUsable(table, row, true);
211             TblSetRowID(table, row, index);
212             // TblSetRowData(table, row, uniqueID);
213             TblMarkRowInvalid(table, row);
214
215             index++;
216         }
217     }
218
219     // mark the rest of the table unusable, if any
220     for ( ; row < totalRows; row++) {
221         TblSetRowUsable(table, row, false);
222         TblSetRowID(table, row, noRecord);
223         TblMarkRowInvalid(table, row);
224     }
225
226     TblSetCustomDrawProcedure(table, 0, RecordListDrawRecord);
227     TblSetColumnUsable(table, 0, true);
228
229     // update scoll bar
230     {
231         ScrollBarPtr scl = PrvGetObjectByID(rl->scrollbarID);
232         Int16 value, min, max, pageSize;
233
234         min = 0;
235         pageSize = visibleRows - 1; // page-scroll overlaps by 1
236         if (totalRecords > pageSize) {
237             max = totalRecords - pageSize - 1;
238             value = rl->topVisibleIndex;
239         } else {
240             // everything fits at once with no scrolling
241             value = 0;
242             max = 0;
243             pageSize = 0;
244         }
245         SclSetScrollBar(scl, value, min, max, pageSize);
246     }
247 }
248
249
250 static void RecordListDraw(RecordList *rl)
251 {
252     TablePtr table;
253     int row, visibleRows;
254
255     if (FrmGetActiveFormID() != rl->formID) return;
256
257     table = PrvGetObjectByID(rl->tableID);
258     visibleRows = VisibleRowCount(table);
259
260     TblDrawTable(table); // draws with NO selection!
261     row = IndexToRow(rl, rl->selectedIndex);
262     if (row >= 0  &&  row < visibleRows) {
263         // fixme use TblGetLastUsableRow?
264         TblUnhighlightSelection(table);
265         TblSelectItem(table, row, 0);
266     }
267 }
268
269
270 UInt16 RecordListSelectedIndex(RecordList *rl)
271 {
272     return rl->selectedIndex;
273 }
274
275
276 void RecordListSetSelectedIndex(RecordList *rl, UInt16 index)
277 {
278     rl->selectedIndex = index;
279     // fixme scroll to selectedIndex if it's not in view
280     RecordListUpdate(rl);
281 }
282
283
284 void RecordListClearSelection(RecordList *rl)
285 {
286     rl->selectedIndex = noRecord;
287     RecordListUpdate(rl);
288 }
289
290
291 MemHandle RecordListQuerySelectedRecord(RecordList *rl)
292 {
293     UInt16 index = RecordListSelectedIndex(rl);
294     return RecordListQueryIndexedRecord(rl, index);
295 }
296
297
298 MemHandle RecordListQueryIndexedRecord(RecordList *rl, UInt16 index)
299 {
300     if (index == noRecord) return NULL;
301     else return DmQueryRecord(rl->db, index);
302 }
303
304
305 MemHandle RecordListGetIndexedRecord(RecordList *rl, UInt16 index)
306 {
307     if (index == noRecord) return NULL;
308     else return DmGetRecord(rl->db, index);
309 }
310
311
312 // if createWithSize is ZERO, then return the record if found or NULL.
313 // if createWithSize is NON-ZERO, then return the RESIZED record or a NEW one.
314 // If a new record is created, the selection is CHANGED to that record.
315 // The returned record must be released with RecordListReleaseRecord.
316 MemHandle RecordListGetSelectedRecord(RecordList *rl, UInt32 createWithSize)
317 {
318     UInt16 index = RecordListSelectedIndex(rl);
319
320     if (index != noRecord) {
321         if (createWithSize == 0) {
322             // Selection exists and no resize requested.
323             return DmGetRecord(rl->db, index);
324         } else {
325             // Selection exists and resize requested.
326             return DmResizeRecord(rl->db, index, createWithSize);
327         }
328     }
329     else {
330         if (createWithSize == 0) {
331             // No selection and no creation requested. 
332             return NULL;
333         } else {
334             // No selection but record creation requested.
335             MemHandle recordH;
336             index = DmNumRecords(rl->db);
337             recordH = DmNewRecord(rl->db, &index, createWithSize);
338             if (recordH) {
339                 // zero it out
340                 DmSet(MemHandleLock(recordH), 0, createWithSize, 0);
341                 MemHandleUnlock(recordH);
342                 RecordListSetSelectedIndex(rl, index);
343             }
344             return recordH;
345         }
346     }
347 }
348
349
350 void RecordListReleaseRecord(RecordList *rl, MemHandle recordH, Boolean dirty)
351 {
352     DmOpenRef db = rl->db;
353     UInt16 index = DmSearchRecord(recordH, &db);
354     // fixme errors
355     DmReleaseRecord(db, index, dirty);
356     if (dirty) RecordListUpdate(rl);
357 }
358
359
360 void RecordListDeleteSelectedRecord(RecordList *rl)
361 {
362     RecordListDeleteIndexedRecord(rl, rl->selectedIndex);
363 }
364
365
366 void RecordListDeleteIndexedRecord(RecordList *rl, UInt16 index)
367 {
368     if (index != noRecord) {
369         DmRemoveRecord(rl->db, index);
370         if (index == rl->selectedIndex) {
371             rl->selectedIndex = noRecord;
372         }
373         RecordListUpdate(rl);
374     }
375 }
376
377
378 static void RecordListDrawRecord(void *t, Int16 row, Int16 column, 
379                                  RectanglePtr bounds)
380 {
381     TablePtr table = (TablePtr)t;
382     UInt16 index;
383     MemHandle recordH;
384
385     RecordList *rl = RecordListForTable(table);
386
387     index = TblGetRowID(table, row);
388     if (index == noRecord) return;
389
390     recordH = DmQueryRecord(rl->db, index);
391     if (recordH) {
392         WinPushDrawState();
393
394         rl->draw(MemHandleLock(recordH), index, bounds);
395
396         WinPopDrawState();
397         MemHandleUnlock(recordH); 
398     }
399 }
400
401
402 Boolean RecordListHandleEvent(RecordList *rl, EventPtr event)
403 {
404     switch (event->eType) {
405     case winEnterEvent:
406         if (FrmGetFormId((FormPtr)event->data.winEnter.enterWindow) == rl->formID) {
407             // fixme could record dirty indicator
408             FrmUpdateForm(rl->formID, rl->tableID);
409         }
410         break;
411
412     case frmUpdateEvent:
413         if (event->data.frmUpdate.updateCode == rl->tableID) {
414             RecordListRepopulate(rl);
415             RecordListDraw(rl);
416         }
417         return true;  // default handler erases form
418
419     case tblSelectEvent:
420         if (RecordListForTable(event->data.tblSelect.pTable) == rl) {
421             RecordListSetSelectedIndex(rl, event->data.tblSelect.row + 
422                                        rl->topVisibleIndex);
423         }
424         break;
425
426     case tblExitEvent:
427         if (RecordListForTable(event->data.tblExit.pTable) == rl) {
428             RecordListClearSelection(rl);
429         }
430         break;
431
432     case sclRepeatEvent:
433         if (event->data.sclRepeat.scrollBarID == rl->scrollbarID) {
434             Int16 delta = (event->data.sclRepeat.newValue - 
435                            event->data.sclRepeat.value);
436             rl->topVisibleIndex += delta;
437             RecordListUpdate(rl);
438         }
439         break;
440
441     default: 
442         break;
443     }
444
445     return false;
446 }