2 * Copyright (c) 2003-2005 Greg Parker. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #include "formutils.h"
27 #include "recordlist.h"
37 RecordListDrawProc draw;
39 UInt16 topVisibleIndex;
43 static RecordList **recordListList = NULL;
44 static int recordListCount = 0;
45 static int recordListAllocated = 0;
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;
57 static RecordList *RecordListForTable(TablePtr table)
64 formID = FrmGetActiveFormID();
65 tableIndex = FrmGetObjectIndexFromPtr(FrmGetActiveForm(), table);
66 tableID = FrmGetObjectId(FrmGetActiveForm(), tableIndex);
68 for (i = 0; i < recordListCount; i++) {
69 RecordList *r = recordListList[i];
70 if (r->formID == formID && r->tableID == tableID) return r;
77 static void AddRecordList(RecordList *rl)
79 if (recordListCount == recordListAllocated) {
83 recordListAllocated += recordListAllocated + 1;
84 newList = MemPtrNew(recordListAllocated * sizeof(RecordList *));
85 for (i = 0; i < recordListCount; i++) {
86 newList[i] = recordListList[i];
88 if (recordListList) MemPtrFree(recordListList);
89 recordListList = newList;
92 recordListList[recordListCount++] = rl;
96 static void RemoveRecordList(RecordList *rl)
100 for (i = 0; i < recordListCount; i++) {
101 if (recordListList[i] == rl) {
102 recordListList[i] = recordListList[--recordListCount];
109 // create and populate
110 RecordList *RecordListNew(DmOpenRef newDB, UInt16 newFormID, UInt16 newTableID, UInt16 newScrollbarID, RecordListDrawProc newDraw)
112 RecordList *rl = MemPtrNew(sizeof(RecordList));
114 rl->formID = newFormID;
115 rl->tableID = newTableID;
116 rl->scrollbarID = newScrollbarID;
119 // fixme read some preferences and restore last selection and visible
120 rl->topVisibleIndex = 0;
121 rl->selectedIndex = noRecord;
128 void RecordListFree(RecordList *rl)
130 // fixme write some preferences here
131 RemoveRecordList(rl);
136 UInt16 RecordListCount(RecordList *rl)
138 return DmNumRecords(rl->db);
142 static Int16 IndexToRow(RecordList *rl, UInt16 index)
144 if (index != noRecord && index >= rl->topVisibleIndex &&
145 index < DmNumRecords(rl->db))
147 return index - rl->topVisibleIndex;
154 static Int16 TotalRowCount(TablePtr table)
156 return TblGetNumberOfRows(table);
160 static Int16 VisibleRowCount(TablePtr table)
163 RectangleType bounds;
164 TblGetBounds(table, &bounds);
165 rowHeight = TblGetRowHeight(table, 0);
166 return MIN(bounds.extent.y / rowHeight, TotalRowCount(table));
170 void RecordListUpdate(RecordList *rl)
172 RecordListRepopulate(rl);
173 FrmUpdateForm(rl->formID, rl->tableID);
177 static void RecordListRepopulate(RecordList *rl)
180 int totalRows, row, visibleRows, totalRecords;
183 if (FrmGetActiveFormID() != rl->formID) return;
185 table = PrvGetObjectByID(rl->tableID);
186 totalRecords = RecordListCount(rl);
187 totalRows = TotalRowCount(table);
188 visibleRows = VisibleRowCount(table);
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);
195 index = rl->topVisibleIndex;
197 for (row = 0; row < visibleRows; row++) {
199 recordH = DmQueryNextInCategory(rl->db, &index, dmAllCategories);
201 // no record for this row or any further rows
204 // install record into row
206 // DmRecordInfo(ConnectionDB, index, NULL, &uniqueID, NULL);
208 TblSetItemStyle(table, row, 0, customTableItem);
210 TblSetRowUsable(table, row, true);
211 TblSetRowID(table, row, index);
212 // TblSetRowData(table, row, uniqueID);
213 TblMarkRowInvalid(table, row);
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);
226 TblSetCustomDrawProcedure(table, 0, RecordListDrawRecord);
227 TblSetColumnUsable(table, 0, true);
231 ScrollBarPtr scl = PrvGetObjectByID(rl->scrollbarID);
232 Int16 value, min, max, pageSize;
235 pageSize = visibleRows - 1; // page-scroll overlaps by 1
236 if (totalRecords > pageSize) {
237 max = totalRecords - pageSize - 1;
238 value = rl->topVisibleIndex;
240 // everything fits at once with no scrolling
245 SclSetScrollBar(scl, value, min, max, pageSize);
250 static void RecordListDraw(RecordList *rl)
253 int row, visibleRows;
255 if (FrmGetActiveFormID() != rl->formID) return;
257 table = PrvGetObjectByID(rl->tableID);
258 visibleRows = VisibleRowCount(table);
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);
270 UInt16 RecordListSelectedIndex(RecordList *rl)
272 return rl->selectedIndex;
276 void RecordListSetSelectedIndex(RecordList *rl, UInt16 index)
278 rl->selectedIndex = index;
279 // fixme scroll to selectedIndex if it's not in view
280 RecordListUpdate(rl);
284 void RecordListClearSelection(RecordList *rl)
286 rl->selectedIndex = noRecord;
287 RecordListUpdate(rl);
291 MemHandle RecordListQuerySelectedRecord(RecordList *rl)
293 UInt16 index = RecordListSelectedIndex(rl);
294 return RecordListQueryIndexedRecord(rl, index);
298 MemHandle RecordListQueryIndexedRecord(RecordList *rl, UInt16 index)
300 if (index == noRecord) return NULL;
301 else return DmQueryRecord(rl->db, index);
305 MemHandle RecordListGetIndexedRecord(RecordList *rl, UInt16 index)
307 if (index == noRecord) return NULL;
308 else return DmGetRecord(rl->db, index);
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)
318 UInt16 index = RecordListSelectedIndex(rl);
320 if (index != noRecord) {
321 if (createWithSize == 0) {
322 // Selection exists and no resize requested.
323 return DmGetRecord(rl->db, index);
325 // Selection exists and resize requested.
326 return DmResizeRecord(rl->db, index, createWithSize);
330 if (createWithSize == 0) {
331 // No selection and no creation requested.
334 // No selection but record creation requested.
336 index = DmNumRecords(rl->db);
337 recordH = DmNewRecord(rl->db, &index, createWithSize);
340 DmSet(MemHandleLock(recordH), 0, createWithSize, 0);
341 MemHandleUnlock(recordH);
342 RecordListSetSelectedIndex(rl, index);
350 void RecordListReleaseRecord(RecordList *rl, MemHandle recordH, Boolean dirty)
352 DmOpenRef db = rl->db;
353 UInt16 index = DmSearchRecord(recordH, &db);
355 DmReleaseRecord(db, index, dirty);
356 if (dirty) RecordListUpdate(rl);
360 void RecordListDeleteSelectedRecord(RecordList *rl)
362 RecordListDeleteIndexedRecord(rl, rl->selectedIndex);
366 void RecordListDeleteIndexedRecord(RecordList *rl, UInt16 index)
368 if (index != noRecord) {
369 DmRemoveRecord(rl->db, index);
370 if (index == rl->selectedIndex) {
371 rl->selectedIndex = noRecord;
373 RecordListUpdate(rl);
378 static void RecordListDrawRecord(void *t, Int16 row, Int16 column,
381 TablePtr table = (TablePtr)t;
385 RecordList *rl = RecordListForTable(table);
387 index = TblGetRowID(table, row);
388 if (index == noRecord) return;
390 recordH = DmQueryRecord(rl->db, index);
394 rl->draw(MemHandleLock(recordH), index, bounds);
397 MemHandleUnlock(recordH);
402 Boolean RecordListHandleEvent(RecordList *rl, EventPtr event)
404 switch (event->eType) {
406 if (FrmGetFormId((FormPtr)event->data.winEnter.enterWindow) == rl->formID) {
407 // fixme could record dirty indicator
408 FrmUpdateForm(rl->formID, rl->tableID);
413 if (event->data.frmUpdate.updateCode == rl->tableID) {
414 RecordListRepopulate(rl);
417 return true; // default handler erases form
420 if (RecordListForTable(event->data.tblSelect.pTable) == rl) {
421 RecordListSetSelectedIndex(rl, event->data.tblSelect.row +
422 rl->topVisibleIndex);
427 if (RecordListForTable(event->data.tblExit.pTable) == rl) {
428 RecordListClearSelection(rl);
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);