usyncbookmarks.pas

来自「FMA is a free1 powerful phone editing to」· PAS 代码 · 共 1,240 行 · 第 1/3 页

PAS
1,240
字号
unit uSyncBookmarks;

{
*******************************************************************************
* Descriptions: Phone Info view implementation
* $Source: /cvsroot/fma/fma/uSyncBookmarks.pas,v $
* $Locker:  $
*
* Todo:
* - Create a custmo view for bookmarks, custom popup menu, custom Properties.
* - Add support for new phones K610 etc.
*
* Change Log:
* $Log: uSyncBookmarks.pas,v $
*
}

interface

uses
  Windows, TntWindows, Messages, SysUtils, TntSysUtils, Variants, Classes, TntClasses, Graphics, TntGraphics,
  Controls, TntControls, Forms, TntForms, Dialogs, TntDialogs, Placemnt, VirtualTrees, ExtCtrls, TntExtCtrls,
  Menus, TntMenus, uConnProgress;

resourcestring
  sHomePageLocked = 'Could not change WAP home page!'+sLinebreak+sLinebreak+
      'In certain terminals a number of WAP profiles may be locked at '+
      'manufacturing to prevent the users from altering the predefined WAP settings. When such a '+
      'profile is active some of the commands in this ensemble will not function according to '+
      'specification. The read and test commands should always function as expected but the set '+
      'command will return ERROR even though the command is given using the correct syntax and '+
      'all parameters are within range.';

type
  TBookmarkStatus = (bsNew, bsModified, bsDeleted, bsNormal);

  TBookmarkData = Record
    status: TBookmarkStatus;
    position: integer;
    title,url: WideString;
    id: TGUID; // HASH for original title and url values as loaded from phone first time!
               // This will be used to detect any changes made in phone for that bookmark.
  end;
  PBookmarkData = ^TBookmarkData;

  TfrmSyncBookmarks = class(TTntFrame)
    ListBookmarks: TVirtualStringTree;
    FormStorage1: TFormStorage;
    NoItemsPanel: TTntPanel;
    PopupMenu1: TTntPopupMenu;
    Properties1: TTntMenuItem;
    N1: TTntMenuItem;
    Delete1: TTntMenuItem;
    NewBookmark1: TTntMenuItem;
    N3: TTntMenuItem;
    OpenTargetLocation1: TTntMenuItem;
    N5: TTntMenuItem;
    DownloadEntireList1: TTntMenuItem;
    N6: TTntMenuItem;
    Import1: TTntMenuItem;
    Export1: TTntMenuItem;
    SyncTo1: TTntMenuItem;
    InternetExplorer1: TTntMenuItem;
    SetasHomePage1: TTntMenuItem;
    N2: TTntMenuItem;
    Firefox1: TTntMenuItem;
    Opera1: TTntMenuItem;
    WAPHomePage1: TTntMenuItem;
    N8: TTntMenuItem;
    SynchrinizePhone1: TTntMenuItem;
    N4: TTntMenuItem;
    N7: TTntMenuItem;
    ForceAs1: TTntMenuItem;
    NotModified1: TTntMenuItem;
    Modified1: TTntMenuItem;
    NewNoUndo1: TTntMenuItem;
    procedure FormStorage1RestorePlacement(Sender: TObject);
    procedure FormStorage1SavePlacement(Sender: TObject);
    procedure ListBookmarksAfterPaint(Sender: TBaseVirtualTree;
      TargetCanvas: TCanvas);
    procedure ListBookmarksCompareNodes(Sender: TBaseVirtualTree; Node1,
      Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
    procedure ListBookmarksGetImageIndex(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
      var Ghosted: Boolean; var ImageIndex: Integer);
    procedure ListBookmarksGetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: WideString);
    procedure ListBookmarksHeaderClick(Sender: TVTHeader;
      Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    procedure ListBookmarksHeaderMouseUp(Sender: TVTHeader;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure ListBookmarksKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure OpenTargetLocation1Click(Sender: TObject);
    procedure PopupMenu1Popup(Sender: TObject);
    procedure DownloadEntireList1Click(Sender: TObject);
    procedure SetasHomePage1Click(Sender: TObject);
    procedure SynchrinizePhone1Click(Sender: TObject);
    procedure NotModified1Click(Sender: TObject);
    procedure Modified1Click(Sender: TObject);
    procedure NewNoUndo1Click(Sender: TObject);
    procedure btnSYNCClick(Sender: TObject);
    procedure btnNEWClick(Sender: TObject);
    procedure btnEDITClick(Sender: TObject);
    procedure btnDELClick(Sender: TObject);
  private
    { Private declarations }
    FRendered: boolean;
    PhoneData: TBookmarkData;
    FFmaBData: PBookmarkData;
    FSyncDialog: TfrmConnect;
    FMaxItems,FMaxTitleLen,FMaxUrlLen,FMaxHomeLen: Cardinal;
    function GetBookmarksCapacity: Integer;
    function GetHomePageCapacity: Integer;
    function ResolveConflict(NameContact: WideString; Info:WideString): integer;
    function Synchronize: boolean;
    procedure GetPhoneBookmarks(var sl: TStringList);
    procedure FullRefresh;
    procedure ForceContact(State: TBookmarkStatus);
  protected
    procedure DoUploadBookmark(Item: PBookmarkData; DeleteOld: boolean = False);
    procedure DoDeleteBookmark(Item: PBookmarkData);
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    procedure RenderList;
    procedure UpdateBookmarks;
    procedure OnConnected;
    procedure LoadBookmarks(FileName:WideString);
    procedure SaveBookmarks(FileName:WideString);
    function FindBookmark(Position: integer; var Data: PBookmarkData): PVirtualNode;
    function FindBookmarkID(AID: TGUID; var Data: PBookmarkData): PVirtualNode;
    function FindBookmarkUrl(AURL: WideString; var Data: PBookmarkData): PVirtualNode;
    function FindBookmarkTitle(ATitle: WideString; var Data: PBookmarkData): PVirtualNode;

    function DoEdit(Node: PVirtualNode): boolean;
    procedure OnConflictChanges(Sender: TObject; const TargetName, Option1Name, Option2Name: WideString);
  published
    property IsRendered: boolean read FRendered write FRendered;
    property MaxHomePageLength: Cardinal read FMaxHomeLen;
  end;

var
  frmSyncBookmarks: TfrmSyncBookmarks;

function SameBookmarkIDs(Data1,Data2: PBookmarkData): boolean;
function IsThisBookmark(ID: TGUID; Data: PBookmarkData): boolean;
function IsBookmarkModified(Data: PBookmarkData): boolean;
function GetBookmarkNewID(Data: PBookmarkData): TGUID;
procedure SetBookmarkStatus(Data: PBookmarkData; Status: TBookmarkStatus);

implementation

{$R *.dfm}

uses
  gnugettext, gnugettexthelpers, cUnicodeCodecs,
  uLogger, ShellAPI, TntComCtrls, Unit1, uGlobal, uPostURL, uDialogs,
  uInfoView, uPromptConflict, uThreadSafe, uConflictChanges, Math;

function SameBookmarkIDs(Data1,Data2: PBookmarkData): boolean;
begin
  Result := CompareMem(@(Data1^.id),@(Data2^.id),SizeOf(Data1^.id));
end;

function IsThisBookmark(ID: TGUID; Data: PBookmarkData): boolean;
begin
  Result := CompareMem(@ID,@(Data^.id),SizeOf(ID));
end;

function IsBookmarkModified(Data: PBookmarkData): boolean;
var
  g: TGUID;
begin
  g := GetBookmarkNewID(Data);
  Result := not CompareMem(@g,@(Data^.id),SizeOf(g));
end;

function GetBookmarkNewID(Data: PBookmarkData): TGUID;
var
  g: TGUID;
  i,s: Integer;
  w: WideString;
  p: PChar;
begin
  p := @g;
  w := Data^.title + '|' + Data^.url;
  s := SizeOf(g);
  FillChar(g,s,0);
  for i := 0 to Length(w) do begin
    (p + i mod s)^ := PChar(@w[i])^;
  end;
  Result := g;
end;

procedure SetBookmarkStatus(Data: PBookmarkData; Status: TBookmarkStatus);
begin
  case Status of
    bsNew:
      FillChar(Data^.id,SizeOf(Data^.id),0);
    bsModified,
    bsDeleted:
      Data^.status := Status;
    bsNormal:
      Data^.id := GetBookmarkNewID(Data);
  end;
end;

{ TfrmSyncBookmarks }

function TfrmSyncBookmarks.Synchronize: boolean;
var
  sl: TStringList;
  i: Integer;
  g: TBookmarkData;
  Item,Data: PBookmarkData;
  Node: PVirtualNode;
  PhoneOnPC,PhoneModified,PhoneDeleted: boolean;
  PhoneSpecialMod: Set of Byte;
  procedure UpdatePositions(PosDeleted: Integer);
  var
    Node: PVirtualNode;
    Data: PBookmarkData;
    bpos: byte;
  begin
    Node := ListBookmarks.GetFirst;
    while Assigned(Node) do begin
      Data := ListBookmarks.GetNodeData(Node);
      if Assigned(Data) then
        if Data.position > PosDeleted then begin
          bpos := byte(Data.position);
          if bpos in PhoneSpecialMod then
            PhoneSpecialMod := PhoneSpecialMod - [bpos] + [bpos-1];
          Data.position := Data.position-1;
        end;
      Node := ListBookmarks.GetNext(Node)
    end;
  end;
  function LocalInPhone(Data: PBookmarkData): boolean;
  var
    bd: TBookmarkData;
  begin
    i := 0;
    while i < sl.Count do begin
      FillChar(bd,SizeOf(bd),0);
      bd.status := bsNormal;
      bd.position := StrToInt(GetToken(sl[i],0));
      bd.url := GetToken(sl[i],1);
      bd.title := UTF8StringToWideString(GetToken(sl[i],2));
      bd.id := GetBookmarkNewID(@bd);
      if SameBookmarkIDs(Data,@bd) then
        break;
      inc(i);
    end;
    Result := i < sl.Count;
  end;
  function DelAndGetNext(Node: PVirtualNode): PVirtualNode;
  var
    Prev: PVirtualNode;
  begin
    Prev := Node;
    Result := ListBookmarks.GetNext(Node);
    ListBookmarks.DeleteNode(Prev);
  end;
  procedure ShowProgressTarget(AName: WideString);
  begin
    if Assigned(FSyncDialog) then
      FSyncDialog.SetDescr(_('Synchronizing phone bookmarks')+sLinebreak+'('+AName+')');
  end;
begin
  Result := False;
  Form1.RequestConnection;
  sl := TStringList.Create;
  try
    { First process phone changes }
    PhoneSpecialMod := [];
    GetPhoneBookmarks(sl);
    for i := 0 to sl.Count-1 do begin
      FillChar(PhoneData,SizeOf(PhoneData),0);
      PhoneData.status := bsNormal;
      PhoneData.position := StrToInt(GetToken(sl[i],0));
      PhoneData.url := GetToken(sl[i],1);
      PhoneData.title := UTF8StringToWideString(WideStringToLongString(GetToken(LongStringToWideString(sl[i]),2)));
      PhoneData.id := GetBookmarkNewID(@PhoneData);
      Log.AddSynchronizationMessage('Debug: Processing phone bookmark '+PhoneData.title, lsDebug);
      { Locate Bookmark in FMA }
      Node := FindBookmarkID(PhoneData.id,Data); // modified both title and url in FMA? (phone item not modified)
      if not Assigned(Node) then
        Node := FindBookmarkUrl(PhoneData.url,Data); // modified title in FMA?
      if not Assigned(Node) then
        Node := FindBookmarkTitle(PhoneData.title,Data); // modified url in FMA?
      if not Assigned(Node) then begin
        Node := FindBookmark(PhoneData.position,Data); // modified phone-title and fma-url? (or reverse)
        if Assigned(Node) then begin
          { Try to lookup local bookmark using unchanged fields in both instances }
          FillChar(g,SizeOf(g),0);
          g.title := PhoneData.title;
          g.url := Data.url;
          g.id := GetBookmarkNewID(@g);
          if not SameBookmarkIDs(@g,Data) then begin
            g.title := Data.title;
            g.url := PhoneData.url;
            g.id := GetBookmarkNewID(@g);
            if not SameBookmarkIDs(@g,Data) then
              Node := nil;
          end;
          { Remember fixed-changes positions, if any }
          if Assigned(Node) then
            PhoneSpecialMod := PhoneSpecialMod + [byte(PhoneData.position)];
        end;
      end;
      { Bookmark found? }
      if Node <> nil then begin
        { yes, check if it is modified in phone }
        PhoneModified := not SameBookmarkIDs(Data,@PhoneData); // Data.id = Hash of original phone values
        if PhoneModified then
          ShowProgressTarget(Data.title);
        Data.position := PhoneData.position; // update bookmark position in phone
        FFmaBData := nil; // used for conflicts details
        case Data.status of
          bsNew,
          bsModified: begin
            if PhoneModified then begin
              FFmaBData := Data;
              PhoneOnPC := ResolveConflict(Data.title, _('is modified on phone and modified on pc.')) = 0;
              if PhoneOnPC then begin
                Data^ := PhoneData;
                SetBookmarkStatus(Data,bsNormal);
                Log.AddSynchronizationMessage(Data.title + _(' modified in FMA by phone.'), lsInformation);
              end;
            end;
          end;
          bsDeleted: begin
            if PhoneModified then begin
              PhoneOnPC := ResolveConflict(Data.title, _('is modified on phone and deleted on pc.')) = 0;
              if PhoneOnPC then begin
                Data^ := PhoneData;
                SetBookmarkStatus(Data,bsNormal);
                Log.AddSynchronizationMessage(Data.title + _(' modified in FMA by phone.'), lsInformation);
              end;
            end;
          end;
          bsNormal: begin
            if PhoneModified then begin
              Data^ := PhoneData;
              SetBookmarkStatus(Data,bsNormal); 
              Log.AddSynchronizationMessage(Data.title + _(' modified in FMA by phone.'), lsInformation);
            end;
          end;
        end;
      end
      else begin
        { no, it is a new bookmark in phone }
        ShowProgressTarget(PhoneData.title);
        Node := ListBookmarks.AddChild(nil);
        Item := ListBookmarks.GetNodeData(Node);
        Item^ := PhoneData;
        SetBookmarkStatus(Item,bsNormal);
        Log.AddSynchronizationMessage(PhoneData.title + _(' added to FMA by phone.'), lsInformation);
      end;
    end;

    { Next process FMA changes }
    Node := ListBookmarks.GetFirst;
    while Assigned(Node) do begin
      Data := ListBookmarks.GetNodeData(Node);
      if Assigned(Data) then begin
        Log.AddSynchronizationMessage('Debug: Processing FMA bookmark '+Data.title, lsDebug);
        { if we're checking mixed-changed position, it is in Phone for sure }
        if byte(Data.position) in PhoneSpecialMod then
          PhoneDeleted := False
        else
          { check if bookmark is deleted in phone? }
          PhoneDeleted := not LocalInPhone(Data);
        if PhoneDeleted or (Data.status <> bsNormal) then
          ShowProgressTarget(Data.title);
        FFmaBData := nil; // used for conflicts details
        case Data.status of
          bsNew: begin
            DoUploadBookmark(Data);
            SetBookmarkStatus(Data,bsNormal);
            Log.AddSynchronizationMessage(Data.title + _(' added in phone by FMA.'), lsInformation);
          end;
          bsModified: begin
            if PhoneDeleted then begin
              PhoneOnPC := ResolveConflict(Data.title, _('is deleted on phone and modified on pc.')) = 0;
              if PhoneOnPC then begin
                Node := DelAndGetNext(Node);
                Log.AddSynchronizationMessage(Data.title + _(' deleted in FMA by phone.'), lsInformation);
                continue;
              end;
            end;
            DoUploadBookmark(Data,not PhoneDeleted);
            SetBookmarkStatus(Data,bsNormal);
            if PhoneDeleted then
              Log.AddSynchronizationMessage(Data.title + _(' added in phone by FMA.'), lsInformation)
            else begin
              UpdatePositions(Data.position);
              Log.AddSynchronizationMessage(Data.title + _(' modified in phone by FMA.'), lsInformation);
            end;  
          end;
          bsDeleted: begin
            if not PhoneDeleted then begin
              DoDeleteBookmark(Data);
              SetBookmarkStatus(Data,bsNormal);
              UpdatePositions(Data.position);
            end;
            Node := DelAndGetNext(Node);
            if not PhoneDeleted then
              Log.AddSynchronizationMessage(Data.title + _(' deleted in phone by FMA.'), lsInformation);
            continue;
          end;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?