home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 Mobile
/
Chip_Mobile_2001.iso
/
palm
/
hobby
/
ghardeno
/
ghardeno.EXE
/
MyTable.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-10-03
|
27KB
|
999 lines
/*
MyWidgets: A widget library for PalmOS
Copyright (C) 2000 Laurent Moussault
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include <Common.h>
#include <System/SysAll.h>
#include <UI/UIAll.h>
#include "DBC.h"
#include "Ghardeno.h" /*** To remove ! ***/
#include "Resources.h" /*** To remove ! ***/
#include "Database.h" /*** To remove ! ***/
#include "MyTable.h"
////
/// Constantes
//
#define fontMask 3
static const FontID fonts[] = {stdFont, boldFont, largeFont};
static Word heights[3]; // = {11, 11, 14};
///
//
Boolean inhibitHighlight = false;
////// _
///// | \ o _ |_ _
//// |_/ |/ | \ / _| | /_
/// | | | \/ |_| |_ \_
//
///
// Making up rows.
static void LoadRow (MyTableType *t, Word n, VoidHand h, Word i) {
Char *r = 0;
Word lh = 0;
REQUIRE(t != 0);
REQUIRE(0 <= n); REQUIRE(n < mtaRowNumber);
REQUIRE(h != 0);
t->rows[n]->handle = h;
t->rows[n]->index = i;
RctCopyRectangle(&t->emptyRect,&t->rows[n]->rect);
t->loadRecord(h,&t->rows[n]->fontID,&t->rows[n]->rect.extent.y);
t->rows[n]->invalid = true;
}
static VoidHand QueryNextRecord (MyTableType *t, Word *i) {
VoidHand result = 0;
Err e = 0;
Word a = 0;
REQUIRE(t != 0);
result = DmQueryNextInCategory(t->db,i,t->category);
/*
*i -= 1; // yup: this is a trick.
do {
*i += 1;
result = DmQueryNextInCategory(t->db,i,t->category);
if(result != 0) {
DmRecordInfo(t->db,*i,&a, 0, 0);
}
} while(result != 0 && (a & dmRecAttrDelete));
*/
return result;
}
static Boolean AddNextRow (MyTableType *t, Word *i) {
Boolean result = false;
VoidHand h = NULL;
Word lh = 0;
REQUIRE(t != NULL);
CHECK(t->size < mtaRowNumber);
h = QueryNextRecord(t,i);
if(h != NULL) {
t->loadRecord(h,&t->rows[t->size]->fontID,&lh);
if(t->emptyRect.extent.y >= lh) {
t->rows[t->size]->handle = h;
t->rows[t->size]->index = *i;
RctCopyRectangle(&t->emptyRect,&t->rows[t->size]->rect);
t->rows[t->size]->rect.extent.y = lh;
t->emptyRect.topLeft.y += lh;
t->emptyRect.extent.y -= lh;
t->rows[t->size]->invalid = true;
if(t->isSelected && *i == t->currentIndex) {
t->current = t->size;
t->isShown = true;
}
t->size++;
result = true;
} else {
t->full = true;
}
}
return result;
}
static void InsertRow(MyTableType *t, int n, VoidHand h, int i) {
int j = 0;
MyTableRowType *r = 0;
Word rh = 0;
REQUIRE(t != 0);
REQUIRE(0 <= n); REQUIRE(n <= t->size/* || n == mtaRowNumber*/);
REQUIRE(h != 0);
CHECK(t->size < mtaRowNumber);
// Load the record after the last row.
LoadRow(t,t->size,h,i);
r = t->rows[t->size]; CHECK(r != 0);
r->rect.topLeft.y = t->rows[n]->rect.topLeft.y;
// Move down alls raw after insertion point.
rh = t->rows[t->size]->rect.extent.y;
for(j = t->size; j > n; j--) {
t->rows[j] = t->rows[j - 1];
t->rows[j]->rect.topLeft.y += rh;
t->rows[j]->invalid = true;
}
// Put back the new record at the right place.
t->rows[n] = r;
t->size += 1;
// If necessary, remove one or two rows at the end.
while(t->emptyRect.extent.y < rh) {
t->size -= 1;
t->emptyRect.topLeft.y -= t->rows[t->size]->rect.extent.y;
t->emptyRect.extent.y += t->rows[t->size]->rect.extent.y;
}
// Adjust table's remaining height.
t->emptyRect.topLeft.y += rh;
t->emptyRect.extent.y -= rh;
}
static void RemoveRow(MyTableType *t, Word n) {
MyTableRowType *r = 0;
int i;
REQUIRE(t != 0);
REQUIRE(0 <= n); REQUIRE(n < t->size);
// The row we remove is recycled, so put it aside.
r = t->rows[n];
// Move upward all raws after it.
t->size -= 1;
for(i = n; i < t->size; i++) {
t->rows[i] = t->rows[i + 1];
t->rows[i]->rect.topLeft.y -= r->rect.extent.y;
t->rows[i]->invalid = true;
}
// Recycle removed row.
t->rows[t->size] = r;
// Update table's remaining area.
t->emptyRect.topLeft.y -= r->rect.extent.y;
t->emptyRect.extent.y += r->rect.extent.y;
if(t->isShown) {
t->current -= 1;
t->isShown = (t->current >= 0);
}
}
static int FindRecordRow (MyTableType *t, Word i) {
int result = 0;
REQUIRE(t != 0);
result = t->size - 1;
while(result >= 0 && t->rows[result]->index != i) {
result -= 1;
}
ENSURE(result == -1 || t->rows[result]->index == i);
return result;
}
static void DeleteRecord(MyTableType *t, Word i) {
Boolean e = false;
int n = 0;
int j = 0;
Word k = 0;
REQUIRE(t != NULL);
// Delete it (!= remove).
DmDeleteRecord(t->db,i);
// I don't know _why_ we have to do that. I don't know _why_ nobody
// explain we have to. But if we don't move the record at the end
// of the database, it becomes a zombie. This one almost drove me
// crazy.
DmMoveRecord (t->db,i,DmNumRecords(t->db));
t->categorySize -= 1;
// If this record was shown, we must update the table.
n = FindRecordRow(t,i);
// By moving the deleted record to the end, we messed up half the
// index we cached: let's update them.
if(n != -1) {
for(j = n+1; j < t->size; j++) {
t->rows[j]->index -= 1;
}
RemoveRow(t,n);
// As the table has one less row, it may be able to display new
// rows at the bottom.
if(t->size > 0) {
do {
k = t->rows[t->size - 1]->index + 1;
e = AddNextRow(t,&k);
} while(e);
}
}
}
static int FindRecordRowByHandle (MyTableType *t, VoidHand h) {
int result = 0;
REQUIRE(t != 0);
result = t->size - 1;
while(result >= 0 && t->rows[result]->handle != h) {
result -= 1;
}
ENSURE(t->rows[result]->handle == h || result == -1);
return result;
}
static void UpdateScrollBar (MyTableType *t) {
ScrollBarType *s = 0;
Word m = 0;
REQUIRE(t != 0);
s = FrmGetObjectPtr(t->form,FrmGetObjectIndex(t->form,id_scrollBar));
if(t->categorySize > t->size)
m = t->categorySize - t->size;
else
m = t->positionInCategory;
SclSetScrollBar(s,t->positionInCategory,0,m,t->size);
}
static void NewFieldEnterEvent (MyTableType *t, EventType *ev) {
FormType *f = NULL;
FieldType *fi = NULL;
EventType e;
f = FrmGetActiveForm(); CHECK(f);
fi = FrmGetObjectPtr(f,FrmGetObjectIndex(f,id_field)); CHECK(fi);
EvtCopyEvent(ev,&e);
e.eType = fldEnterEvent;
e.data.fldEnter.fieldID = fi->id;
e.data.fldEnter.pField = fi;
//FldHandleEvent (fi,&e);
}
static void TrackStrokeEvent (MyTableType *t, SWord ox) {
SWord x = 0, y = 0;
Boolean p = false;
Boolean wc = false, nc = false; // "was changed" and "now changed"
Char *r = NULL;
RectangleType b;
REQUIRE(t != NULL);
r = MemHandleLock(t->rows[t->current]->handle); CHECK(r);
do {
PenGetPoint(&x,&y,&p);
nc = abs(ox - x) > 11 ;
if(nc != wc) {
SndPlaySystemSound(sndClick);
DmSet(r,0,1,(*r&3)|(*r&4?0:4));
DbDrawRecord(t->rows[t->current]->handle,&t->rows[t->current]->rect,false);
if(inhibitHighlight) {
/*
RctCopyRectangle(&t->rows[t->current]->rect,&b);
b.extent.x = bulletWidth - 1;
WinInvertRectangle(&b,3);
*/
} else {
WinInvertRectangle(&t->rows[t->current]->rect,3);
}
UpdateToolBar(t);
wc = nc;
}
} while(p);
MemHandleUnlock(t->rows[t->current]->handle);
}
static void TrackDragEvent (MyTableType *t) {
static CustomPatternType gr = { 0xAA55, 0xAA55, 0xAA55, 0xAA55 };
SWord x = 0, y = 0;
Boolean p = false;
int cr = 0, dr = 0;
RectangleType r;
int i;
Boolean s = false;
REQUIRE(t != NULL);
WinSetPattern(gr);
cr = t->current; CHECK(cr != -1);
do {
PenGetPoint(&x,&y,&p);
// First, let's find in what row the pen is.
dr = t->size;
for(i = 0; i < t->size && dr == t->size; i++)
if(y < t->rows[i]->rect.topLeft.y + (t->rows[i]->rect.extent.y/2) + (i<cr+1?-2:2))
dr = i;
dr -= 1;
if(dr != cr) { // We are in a different row.
if(!((cr==t->current&&dr==t->current-1) ||(dr==t->current&&cr==t->current-1)))
SndPlaySystemSound(sndClick);
if(cr != t->current && cr != t->current - 1) {
// Erase the previous position.
if(cr >= 0)
DbDrawRecord(t->rows[cr]->handle,&t->rows[cr]->rect,false);
else
WinEraseLine(t->rows[0]->rect.topLeft.x,
t->rows[0]->rect.topLeft.y-1,
t->rows[0]->rect.topLeft.x+t->rows[0]->rect.extent.x-1,
t->rows[0]->rect.topLeft.y-1 );
if(cr+1 < t->size)
DbDrawRecord(t->rows[cr + 1]->handle,&t->rows[cr + 1]->rect,false);
else
WinEraseLine(t->rows[cr]->rect.topLeft.x,
t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y,
t->rows[cr]->rect.topLeft.x+t->rows[cr]->rect.extent.x-1,
t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y );
}
if(dr != t->current && dr != t->current - 1) {
// Draw the new position.
if(dr >= 0) {
RctCopyRectangle(&t->rows[dr]->rect,&r);
r.topLeft.y += r.extent.y - 1;
} else {
RctCopyRectangle(&t->rows[0]->rect,&r);
r.topLeft.y -= 1;
}
r.extent.y = 2;
WinFillRectangle(&r,0);
}
cr = dr;
} else if(dr == -1 && t->positionInCategory > 0) {
if(cr != t->current && cr != t->current - 1) {
// Erase the previous position.
if(cr >= 0)
DbDrawRecord(t->rows[cr]->handle,&t->rows[cr]->rect,false);
else
WinEraseLine(t->rows[0]->rect.topLeft.x,
t->rows[0]->rect.topLeft.y-1,
t->rows[0]->rect.topLeft.x+t->rows[0]->rect.extent.x-1,
t->rows[0]->rect.topLeft.y-1 );
if(cr+1 < t->size)
DbDrawRecord(t->rows[cr + 1]->handle,&t->rows[cr + 1]->rect,false);
else
WinEraseLine(t->rows[cr]->rect.topLeft.x,
t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y,
t->rows[cr]->rect.topLeft.x+t->rows[cr]->rect.extent.x-1,
t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y );
}
s = MtaScrollUpOneRow(t);
if(! s) {
SndPlaySystemSound(sndClick);
MtaRedraw(t);
// Draw the new position.
if(t->rows[0]->index != t->currentIndex) {
RctCopyRectangle(&t->rows[0]->rect,&r);
r.topLeft.y -= 1;
r.extent.y = 2;
WinFillRectangle(&r,0);
}
}
} else if(dr == t->size - 1) {
if(cr != t->current && cr != t->current - 1) {
// Erase the previous position.
if(cr >= 0)
DbDrawRecord(t->rows[cr]->handle,&t->rows[cr]->rect,false);
else
WinEraseLine(t->rows[0]->rect.topLeft.x,
t->rows[0]->rect.topLeft.y-1,
t->rows[0]->rect.topLeft.x+t->rows[0]->rect.extent.x-1,
t->rows[0]->rect.topLeft.y-1 );
if(cr+1 < t->size)
DbDrawRecord(t->rows[cr + 1]->handle,&t->rows[cr + 1]->rect,false);
else
WinEraseLine(t->rows[cr]->rect.topLeft.x,
t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y,
t->rows[cr]->rect.topLeft.x+t->rows[cr]->rect.extent.x-1,
t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y );
}
s = MtaScrollDownOneRow(t);
if(! s) {
SndPlaySystemSound(sndClick);
MtaRedraw(t);
// Draw the new position.
if(t->rows[t->size - 1]->index != t->currentIndex) {
RctCopyRectangle(&t->rows[t->size - 1]->rect,&r);
r.topLeft.y += r.extent.y - 1;
r.extent.y = 2;
WinFillRectangle(&r,0);
}
}
// As we have scrolled, this may have change.
dr = t->size - 1;
cr = dr;
}
} while(p);
if(cr != t->current && cr != t->current - 1) {
//SndPlaySystemSound(sndClick);
if(cr >= 0)
DbDrawRecord(t->rows[cr]->handle,&t->rows[cr]->rect,false);
else
WinEraseLine(t->rows[0]->rect.topLeft.x,
t->rows[0]->rect.topLeft.y-1,
t->rows[0]->rect.topLeft.x+t->rows[0]->rect.extent.x-1,
t->rows[0]->rect.topLeft.y-1 );
if(cr+1 < t->size)
DbDrawRecord(t->rows[cr + 1]->handle,&t->rows[cr + 1]->rect,false);
else
WinEraseLine(t->rows[cr]->rect.topLeft.x,
t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y,
t->rows[cr]->rect.topLeft.x+t->rows[cr]->rect.extent.x-1,
t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y );
if(cr == -1) {
DmMoveRecord(t->db,t->currentIndex,
t->rows[0]->index );
MtaFillTableFromTop(t,t->rows[0]->index);
} else {
DmMoveRecord(t->db,t->currentIndex,
t->rows[cr]->index + 1 );
MtaFillTableFromTop(t,t->rows[0]->index);
}
MtaChangeSelection(t,cr + (cr > t->current ? 0 : 1));
MtaRedraw(t);
UpdateToolBar(t);
}
}
////// _
///// | \ | | o _
//// |_/ | | |\ | | /
/// | |_| |/ | | \_
//
////
/// Module initialisation
//
void MtaInit() {
int i;
for(i = 0; i < 3; i++) {
FntSetFont(fonts[i]);
heights[i] = FntLineHeight();
}
}
////
/// Creation
//
VoidHand MtaNew(FormType *f, Word i, Word m, DmOpenRef d) {
VoidHand result = 0;
MyTableType *t = 0;
Boolean e = false;
int j;
REQUIRE(f != 0);
result = MemHandleNew(sizeof(MyTableType));
if(result != 0) {
t = MemHandleLock(result); CHECK(t != 0);
t->form = f;
t->id = i;
FrmGetObjectBounds(f,FrmGetObjectIndex(f,i),&t->bounds);
t->maxSize = m;
t->size = 0;
t->current = -1;
// Not necessary, but helps to find bugs:
t->currentIndex = 0; t->isShown = false; t->positionInCategory = 0;
t->isSelected = false;
t->isEditing = false;
t->rows = MemPtrNew(sizeof(MyTableRowType*[m]));
for(j = 0; j < m; j++)
t->rows[j] = MemPtrNew(sizeof(MyTableRowType));
t->db = d;
t->category = dmAllCategories;
t->categorySize = DmNumRecordsInCategory(t->db,t->category);
t->full = false;
e = t->rows == 0; // If allocation failed, we need to free 'result'.
MemHandleUnlock(result);
FrmSetGadgetData(f,FrmGetObjectIndex(f,i),result);
}
if(e) {
MemHandleFree(result);
result = 0;
}
return result;
}
void MtaSetLoadRecordProcedure (MyTableType *t, MtaLoadRecordFuncType *lrf) {
REQUIRE(t != 0);
REQUIRE(lrf != 0);
t->loadRecord = lrf;
}
////
///
//
void MtaNewRecord (MyTableType *t) {
int i = 0;
Word ri = 0;
int ip = 0;
VoidHand h = NULL;
Char *p = NULL;
Word a = 0;
REQUIRE(t != NULL);
if(t->isSelected) {
ri = t->currentIndex + 1;
}
h = DmNewRecord(t->db,&ri,2); CHECK(h);
DmRecordInfo(t->db,ri,&a,0,0);
if(t->category == dmAllCategories) {
a = (a & ~dmRecAttrCategoryMask) | dmUnfiledCategory;
} else {
a = (a & ~dmRecAttrCategoryMask) | t->category;
}
DmSetRecordInfo(t->db,ri,&a,0);
p = MemHandleLock(h); CHECK(p);
InitNewRecord(p);
MemHandleUnlock(h);
DmReleaseRecord(t->db,ri,true);
t->categorySize += 1;
ip = t->isShown ? t->current + 1 : 0;
InsertRow(t,ip,h,ri);
// As we cache the index of each displayed record, we must update it
// by hand.
for(i = ip + 1; i < t->size; i++) {
t->rows[i]->index += 1;
}
MtaScrollToRecord(t,ri);
CHECK(FindRecordRowByHandle(t,h) != -1);
CHECK(t->rows[FindRecordRowByHandle(t,h)]->index == ri);
MtaChangeSelection(t,FindRecordRow(t,ri));
}
void MtaDeleteCurrentRecord (MyTableType *t) {
VoidHand h = 0;
Boolean e = false;
REQUIRE(t != NULL);
REQUIRE(t->isSelected);
DeleteRecord(t,t->currentIndex);
t->isShown = false;
t->isSelected = false;
t->isEditing = false;
/*
h = QueryNextRecord(t,&t->currentIndex);
if(h !=0) {
MtaScrollToRecord(t,t->currentIndex);
MtaChangeSelection(t,FindRecordRow(t,t->currentIndex));
} else if(t->size > 0) {
// If there is no "next" record, that must be because we just
// deleted the last one, so let's select the new last one.
MtaChangeSelection(t,t->size - 1);
}
*/
}
////
///
//
void MtaFillTableFromTop (MyTableType *t, UInt i) {
FormType *f = NULL;
Boolean e = false;
REQUIRE(t != NULL);
t->size = 0;
t->isShown = false;
t->full = false;
t->categorySize = DmNumRecordsInCategory(t->db,t->category);
f = FrmGetActiveForm(); CHECK(f);
FrmGetObjectBounds(f,FrmGetObjectIndex(f,t->id),&t->emptyRect);
do {
e = AddNextRow(t,&i);
i++;
} while(e);
if(t->size > 0) {
t->positionInCategory = DmPositionInCategory(t->db,t->rows[0]->index,t->category);
}
}
void MtaEditSelectedRow (MyTableType *t) {
FormType *f = FrmGetActiveForm();
FieldType *fi = FrmGetObjectPtr(f,FrmGetObjectIndex(f,id_field));
Char *c;
MemSet(fi,sizeof(FieldType),0);
fi->id = id_field;
RctCopyRectangle(&t->rows[t->current]->rect,&fi->rect);
fi->rect.topLeft.x += 2;
fi->rect.extent.x -= 2;
fi->attr.usable = true;
fi->attr.visible = true;
fi->attr.editable = true;
fi->attr.singleLine = true;
fi->attr.hasFocus = false; // otherwise FrmSetFocus doesn't work !
fi->attr.dynamicSize = false;
fi->attr.insPtVisible = true;
fi->attr.dirty = false;
//fi->attr.underlined = 3;
fi->attr.justification = leftAlign;
fi->attr.hasScrollBar = false;
fi->attr.autoShift = true;
fi->attr.numeric = false;
fi->maxChars = 111;
fi->fontID = t->rows[t->current]->fontID;
FldSetText(fi,t->rows[t->current]->handle,1,
MemHandleSize(t->rows[t->current]->handle)-1 );
FrmSetFocus(f,FrmGetObjectIndex(f,id_field));
WinEraseRectangle(&t->rows[t->current]->rect,0);
t->isEditing = true;
t->rows[t->current]->invalid = true;
}
////
/// Public funcs.
//
void MtaRedraw (MyTableType *t) {
FormType *f = NULL;
int i = 0;
RectangleType b;
REQUIRE(t != NULL);
f = FrmGetActiveForm(); CHECK(f);
FrmHideObject(f,FrmGetObjectIndex(f,id_field));
UpdateScrollBar(t);
for(i = 0; i < t->size; i++) {
if(t->rows[i]->invalid) {
DbDrawRecord(t->rows[i]->handle,&t->rows[i]->rect,
(t->isShown && t->isEditing && i == t->current) );
if(t->isShown && i == t->current && ! t->isEditing) {
if (inhibitHighlight) {
/*
RctCopyRectangle(&t->rows[i]->rect,&b);
b.extent.x = bulletWidth - 1;
WinInvertRectangle(&b,3);
*/
} else {
WinInvertRectangle(&t->rows[i]->rect,3);
}
}
t->rows[i]->invalid = false;
}
}
WinEraseRectangle(&t->emptyRect,0);
}
void MtaUnselect (MyTableType *t) {
FieldType *fi = NULL;
Boolean rm = false;
REQUIRE(t != 0);
REQUIRE(t->isSelected);
if(t->isEditing) {
fi = FrmGetObjectPtr(t->form,FrmGetObjectIndex(t->form,id_field)); CHECK(fi);
// If the current edited row is empty, we'll have to remove it.
rm = (FldGetTextLength(fi) == 0);
// Unconnect the record and the field.
FldSetTextHandle(fi,NULL);
DmReleaseRecord(t->db,t->currentIndex,true);
//FrmHideObject(f,FrmGetObjectIndex(f,id_field));
t->isEditing = false;
}
if(t->isShown) {
// The selection/edition was shown, so we'll have to redraw it.
t->isShown = false;
t->rows[t->current]->invalid = true;
}
if(rm) {
DeleteRecord(t,t->currentIndex);
}
t->isSelected = false;
}
void MtaChangeSelection (MyTableType *t, int i) {
REQUIRE(t != NULL);
REQUIRE(i == -1 || (0 <= i && i < mtaRowNumber));
if(t->isSelected) {
MtaUnselect(t);
} else CHECK(! t->isEditing);
t->current = i;
t->isSelected = (i != -1);
t->isShown = (i != -1);
if(t->isShown) {
t->rows[t->current]->invalid = true;
t->currentIndex = t->rows[t->current]->index;
}
}
void MtaChangeSelectionIndex (MyTableType *t, Word i) {
REQUIRE(t != NULL);
if(t->isSelected)
{
MtaUnselect(t);
} else CHECK(! t->isEditing);
t->isSelected = true;
t->currentIndex = i;
t->current = FindRecordRow (t, i);
t->isShown = t->current != -1;
if(t->isShown)
{
t->rows[t->current]->invalid = true;
}
}
void MtaUpdateCurrentRow (MyTableType *t) {
int i = 0;
Word oy = 0;
int dh = 0; // *Must* be int, not Word !
REQUIRE(t != 0);
if(t->isShown) {
// Load the row and compute height difference.
dh = t->rows[t->current]->rect.extent.y;
oy = t->rows[t->current]->rect.topLeft.y;
LoadRow(t,t->current,t->rows[t->current]->handle,t->currentIndex);
t->rows[t->current]->rect.topLeft.y = oy;
dh = t->rows[t->current]->rect.extent.y - dh;
if(dh != 0) {
// There's a height difference, so update all following rows'
// position.
for(i = t->current + 1; i < t->size; i++) {
t->rows[i]->rect.topLeft.y += dh;
t->rows[i]->invalid = true;
}
// We may have pushed down one or two rows outside of the table:
// remove them.
while(t->emptyRect.extent.y < dh) {
t->size -= 1;
t->emptyRect.topLeft.y -= t->rows[t->size]->rect.extent.y;
t->emptyRect.extent.y += t->rows[t->size]->rect.extent.y;
}
// Update the bounds of the empty area.
t->emptyRect.topLeft.y += dh;
t->emptyRect.extent.y -= dh;
// Now we may have removed the current row !
t->isShown = (t->current < t->size);
while(t->current >= t->size) {
MtaScrollDownOneRow(t);
}
}
}
}
Boolean MtaHandlePenDownEvent (MyTableType *t, EventType *ev) {
Boolean result = false;
FormType *f = NULL;
Boolean p = 0;
SWord x = 0, y = 0;
int os = 0, ds = 0;
Boolean oss = false;
int i = 0;
Boolean st = false, dr = false;
Char *r = 0;
REQUIRE(t != NULL);
REQUIRE(ev != NULL);
f = FrmGetActiveForm(); CHECK(f);
oss = t->isSelected;
os = t->isShown ? t->current : -1;
// First, let's find where the pen is.
ds = -1;
for(i = 0; i<t->size && ds == -1; i++)
if(RctPtInRectangle(ev->screenX,ev->screenY,&t->rows[i]->rect))
ds = i;
if (ds == -1)
{
if (t->isSelected)
{
MtaUnselect (t);
MtaRedraw (t);
}
result = true;
}
else if (ev->screenX > bulletWidth)
{
// The tap is inside the text, let's edit it and pass the event
// to the system handlers.
if ((t->isShown && (ds != t->current || ! t->isEditing)) || ! t->isShown)
{
MtaChangeSelection (t, ds);
r = MemHandleLock (t->rows[t->current]->handle);
if (!(*r & 4))
{
MtaEditSelectedRow (t);
NewFieldEnterEvent (t, ev);
}
MemHandleUnlock (t->rows[t->current]->handle);
MtaRedraw (t);
UpdateToolBar (t);
}
// 'result' is false.
}
else
{
// The tap is in the bullet.
if (t->isShown && ds == t->current && t->isEditing)
{
MtaUnselect (t);
}
if (! t->isShown || ds != t->current)
{
MtaChangeSelection(t,ds);
MtaRedraw(t);
UpdateToolBar(t);
}
if (t->isShown) {
do {
PenGetPoint(&x,&y,&p);
if(RctPtInRectangle(x,y,&t->rows[t->current]->rect)) {
if(!st && abs(ev->screenX - x) > 10)
st = true;
} else {
dr = true;
}
} while(p && ! st && ! dr);
if(st) {
TrackStrokeEvent(t, ev->screenX);
if(! oss) {
MtaUnselect(t);
MtaRedraw(t);
}
result = true;
} else if(dr) {
TrackDragEvent(t);
result = true;
} else if(ev->screenX > bulletWidth/* && t->current == os*/) {
//SndPlaySystemSound(sndClick);
MtaEditSelectedRow(t);
NewFieldEnterEvent(t,ev);
MtaRedraw(t);
result = false;
}
}
}
return result;
}
////
/// Scrolling
//
void MtaScrollTo(MyTableType *t, int n) {
Word i = 0;
REQUIRE(t != NULL);
//DmQueryNextInCategory(t->db,&i,t->category);
QueryNextRecord(t,&i);
DmSeekRecordInCategory(t->db,&i,n,dmSeekForward,t->category);
MtaFillTableFromTop(t,i);
t->positionInCategory = n;
//MtaUnselect(t);//MtaChangeSelection(t,-1);
UpdateToolBar(t);
MtaRedraw(t);
}
Boolean MtaScrollDownOneRow(MyTableType *t) {
Boolean result = false;
Word i = 0;
Word rh = 0;
VoidHand h = 0;
REQUIRE(t != 0);
REQUIRE(t->size < mtaRowNumber);
// Find next record.
i = t->rows[t->size - 1]->index + 1;
h = QueryNextRecord(t,&i);
if(h != 0) {
// Insert the record after last row.
LoadRow(t,t->size,h,i);
rh = t->rows[t->size]->rect.extent.y;
t->size += 1;
if(t->isSelected && t->currentIndex == t->rows[t->size - 1]->index) {
// The current row is the one just inserted, so show it.
CHECK(! t->isShown);
t->current = t->size - 1;
t->isShown = true;
}
// Remove as many top rows as necessary to show the new one.
while(rh > t->emptyRect.extent.y) {
RemoveRow(t,0);
t->positionInCategory += 1;
}
// Adjust table's remaining area.
t->emptyRect.topLeft.y += rh;
t->emptyRect.extent.y -= rh;
} else {
// We're at the end of the database, so can't scroll down.
result = true;
}
return result;
}
void MtaScrollDownOnePage(MyTableType *t) {
Word l = 0;
Boolean e = false;
REQUIRE(t != NULL);
if(t->size > 0) {
l = t->rows[t->size - 1]->index;
if(t->full) {
while(t->rows[0]->index != l && ! e) {
e = MtaScrollDownOneRow(t);
}
}
}
}
Boolean MtaScrollUpOneRow(MyTableType *t) {
Boolean result = false;
Word i = 0;
Word rh = 0;
VoidHand h = 0;
Boolean e = false;
REQUIRE(t != 0);
REQUIRE(t->size < mtaRowNumber);
// Find the record before the first row.
i = t->rows[0]->index;
e = DmSeekRecordInCategory(t->db,&i,1,dmSeekBackward,t->category);
if(e == 0) {
// Insert it at top.
h = DmQueryNextInCategory(t->db,&i,t->category); CHECK(h);
InsertRow(t,0,h,i);
t->positionInCategory -= 1;
if(t->isShown) {
// Update current position, or hide it if necessary.
t->current += 1;
t->isShown = (t->current < t->size);
} else if(t->isSelected && t->currentIndex == t->rows[0]->index) {
// The selected row is the one just inserted at top, so show it.
t->current = 0;
t->isShown = true;
}
} else {
// We're at the top of the database, so can't scroll up.
result = true;
}
return result;
}
void MtaScrollUpOnePage(MyTableType *t) {
// Yep, this algorithm is quite hairy. It's due to the fact that I
// use 'MtaScrollUpOneRow', wich aim is not to put the last - 1 row at
// bottom but to introduce one row at top. May be that's not the
// Right Way ? Well, it works, so I'll think about it later.
Word i = 0;
Word l = 0;
Boolean e = false;
REQUIRE(t != NULL);
if(t->size > 0) {
// Remember which row we want at the bottom.
l = t->rows[i]->index;
// Scroll up until this row disappears past the bottom.
while(i < t->size && ! e) {
e = MtaScrollUpOneRow(t);
i++;
}
// Then scroll down until it's at the bottom.
while(!e && t->rows[t->size - 1]->index != l) {
MtaScrollDownOneRow(t); // Note that we _know_ that we can scroll !
}
}
}
void MtaScrollToRecord(MyTableType *t, Word i) {
REQUIRE(t != 0);
if(t->size > 1) {
while(i < t->rows[0]->index) {
MtaScrollUpOneRow(t);
}
while(i > t->rows[t->size - 1]->index) {
MtaScrollDownOneRow(t);
}
}
}
void MtaCloseMyTable (MyTableType *t) {
//FormType *f = NULL;
//FieldType *fi = NULL;
REQUIRE(t != NULL);
//f = FrmGetActiveForm(); CHECK(f);
//fi = FrmGetObjectPtr(f,FrmGetObjectIndex(f,id_field)); CHECK(fi);
//FldSetTextHandle(fi,NULL);
if(t->isEditing) {
DmReleaseRecord(t->db,t->rows[t->current]->index,true);
t->isEditing = false;
}
}