« Evernoteへメモを追加する。 | トップページ | JWWファイルの超簡易表示用コンポーネント for Delphi 2007(zipファイル) »

Evernoteへメモを追加する。(添付ファイル付)

今回は、添付ファイルと共にメモを追加してみます。前回のソースにListView1, ImageList1, Splitter1を追加します。添付ファイルは、ExplorerからのDrag&Dropで取得します。ExplorerからのターゲットをMemo1としているため、Mr.XRAYさんところのSubClassコンポーネントを使わせて頂いています。(フォームをターゲットとして、WM_DROPFILESで処理するとサブクラスは必要はないです。)

Design


unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
  Vcl.ToolWin, Vcl.ComCtrls, Vcl.ImgList, SubClassUnit;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    Title: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Button1: TButton;
    Button2: TButton;
    ListView1: TListView;
    Splitter1: TSplitter;
    ImageList1: TImageList;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    SubClass1: TSubClass;
    procedure SubClass1MessageAfter(Sender: TObject;
      var Message: TMessage);
    procedure DropFiles(H: THandle);
    procedure MakeTempFile(FileName: String);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses ShellAPI,
     EncdDecd,            // BASE64 Encording
     IdHashMessageDigest; // MD5値

const
  Temp = 'Evernote.enex';
  S1 = '<?xml version="1.0" encoding="UTF-8"?>'+
       '<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export.dtd">'+
       '<en-export>';  // エクスポートしたファイルには、その時の日時や
                       // EverNoteのバージョンが入っていますが、
                       // なくても動作しましたので、消しました。

  // 複数ある場合は、<note></note>までを繰り返す。
  S2 = '<note>';

  // ここにタイトル <title>タイトル</title>

  S3 = '<content>' +
       '<![CDATA[' +
       '<?xml version="1.0" encoding="UTF-8"?>' +
       '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'+
       '<en-note style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;">';

  // ここに本文・添付ファイル

  // e.g.)
  // 1行目文字-ファイル1-2行目文字-ファイル2、ファイル3-3行目文字の場合
  // <div>1行目文字</div>
  // <en-media hash="ファイル1のMD5値" style="cursor: default; vertical-align: middle;" type="application/octet-stream"/>
  // <div>2行目文字</div>
  // <div>
  // <en-media hash="ファイル2のMD5値" style="cursor: default; vertical-align: middle;" type="application/octet-stream"/>
  // <en-media hash="ファイル3のMD5値" style="cursor: default; vertical-align: middle;" type="application/octet-stream"/>
  // <div>3行目文字</div>
  // </div>
  F1 = '<en-media hash=';
  F2 = ' style="cursor: default; vertical-align: middle;" type="application/octet-stream"/>';

  S4 = '</en-note>' +
       ']]>' +
       '</content>';

  // ここに作成日時 <created>20111001T042516Z</created>
  // ここにタグ <tag>タグ1</tag> <tag>タグ2</tag>

  S5 = '<note-attributes>';

  // ここにURL <source-url>http://delphi-fan</source-url>

  S6 = '</note-attributes>';

  // ここに添付ファイル本体 - 複数ある場合は、<resource></resource>の繰り返し
  // <resource><data encoding="base64">
  //  ファイルデータ(BASE64でエンコード)
  // </data>
  // <mime>application/octet-stream</mime>
  // <resource-attributes>
  // <source-url>file://ファイル名(パス付)</source-url>
  // <file-name>ファイル名(パス無)</file-name>
  // </resource-attributes>
  // </resource>

  F3 = '<resource><data encoding="base64">';

  // ここにデータ

  F4 = '</data>';
  F5 = '<mime>application/octet-stream</mime>';
  F6 = '<resource-attributes>';
  F7 = '<source-url>file://';

  // ここにファイル名(パス付)

  F8 = '</source-url>';

  F9 = '<file-name>';

  // ここにファイル名(パス無)

  F10 = '</file-name>';
  F11 = '</resource-attributes>';
  F12 = '</resource>';

  S7 = '</note>';
  S8 = '</en-export>';


// Evernoteエクスポートファイル (ENEX)の作成
procedure TForm1.MakeTempFile(FileName: String);

  // UTCの日時-「年月日T時分秒Z」という文字列にします。
  function GetCreateDateTime: String;
  var
    UTC: TSystemTime;
  begin
    GetSystemTime(UTC);
  with UTC do
    Result := Format('%.2d%.2d%.2dT%.2d%.2d%.2dZ',
      [wYear,wMonth,wDay,wHour,wMinute,wSecond]);
  end;

  // ファイルをBASE64文字列で返します。
  function GetBody(const FileName: String): String;
  var
    MS: TMemoryStream;
  begin
    MS := TMemoryStream.Create;
    try
      MS.LoadFromFile(FileName);
      Result := EncodeBase64(MS.Memory, MS.Size);
    finally
      MS.Free;
    end;
  end;

  // ファイルのMD5値を取得します。
  function GetMD5(const FileName : String) : String;
  var
    idHMD5 : TIdHashMessageDigest5;
    FS : TFileStream;
  begin
    idHMD5 := TIdHashMessageDigest5.Create;
    FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
    try
      // Evernoteエクスポートファイル (ENEX)では小文字が使われています。
      // 大文字のままで動作しましたが、一応元ファイルに合わせています。
      Result := AnsiLowerCase(idHMD5.HashStreamAsHex(FS));
    finally
      FS.Free;
      idHMD5.Free;
    end;
  end;

var
  SL1, SL2: TStringList;
  I: Integer;
  S: String;
begin
  SL1 := TStringList.Create;
  SL2 := TStringList.Create;
  try
    SL2.Delimiter := ',';
    SL2.StrictDelimiter := True;

    SL1.Add(S1);
    SL1.Add(S2);

    // タイトル
    SL1.Add('<title>'+Edit1.Text+'</title>');
    SL1.Add(S3);

    // 本文 1行目から<div></div>でいいみたい
    for I := 0 to Memo1.Lines.Count -1 do
      SL1.Add('<div>'+Memo1.Lines[I]+'</div>');

    // 添付ファイル - このサンプルでは、本文の最後にまとめて追加します。
    for I := 0 to ListView1.Items.Count -1 do
    begin
      S := ListView1.Items[I].SubItems[0]; // ファイル名
      SL1.Add(F1+'"'+GetMD5(S)+'"'+F2); // MD5
    end;

    SL1.Add(S4);

    // 作成日時
    SL1.Add('<created>'+GetCreateDateTime+'</created>');

    // タグ
    SL2.CommaText := Edit2.Text;
    for I := 0 to SL2.Count -1 do
      SL1.Add('<tag>'+SL2[I]+'</tag>');
    SL1.Add(S5);

    // URL
    if Trim(Edit3.Text) <> '' then
      SL1.Add('<source-url>' + Edit3.Text + '</source-url>');
    SL1.Add(S6);

    // ファイルデータ
    for I := 0 to ListView1.Items.Count -1 do
    begin
      S := ListView1.Items[I].SubItems[0]; // ファイル名
      SL1.Add(F3+GetBody(S)+F4+F5+F6);     // ファイルデータ
      SL1.Add(F7+S+F8);
      SL1.Add(F9+ExtractFileName(S)+F10+F11+F12);
    end;

    SL1.Add(S7);
    SL1.Add(S8);
    SL1.SaveToFile(FileName, TEncoding.UTF8);
    Sleep(50); // おまじない
  finally
    SL2.Free;
    SL1.Free;
  end;
end;

// ファイルを作成してEvernoteに読み込ませます。
procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: String;
  Param1, Param2: String;
begin
  FileName := ExtractFilePath(Application.ExeName) + Temp;
  MakeTempFile(FileName);
  if FileExists(FileName) then
  begin
    Param1 := 'C:\Program Files\Evernote\Evernote\ENScript';
    Param2 := 'importNotes /s ' + FileName + ' /n ' + Edit4.Text;
    ShellExecute(Handle, 'OPEN', PWideChar(Param1), PWideChar(Param2), nil, SW_HIDE);
  end;
end;

// 同期
procedure TForm1.Button2Click(Sender: TObject);
var
  Param1, Param2: String;
begin
  Param1 := 'C:\Program Files\Evernote\Evernote\ENScript';
  Param2 := 'syncDatabase';
  ShellExecute(Handle, 'OPEN', PWideChar(Param1), PWideChar(Param2), nil, SW_HIDE);
end;

// 添付ファイルは、ExplorerからのDrag&Dropにて取得します。
procedure TForm1.FormCreate(Sender: TObject);
begin
  //ListView1.
  // サブクラスの設定
  SubClass1:= TSubClass.Create(Self);
  SubClass1.TargetControl := Memo1;
  SubClass1.OnMessageAfter := SubClass1MessageAfter;
  DragAcceptFiles(Memo1.Handle, True);
end;

procedure TForm1.SubClass1MessageAfter(Sender: TObject; var Message: TMessage);
begin
  case Message.Msg of
    WM_DROPFILES: DropFiles(Message.WParam);
  end;
end;

// 添付ファイル
procedure TForm1.DropFiles(H: THandle);

  // ファイルに関連付けられたアイコンを取出し、ImageListに追加します。
  function GetIconToImageList(FileName: String): Integer;
  var
    Icon : TIcon;
    SHFileInfo: TSHFileInfo;
  begin
     Icon:= TIcon.Create;
     try
       SHGetFileInfo(PChar(FileName), 0, SHFileInfo, SizeOf(SHFileInfo),
         SHGFI_SMALLICON or SHGFI_ICON);
       Icon.Handle := SHFileInfo.hIcon;
       Result := ImageList1.AddIcon(Icon);
     finally
       Icon.Free;
     end;
  end;

 // 実行ファイル、リンクファイルとフォルダは、添付ファイルにしない。
 function IsCorrectFileAsAttachment(FileName: String): Boolean;
 var
   S: String;
 begin
   S := UpperCase(ExtractFileExt(FileName));
   Result := (S <> '.LNK') or (S <> '.EXE') or (not FileExists(FileName));
 end;

var
  I, Count: Integer;
  FileName: array [0..255] of Char;
  Files: TStringList;
  S: String;
  LI: TListItem;
begin
  Files := TStringList.Create;
  try
    // ドロップされたファイルの取得
    Count := DragQueryFile(H, Cardinal(-1), nil, 0);
    for I:=0 to Count-1 do
    begin
      DragQueryFile(H, I, FileName, SizeOf(FileName)-1);
      Files.Add(FileName);
    end;
    DragFinish(H);

    // ListViewに追加します。
    for I := 0 to Files.Count - 1 do
    begin
      S := Files[I];
      if IsCorrectFileAsAttachment(S) then
      begin
        LI := ListView1.Items.Add;
        LI.Caption := ExtractFileName(S);
        LI.SubItems.Add(S);
        LI.ImageIndex := GetIconToImageList(S);
      end;
    end;
  finally
    Files.Free;
  end;
end;

end.


実行するとこんな感じ

Runtime



Evernoteではこんな感じ

Evernote




サブクラス用コンポーネント
Delphi Library [Mr.XRAY]
SubClassUnit
http://homepage2.nifty.com/Mr_XRAY/Halbow/Notes/N004.html

|

« Evernoteへメモを追加する。 | トップページ | JWWファイルの超簡易表示用コンポーネント for Delphi 2007(zipファイル) »