home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 Mobile / Chip_Mobile_2001.iso / palm / hobby / ghardeno / ghardeno.EXE / MyTable.c < prev    next >
C/C++ Source or Header  |  2000-10-03  |  27KB  |  999 lines

  1. /* 
  2.    MyWidgets: A widget library for PalmOS
  3.    Copyright (C) 2000 Laurent Moussault
  4.     
  5.    This library is free software; you can redistribute it and/or
  6.    modify it under the terms of the GNU Library General Public
  7.    License as published by the Free Software Foundation; either
  8.    version 2 of the License, or (at your option) any later version.
  9.   
  10.    This library is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.    Library General Public License for more details.
  14.   
  15.    You should have received a copy of the GNU Library General Public
  16.    License along with this library; if not, write to the
  17.    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  18.    Boston, MA 02111-1307, USA.
  19. */
  20.  
  21.  
  22. #include <Common.h>
  23. #include <System/SysAll.h>
  24. #include <UI/UIAll.h>
  25.  
  26. #include "DBC.h"
  27. #include "Ghardeno.h" /*** To remove ! ***/
  28. #include "Resources.h" /*** To remove ! ***/
  29. #include "Database.h" /*** To remove ! ***/
  30. #include "MyTable.h"
  31.  
  32.  
  33. ////
  34. /// Constantes
  35. //
  36.  
  37. #define fontMask 3
  38.  
  39. static const FontID fonts[] = {stdFont, boldFont, largeFont};
  40. static Word heights[3]; // = {11, 11, 14};
  41.  
  42. ///
  43. //
  44.  
  45. Boolean inhibitHighlight = false;
  46.  
  47. //////   _
  48. /////   | \    o       _  |_  _
  49. ////    |_/ |/ | \  /  _| |  /_     
  50. ///     |   |  |  \/  |_| |_ \_  
  51. //
  52.  
  53. ///
  54. // Making up rows.
  55.  
  56. static void LoadRow (MyTableType *t, Word n, VoidHand h, Word i) {
  57.   Char *r = 0;
  58.   Word lh = 0;
  59.   REQUIRE(t != 0);
  60.   REQUIRE(0 <= n); REQUIRE(n < mtaRowNumber);
  61.   REQUIRE(h != 0);
  62.   t->rows[n]->handle = h;
  63.   t->rows[n]->index = i;
  64.   RctCopyRectangle(&t->emptyRect,&t->rows[n]->rect);
  65.   t->loadRecord(h,&t->rows[n]->fontID,&t->rows[n]->rect.extent.y);
  66.   t->rows[n]->invalid = true;
  67. }
  68.  
  69. static VoidHand QueryNextRecord (MyTableType *t, Word *i) {
  70.   VoidHand result = 0;
  71.   Err e = 0;
  72.   Word a = 0;
  73.   REQUIRE(t != 0);
  74.   result = DmQueryNextInCategory(t->db,i,t->category);
  75.   /*
  76.   *i -= 1; // yup: this is a trick. 
  77.   do { 
  78.     *i += 1; 
  79.     result = DmQueryNextInCategory(t->db,i,t->category); 
  80.     if(result != 0) { 
  81.       DmRecordInfo(t->db,*i,&a, 0, 0); 
  82.     } 
  83.   } while(result != 0 && (a & dmRecAttrDelete)); 
  84.   */ 
  85.   return result;
  86. }
  87.  
  88. static Boolean AddNextRow (MyTableType *t, Word *i) {
  89.   Boolean result = false;
  90.   VoidHand h = NULL;
  91.   Word lh = 0;
  92.   REQUIRE(t != NULL);
  93.   CHECK(t->size < mtaRowNumber);
  94.   h = QueryNextRecord(t,i);
  95.   if(h != NULL) {
  96.     t->loadRecord(h,&t->rows[t->size]->fontID,&lh);
  97.     if(t->emptyRect.extent.y >= lh) {
  98.       t->rows[t->size]->handle = h;
  99.       t->rows[t->size]->index = *i;
  100.       RctCopyRectangle(&t->emptyRect,&t->rows[t->size]->rect);
  101.       t->rows[t->size]->rect.extent.y = lh;
  102.       t->emptyRect.topLeft.y += lh;
  103.       t->emptyRect.extent.y -= lh;
  104.       t->rows[t->size]->invalid = true;
  105.       if(t->isSelected && *i == t->currentIndex) {
  106.     t->current = t->size;
  107.     t->isShown = true;
  108.       }
  109.       t->size++;
  110.       result = true;
  111.     } else {
  112.       t->full = true;
  113.     }
  114.   }
  115.   return result;
  116. }
  117.  
  118. static void InsertRow(MyTableType *t, int n, VoidHand h, int i) {
  119.   int j = 0;
  120.   MyTableRowType *r = 0;
  121.   Word rh = 0;
  122.   REQUIRE(t != 0);
  123.   REQUIRE(0 <= n); REQUIRE(n <= t->size/* || n == mtaRowNumber*/);
  124.   REQUIRE(h != 0);
  125.   CHECK(t->size < mtaRowNumber);
  126.   // Load the record after the last row.
  127.   LoadRow(t,t->size,h,i);
  128.   r = t->rows[t->size]; CHECK(r != 0);
  129.   r->rect.topLeft.y = t->rows[n]->rect.topLeft.y;
  130.   // Move down alls raw after insertion point.
  131.   rh = t->rows[t->size]->rect.extent.y;
  132.   for(j = t->size; j > n; j--) {
  133.     t->rows[j] = t->rows[j - 1];
  134.     t->rows[j]->rect.topLeft.y += rh;
  135.     t->rows[j]->invalid = true;
  136.   }
  137.   // Put back the new record at the right place.
  138.   t->rows[n] = r;
  139.   t->size += 1;
  140.   // If necessary, remove one or two rows at the end.
  141.   while(t->emptyRect.extent.y < rh) {
  142.     t->size -= 1;
  143.     t->emptyRect.topLeft.y -= t->rows[t->size]->rect.extent.y;
  144.     t->emptyRect.extent.y += t->rows[t->size]->rect.extent.y;
  145.   }
  146.   // Adjust table's remaining height.
  147.   t->emptyRect.topLeft.y += rh;
  148.   t->emptyRect.extent.y -= rh;
  149. }
  150.  
  151. static void RemoveRow(MyTableType *t, Word n) {
  152.   MyTableRowType *r = 0;
  153.   int i;
  154.   REQUIRE(t != 0);
  155.   REQUIRE(0 <= n); REQUIRE(n < t->size);
  156.   // The row we remove is recycled, so put it aside.
  157.   r = t->rows[n];
  158.   // Move upward all raws after it.
  159.   t->size -= 1;
  160.   for(i = n; i < t->size; i++) {
  161.     t->rows[i] = t->rows[i + 1];
  162.     t->rows[i]->rect.topLeft.y -= r->rect.extent.y;
  163.     t->rows[i]->invalid = true;
  164.   }
  165.   // Recycle removed row.
  166.   t->rows[t->size] = r;
  167.   // Update table's remaining area.
  168.   t->emptyRect.topLeft.y -= r->rect.extent.y;
  169.   t->emptyRect.extent.y += r->rect.extent.y;
  170.   if(t->isShown) {
  171.     t->current -= 1;
  172.     t->isShown = (t->current >= 0);
  173.   }
  174. }
  175.  
  176. static int FindRecordRow (MyTableType *t, Word i) {
  177.   int result = 0;
  178.   REQUIRE(t != 0);
  179.   result = t->size - 1;
  180.   while(result >= 0 && t->rows[result]->index != i) {
  181.     result -= 1;
  182.   }
  183.   ENSURE(result == -1 || t->rows[result]->index == i);
  184.   return result;
  185. }
  186.  
  187. static void DeleteRecord(MyTableType *t, Word i) {
  188.   Boolean e = false;
  189.   int n = 0;
  190.   int j = 0;
  191.   Word k = 0;
  192.   REQUIRE(t != NULL);
  193.   // Delete it (!= remove).
  194.   DmDeleteRecord(t->db,i);
  195.   // I don't know _why_ we have to do that.  I don't know _why_ nobody
  196.   // explain we have to.  But if we don't move the record at the end
  197.   // of the database, it becomes a zombie.  This one almost drove me
  198.   // crazy.
  199.   DmMoveRecord (t->db,i,DmNumRecords(t->db));
  200.   t->categorySize -= 1;
  201.   // If this record was shown, we must update the table.
  202.   n = FindRecordRow(t,i);
  203.   // By moving the deleted record to the end, we messed up half the
  204.   // index we cached: let's update them.
  205.   if(n != -1) {
  206.     for(j = n+1; j < t->size; j++) {
  207.       t->rows[j]->index -= 1;
  208.     }
  209.     RemoveRow(t,n);
  210.     // As the table has one less row, it may be able to display new
  211.     // rows at the bottom.
  212.     if(t->size > 0) {
  213.       do {
  214.     k = t->rows[t->size - 1]->index + 1;
  215.     e = AddNextRow(t,&k);
  216.       } while(e);
  217.     }
  218.   }
  219. }
  220.  
  221. static int FindRecordRowByHandle (MyTableType *t, VoidHand h) {
  222.   int result = 0;
  223.   REQUIRE(t != 0);
  224.   result = t->size - 1;
  225.   while(result >= 0 && t->rows[result]->handle != h) {
  226.     result -= 1;
  227.   }
  228.   ENSURE(t->rows[result]->handle == h || result == -1);
  229.   return result;
  230. }
  231.  
  232. static void UpdateScrollBar (MyTableType *t) {
  233.   ScrollBarType *s = 0; 
  234.   Word m = 0;
  235.   REQUIRE(t != 0);
  236.   s = FrmGetObjectPtr(t->form,FrmGetObjectIndex(t->form,id_scrollBar)); 
  237.   if(t->categorySize > t->size)
  238.     m = t->categorySize - t->size;
  239.   else
  240.     m = t->positionInCategory;
  241.   SclSetScrollBar(s,t->positionInCategory,0,m,t->size);
  242. }
  243.  
  244. static void NewFieldEnterEvent (MyTableType *t, EventType *ev) {
  245.   FormType *f = NULL;
  246.   FieldType *fi = NULL;
  247.   EventType e;
  248.   f = FrmGetActiveForm(); CHECK(f);
  249.   fi = FrmGetObjectPtr(f,FrmGetObjectIndex(f,id_field)); CHECK(fi);
  250.   EvtCopyEvent(ev,&e);
  251.   e.eType = fldEnterEvent;
  252.   e.data.fldEnter.fieldID = fi->id;
  253.   e.data.fldEnter.pField = fi;
  254.   //FldHandleEvent (fi,&e);
  255. }
  256.  
  257. static void TrackStrokeEvent (MyTableType *t, SWord ox) {
  258.   SWord x = 0, y = 0;
  259.   Boolean p = false;
  260.   Boolean wc = false, nc = false; // "was changed" and "now changed"
  261.   Char *r = NULL;
  262.   RectangleType b;
  263.   REQUIRE(t != NULL);
  264.   r = MemHandleLock(t->rows[t->current]->handle); CHECK(r);
  265.   do {
  266.     PenGetPoint(&x,&y,&p);
  267.     nc = abs(ox - x) > 11 ;
  268.     if(nc != wc) {
  269.       SndPlaySystemSound(sndClick);
  270.       DmSet(r,0,1,(*r&3)|(*r&4?0:4));
  271.       DbDrawRecord(t->rows[t->current]->handle,&t->rows[t->current]->rect,false);
  272.       if(inhibitHighlight) {
  273.     /*
  274.       RctCopyRectangle(&t->rows[t->current]->rect,&b);
  275.       b.extent.x = bulletWidth - 1;
  276.       WinInvertRectangle(&b,3);
  277.     */
  278.       } else {
  279.     WinInvertRectangle(&t->rows[t->current]->rect,3);
  280.       }
  281.       UpdateToolBar(t);
  282.       wc = nc;
  283.     }
  284.   } while(p);
  285.   MemHandleUnlock(t->rows[t->current]->handle);
  286. }
  287.  
  288. static void TrackDragEvent (MyTableType *t) {
  289.   static CustomPatternType gr = { 0xAA55, 0xAA55, 0xAA55, 0xAA55 };
  290.   SWord x = 0, y = 0;
  291.   Boolean p = false;
  292.   int cr = 0, dr = 0;
  293.   RectangleType r;
  294.   int i;
  295.   Boolean s = false;
  296.   REQUIRE(t != NULL);
  297.   WinSetPattern(gr);
  298.   cr = t->current; CHECK(cr != -1);
  299.   do {
  300.     PenGetPoint(&x,&y,&p);
  301.     // First, let's find in what row the pen is.
  302.     dr = t->size;
  303.     for(i = 0; i < t->size && dr == t->size; i++)
  304.       if(y < t->rows[i]->rect.topLeft.y + (t->rows[i]->rect.extent.y/2) + (i<cr+1?-2:2))
  305.     dr = i;
  306.     dr -= 1;
  307.     if(dr != cr) { // We are in a different row.
  308.       if(!((cr==t->current&&dr==t->current-1) ||(dr==t->current&&cr==t->current-1)))
  309.     SndPlaySystemSound(sndClick);
  310.       if(cr != t->current && cr != t->current - 1) {
  311.     // Erase the previous position.
  312.     if(cr >= 0)
  313.       DbDrawRecord(t->rows[cr]->handle,&t->rows[cr]->rect,false);
  314.     else
  315.       WinEraseLine(t->rows[0]->rect.topLeft.x,
  316.                t->rows[0]->rect.topLeft.y-1,
  317.                t->rows[0]->rect.topLeft.x+t->rows[0]->rect.extent.x-1,
  318.                t->rows[0]->rect.topLeft.y-1 );
  319.     if(cr+1 < t->size)
  320.       DbDrawRecord(t->rows[cr + 1]->handle,&t->rows[cr + 1]->rect,false);
  321.     else
  322.       WinEraseLine(t->rows[cr]->rect.topLeft.x,
  323.                t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y,
  324.                t->rows[cr]->rect.topLeft.x+t->rows[cr]->rect.extent.x-1,
  325.                t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y );
  326.       }
  327.       if(dr != t->current && dr != t->current - 1) {
  328.     // Draw the new position.
  329.     if(dr >= 0) {
  330.       RctCopyRectangle(&t->rows[dr]->rect,&r);
  331.       r.topLeft.y += r.extent.y - 1;
  332.     } else {
  333.       RctCopyRectangle(&t->rows[0]->rect,&r);
  334.       r.topLeft.y -= 1;
  335.     }
  336.     r.extent.y = 2;
  337.     WinFillRectangle(&r,0);
  338.       }
  339.       cr = dr;
  340.     } else if(dr == -1 && t->positionInCategory > 0) {
  341.       if(cr != t->current && cr != t->current - 1) {
  342.     // Erase the previous position.
  343.     if(cr >= 0)
  344.       DbDrawRecord(t->rows[cr]->handle,&t->rows[cr]->rect,false);
  345.     else
  346.       WinEraseLine(t->rows[0]->rect.topLeft.x,
  347.                t->rows[0]->rect.topLeft.y-1,
  348.                t->rows[0]->rect.topLeft.x+t->rows[0]->rect.extent.x-1,
  349.                t->rows[0]->rect.topLeft.y-1 );
  350.     if(cr+1 < t->size)
  351.       DbDrawRecord(t->rows[cr + 1]->handle,&t->rows[cr + 1]->rect,false);
  352.     else
  353.       WinEraseLine(t->rows[cr]->rect.topLeft.x,
  354.                t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y,
  355.                t->rows[cr]->rect.topLeft.x+t->rows[cr]->rect.extent.x-1,
  356.                t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y );
  357.       }
  358.       s = MtaScrollUpOneRow(t);
  359.       if(! s) {
  360.     SndPlaySystemSound(sndClick);
  361.     MtaRedraw(t);
  362.     // Draw the new position.
  363.     if(t->rows[0]->index != t->currentIndex) {
  364.       RctCopyRectangle(&t->rows[0]->rect,&r);
  365.       r.topLeft.y -= 1;
  366.       r.extent.y = 2;
  367.       WinFillRectangle(&r,0);
  368.     }
  369.       }
  370.     } else if(dr == t->size - 1) {
  371.       if(cr != t->current && cr != t->current - 1) {
  372.     // Erase the previous position.
  373.     if(cr >= 0)
  374.       DbDrawRecord(t->rows[cr]->handle,&t->rows[cr]->rect,false);
  375.     else
  376.       WinEraseLine(t->rows[0]->rect.topLeft.x,
  377.                t->rows[0]->rect.topLeft.y-1,
  378.                t->rows[0]->rect.topLeft.x+t->rows[0]->rect.extent.x-1,
  379.                t->rows[0]->rect.topLeft.y-1 );
  380.     if(cr+1 < t->size)
  381.       DbDrawRecord(t->rows[cr + 1]->handle,&t->rows[cr + 1]->rect,false);
  382.     else
  383.       WinEraseLine(t->rows[cr]->rect.topLeft.x,
  384.                t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y,
  385.                t->rows[cr]->rect.topLeft.x+t->rows[cr]->rect.extent.x-1,
  386.                t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y );
  387.       }
  388.       s = MtaScrollDownOneRow(t);
  389.       if(! s) {
  390.     SndPlaySystemSound(sndClick);
  391.     MtaRedraw(t);
  392.     // Draw the new position.
  393.     if(t->rows[t->size - 1]->index != t->currentIndex) {
  394.       RctCopyRectangle(&t->rows[t->size - 1]->rect,&r);
  395.       r.topLeft.y += r.extent.y - 1;
  396.       r.extent.y = 2;
  397.       WinFillRectangle(&r,0);
  398.     }
  399.       }
  400.       // As we have scrolled, this may have change.
  401.       dr = t->size - 1;
  402.       cr = dr;
  403.     }
  404.   } while(p);
  405.   if(cr != t->current && cr != t->current - 1) {
  406.     //SndPlaySystemSound(sndClick);
  407.     if(cr >= 0)
  408.       DbDrawRecord(t->rows[cr]->handle,&t->rows[cr]->rect,false);
  409.     else
  410.       WinEraseLine(t->rows[0]->rect.topLeft.x,
  411.            t->rows[0]->rect.topLeft.y-1,
  412.            t->rows[0]->rect.topLeft.x+t->rows[0]->rect.extent.x-1,
  413.            t->rows[0]->rect.topLeft.y-1 );
  414.     if(cr+1 < t->size)
  415.       DbDrawRecord(t->rows[cr + 1]->handle,&t->rows[cr + 1]->rect,false);
  416.     else
  417.       WinEraseLine(t->rows[cr]->rect.topLeft.x,
  418.            t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y,
  419.            t->rows[cr]->rect.topLeft.x+t->rows[cr]->rect.extent.x-1,
  420.            t->rows[cr]->rect.topLeft.y+t->rows[cr]->rect.extent.y );
  421.     if(cr == -1) {
  422.       DmMoveRecord(t->db,t->currentIndex,
  423.            t->rows[0]->index );
  424.       MtaFillTableFromTop(t,t->rows[0]->index);
  425.     } else {
  426.       DmMoveRecord(t->db,t->currentIndex,
  427.            t->rows[cr]->index + 1 );
  428.       MtaFillTableFromTop(t,t->rows[0]->index);
  429.     }
  430.     MtaChangeSelection(t,cr + (cr > t->current ? 0 : 1));
  431.     MtaRedraw(t);
  432.     UpdateToolBar(t);
  433.   }
  434. }
  435.  
  436.  
  437. //////   _
  438. /////   | \     |  | o  _
  439. ////    |_/ | | |\ | | / 
  440. ///     |   |_| |/ | | \_
  441. //
  442.  
  443. ////
  444. /// Module initialisation
  445. //
  446.  
  447. void MtaInit() {
  448.   int i;
  449.   for(i = 0; i < 3; i++) {
  450.     FntSetFont(fonts[i]);
  451.     heights[i] = FntLineHeight();
  452.   }
  453. }
  454.  
  455. ////
  456. /// Creation
  457. //
  458.  
  459. VoidHand MtaNew(FormType *f, Word i, Word m, DmOpenRef d) {
  460.   VoidHand result = 0;
  461.   MyTableType *t = 0;
  462.   Boolean e = false;
  463.   int j;
  464.   REQUIRE(f != 0);
  465.   result = MemHandleNew(sizeof(MyTableType));
  466.   if(result != 0) {
  467.     t = MemHandleLock(result); CHECK(t != 0);
  468.     t->form = f;
  469.     t->id = i;
  470.     FrmGetObjectBounds(f,FrmGetObjectIndex(f,i),&t->bounds);
  471.     t->maxSize = m;
  472.     t->size = 0;
  473.     t->current = -1;
  474.     // Not necessary, but helps to find bugs:
  475.     t->currentIndex = 0; t->isShown = false; t->positionInCategory = 0; 
  476.     t->isSelected = false;
  477.     t->isEditing = false;
  478.     t->rows = MemPtrNew(sizeof(MyTableRowType*[m]));
  479.     for(j = 0; j < m; j++)
  480.       t->rows[j] = MemPtrNew(sizeof(MyTableRowType));
  481.     t->db = d;
  482.     t->category = dmAllCategories;
  483.     t->categorySize = DmNumRecordsInCategory(t->db,t->category);
  484.     t->full = false;
  485.     e = t->rows == 0; // If allocation failed, we need to free 'result'.
  486.     MemHandleUnlock(result);
  487.     FrmSetGadgetData(f,FrmGetObjectIndex(f,i),result);
  488.   }
  489.   if(e) {
  490.     MemHandleFree(result);
  491.     result = 0;
  492.   }
  493.   return result;
  494. }
  495.  
  496. void MtaSetLoadRecordProcedure (MyTableType *t, MtaLoadRecordFuncType *lrf) {
  497.   REQUIRE(t != 0);
  498.   REQUIRE(lrf != 0);
  499.   t->loadRecord = lrf;
  500. }
  501.  
  502.  
  503. ////
  504. ///
  505. //
  506.  
  507. void MtaNewRecord (MyTableType *t) {
  508.   int i = 0;
  509.   Word ri = 0;
  510.   int ip = 0;
  511.   VoidHand h = NULL;
  512.   Char *p = NULL;
  513.   Word a = 0;
  514.   REQUIRE(t != NULL);
  515.   if(t->isSelected) {
  516.     ri = t->currentIndex + 1;
  517.   }
  518.   h = DmNewRecord(t->db,&ri,2); CHECK(h);
  519.   DmRecordInfo(t->db,ri,&a,0,0);
  520.   if(t->category == dmAllCategories) {
  521.     a = (a & ~dmRecAttrCategoryMask) | dmUnfiledCategory;
  522.   } else {
  523.     a = (a & ~dmRecAttrCategoryMask) | t->category;
  524.   }
  525.   DmSetRecordInfo(t->db,ri,&a,0);
  526.   p = MemHandleLock(h); CHECK(p);
  527.   InitNewRecord(p);
  528.   MemHandleUnlock(h);
  529.   DmReleaseRecord(t->db,ri,true);
  530.   t->categorySize += 1;
  531.   ip = t->isShown ? t->current + 1 : 0;
  532.   InsertRow(t,ip,h,ri);
  533.   // As we cache the index of each displayed record, we must update it
  534.   // by hand.
  535.   for(i = ip + 1; i < t->size; i++) {
  536.     t->rows[i]->index += 1;
  537.   }
  538.   MtaScrollToRecord(t,ri);
  539.   CHECK(FindRecordRowByHandle(t,h) != -1);
  540.   CHECK(t->rows[FindRecordRowByHandle(t,h)]->index == ri);
  541.   MtaChangeSelection(t,FindRecordRow(t,ri));
  542. }
  543.  
  544. void MtaDeleteCurrentRecord (MyTableType *t) {
  545.   VoidHand h = 0;
  546.   Boolean e = false;
  547.   REQUIRE(t != NULL);
  548.   REQUIRE(t->isSelected);
  549.   DeleteRecord(t,t->currentIndex);
  550.   t->isShown = false;
  551.   t->isSelected = false;
  552.   t->isEditing = false;
  553.   /*
  554.   h = QueryNextRecord(t,&t->currentIndex);
  555.   if(h !=0) {
  556.     MtaScrollToRecord(t,t->currentIndex);
  557.     MtaChangeSelection(t,FindRecordRow(t,t->currentIndex));
  558.   } else if(t->size > 0) {
  559.     // If there is no "next" record, that must be because we just
  560.     // deleted the last one, so let's select the new last one.
  561.     MtaChangeSelection(t,t->size - 1);
  562.   }
  563.   */
  564. }
  565.  
  566.  
  567. ////
  568. ///
  569. //
  570.  
  571. void MtaFillTableFromTop (MyTableType *t, UInt i) {
  572.   FormType *f = NULL;
  573.   Boolean e = false;
  574.   REQUIRE(t != NULL);
  575.   t->size = 0;
  576.   t->isShown = false;
  577.   t->full = false;
  578.   t->categorySize = DmNumRecordsInCategory(t->db,t->category);
  579.   f = FrmGetActiveForm(); CHECK(f);
  580.   FrmGetObjectBounds(f,FrmGetObjectIndex(f,t->id),&t->emptyRect);
  581.   do {
  582.     e = AddNextRow(t,&i);
  583.     i++;
  584.   } while(e);
  585.   if(t->size > 0) {
  586.     t->positionInCategory = DmPositionInCategory(t->db,t->rows[0]->index,t->category);
  587.   }
  588. }
  589.  
  590.  
  591.  
  592. void MtaEditSelectedRow (MyTableType *t) {
  593.   FormType *f = FrmGetActiveForm();
  594.   FieldType *fi = FrmGetObjectPtr(f,FrmGetObjectIndex(f,id_field));
  595.   Char *c;
  596.   MemSet(fi,sizeof(FieldType),0);
  597.   fi->id = id_field;
  598.   RctCopyRectangle(&t->rows[t->current]->rect,&fi->rect);
  599.   fi->rect.topLeft.x += 2;
  600.   fi->rect.extent.x -= 2;
  601.   fi->attr.usable = true;
  602.   fi->attr.visible = true;
  603.   fi->attr.editable = true;
  604.   fi->attr.singleLine = true;
  605.   fi->attr.hasFocus = false; // otherwise FrmSetFocus doesn't work !
  606.   fi->attr.dynamicSize = false;
  607.   fi->attr.insPtVisible = true;
  608.   fi->attr.dirty = false;
  609.   //fi->attr.underlined = 3;
  610.   fi->attr.justification = leftAlign;
  611.   fi->attr.hasScrollBar = false;
  612.   fi->attr.autoShift = true;
  613.   fi->attr.numeric = false;
  614.   fi->maxChars = 111;
  615.   fi->fontID = t->rows[t->current]->fontID;
  616.   FldSetText(fi,t->rows[t->current]->handle,1,
  617.          MemHandleSize(t->rows[t->current]->handle)-1 );
  618.   FrmSetFocus(f,FrmGetObjectIndex(f,id_field));
  619.   WinEraseRectangle(&t->rows[t->current]->rect,0);
  620.   t->isEditing = true;
  621.   t->rows[t->current]->invalid = true;
  622. }
  623.  
  624.  
  625. ////
  626. /// Public funcs.
  627. //
  628.  
  629. void MtaRedraw (MyTableType *t) {
  630.   FormType *f = NULL;
  631.   int i = 0;
  632.   RectangleType b;
  633.   REQUIRE(t != NULL);
  634.   f = FrmGetActiveForm(); CHECK(f);
  635.   FrmHideObject(f,FrmGetObjectIndex(f,id_field));
  636.   UpdateScrollBar(t);
  637.   for(i = 0; i < t->size; i++) {
  638.     if(t->rows[i]->invalid) {
  639.       DbDrawRecord(t->rows[i]->handle,&t->rows[i]->rect,
  640.            (t->isShown && t->isEditing && i == t->current) );
  641.       if(t->isShown && i == t->current && ! t->isEditing) {
  642.     if (inhibitHighlight) {
  643.       /*
  644.       RctCopyRectangle(&t->rows[i]->rect,&b);
  645.       b.extent.x = bulletWidth - 1;
  646.       WinInvertRectangle(&b,3);
  647.       */
  648.     } else {
  649.       WinInvertRectangle(&t->rows[i]->rect,3);
  650.     }
  651.       } 
  652.       t->rows[i]->invalid = false; 
  653.     }
  654.   }
  655.   WinEraseRectangle(&t->emptyRect,0);
  656. }
  657.  
  658. void MtaUnselect (MyTableType *t) {
  659.   FieldType *fi = NULL;
  660.   Boolean rm = false;
  661.   REQUIRE(t != 0);
  662.   REQUIRE(t->isSelected);
  663.   if(t->isEditing) {
  664.     fi = FrmGetObjectPtr(t->form,FrmGetObjectIndex(t->form,id_field)); CHECK(fi);
  665.     // If the current edited row is empty, we'll have to remove it.
  666.     rm = (FldGetTextLength(fi) == 0);
  667.     // Unconnect the record and the field.
  668.     FldSetTextHandle(fi,NULL);
  669.     DmReleaseRecord(t->db,t->currentIndex,true);
  670.     //FrmHideObject(f,FrmGetObjectIndex(f,id_field));
  671.     t->isEditing = false;
  672.   }
  673.   if(t->isShown) {
  674.     // The selection/edition was shown, so we'll have to redraw it.
  675.     t->isShown = false;
  676.     t->rows[t->current]->invalid = true;
  677.   }
  678.   if(rm) {
  679.     DeleteRecord(t,t->currentIndex);
  680.   }
  681.   t->isSelected = false;
  682. }
  683.  
  684. void MtaChangeSelection (MyTableType *t, int i) {
  685.   REQUIRE(t != NULL);
  686.   REQUIRE(i == -1 || (0 <= i && i < mtaRowNumber));
  687.   if(t->isSelected) {
  688.     MtaUnselect(t);
  689.   } else CHECK(! t->isEditing);
  690.   t->current = i;
  691.   t->isSelected = (i != -1);
  692.   t->isShown = (i != -1);
  693.   if(t->isShown) {
  694.     t->rows[t->current]->invalid = true;
  695.     t->currentIndex = t->rows[t->current]->index;
  696.   }
  697. }
  698.  
  699. void MtaChangeSelectionIndex (MyTableType *t, Word i) {
  700.   REQUIRE(t != NULL);
  701.   if(t->isSelected)
  702.     {
  703.       MtaUnselect(t);
  704.     } else CHECK(! t->isEditing);
  705.   t->isSelected = true;
  706.   t->currentIndex = i;
  707.   t->current = FindRecordRow (t, i);
  708.   t->isShown = t->current != -1;
  709.   if(t->isShown)
  710.     {
  711.       t->rows[t->current]->invalid = true;
  712.     }
  713. }
  714.  
  715.  
  716. void MtaUpdateCurrentRow (MyTableType *t) {
  717.   int i = 0;
  718.   Word oy = 0;
  719.   int dh = 0; // *Must* be int, not Word !
  720.   REQUIRE(t != 0);
  721.   if(t->isShown) {
  722.     // Load the row and compute height difference.
  723.     dh = t->rows[t->current]->rect.extent.y;
  724.     oy = t->rows[t->current]->rect.topLeft.y;
  725.     LoadRow(t,t->current,t->rows[t->current]->handle,t->currentIndex);
  726.     t->rows[t->current]->rect.topLeft.y = oy;
  727.     dh = t->rows[t->current]->rect.extent.y - dh;
  728.     if(dh != 0) {
  729.       // There's a height difference, so update all following rows'
  730.       // position.
  731.       for(i = t->current + 1; i < t->size; i++) {
  732.     t->rows[i]->rect.topLeft.y += dh;
  733.     t->rows[i]->invalid = true;
  734.       }
  735.       // We may have pushed down one or two rows outside of the table:
  736.       // remove them.
  737.       while(t->emptyRect.extent.y < dh) {
  738.     t->size -= 1;
  739.     t->emptyRect.topLeft.y -= t->rows[t->size]->rect.extent.y;
  740.     t->emptyRect.extent.y += t->rows[t->size]->rect.extent.y;
  741.       }
  742.       // Update the bounds of the empty area.
  743.       t->emptyRect.topLeft.y += dh;
  744.       t->emptyRect.extent.y -= dh;
  745.       // Now we may have removed the current row !
  746.       t->isShown = (t->current < t->size);
  747.       while(t->current >= t->size) {
  748.     MtaScrollDownOneRow(t);
  749.       }
  750.     }
  751.   }
  752. }
  753.  
  754.  
  755. Boolean MtaHandlePenDownEvent (MyTableType *t, EventType *ev) {
  756.   Boolean result = false;
  757.   FormType *f = NULL;
  758.   Boolean p = 0;
  759.   SWord x = 0, y = 0;
  760.   int os = 0, ds = 0;
  761.   Boolean oss = false;
  762.   int i = 0;
  763.   Boolean st = false, dr = false;
  764.   Char *r = 0;
  765.   REQUIRE(t != NULL);
  766.   REQUIRE(ev != NULL);
  767.   f = FrmGetActiveForm(); CHECK(f);
  768.   oss = t->isSelected;
  769.   os = t->isShown ? t->current : -1;
  770.   // First, let's find where the pen is.
  771.   ds = -1;
  772.   for(i = 0; i<t->size && ds == -1; i++)
  773.     if(RctPtInRectangle(ev->screenX,ev->screenY,&t->rows[i]->rect))
  774.       ds = i;
  775.   if (ds == -1)
  776.     {
  777.       if (t->isSelected)
  778.     {
  779.       MtaUnselect (t);
  780.       MtaRedraw (t);
  781.     }
  782.       result = true;
  783.     }
  784.   else if (ev->screenX > bulletWidth)
  785.     {
  786.       // The tap is inside the text, let's edit it and pass the event
  787.       // to the system handlers.
  788.       if ((t->isShown && (ds != t->current || ! t->isEditing)) || ! t->isShown)
  789.     {
  790.       MtaChangeSelection (t, ds);
  791.       r = MemHandleLock (t->rows[t->current]->handle);
  792.       if (!(*r & 4))
  793.         {
  794.           MtaEditSelectedRow (t);
  795.           NewFieldEnterEvent (t, ev);
  796.         }
  797.       MemHandleUnlock (t->rows[t->current]->handle);
  798.       MtaRedraw (t);
  799.       UpdateToolBar (t);
  800.     }
  801.       // 'result' is false.
  802.     }
  803.   else
  804.     {
  805.       // The tap is in the bullet.
  806.       if (t->isShown && ds == t->current && t->isEditing)
  807.     {
  808.       MtaUnselect (t);
  809.     }
  810.       if (! t->isShown || ds != t->current)
  811.     {
  812.       MtaChangeSelection(t,ds);
  813.       MtaRedraw(t);
  814.       UpdateToolBar(t);
  815.     }
  816.       if (t->isShown) {
  817.     do {
  818.       PenGetPoint(&x,&y,&p);
  819.       if(RctPtInRectangle(x,y,&t->rows[t->current]->rect)) {
  820.         if(!st && abs(ev->screenX - x) > 10)
  821.           st = true;
  822.       } else {
  823.         dr = true;
  824.       }
  825.     } while(p && ! st && ! dr);
  826.     if(st) {
  827.       TrackStrokeEvent(t, ev->screenX);
  828.       if(! oss) {
  829.         MtaUnselect(t);
  830.         MtaRedraw(t);
  831.       }
  832.       result = true;
  833.     } else if(dr) {
  834.       TrackDragEvent(t);
  835.       result = true;
  836.     } else if(ev->screenX > bulletWidth/* && t->current == os*/) {
  837.       //SndPlaySystemSound(sndClick);
  838.       MtaEditSelectedRow(t);
  839.       NewFieldEnterEvent(t,ev);
  840.       MtaRedraw(t);
  841.       result = false;
  842.     }
  843.       }
  844.     }
  845.   return result;
  846. }
  847.  
  848.  
  849. ////
  850. /// Scrolling
  851. //
  852.  
  853. void MtaScrollTo(MyTableType *t, int n) {
  854.   Word i = 0;
  855.   REQUIRE(t != NULL);
  856.   //DmQueryNextInCategory(t->db,&i,t->category);
  857.   QueryNextRecord(t,&i);
  858.   DmSeekRecordInCategory(t->db,&i,n,dmSeekForward,t->category);
  859.   MtaFillTableFromTop(t,i);
  860.   t->positionInCategory = n;
  861.   //MtaUnselect(t);//MtaChangeSelection(t,-1);
  862.   UpdateToolBar(t);
  863.   MtaRedraw(t);
  864. }
  865.  
  866. Boolean MtaScrollDownOneRow(MyTableType *t) {
  867.   Boolean result = false;
  868.   Word i = 0;
  869.   Word rh = 0;
  870.   VoidHand h = 0;
  871.   REQUIRE(t != 0);
  872.   REQUIRE(t->size < mtaRowNumber);
  873.   // Find next record.
  874.   i = t->rows[t->size - 1]->index + 1;
  875.   h = QueryNextRecord(t,&i);
  876.   if(h != 0) {
  877.     // Insert the record after last row.
  878.     LoadRow(t,t->size,h,i);
  879.     rh = t->rows[t->size]->rect.extent.y;
  880.     t->size += 1;
  881.     if(t->isSelected && t->currentIndex == t->rows[t->size - 1]->index) {
  882.       // The current row is the one just inserted, so show it.
  883.       CHECK(! t->isShown);
  884.       t->current = t->size - 1;
  885.       t->isShown = true;
  886.     }
  887.     // Remove as many top rows as necessary to show the new one.
  888.     while(rh > t->emptyRect.extent.y) {
  889.       RemoveRow(t,0);
  890.       t->positionInCategory += 1;
  891.     }
  892.     // Adjust table's remaining area.
  893.     t->emptyRect.topLeft.y += rh;
  894.     t->emptyRect.extent.y -= rh;
  895.   } else {
  896.     // We're at the end of the database, so can't scroll down.
  897.     result = true;
  898.   }
  899.   return result;
  900. }
  901.  
  902. void MtaScrollDownOnePage(MyTableType *t) {
  903.   Word l = 0;
  904.   Boolean e = false;
  905.   REQUIRE(t != NULL);
  906.   if(t->size > 0) {
  907.     l = t->rows[t->size - 1]->index;
  908.     if(t->full) { 
  909.       while(t->rows[0]->index != l && ! e) {
  910.     e = MtaScrollDownOneRow(t);
  911.       }
  912.     }
  913.   } 
  914. }
  915.  
  916. Boolean MtaScrollUpOneRow(MyTableType *t) {
  917.   Boolean result = false;
  918.   Word i = 0;
  919.   Word rh = 0;
  920.   VoidHand h = 0;
  921.   Boolean e = false;
  922.   REQUIRE(t != 0);
  923.   REQUIRE(t->size < mtaRowNumber);
  924.   // Find the record before the first row.
  925.   i = t->rows[0]->index;
  926.   e = DmSeekRecordInCategory(t->db,&i,1,dmSeekBackward,t->category);
  927.   if(e == 0) {
  928.     // Insert it at top.
  929.     h = DmQueryNextInCategory(t->db,&i,t->category); CHECK(h);
  930.     InsertRow(t,0,h,i);
  931.     t->positionInCategory -= 1;
  932.     if(t->isShown) {
  933.       // Update current position, or hide it if necessary.
  934.       t->current += 1;
  935.       t->isShown = (t->current < t->size);
  936.     } else if(t->isSelected && t->currentIndex == t->rows[0]->index) {
  937.       // The selected row is the one just inserted at top, so show it.
  938.       t->current = 0;
  939.       t->isShown = true;
  940.     }
  941.   } else {
  942.     // We're at the top of the database, so can't scroll up.
  943.     result = true;
  944.   }
  945.   return result;
  946. }
  947.  
  948.  
  949. void MtaScrollUpOnePage(MyTableType *t) {
  950.   // Yep, this algorithm is quite hairy. It's due to the fact that I
  951.   // use 'MtaScrollUpOneRow', wich aim is not to put the last - 1 row at
  952.   // bottom but to introduce one row at top.  May be that's not the
  953.   // Right Way ?  Well, it works, so I'll think about it later.
  954.   Word i = 0;
  955.   Word l = 0;
  956.   Boolean e = false;
  957.   REQUIRE(t != NULL);
  958.   if(t->size > 0) {
  959.     // Remember which row we want at the bottom.
  960.     l = t->rows[i]->index;
  961.     // Scroll up until this row disappears past the bottom.
  962.     while(i < t->size && ! e) {
  963.       e = MtaScrollUpOneRow(t);
  964.       i++;
  965.     }
  966.     // Then scroll down until it's at the bottom.
  967.     while(!e && t->rows[t->size - 1]->index != l) {
  968.       MtaScrollDownOneRow(t); // Note that we _know_ that we can scroll !
  969.     }
  970.   } 
  971. }
  972.  
  973. void MtaScrollToRecord(MyTableType *t, Word i) {
  974.   REQUIRE(t != 0);
  975.   if(t->size > 1) {
  976.     while(i < t->rows[0]->index) {
  977.       MtaScrollUpOneRow(t);
  978.     }
  979.     while(i > t->rows[t->size - 1]->index) {
  980.       MtaScrollDownOneRow(t);
  981.     }
  982.   }
  983. }
  984.  
  985.  
  986.  
  987. void MtaCloseMyTable (MyTableType *t) {
  988.   //FormType *f = NULL;
  989.   //FieldType *fi = NULL;
  990.   REQUIRE(t != NULL);
  991.   //f = FrmGetActiveForm(); CHECK(f);
  992.   //fi = FrmGetObjectPtr(f,FrmGetObjectIndex(f,id_field)); CHECK(fi);
  993.   //FldSetTextHandle(fi,NULL);
  994.   if(t->isEditing) {
  995.     DmReleaseRecord(t->db,t->rows[t->current]->index,true);
  996.     t->isEditing = false;
  997.   }
  998. }
  999.