home *** CD-ROM | disk | FTP | other *** search
/ The Devil's Doorknob BBS Capture (1996-2003) / devilsdoorknobbbscapture1996-2003.iso / Dloads / OTHERUTI / TPASCAL3.ZIP / TVDEMOS.ZIP / LISTDLG.PAS < prev    next >
Pascal/Delphi Source File  |  1991-06-11  |  15KB  |  553 lines

  1. {************************************************}
  2. {                                                }
  3. {   Turbo Pascal 6.0                             }
  4. {   Turbo Vision Forms Demo                      }
  5. {   Copyright (c) 1990 by Borland International  }
  6. {                                                }
  7. {************************************************}
  8.  
  9. unit ListDlg;
  10.  
  11. {$F+,O+,X+,S-,D+}
  12.  
  13. interface
  14.  
  15. uses
  16.   Dos, Objects, Memory, Drivers, Views, Dialogs, Stddlg,
  17.   DataColl, FormCmds;
  18.  
  19. type
  20.   PListKeyBox = ^TListKeyBox;
  21.   TListKeyBox = object(TSortedListBox)
  22.     function GetText(Item: Integer; MaxLen: Integer): String; virtual;
  23.   end;
  24.  
  25.   PListDialog = ^TListDialog;
  26.   TListDialog = object(TDialog)
  27.     DataCollection: PDataCollection;
  28.     FileName: PString;
  29.     FormDataFile: PResourceFile;
  30.     IsValid: Boolean;
  31.     List: PListKeyBox;
  32.     Modified: Boolean;
  33.     constructor Init(RezName: PathStr);
  34.     destructor Done; virtual;
  35.     procedure Close; virtual;
  36.     procedure HandleEvent(var Event: TEvent); virtual;
  37.     function OpenDataFile(Name: PathStr;
  38.       var DataFile: PResourceFile; Mode: Word): Boolean;
  39.     function SaveList: Boolean;
  40.     function SaveForm(F: PDialog): Boolean;
  41.     procedure StackOnPrev(F: PDialog);
  42.     function Valid(Command: Word): Boolean; virtual;
  43.   end;
  44.  
  45. function FileExists(Name: PathStr): Boolean;
  46.  
  47. implementation
  48.  
  49. uses App, Forms, MsgBox;
  50.  
  51. function FileExists(Name: PathStr): Boolean;
  52. var
  53.   SR: SearchRec;
  54. begin
  55.   FindFirst(Name, 0, SR);
  56.   FileExists := DosError = 0;
  57. end;
  58.  
  59. { TListKeyBox }
  60. function TListKeyBox.GetText(Item: Integer; MaxLen: Integer): String;
  61. var
  62.   S: String;
  63. begin
  64.   with PDataCollection(List)^ do
  65.   begin
  66.     case KeyType of
  67.       StringKey: GetText := TSortedListBox.GetText(Item, MaxLen);
  68.       LongIntKey:
  69.         begin
  70.           Str(LongInt(KeyOf(At(Item))^):MaxLen - 3, S);
  71.           GetText := Copy(S, 1, MaxLen);
  72.         end;
  73.     end;
  74.   end;
  75. end;
  76.  
  77. { TListDialog }
  78. constructor TListDialog.Init(RezName: PathStr);
  79. const
  80.   ButtonCt = 4;
  81.   FormX = 2;
  82.   FormY = 2;
  83.   FormWd = 30;
  84.   FormHt = 13;
  85.   ListX = 2;
  86.   ListY = 3;
  87.   DefaultListWd = 12;
  88.   ListHt = ButtonCt * 2;
  89.   ButtonWd = 12;
  90.   ButtonY = ListY;
  91.  
  92. var
  93.   R: TRect;
  94.   SB: PScrollBar;
  95.   Y: Integer;
  96.   D: DirStr;
  97.   N: NameStr;
  98.   E: ExtStr;
  99.   F: PForm;
  100.   ListWd: Word;
  101.   ButtonX: Word;
  102. begin
  103.   FSplit(FExpand(RezName), D, N, E);
  104.   R.Assign(FormX, FormY, FormX + FormWd, FormY + FormHt);
  105.   TDialog.Init(R, N);
  106.  
  107.   FileName := NewStr(D + N + E);
  108.   { Read data off resource stream }
  109.   if OpenDataFile(FileName^, FormDataFile, stOpen) then
  110.   begin
  111.     { Get horizontal size of key field }
  112.     F := PForm(FormDataFile^.Get('FormDialog'));
  113.     if F = nil then
  114.     begin
  115.       MessageBox('Error accessing file data.', nil, mfError or mfOkButton);
  116.       Exit;
  117.     end;
  118.  
  119.     { Base listbox width on key width. Grow entire dialog if required }
  120.     if F^.KeyWidth > DefaultListWd then
  121.     begin
  122.       ListWd := F^.KeyWidth;
  123.       GrowTo(FormWd + ListWd - DefaultListWd, FormHt);
  124.     end
  125.     else ListWd := DefaultListWd;
  126.  
  127.     { Move to upper right corner of desktop }
  128.     Desktop^.GetExtent(R);                    { Desktop coordinates }
  129.     MoveTo(R.B.X - Size.X, 1);
  130.  
  131.     Dispose(F, Done);
  132.  
  133.     { Read data collection into memory }
  134.     DataCollection := PDataCollection(FormDataFile^.Get('FormData'));
  135.     if DataCollection <> nil then
  136.     begin
  137.       { Loaded successfully: build ListDialog dialog }
  138.  
  139.       { Scrollbar }
  140.       R.Assign(ListX + ListWd, ListY, ListX + ListWd + 1, ListY + ListHt);
  141.       SB := New(PScrollBar, Init(R));
  142.       Insert(SB);
  143.  
  144.       { List box }
  145.       R.Assign(ListX, ListY, ListX + ListWd, ListY + ListHt);
  146.       List := New(PListKeyBox, Init(R, 1, SB));
  147.       List^.NewList(DataCollection);
  148.       Insert(List);
  149.  
  150.       { Label }
  151.       R.Assign(ListX, ListY - 1, ListX + 10, ListY);
  152.       Insert(New(PLabel, Init(R, '~K~eys', List)));
  153.  
  154.       { Buttons }
  155.       ButtonX := ListX + ListWd + 2;
  156.       Y := ButtonY;
  157.       R.Assign(ButtonX, Y, ButtonX + ButtonWd, Y + 2);
  158.       Insert(New(PButton, Init(R, '~E~dit', cmFormEdit, bfDefault)));
  159.  
  160.       Inc(Y, 2);
  161.       R.Assign(ButtonX, Y, ButtonX + ButtonWd, Y + 2);
  162.       Insert(New(PButton, Init(R, '~N~ew', cmFormNew, bfNormal)));
  163.  
  164.       Inc(Y, 2);
  165.       R.Assign(ButtonX, Y, ButtonX + ButtonWd, Y + 2);
  166.       Insert(New(PButton, Init(R, '~D~elete', cmFormDel, bfNormal)));
  167.  
  168.       Inc(Y, 2);
  169.       R.Assign(ButtonX, Y, ButtonX + ButtonWd, Y + 2);
  170.       Insert(New(PButton, Init(R, '~S~ave', cmListSave, bfNormal)));
  171.  
  172.       SelectNext(False);      { Select first field }
  173.       IsValid := True;
  174.     end;
  175.   end;
  176. end;
  177.  
  178. destructor TListDialog.Done;
  179. begin
  180.   if List <> nil then Dispose(List, Done);
  181.   if DataCollection <> nil then Dispose(DataCollection, Done);
  182.   if FormDataFile <> nil then Dispose(FormDataFile, Done);
  183.   if FileName <> nil then DisposeStr(FileName);
  184.   TDialog.Done;
  185. end;
  186.  
  187. procedure TListDialog.Close;
  188. begin
  189.   { TDialog.Close calls Valid and then Free. Before calling
  190.     Free (which calls Done), tell all attached forms to close.
  191.   }
  192.   if Valid(cmClose) then
  193.   begin
  194.     { Stop desktop video update in case there are scores of attached forms }
  195.     Desktop^.Lock;
  196.     Message(Desktop, evBroadcast, cmCloseForm, @Self);
  197.     Desktop^.Unlock;
  198.     Free;
  199.   end;
  200. end;
  201.  
  202. procedure TListDialog.HandleEvent(var Event: TEvent);
  203.  
  204. function EditingForm: PForm;
  205. { Return pointer to the form that is editing the current selection }
  206. begin
  207.   EditingForm := Message(Desktop, evBroadcast,
  208.     cmEditingForm, DataCollection^.At(List^.Focused));
  209. end;
  210.  
  211. procedure FormOpen(NewForm: Boolean);
  212. var
  213.   F: PForm;
  214. begin
  215.   if not NewForm then
  216.   begin
  217.     { Empty collection? }
  218.     if DataCollection^.Count = 0 then Exit;
  219.  
  220.     { If selection is being edited, then bring its form to top }
  221.     F := EditingForm;
  222.     if F <> nil then
  223.     begin
  224.       F^.Select;
  225.       Exit;
  226.     end;
  227.   end;
  228.  
  229.   { Selection is not being edited: open new form from the resource file }
  230.   F := PForm(FormDataFile^.Get('FormDialog'));
  231.   if F = nil then
  232.     MessageBox('Error opening form.', nil, mfError or mfOkButton)
  233.   else
  234.   begin
  235.     with F^ do
  236.     begin
  237.       ListDialog := @Self;                 { Form points back to List }
  238.       if NewForm then
  239.         PrevData := nil                    { Adding new form }
  240.       else
  241.       begin
  242.         { Edit data from collection }
  243.         PrevData := DataCollection^.At(List^.Focused);
  244.         SetData(PrevData^);
  245.       end;
  246.     end;
  247.     if Application^.ValidView(F) <> nil then
  248.     begin
  249.       StackOnPrev(F);
  250.       if NewForm then Desktop^.Insert(F)      { Insert & select }
  251.       else Desktop^.InsertBefore(F, Next);    { Insert but keep focus }
  252.     end;
  253.   end;
  254. end;
  255.  
  256. procedure DeleteSelection;
  257. var
  258.   F: PForm;
  259. begin
  260.   { Empty collection? }
  261.   if DataCollection^.Count = 0 then Exit;
  262.  
  263.   { Don't allow delete of data already being edited }
  264.   F := EditingForm;
  265.   if F <> nil then
  266.   begin
  267.     F^.Select;
  268.     MessageBox('Data is already being edited. Close form before deleting.',
  269.       nil, mfWarning or mfOkButton);
  270.     Exit;
  271.   end;
  272.  
  273.   { Confirm delete }
  274.   if MessageBox('Are you sure you want to delete this item?', nil,
  275.     mfWarning or mfYesNoCancel) = cmYes then
  276.     begin
  277.       DataCollection^.AtFree(List^.Focused);
  278.       List^.SetRange(DataCollection^.Count);
  279.       List^.DrawView;
  280.       Modified := True;
  281.     end;
  282. end;
  283.  
  284. begin
  285.   with Event do
  286.     if (What = evKeyDown) and (KeyCode = kbEsc) then
  287.     begin
  288.       What := Command;
  289.       Command := cmClose;
  290.     end;
  291.  
  292.   TDialog.HandleEvent(Event);
  293.  
  294.   case Event.What of
  295.     evCommand:
  296.       begin
  297.         case Event.Command of
  298.           cmFormEdit: FormOpen(False);
  299.           cmFormNew: FormOpen(True);
  300.           cmFormDel: DeleteSelection;
  301.           cmListSave: if Modified then SaveList;
  302.         else
  303.           Exit;
  304.         end;
  305.         ClearEvent(Event);
  306.       end;
  307.     evKeyDown:
  308.       begin
  309.         case Event.KeyCode of
  310.           kbIns: FormOpen(True);
  311.         else
  312.           Exit;
  313.         end;
  314.         ClearEvent(Event);
  315.       end;
  316.     evBroadcast:
  317.       begin
  318.         case Event.Command of
  319.           { Respond to broadcast from TSortedListBox }
  320.           cmListItemSelected: FormOpen(False);
  321.   
  322.           { Keep file from being edited simultaneously by 2 lists }
  323.           cmEditingFile: if FileName^ = PString(Event.InfoPtr)^ then
  324.             ClearEvent(Event);
  325.  
  326.           { Respond to search for topmost list dialog }
  327.           cmTopList: ClearEvent(Event);
  328.         end;
  329.       end;
  330.   end;
  331. end;
  332.  
  333. function TListDialog.OpenDataFile(Name: PathStr;
  334.   var DataFile: PResourceFile; Mode: Word): Boolean;
  335. var
  336.   S: PStream;
  337. begin
  338.   S := New(PBufStream, Init(Name, Mode, 1024));
  339.   DataFile := New(PResourceFile, Init(S));
  340.   if S^.Status <> stOk then
  341.   begin
  342.     Dispose(DataFile, Done);
  343.     DataFile := nil;
  344.     OpenDataFile := False;
  345.   end
  346.   else OpenDataFile := True;
  347. end;
  348.  
  349. function TListDialog.SaveList: Boolean;
  350. var
  351.   S: PStream;
  352.   NewDataFile: PResourceFile;
  353.   Form: PForm;
  354.   D: DirStr;
  355.   N: NameStr;
  356.   E: ExtStr;
  357.   F: File;
  358. begin
  359.   { Empty collection? Unedited? }
  360.   if (DataCollection^.Count = 0) or not Modified then
  361.   begin
  362.     SaveList := True;
  363.     Exit;
  364.   end;
  365.  
  366.   SaveList := False;
  367.   { Read form definition out of original form file }
  368.   Form := PForm(FormDataFile^.Get('FormDialog'));
  369.   if Form = nil then
  370.     MessageBox('Cannot find original file. Data not saved.',
  371.       nil, mfError or mfOkButton)
  372.   else
  373.   begin
  374.     { Create new data file }
  375.     FSplit(FileName^, D, N, E);
  376.     if not OpenDataFile(D + N + '.$$$', NewDataFile, stCreate) then
  377.       MessageBox('Cannot create file. Data not saved.',
  378.         nil, mfError or mfOkButton)
  379.     else
  380.     begin
  381.       { Create new from form and collection in memory }
  382.       NewDataFile^.Put(Form, 'FormDialog');
  383.       NewDataFile^.Put(DataCollection, 'FormData');
  384.       NewDataFile^.Flush;
  385.       Dispose(NewDataFile, Done);
  386.  
  387.       { Close original file, rename to .BAK }
  388.       Dispose(FormDataFile, Done);
  389.       FormDataFile := nil;
  390.       {$I-}
  391.       if FileExists(D + N + '.BAK') then
  392.       begin
  393.         Assign(F, D + N + '.BAK');
  394.         Erase(F);
  395.       end;
  396.       Assign(F, FileName^);
  397.       Rename(F, D + N + '.BAK');
  398.       {$I+}
  399.  
  400.       { Error trying to erase old .BAK or rename original to .BAK? }
  401.       if IOResult <> 0 then
  402.       begin
  403.         MessageBox('Cannot create .BAK file. Data not saved.',
  404.           nil, mfError or mfOkButton);
  405.  
  406.         { Try to re-open original. New data will still be in memory }
  407.         if not OpenDataFile(FileName^, FormDataFile, stOpen) then
  408.         begin
  409.           MessageBox('Cannot re-open original file.',
  410.             nil, mfError or mfOkButton);
  411.           Free;        { Cannot proceed. Free data and close window }
  412.         end;
  413.       end
  414.       else
  415.       begin
  416.         { Rename temp file to original file and re-open }
  417.         Assign(F, D + N + '.$$$');
  418.         Rename(F, FileName^);
  419.         OpenDataFile(FileName^, FormDataFile, stOpen);
  420.  
  421.         Modified := False;
  422.         SaveList := True;
  423.       end;
  424.     end;
  425.     Dispose(Form, Done);
  426.   end;
  427. end;
  428.  
  429. function TListDialog.SaveForm(F: PDialog): Boolean;
  430. var
  431.   i: Integer;
  432.   P: Pointer;
  433. begin
  434.   SaveForm := False;
  435.   with PForm(F)^, DataCollection^ do
  436.   begin
  437.     { Validate data before updating collection }
  438.     if not F^.Valid(cmFormSave) then Exit;
  439.  
  440.     { Extract data from form. Don't use safety pool. }
  441.     P := MemAlloc(ItemSize);
  442.     if P = nil then
  443.     begin
  444.       Application^.OutOfMemory;
  445.       Exit;
  446.     end;
  447.  
  448.     GetData(P^);
  449.     { If no duplicates, make sure not attempting to add duplicate key }
  450.     if not Duplicates and Search(KeyOf(P), i) then
  451.       if (PrevData = nil) or (PrevData <> At(i)) then
  452.     begin
  453.       FreeMem(P, ItemSize);
  454.       MessageBox('Duplicate keys are not allowed in this database.'+
  455.         '  Delete duplicate record before saving this form.', nil,
  456.         mfError or mfOkButton);
  457.       Exit;
  458.     end;
  459.  
  460.     { Free previous data? }
  461.     if (PrevData <> nil) then Free(PrevData);
  462.  
  463.     { TDataCollection.Insert may fail because it doesn't use
  464.       the safety pool. Check status field after insert and cleanup
  465.       if necessary.
  466.     }
  467.     Insert(P);
  468.     if Status <> 0 then
  469.     begin
  470.       FreeMem(P, ItemSize);
  471.       Application^.OutOfMemory;
  472.       Exit;
  473.     end;
  474.  
  475.     { Success: store off original data pointer }
  476.     PrevData := P;
  477.  
  478.     { Redraw list }
  479.     List^.SetRange(Count);
  480.     List^.DrawView;
  481.  
  482.     Modified := True;
  483.     SaveForm := True;
  484.   end;
  485. end;
  486.  
  487. procedure TListDialog.StackOnPrev(F: PDialog);
  488. var
  489.   TopForm: PForm;
  490.   R: TRect;
  491. begin
  492.   { Stack on top topmost form or on top list if first form }
  493.   TopForm := Message(Owner, evBroadcast, cmTopForm, @Self);
  494.   if (TopForm <> nil) then
  495.     { Stack on top previous topmost form }
  496.     with TopForm^.Origin do
  497.       F^.MoveTo(X + 1, Y + 1)
  498.   else
  499.   begin
  500.     { Stack right or left of ListDialog }
  501.     if Origin.X > F^.Size.X then F^.Moveto(0, Origin.Y)
  502.     else F^.Moveto(Origin.X + Size.X + 1, Origin.Y);
  503.   end;
  504.  
  505.   { Visible on desktop? Make sure at least half of form is visible }
  506.   Owner^.GetExtent(R);                      { Desktop coordinates }
  507.   with F^, F^.Origin do                     { Keep stack on screen }
  508.   begin
  509.     if (X + Size.X div 2 > R.B.X) then F^.MoveTo(0, 1);
  510.      if (Y + Size.Y div 2 > R.B.Y) then F^.MoveTo(X, 1);
  511.   end;
  512. end;
  513.  
  514. function TListDialog.Valid(Command: Word): Boolean;
  515. var
  516.   Ok: Boolean;
  517.   Reply: Word;
  518. begin
  519.   Ok := True;
  520.   case Command of
  521.     cmValid:
  522.       begin
  523.         Ok := IsValid;
  524.         if not Ok then
  525.           MessageBox('Error opening file (%S).',
  526.             @FileName, mfError or mfOkButton);
  527.       end;
  528.     cmQuit, cmClose:
  529.       begin
  530.         { Any forms open that cannot close? }
  531.         Ok := Message(Desktop, evBroadcast, cmCanCloseForm, @Self) = nil;
  532.  
  533.         { Any data modified? }
  534.         if Ok and Modified then
  535.         begin
  536.           Select;
  537.           Reply := MessageBox('Database has been modified. Save? ', nil,
  538.             mfYesNoCancel);
  539.           case Reply of
  540.             cmYes: Ok := SaveList;
  541.             cmNo: Modified := False;                { abandon changes }
  542.           else
  543.             Ok := False;                            { cancel close request }
  544.           end;
  545.         end;
  546.       end;
  547.   end;
  548.   if Ok then Valid := TDialog.Valid(Command)
  549.   else Valid := False;
  550. end;
  551.  
  552. end.
  553.