/********** * Copyright (c) 2003-2005 Greg Parker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY GREG PARKER ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **********/ #include "includes.h" #include "formutils.h" #include "recordlist.h" struct RecordList { DmOpenRef db; UInt16 formID; UInt16 tableID; UInt16 scrollbarID; RecordListDrawProc draw; UInt16 topVisibleIndex; UInt16 selectedIndex; }; static RecordList **recordListList = NULL; static int recordListCount = 0; static int recordListAllocated = 0; static RecordList *RecordListForTable(TablePtr table) RECORDLIST_SEGMENT; static void AddRecordList(RecordList *rl) RECORDLIST_SEGMENT; static void RemoveRecordList(RecordList *rl) RECORDLIST_SEGMENT; static Int16 IndexToRow(RecordList *rl, UInt16 index) RECORDLIST_SEGMENT; static Int16 TotalRowCount(TablePtr table) RECORDLIST_SEGMENT; static Int16 VisibleRowCount(TablePtr table) RECORDLIST_SEGMENT; static void RecordListRepopulate(RecordList *rl) RECORDLIST_SEGMENT; static void RecordListDraw(RecordList *rl) RECORDLIST_SEGMENT; static void RecordListDrawRecord(void *t, Int16 row, Int16 column, RectanglePtr bounds) RECORDLIST_SEGMENT; static RecordList *RecordListForTable(TablePtr table) { int i; UInt16 tableIndex; UInt16 tableID; UInt16 formID; formID = FrmGetActiveFormID(); tableIndex = FrmGetObjectIndexFromPtr(FrmGetActiveForm(), table); tableID = FrmGetObjectId(FrmGetActiveForm(), tableIndex); for (i = 0; i < recordListCount; i++) { RecordList *r = recordListList[i]; if (r->formID == formID && r->tableID == tableID) return r; } return NULL; } static void AddRecordList(RecordList *rl) { if (recordListCount == recordListAllocated) { RecordList **newList; int i; recordListAllocated += recordListAllocated + 1; newList = MemPtrNew(recordListAllocated * sizeof(RecordList *)); for (i = 0; i < recordListCount; i++) { newList[i] = recordListList[i]; } if (recordListList) MemPtrFree(recordListList); recordListList = newList; } recordListList[recordListCount++] = rl; } static void RemoveRecordList(RecordList *rl) { int i; for (i = 0; i < recordListCount; i++) { if (recordListList[i] == rl) { recordListList[i] = recordListList[--recordListCount]; return; } } } // create and populate RecordList *RecordListNew(DmOpenRef newDB, UInt16 newFormID, UInt16 newTableID, UInt16 newScrollbarID, RecordListDrawProc newDraw) { RecordList *rl = MemPtrNew(sizeof(RecordList)); rl->db = newDB; rl->formID = newFormID; rl->tableID = newTableID; rl->scrollbarID = newScrollbarID; rl->draw = newDraw; // fixme read some preferences and restore last selection and visible rl->topVisibleIndex = 0; rl->selectedIndex = noRecord; AddRecordList(rl); return rl; } void RecordListFree(RecordList *rl) { // fixme write some preferences here RemoveRecordList(rl); MemPtrFree(rl); } UInt16 RecordListCount(RecordList *rl) { return DmNumRecords(rl->db); } static Int16 IndexToRow(RecordList *rl, UInt16 index) { if (index != noRecord && index >= rl->topVisibleIndex && index < DmNumRecords(rl->db)) { return index - rl->topVisibleIndex; } else { return -1; } } static Int16 TotalRowCount(TablePtr table) { return TblGetNumberOfRows(table); } static Int16 VisibleRowCount(TablePtr table) { Coord rowHeight; RectangleType bounds; TblGetBounds(table, &bounds); rowHeight = TblGetRowHeight(table, 0); return MIN(bounds.extent.y / rowHeight, TotalRowCount(table)); } void RecordListUpdate(RecordList *rl) { RecordListRepopulate(rl); FrmUpdateForm(rl->formID, rl->tableID); } static void RecordListRepopulate(RecordList *rl) { TablePtr table; int totalRows, row, visibleRows, totalRecords; UInt16 index; if (FrmGetActiveFormID() != rl->formID) return; table = PrvGetObjectByID(rl->tableID); totalRecords = RecordListCount(rl); totalRows = TotalRowCount(table); visibleRows = VisibleRowCount(table); if (rl->topVisibleIndex + visibleRows > totalRecords) { // top visible leaves a gap at the bottom - move it up rl->topVisibleIndex = MAX(totalRecords - visibleRows, 0); } index = rl->topVisibleIndex; for (row = 0; row < visibleRows; row++) { MemHandle recordH = DmQueryNextInCategory(rl->db, &index, dmAllCategories); if (!recordH) { // no record for this row or any further rows break; } else { // install record into row // UInt32 uniqueID; // DmRecordInfo(ConnectionDB, index, NULL, &uniqueID, NULL); TblSetItemStyle(table, row, 0, customTableItem); TblSetRowUsable(table, row, true); TblSetRowID(table, row, index); // TblSetRowData(table, row, uniqueID); TblMarkRowInvalid(table, row); index++; } } // mark the rest of the table unusable, if any for ( ; row < totalRows; row++) { TblSetRowUsable(table, row, false); TblSetRowID(table, row, noRecord); TblMarkRowInvalid(table, row); } TblSetCustomDrawProcedure(table, 0, RecordListDrawRecord); TblSetColumnUsable(table, 0, true); // update scoll bar { ScrollBarPtr scl = PrvGetObjectByID(rl->scrollbarID); Int16 value, min, max, pageSize; min = 0; pageSize = visibleRows - 1; // page-scroll overlaps by 1 if (totalRecords > pageSize) { max = totalRecords - pageSize - 1; value = rl->topVisibleIndex; } else { // everything fits at once with no scrolling value = 0; max = 0; pageSize = 0; } SclSetScrollBar(scl, value, min, max, pageSize); } } static void RecordListDraw(RecordList *rl) { TablePtr table; int row, visibleRows; if (FrmGetActiveFormID() != rl->formID) return; table = PrvGetObjectByID(rl->tableID); visibleRows = VisibleRowCount(table); TblDrawTable(table); // draws with NO selection! row = IndexToRow(rl, rl->selectedIndex); if (row >= 0 && row < visibleRows) { // fixme use TblGetLastUsableRow? TblUnhighlightSelection(table); TblSelectItem(table, row, 0); } } UInt16 RecordListSelectedIndex(RecordList *rl) { return rl->selectedIndex; } void RecordListSetSelectedIndex(RecordList *rl, UInt16 index) { rl->selectedIndex = index; // fixme scroll to selectedIndex if it's not in view RecordListUpdate(rl); } void RecordListClearSelection(RecordList *rl) { rl->selectedIndex = noRecord; RecordListUpdate(rl); } MemHandle RecordListQuerySelectedRecord(RecordList *rl) { UInt16 index = RecordListSelectedIndex(rl); return RecordListQueryIndexedRecord(rl, index); } MemHandle RecordListQueryIndexedRecord(RecordList *rl, UInt16 index) { if (index == noRecord) return NULL; else return DmQueryRecord(rl->db, index); } MemHandle RecordListGetIndexedRecord(RecordList *rl, UInt16 index) { if (index == noRecord) return NULL; else return DmGetRecord(rl->db, index); } // if createWithSize is ZERO, then return the record if found or NULL. // if createWithSize is NON-ZERO, then return the RESIZED record or a NEW one. // If a new record is created, the selection is CHANGED to that record. // The returned record must be released with RecordListReleaseRecord. MemHandle RecordListGetSelectedRecord(RecordList *rl, UInt32 createWithSize) { UInt16 index = RecordListSelectedIndex(rl); if (index != noRecord) { if (createWithSize == 0) { // Selection exists and no resize requested. return DmGetRecord(rl->db, index); } else { // Selection exists and resize requested. return DmResizeRecord(rl->db, index, createWithSize); } } else { if (createWithSize == 0) { // No selection and no creation requested. return NULL; } else { // No selection but record creation requested. MemHandle recordH; index = DmNumRecords(rl->db); recordH = DmNewRecord(rl->db, &index, createWithSize); if (recordH) { // zero it out DmSet(MemHandleLock(recordH), 0, createWithSize, 0); MemHandleUnlock(recordH); RecordListSetSelectedIndex(rl, index); } return recordH; } } } void RecordListReleaseRecord(RecordList *rl, MemHandle recordH, Boolean dirty) { DmOpenRef db = rl->db; UInt16 index = DmSearchRecord(recordH, &db); // fixme errors DmReleaseRecord(db, index, dirty); if (dirty) RecordListUpdate(rl); } void RecordListDeleteSelectedRecord(RecordList *rl) { RecordListDeleteIndexedRecord(rl, rl->selectedIndex); } void RecordListDeleteIndexedRecord(RecordList *rl, UInt16 index) { if (index != noRecord) { DmRemoveRecord(rl->db, index); if (index == rl->selectedIndex) { rl->selectedIndex = noRecord; } RecordListUpdate(rl); } } static void RecordListDrawRecord(void *t, Int16 row, Int16 column, RectanglePtr bounds) { TablePtr table = (TablePtr)t; UInt16 index; MemHandle recordH; RecordList *rl = RecordListForTable(table); index = TblGetRowID(table, row); if (index == noRecord) return; recordH = DmQueryRecord(rl->db, index); if (recordH) { WinPushDrawState(); rl->draw(MemHandleLock(recordH), index, bounds); WinPopDrawState(); MemHandleUnlock(recordH); } } Boolean RecordListHandleEvent(RecordList *rl, EventPtr event) { switch (event->eType) { case winEnterEvent: if (FrmGetFormId((FormPtr)event->data.winEnter.enterWindow) == rl->formID) { // fixme could record dirty indicator FrmUpdateForm(rl->formID, rl->tableID); } break; case frmUpdateEvent: if (event->data.frmUpdate.updateCode == rl->tableID) { RecordListRepopulate(rl); RecordListDraw(rl); } return true; // default handler erases form case tblSelectEvent: if (RecordListForTable(event->data.tblSelect.pTable) == rl) { RecordListSetSelectedIndex(rl, event->data.tblSelect.row + rl->topVisibleIndex); } break; case tblExitEvent: if (RecordListForTable(event->data.tblExit.pTable) == rl) { RecordListClearSelection(rl); } break; case sclRepeatEvent: if (event->data.sclRepeat.scrollBarID == rl->scrollbarID) { Int16 delta = (event->data.sclRepeat.newValue - event->data.sclRepeat.value); rl->topVisibleIndex += delta; RecordListUpdate(rl); } break; default: break; } return false; }