- Timestamp:
- Feb 14, 2022, 5:43:30 PM (3 years ago)
- Location:
- trunk
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Forms/UFormContact.lfm
r108 r109 18 18 Top = 8 19 19 Width = 1009 20 ActivePage = TabSheet General20 ActivePage = TabSheetHome 21 21 Anchors = [akTop, akLeft, akRight, akBottom] 22 22 ParentFont = False 23 TabIndex = 023 TabIndex = 1 24 24 TabOrder = 0 25 25 object TabSheetGeneral: TTabSheet … … 462 462 Anchors = [akTop, akLeft, akRight] 463 463 Caption = 'Address' 464 ClientHeight = 2 85464 ClientHeight = 259 465 465 ClientWidth = 895 466 466 TabOrder = 6 -
trunk/Forms/UFormContact.pas
r108 r109 735 735 736 736 procedure TFormContact.EditFullNameChange(Sender: TObject); 737 var 738 739 Before, First, Middle, Last, After: string; 740 begin 741 Contact.FullNameToNameParts(EditFullName.Text, Before, First, Middle, Last, After); 742 UpdateEditNoOnChange(EditTitleBefore, Before); 743 UpdateEditNoOnChange(EditFirstName, First); 744 UpdateEditNoOnChange(EditMiddleName, Middle); 745 UpdateEditNoOnChange(EditLastName, Last); 746 UpdateEditNoOnChange(EditTitleAfter, After); 747 UpdateInterface; 737 begin 738 with TNameDetails.Create do 739 try 740 Prefix := EditTitleBefore.Text; 741 First := EditFirstName.Text; 742 Middle := EditMiddleName.Text; 743 Last := EditLastName.Text; 744 Suffix := EditTitleAfter.Text; 745 Split(EditFullName.Text); 746 UpdateEditNoOnChange(EditTitleBefore, Prefix); 747 UpdateEditNoOnChange(EditFirstName, First); 748 UpdateEditNoOnChange(EditMiddleName, Middle); 749 UpdateEditNoOnChange(EditLastName, Last); 750 UpdateEditNoOnChange(EditTitleAfter, Suffix); 751 UpdateInterface; 752 finally 753 Free; 754 end; 748 755 end; 749 756 750 757 procedure TFormContact.NamePartChange(Sender: TObject); 751 758 begin 752 UpdateEditNoOnChange(EditFullName, Contact.NamePartsToFullName(EditTitleBefore.Text, 753 EditFirstName.Text, EditMiddleName.Text, EditLastName.Text, EditTitleAfter.Text)); 759 with TNameDetails.Create do 760 try 761 Prefix := EditTitleBefore.Text; 762 First := EditFirstName.Text; 763 Middle := EditMiddleName.Text; 764 Last := EditLastName.Text; 765 Suffix := EditTitleAfter.Text; 766 UpdateEditNoOnChange(EditFullName, GetCombined); 767 finally 768 Free; 769 end; 754 770 end; 755 771 -
trunk/Forms/UFormProperty.pas
r103 r109 124 124 Groups.StrictDelimiter := True; 125 125 Groups.DelimitedText := EditAttributes.Text; 126 GroupsArray := Default(TStringArray); 126 127 SetLength(GroupsArray, Groups.Count); 127 128 for I := 0 to Groups.Count - 1 do -
trunk/Languages/vCardStudio.cs.po
r108 r109 182 182 #: tformcontact.label1.caption 183 183 msgid "First name:" 184 msgstr " Křestní jméno:"184 msgstr "První jméno:" 185 185 186 186 #: tformcontact.label10.caption … … 273 273 msgctxt "tformcontact.label28.caption" 274 274 msgid "Street:" 275 msgstr " Adresa:"275 msgstr "Ulice:" 276 276 277 277 #: tformcontact.label29.caption … … 387 387 #: tformcontact.label5.caption 388 388 msgid "Last name" 389 msgstr "P říjmení"389 msgstr "Poslední jméno" 390 390 391 391 #: tformcontact.label50.caption … … 630 630 msgctxt "tformcontacts.listview1.columns[1].caption" 631 631 msgid "First name" 632 msgstr " Křestní jméno"632 msgstr "První jméno" 633 633 634 634 #: tformcontacts.listview1.columns[2].caption … … 640 640 msgctxt "tformcontacts.listview1.columns[3].caption" 641 641 msgid "Last Name" 642 msgstr "P říjmení"642 msgstr "Poslední jméno" 643 643 644 644 #: tformcontacts.listview1.columns[4].caption … … 1018 1018 #: ucontact.sfirstname 1019 1019 msgid "First Name" 1020 msgstr " Křestní jméno"1020 msgstr "První Jméno" 1021 1021 1022 1022 #: ucontact.sfullname … … 1117 1117 msgctxt "ucontact.slastname" 1118 1118 msgid "Last Name" 1119 msgstr "P říjmení"1119 msgstr "Poslední Jméno" 1120 1120 1121 1121 #: ucontact.slasttimecontacted -
trunk/Packages/Common/UCommon.pas
r108 r109 304 304 end else Break; 305 305 until False; 306 SetLength(Result, Length(Result) + 1); 307 Result[High(Result)] := Data; 306 if Data <> '' then begin 307 SetLength(Result, Length(Result) + 1); 308 Result[High(Result)] := Data; 309 end; 308 310 end; 309 311 -
trunk/UContact.pas
r108 r109 9 9 10 10 type 11 TNamePartKind = (npNone, npPrefix, npFirst, npMiddle, npLast, npSuffix); 12 13 TNamePart = record 14 Index: Integer; 15 Text: string; 16 PartKind: TNamePartKind; 17 NamePart: ^TNamePart; 18 Previous: ^TNamePart; 19 Next: ^TNamePart; 20 end; 21 22 TNameParts = array of TNamePart; 23 24 { TNameDetails } 25 26 TNameDetails = class 27 private 28 function GetAsNameParts: TNameParts; 29 function GetDetail(NamePartKind: TNamePartKind): string; 30 function IsSuffix(Text: string): Boolean; 31 public 32 Prefix: string; 33 First: string; 34 Middle: string; 35 Last: string; 36 Suffix: string; 37 procedure Split(FullName: string); 38 function GetCombined: string; 39 end; 40 11 41 TContactsFile = class; 12 42 … … 141 171 function GetProperty(Field: TContactField): TContactProperty; overload; 142 172 function GetProperty(FieldIndex: TContactFieldIndex): TContactProperty; overload; 143 procedure FullNameToNameParts(FullName: string; out Before, First, Middle,144 Last, After: string);145 function NamePartsToFullName(Before, First, Middle, Last, After: string): string;146 173 procedure Assign(Source: TContact); 147 174 function UpdateFrom(Source: TContact): Boolean; … … 330 357 end; 331 358 359 function StartsWith(Text, What: string): Boolean; 360 begin 361 Result := Copy(Text, 1, Length(Text)) = What; 362 end; 363 332 364 function EndsWith(Text, What: string): Boolean; 333 365 begin … … 408 440 end; 409 441 SetLength(Result, O - 1); 442 end; 443 444 { TNameDetails } 445 446 function IsNumber(Text: string): Boolean; 447 var 448 Value: Integer; 449 begin 450 Result := TryStrToInt(Text, Value); 451 end; 452 453 function IsRomanNumber(Text: string): Boolean; 454 var 455 I: Integer; 456 begin 457 Result := True; 458 for I := 1 to Length(Text) do 459 if not (Text[I] in ['I', 'V', 'X', 'L', 'C', 'D', 'M']) then begin 460 Result := False; 461 Break; 462 end; 463 end; 464 465 procedure SearchPart(var NameParts: TNameParts; var NamePart: TNamePart); 466 var 467 I: Integer; 468 begin 469 for I := 0 to Length(NameParts) - 1 do begin 470 if (NameParts[I].PartKind = npNone) and (NameParts[I].Text = NamePart.Text) then begin 471 NameParts[I].PartKind := NamePart.PartKind; 472 NameParts[I].NamePart := @NamePart; 473 NamePart.NamePart := @NameParts[I]; 474 Break; 475 end; 476 end; 477 end; 478 479 procedure SearchPartBackward(var NameParts: TNameParts; var NamePart: TNamePart); 480 var 481 I: Integer; 482 begin 483 for I := Length(NameParts) - 1 downto 0 do begin 484 if (NameParts[I].PartKind = npNone) and (NameParts[I].Text = NamePart.Text) then begin 485 NameParts[I].PartKind := NamePart.PartKind; 486 NameParts[I].NamePart := @NamePart; 487 NamePart.NamePart := @NameParts[I]; 488 Break; 489 end; 490 end; 491 end; 492 493 function UsedInNameParts(NamePart: TNamePartKind; NameParts: TNameParts): Boolean; 494 var 495 I: Integer; 496 begin 497 I := 0; 498 while (I < Length(NameParts)) and (NameParts[I].PartKind <> NamePart) do Inc(I); 499 Result := I < Length(NameParts); 500 end; 501 502 function TNameDetails.GetAsNameParts: TNameParts; 503 var 504 I: Integer; 505 K: TNamePartKind; 506 Parts: TStringArray; 507 begin 508 Result := Default(TNameParts); 509 for K := Low(TNamePartKind) to High(TNamePartKind) do begin 510 if GetDetail(K) <> '' then begin 511 Parts := Explode(' ', GetDetail(K)); 512 for I := 0 to Length(Parts) - 1 do begin 513 SetLength(Result, Length(Result) + 1); 514 Result[Length(Result) - 1].Text := Parts[I]; 515 Result[Length(Result) - 1].PartKind := K; 516 Result[Length(Result) - 1].Index := Length(Result) - 1; 517 end; 518 end; 519 end; 520 521 // Update previous and next links 522 for I := 0 to Length(Result) - 1 do begin 523 if I > 0 then 524 Result[I].Previous := @Result[I - 1]; 525 if (I + 1) < Length(Result) then 526 Result[I].Next := @Result[I + 1]; 527 end; 528 end; 529 530 function TNameDetails.GetDetail(NamePartKind: TNamePartKind): string; 531 begin 532 case NamePartKind of 533 npPrefix: Result := Prefix; 534 npFirst: Result := First; 535 npMiddle: Result := Middle; 536 npLast: Result := Last; 537 npSuffix: Result := Suffix; 538 end; 539 end; 540 541 function TNameDetails.IsSuffix(Text: string): Boolean; 542 begin 543 Result := (Pos('.', Text) > 0) or IsNumber(Text) or 544 IsRomanNumber(Text); 545 end; 546 547 procedure TNameDetails.Split(FullName: string); 548 var 549 Parts: TStringArray; 550 NewNameParts: TNameParts; 551 OldNameParts: TNameParts; 552 I: Integer; 553 J: Integer; 554 Text: string; 555 NextKind: TNamePartKind; 556 begin 557 OldNameParts := GetAsNameParts; 558 559 Text := FullName; 560 while Pos(' ', FullName) > 0 do 561 FullName := StringReplace(FullName, ' ', ' ', [rfReplaceAll]); 562 Text := Trim(Text); 563 564 Parts := Explode(' ', Text); 565 NewNameParts := Default(TNameParts); 566 SetLength(NewNameParts, Length(Parts)); 567 for I := 0 to Length(NewNameParts) - 1 do begin 568 NewNameParts[I].Index := I; 569 NewNameParts[I].PartKind := npNone; 570 NewNameParts[I].Text := Parts[I]; 571 if I > 0 then 572 NewNameParts[I].Previous := @NewNameParts[I - 1]; 573 if (I + 1) < Length(NewNameParts) then 574 NewNameParts[I].Next := @NewNameParts[I + 1]; 575 end; 576 577 // Match existing parts 578 for I := 0 to Length(OldNameParts) - 1 do begin 579 if OldNameParts[I].Text <> '' then 580 SearchPart(NewNameParts, OldNameParts[I]); 581 end; 582 583 // Check incorrect matches 584 for I := 0 to Length(OldNameParts) - 1 do begin 585 for J := I + 1 to Length(OldNameParts) - 1 do 586 if Assigned(OldNameParts[I].NamePart) and Assigned(OldNameParts[J].NamePart) and 587 (OldNameParts[I].NamePart^.Index >= OldNameParts[J].NamePart^.Index) then begin 588 if Abs(I - OldNameParts[I].NamePart^.Index) > 589 Abs(J - OldNameParts[J].NamePart^.Index) then begin 590 OldNameParts[I].NamePart^.PartKind := npNone; 591 OldNameParts[I].NamePart^.NamePart := nil; 592 OldNameParts[I].NamePart := nil; 593 end else begin 594 OldNameParts[J].NamePart^.PartKind := npNone; 595 OldNameParts[J].NamePart^.NamePart := nil; 596 OldNameParts[J].NamePart := nil; 597 end; 598 end; 599 end; 600 for I := Length(OldNameParts) - 1 downto 0 do begin 601 for J := I - 1 downto 0 do 602 if Assigned(OldNameParts[I].NamePart) and Assigned(OldNameParts[J].NamePart) and 603 (OldNameParts[I].NamePart^.Index <= OldNameParts[J].NamePart^.Index) then begin 604 if Abs(I - OldNameParts[I].NamePart^.Index) > 605 Abs(J - OldNameParts[J].NamePart^.Index) then begin 606 OldNameParts[I].NamePart^.PartKind := npNone; 607 OldNameParts[I].NamePart^.NamePart := nil; 608 OldNameParts[I].NamePart := nil; 609 end else begin 610 OldNameParts[J].NamePart^.PartKind := npNone; 611 OldNameParts[J].NamePart^.NamePart := nil; 612 OldNameParts[J].NamePart := nil; 613 end; 614 end; 615 end; 616 617 // Match existing parts backqards 618 for I := Length(OldNameParts) - 1 downto 0 do begin 619 if (OldNameParts[I].Text <> '') and not Assigned(OldNameParts[I].NamePart) then 620 SearchPartBackward(NewNameParts, OldNameParts[I]); 621 end; 622 623 // Match uncertain parts 624 for I := 0 to Length(OldNameParts) - 1 do 625 if not Assigned(OldNameParts[I].NamePart) then begin 626 if Assigned(OldNameParts[I].Next) and 627 Assigned(OldNameParts[I].Next^.NamePart) and 628 Assigned(OldNameParts[I].Next^.NamePart^.Previous) and 629 (OldNameParts[I].Next^.NamePart^.Previous^.PartKind = npNone) then begin 630 OldNameParts[I].NamePart := OldNameParts[I].Next^.NamePart^.Previous; 631 OldNameParts[I].Next^.NamePart^.Previous^.NamePart := @OldNameParts[I]; 632 OldNameParts[I].Next^.NamePart^.Previous^.PartKind := OldNameParts[I].PartKind; 633 end else 634 if Assigned(OldNameParts[I].Previous) and 635 Assigned(OldNameParts[I].Previous^.NamePart) and 636 Assigned(OldNameParts[I].Previous^.NamePart^.Next) and 637 (OldNameParts[I].Previous^.NamePart^.Next^.PartKind = npNone) then begin 638 OldNameParts[I].NamePart := OldNameParts[I].Previous^.NamePart^.Next; 639 OldNameParts[I].Previous^.NamePart^.Next^.NamePart := @OldNameParts[I]; 640 OldNameParts[I].Previous^.NamePart^.Next^.PartKind := OldNameParts[I].PartKind; 641 end; 642 end; 643 644 // Mark new unknown parts according existing parts 645 for I := Length(Parts) - 1 downto 0 do 646 if (NewNameParts[I].PartKind = npNone) and 647 Assigned(NewNameParts[I].Next) and 648 (NewNameParts[I].Next^.PartKind <> npNone) then begin 649 if (NewNameParts[I].Next^.PartKind = npFirst) and 650 EndsWith(NewNameParts[I].Text, '.') then begin 651 NewNameParts[I].PartKind := npPrefix; 652 end else NewNameParts[I].PartKind := NewNameParts[I].Next^.PartKind; 653 end; 654 655 // Mark unknown parts according to neighbouring parts 656 for I := 0 to Length(Parts) - 1 do 657 if (NewNameParts[I].PartKind = npNone) and 658 Assigned(NewNameParts[I].Previous) and 659 (NewNameParts[I].Previous^.PartKind <> npNone) then begin 660 if (NewNameParts[I].Previous^.PartKind in [npLast, npMiddle]) and 661 IsSuffix(NewNameParts[I].Text) then begin 662 NewNameParts[I].PartKind := npSuffix; 663 end else 664 if (NewNameParts[I].Previous^.PartKind = npFirst) and 665 (Last = '') then begin 666 NewNameParts[I].PartKind := npLast; 667 end else 668 if (NewNameParts[I].Previous^.PartKind = npLast) and 669 (Middle = '') then begin 670 NewNameParts[I].PartKind := npLast; 671 NewNameParts[I].Previous^.PartKind := npMiddle; 672 end else 673 if (NewNameParts[I].Previous^.PartKind = npPrefix) then begin 674 NewNameParts[I].PartKind := npFirst; 675 end else 676 NewNameParts[I].PartKind := NewNameParts[I].Previous^.PartKind; 677 end; 678 679 // Mark remaining unknown parts based on defined filling sequence 680 NextKind := npFirst; 681 for I := 0 to Length(Parts) - 1 do 682 if NewNameParts[I].PartKind = npNone then begin 683 if EndsWith(NewNameParts[I].Text, '.') and (NextKind = npFirst) then begin 684 NewNameParts[I].PartKind := npPrefix; 685 end else 686 if (NextKind = npMiddle) and IsSuffix(NewNameParts[I].Text) then begin 687 NewNameParts[I].PartKind := npSuffix; 688 NextKind := npSuffix; 689 end else 690 if NextKind = npMiddle then begin 691 NewNameParts[I].Previous^.PartKind := npMiddle; 692 NewNameParts[I].PartKind := npLast; 693 end else begin 694 NewNameParts[I].PartKind := NextKind; 695 if NextKind = npFirst then NextKind := npLast 696 else if NextKind = npLast then NextKind := npMiddle; 697 end; 698 end; 699 700 // Combine multiple parts to base parts 701 Prefix := ''; 702 First := ''; 703 Middle := ''; 704 Last := ''; 705 Suffix := ''; 706 for I := 0 to Length(Parts) - 1 do 707 case NewNameParts[I].PartKind of 708 npPrefix: Prefix := Trim(Prefix + ' ' + Parts[I]); 709 npFirst: First := Trim(First + ' ' + Parts[I]); 710 npMiddle: Middle := Trim(Middle + ' ' + Parts[I]); 711 npLast: Last := Trim(Last + ' ' + Parts[I]); 712 npSuffix: Suffix := Trim(Suffix + ' ' + Parts[I]); 713 end; 714 715 { 716 // Title Prefix 717 while (Length(Parts) > 0) and EndsWith(Parts[0], '.') do begin 718 Prefix := Trim(Prefix + ' ' + Parts[0]); 719 Delete(Parts, 0, 1); 720 end; 721 722 // Title Suffix 723 if ProcessAfter then 724 for I := 0 to High(Parts) do 725 if (Pos('.', Parts[I]) > 0) or IsNumber(Parts[I]) or IsRomanNumber(Parts[I]) then begin 726 for J := I to High(Parts) do 727 Suffix := Trim(Suffix + ' ' + Parts[J]); 728 SetLength(Parts, I); 729 Break; 730 end; 731 732 if Length(Parts) = 0 then begin 733 end else 734 if Length(Parts) = 1 then begin 735 First := Parts[0]; 736 end else 737 if Length(Parts) = 2 then begin 738 First := Parts[0]; 739 Last := Parts[1]; 740 end else begin 741 First := Parts[0]; 742 for I := 0 to Length(Parts) - 3 do 743 Middle := Trim(Middle + ' ' + Parts[I + 1]); 744 Last := Parts[High(Parts)]; 745 end;} 746 end; 747 748 function TNameDetails.GetCombined: string; 749 begin 750 Result := ''; 751 if Prefix <> '' then Result := Result + ' ' + Prefix; 752 if First <> '' then Result := Result + ' ' + First; 753 if Middle <> '' then Result := Result + ' ' + Middle; 754 if Last <> '' then Result := Result + ' ' + Last; 755 if Suffix <> '' then Result := Result + ' ' + Suffix; 756 Result := Trim(Result); 410 757 end; 411 758 … … 1119 1466 end; 1120 1467 1121 function IsNumber(Text: string): Boolean;1122 var1123 Value: Integer;1124 begin1125 Result := TryStrToInt(Text, Value);1126 end;1127 1128 function IsRomanNumber(Text: string): Boolean;1129 var1130 I: Integer;1131 begin1132 Result := True;1133 for I := 1 to Length(Text) do1134 if not (Text[I] in ['I', 'V', 'X', 'L', 'C', 'D', 'M']) then begin1135 Result := False;1136 Break;1137 end;1138 end;1139 1140 procedure TContact.FullNameToNameParts(FullName: string; out Before, First,1141 Middle, Last, After: string);1142 var1143 Parts: TStringArray;1144 I, J: Integer;1145 begin1146 Before := '';1147 First := '';1148 Middle := '';1149 Last := '';1150 After := '';1151 while Pos(' ', FullName) > 0 do1152 FullName := StringReplace(FullName, ' ', ' ', [rfReplaceAll]);1153 Parts := Explode(' ', Trim(FullName));1154 1155 // Title before1156 while (Length(Parts) > 0) and EndsWith(Parts[0], '.') do begin1157 Before := Trim(Before + ' ' + Parts[0]);1158 Delete(Parts, 0, 1);1159 end;1160 1161 // Title after1162 for I := 0 to High(Parts) do1163 if (Pos('.', Parts[I]) > 0) or IsNumber(Parts[I]) or IsRomanNumber(Parts[I]) then begin1164 for J := I to High(Parts) do1165 After := Trim(After + ' ' + Parts[J]);1166 SetLength(Parts, I);1167 Break;1168 end;1169 1170 if Length(Parts) = 0 then begin1171 end else1172 if Length(Parts) = 1 then begin1173 First := Parts[0];1174 end else1175 if Length(Parts) = 2 then begin1176 First := Parts[0];1177 Last := Parts[1];1178 end else begin1179 First := Parts[0];1180 for I := 0 to Length(Parts) - 3 do1181 Middle := Trim(Middle + ' ' + Parts[I + 1]);1182 Last := Parts[High(Parts)];1183 end;1184 end;1185 1186 function TContact.NamePartsToFullName(Before, First, Middle, Last, After: string1187 ): string;1188 begin1189 Result := '';1190 if Before <> '' then Result := Result + ' ' + Before;1191 if First <> '' then Result := Result + ' ' + First;1192 if Middle <> '' then Result := Result + ' ' + Middle;1193 if Last <> '' then Result := Result + ' ' + Last;1194 if After <> '' then Result := Result + ' ' + After;1195 Result := Trim(Result);1196 end;1197 1198 1468 procedure TContact.Assign(Source: TContact); 1199 1469 begin
Note:
See TracChangeset
for help on using the changeset viewer.