Changeset 129 for trunk/UVCard.pas
- Timestamp:
- Apr 9, 2022, 11:52:13 AM (3 years ago)
- File:
-
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/UVCard.pas
r127 r129 1 unit U Contact;1 unit UVCard; 2 2 3 3 interface 4 4 5 5 uses 6 Classes, SysUtils, fgl, Dialogs, UDataFile, LazUTF8, Base64, Graphics; 6 Classes, SysUtils, Dialogs, LazUTF8, Base64, Graphics, UListViewSort, 7 Generics.Collections, Generics.Defaults; 7 8 8 9 type … … 36 37 function GetCombined: string; 37 38 end; 38 39 TContactsFile = class;40 39 41 40 TErrorEvent = procedure (Text: string; Line: Integer) of object; … … 62 61 cfPeerTube, cfLinkedIn, cfMastodon, cfMySpace, cfReddit); 63 62 64 TContactFieldIndexes = T FPGList<TContactFieldIndex>;63 TContactFieldIndexes = TList<TContactFieldIndex>; 65 64 66 65 TContactFilterItem = class … … 71 70 { TContactFilterItems } 72 71 73 TContactFilterItems = class(T FPGObjectList<TContactFilterItem>)72 TContactFilterItems = class(TObjectList<TContactFilterItem>) 74 73 function AddNew(FieldIndex: TContactFieldIndex; Value: string): TContactFilterItem; 75 74 end; … … 98 97 { TContactFields } 99 98 100 TContactFields = class(T FPGObjectList<TContactField>)99 TContactFields = class(TObjectList<TContactField>) 101 100 private 102 101 Indexes: array[TContactFieldIndex] of TContactField; … … 143 142 { TContactProperties } 144 143 145 TContactProperties = class(T FPGObjectList<TContactProperty>)144 TContactProperties = class(TObjectList<TContactProperty>) 146 145 function AddNew(Name, Value: string): TContactProperty; 147 146 procedure Assign(Source: TContactProperties); 148 procedure AssignToList(List: T FPGObjectList<TObject>);147 procedure AssignToList(List: TObjects); 149 148 function GetByName(Name: string): TContactProperty; 150 149 function GetByNameGroups(Name: string; Groups: TStringArray; … … 153 152 NoGroups: TStringArray): TContactProperties; 154 153 end; 154 155 TVCard = class; 155 156 156 157 { TContact } … … 170 171 public 171 172 Properties: TContactProperties; 172 ContactsFile: TContactsFile;173 ParentVCard: TVCard; 173 174 class function GetFields: TContactFields; static; 174 175 function HasField(FieldIndex: TContactFieldIndex): Boolean; … … 197 198 { TContacts } 198 199 199 TContacts = class(T FPGObjectList<TContact>)200 ContactsFile: TContactsFile;200 TContacts = class(TObjectList<TContact>) 201 ParentVCard: TVCard; 201 202 procedure Assign(Source: TContacts); 203 procedure AssignToList(List: TObjects); 202 204 procedure AddContacts(Contacts: TContacts); 203 205 procedure InsertContacts(Index: Integer; Contacts: TContacts); 204 procedure AssignToList(List: TFPGObjectList<TObject>);205 206 function AddNew: TContact; 206 207 function Search(Text: string; FieldIndex: TContactFieldIndex): TContact; … … 209 210 function ToString: ansistring; override; 210 211 procedure RemoveExactDuplicates; 211 end; 212 213 { TContactsFile } 214 215 TContactsFile = class(TDataFile) 212 procedure Sort; 213 end; 214 215 { TVCard } 216 217 TVCard = class(TComponent) 216 218 private 217 219 FMaxLineLength: Integer; 220 FModified: Boolean; 221 FOnModify: TNotifyEvent; 218 222 FOnError: TErrorEvent; 223 function GetString: string; 224 procedure SetModified(AValue: Boolean); 225 procedure SetString(AValue: string); 219 226 procedure Error(Text: string; Line: Integer); 220 function GetString: string; 221 function NewItem(Key, Value: string): string; 222 procedure SetString(AValue: string); 227 procedure DoOnModify; 223 228 public 224 229 Contacts: TContacts; 225 function GetFileName: string; override; 226 function GetFileExt: string; override; 227 function GetFileFilter: string; override; 230 procedure Assign(Source: TPersistent); override; 228 231 procedure SaveToStrings(Output: TStrings); 229 232 procedure LoadFromStrings(Lines: TStrings); 230 procedure SaveToFile(FileName: string); override; 231 procedure LoadFromFile(FileName: string); override; 232 procedure Sort; 233 procedure Assign(Source: TContactsFile); 234 constructor Create; override; 233 procedure SaveToFile(FileName: string); 234 procedure LoadFromFile(FileName: string); 235 constructor Create(AOwner: TComponent); override; 235 236 destructor Destroy; override; 236 237 property AsString: string read GetString write SetString; 238 property Modified: Boolean read FModified write SetModified; 237 239 published 240 property MaxLineLength: Integer read FMaxLineLength write FMaxLineLength; 241 property OnModify: TNotifyEvent read FOnModify write FOnModify; 238 242 property OnError: TErrorEvent read FOnError write FOnError; 239 property MaxLineLength: Integer read FMaxLineLength write FMaxLineLength;240 243 end; 241 244 … … 261 264 262 265 resourcestring 263 SVCardFile = 'vCard file';264 266 SFieldIndexRedefined = 'Field index %d redefined'; 265 267 SExpectedVCardBegin = 'Expected vCard begin'; … … 518 520 end; 519 521 522 { TVCard } 523 524 function TVCard.GetString: string; 525 var 526 I: Integer; 527 begin 528 Result := ''; 529 for I := 0 to Contacts.Count - 1 do 530 Result := Result + Contacts[I].AsString; 531 end; 532 533 procedure TVCard.SetModified(AValue: Boolean); 534 begin 535 if FModified = AValue then Exit; 536 FModified := AValue; 537 DoOnModify; 538 end; 539 540 procedure TVCard.SetString(AValue: string); 541 var 542 Lines: TStringList; 543 begin 544 Lines := TStringList.Create; 545 try 546 Lines.Text := AValue; 547 LoadFromStrings(Lines); 548 Modified := True; 549 finally 550 Lines.Free; 551 end; 552 end; 553 554 procedure TVCard.DoOnModify; 555 begin 556 if Assigned(FOnModify) then FOnModify(Self); 557 end; 558 559 procedure TVCard.Error(Text: string; Line: Integer); 560 begin 561 if Assigned(FOnError) then FOnError(Text, Line); 562 end; 563 564 procedure TVCard.Assign(Source: TPersistent); 565 begin 566 inherited; 567 if Source is TVCard then Contacts.Assign((Source as TVCard).Contacts); 568 end; 569 570 procedure TVCard.SaveToStrings(Output: TStrings); 571 var 572 I: Integer; 573 begin 574 for I := 0 to Contacts.Count - 1 do 575 Contacts[I].SaveToStrings(Output); 576 end; 577 578 procedure TVCard.LoadFromStrings(Lines: TStrings); 579 var 580 Contact: TContact; 581 I: Integer; 582 begin 583 Contacts.Clear; 584 //MaxLineLength := 10; 585 586 I := 0; 587 while I < Lines.Count do begin 588 Contact := TContact.Create; 589 Contact.ParentVCard := Self; 590 if Contact.LoadFromStrings(Lines, I) then begin 591 Contacts.Add(Contact); 592 end else begin 593 FreeAndNil(Contact); 594 Inc(I); 595 end; 596 end; 597 end; 598 599 procedure TVCard.SaveToFile(FileName: string); 600 var 601 Lines: TStringList; 602 begin 603 Lines := TStringList.Create; 604 try 605 SaveToStrings(Lines); 606 Lines.SaveToFile(FileName); 607 finally 608 Lines.Free; 609 end 610 end; 611 612 procedure TVCard.LoadFromFile(FileName: string); 613 var 614 Lines: TStringList; 615 begin 616 Lines := TStringList.Create; 617 Lines.LoadFromFile(FileName); 618 {$IF FPC_FULLVERSION>=30200} 619 if (Length(Lines.Text) > 0) and (Pos(VCardBegin, Lines.Text) = 0) then begin 620 Lines.LoadFromFile(FileName, TEncoding.Unicode); 621 if (Length(Lines.Text) > 0) and (Pos(VCardBegin, Lines.Text) = 0) then begin 622 Lines.LoadFromFile(FileName, TEncoding.BigEndianUnicode); 623 end; 624 end; 625 {$ENDIF} 626 try 627 LoadFromStrings(Lines); 628 finally 629 Lines.Free; 630 end; 631 end; 632 633 constructor TVCard.Create(AOwner: TComponent); 634 begin 635 inherited; 636 FMaxLineLength := DefaultMaxLineLength; 637 Contacts := TContacts.Create; 638 Contacts.ParentVCard := Self; 639 end; 640 641 destructor TVCard.Destroy; 642 begin 643 FreeAndNil(Contacts); 644 inherited; 645 end; 646 520 647 function TNameDetails.GetAsNameParts: TNameParts; 521 648 var … … 846 973 I: Integer; 847 974 begin 848 while Count < Source.Count do 849 Add(TContactProperty.Create); 850 while Count > Source.Count do 851 Delete(Count - 1); 852 for I := 0 to Count - 1 do 853 Items[I].Assign(Source.Items[I]); 854 end; 855 856 procedure TContactProperties.AssignToList(List: TFPGObjectList<TObject>); 975 while Count > Source.Count do Delete(Count - 1); 976 while Count < Source.Count do Add(TContactProperty.Create); 977 for I := 0 to Count - 1 do Items[I].Assign(Source.Items[I]); 978 end; 979 980 procedure TContactProperties.AssignToList(List: TObjects); 857 981 var 858 982 I: Integer; 859 983 begin 860 984 while List.Count > Count do List.Delete(List.Count - 1); 861 while List.Count < Count do List.Add(nil); 862 for I := 0 to Count - 1 do 863 List[I] := Items[I]; 985 for I := 0 to List.Count - 1 do List[I] := Items[I]; 986 while List.Count < Count do List.Add(Items[List.Count]); 864 987 end; 865 988 … … 1085 1208 I: Integer; 1086 1209 begin 1087 while Count < Source.Count do 1088 Add(TContact.Create); 1089 while Count > Source.Count do 1090 Delete(Count - 1); 1210 while Count > Source.Count do Delete(Count - 1); 1211 while Count < Source.Count do Add(TContact.Create); 1091 1212 for I := 0 to Count - 1 do begin 1092 1213 Items[I].Assign(Source.Items[I]); 1093 Items[I]. ContactsFile := ContactsFile;1214 Items[I].ParentVCard := ParentVCard; 1094 1215 end; 1095 1216 end; … … 1108 1229 NewContact := TContact.Create; 1109 1230 NewContact.Assign(Contacts[I]); 1110 NewContact. ContactsFile := ContactsFile;1231 NewContact.ParentVCard := ParentVCard; 1111 1232 Insert(Index, NewContact); 1112 1233 Inc(Index); … … 1126 1247 end; 1127 1248 1128 procedure TContacts.AssignToList(List: TFPGObjectList<TObject>); 1249 function ComparePropertyName(constref Item1, Item2: TContactProperty): Integer; 1250 begin 1251 Result := CompareStr(Item1.Name + ';' + Item1.Attributes.Text, 1252 Item2.Name + ';' + Item2.Attributes.Text); 1253 end; 1254 1255 function CompareContactFullName(constref Item1, Item2: TContact): Integer; 1256 begin 1257 Result := CompareStr(Item1.Fields[cfFullName], Item2.Fields[cfFullName]); 1258 end; 1259 1260 procedure TContacts.Sort; 1261 var 1262 I: Integer; 1263 begin 1264 inherited Sort(TComparer<TContact>.Construct(CompareContactFullName)); 1265 for I := 0 to Count - 1 do 1266 Items[I].Properties.Sort(TComparer<TContactProperty>.Construct(ComparePropertyName)); 1267 end; 1268 1269 procedure TContacts.AssignToList(List: TObjects); 1129 1270 var 1130 1271 I: Integer; 1131 1272 begin 1132 1273 while List.Count > Count do List.Delete(List.Count - 1); 1133 while List.Count < Count do List.Add(nil); 1134 for I := 0 to Count - 1 do 1135 List[I] := Items[I]; 1274 for I := 0 to List.Count - 1 do List[I] := Items[I]; 1275 while List.Count < Count do List.Add(Items[List.Count]); 1136 1276 end; 1137 1277 … … 1139 1279 begin 1140 1280 Result := TContact.Create; 1141 Result. ContactsFile := ContactsFile;1281 Result.ParentVCard := ParentVCard; 1142 1282 Add(Result); 1143 1283 end; … … 1175 1315 NewContact := TContact.Create; 1176 1316 NewContact.Assign(Contact); 1177 NewContact. ContactsFile := ContactsFile;1317 NewContact.ParentVCard := ParentVCard; 1178 1318 Add(NewContact); 1179 1319 end; … … 1400 1540 Field: TContactField; 1401 1541 begin 1402 if not Assigned( ContactsFile) then1542 if not Assigned(ParentVCard) then 1403 1543 raise Exception.Create(SContactHasNoParent); 1404 1544 Field := GetFields.GetByIndex(Index); … … 1435 1575 I: Integer; 1436 1576 begin 1437 if not Assigned( ContactsFile) then1577 if not Assigned(ParentVCard) then 1438 1578 raise Exception.Create(SContactHasNoParent); 1439 1579 Field := GetFields.GetByIndex(Index); … … 1486 1626 Dec(LineLength); 1487 1627 end; 1488 if LineLength > ContactsFile.MaxLineLength then1489 ContactsFile.MaxLineLength := LineLength;1628 if LineLength > ParentVCard.MaxLineLength then 1629 ParentVCard.MaxLineLength := LineLength; 1490 1630 end; 1491 1631 … … 1509 1649 Field: TContactField; 1510 1650 begin 1511 if not Assigned( ContactsFile) then raise Exception.Create(SContactHasNoParent);1651 if not Assigned(ParentVCard) then raise Exception.Create(SContactHasNoParent); 1512 1652 Field := GetFields.GetByIndex(FieldIndex); 1513 1653 if Assigned(Field) then begin … … 1545 1685 Field: TContactField; 1546 1686 begin 1547 if not Assigned( ContactsFile) then raise Exception.Create(SContactHasNoParent);1687 if not Assigned(ParentVCard) then raise Exception.Create(SContactHasNoParent); 1548 1688 Field := GetFields.GetByIndex(FieldIndex); 1549 1689 if Assigned(Field) then begin … … 1562 1702 I: Integer; 1563 1703 begin 1564 if not Assigned( ContactsFile) then raise Exception.Create(SContactHasNoParent);1704 if not Assigned(ParentVCard) then raise Exception.Create(SContactHasNoParent); 1565 1705 Result := False; 1566 1706 for I := 0 to GetFields.Count - 1 do begin … … 1637 1777 LinePrefix := ''; 1638 1778 while True do begin 1639 if UTF8Length(OutText) > ContactsFile.MaxLineLength then begin1640 CutLength := ContactsFile.MaxLineLength;1779 if UTF8Length(OutText) > ParentVCard.MaxLineLength then begin 1780 CutLength := ParentVCard.MaxLineLength; 1641 1781 if Encoding = veQuotedPrintable then begin 1642 1782 Dec(CutLength); // There will be softline break at the end … … 1696 1836 ParseState := psInside; 1697 1837 end else begin 1698 ContactsFile.Error(SExpectedVCardBegin, I + 1);1838 ParentVCard.Error(SExpectedVCardBegin, I + 1); 1699 1839 Break; 1700 1840 end; … … 1742 1882 NewProperty.EvaluateAttributes; 1743 1883 end else begin 1744 ContactsFile.Error(SExpectedProperty, I + 1);1884 ParentVCard.Error(SExpectedProperty, I + 1); 1745 1885 Break; 1746 1886 end; … … 1787 1927 end; 1788 1928 1789 { TContactsFile }1790 1791 procedure TContactsFile.Error(Text: string; Line: Integer);1792 begin1793 if Assigned(FOnError) then FOnError(Text, Line);1794 end;1795 1796 function TContactsFile.GetString: string;1797 var1798 I: Integer;1799 begin1800 Result := '';1801 for I := 0 to Contacts.Count - 1 do1802 Result := Result + Contacts[I].AsString;1803 end;1804 1805 function TContactsFile.GetFileName: string;1806 begin1807 Result := SVCardFile;1808 end;1809 1810 function TContactsFile.GetFileExt: string;1811 begin1812 Result := VCardFileExt;1813 end;1814 1815 function TContactsFile.GetFileFilter: string;1816 begin1817 Result := GetFileName + ' (' + GetFileExt + ')|*' + GetFileExt + '|' + inherited;1818 end;1819 1820 procedure TContactsFile.SaveToStrings(Output: TStrings);1821 var1822 I: Integer;1823 begin1824 for I := 0 to Contacts.Count - 1 do1825 Contacts[I].SaveToStrings(Output);1826 end;1827 1828 procedure TContactsFile.LoadFromStrings(Lines: TStrings);1829 var1830 Contact: TContact;1831 I: Integer;1832 begin1833 Contacts.Clear;1834 //MaxLineLength := 10;1835 1836 I := 0;1837 while I < Lines.Count do begin1838 Contact := TContact.Create;1839 Contact.ContactsFile := Self;1840 if Contact.LoadFromStrings(Lines, I) then begin1841 Contacts.Add(Contact);1842 end else begin1843 FreeAndNil(Contact);1844 Inc(I);1845 end;1846 end;1847 end;1848 1849 function TContactsFile.NewItem(Key, Value: string): string;1850 var1851 Charset: string;1852 begin1853 if not IsAsciiString(Value) then Charset := ';CHARSET=UTF-8'1854 else Charset := '';1855 Result := Key + Charset + ':' + Value;1856 end;1857 1858 procedure TContactsFile.SetString(AValue: string);1859 var1860 Lines: TStringList;1861 begin1862 Lines := TStringList.Create;1863 try1864 Lines.Text := AValue;1865 LoadFromStrings(Lines);1866 Modified := True;1867 finally1868 Lines.Free;1869 end;1870 end;1871 1872 procedure TContactsFile.SaveToFile(FileName: string);1873 var1874 Lines: TStringList;1875 begin1876 inherited;1877 Lines := TStringList.Create;1878 try1879 SaveToStrings(Lines);1880 Lines.SaveToFile(FileName);1881 finally1882 Lines.Free;1883 end1884 end;1885 1886 procedure TContactsFile.LoadFromFile(FileName: string);1887 var1888 Lines: TStringList;1889 begin1890 inherited;1891 Lines := TStringList.Create;1892 Lines.LoadFromFile(FileName);1893 {$IF FPC_FULLVERSION>=30200}1894 if (Length(Lines.Text) > 0) and (Pos(VCardBegin, Lines.Text) = 0) then begin1895 Lines.LoadFromFile(FileName, TEncoding.Unicode);1896 if (Length(Lines.Text) > 0) and (Pos(VCardBegin, Lines.Text) = 0) then begin1897 Lines.LoadFromFile(FileName, TEncoding.BigEndianUnicode);1898 end;1899 end;1900 {$ENDIF}1901 try1902 LoadFromStrings(Lines);1903 finally1904 Lines.Free;1905 end;1906 end;1907 1908 function CompareContactFullName(const Item1, Item2: TContact): Integer;1909 begin1910 Result := CompareStr(Item1.Fields[cfFullName], Item2.Fields[cfFullName]);1911 end;1912 1913 function ComparePropertyName(const Item1, Item2: TContactProperty): Integer;1914 begin1915 Result := CompareStr(Item1.Name + ';' + Item1.Attributes.Text,1916 Item2.Name + ';' + Item2.Attributes.Text);1917 end;1918 1919 procedure TContactsFile.Sort;1920 var1921 I: Integer;1922 begin1923 Contacts.Sort(CompareContactFullName);1924 for I := 0 to Contacts.Count - 1 do1925 Contacts[I].Properties.Sort(ComparePropertyName);1926 end;1927 1928 procedure TContactsFile.Assign(Source: TContactsFile);1929 begin1930 inherited Assign(Source);1931 Contacts.Assign(Source.Contacts);1932 end;1933 1934 constructor TContactsFile.Create;1935 begin1936 inherited;1937 Contacts := TContacts.Create;1938 Contacts.ContactsFile := Self;1939 MaxLineLength := DefaultMaxLineLength;1940 end;1941 1942 destructor TContactsFile.Destroy;1943 begin1944 FreeAndNil(Contacts);1945 inherited;1946 end;1947 1948 1929 end. 1949 1930
Note:
See TracChangeset
for help on using the changeset viewer.