(2015/11/01~)
2015/11/02

Delphi / C++Builder のエンバカデロを米 IDERA が買収へ

Publickey の記事はこちら。

さて、IDERA CEOからのメッセージもあった通り、Embarcadero が IDERA に買収・合併される事が正式に発表されました。

Delphi / C++Builder としては二度目の買収となります…「いやもっとあっただろう!」と仰るかもしれませんが、

これらはいずれも Borland あるいはその子会社であり、買収は Embaracadero と 今回の IDERA によるものの二回です。「開発元が変わるのは信頼できない!」と仰るのなら、私は Adobe Director の例を引き合いに出しましょう。Adobe Director は何度も親会社が変わっています。 「Adobe Director は親会社がよく変わるので信頼できない」なんて聞いたことがありませんが、誰か言ってました?海外ではよくある事なので僕は別に気にしていません。日本だと "損害保険ジャパン日本興亜" 等の例もありますしね。

そもそも「昔は~」とか「親会社が~」と前置きして語る人は間違いなく最近の Delphi / C++Builder を知らないので、一度エンバカさんのサイトを覗いてくればいいと思うの。

追伸: エンバカデロの藤井さん曰く「(開発ツールの) Embaracadero という名前は残ると思う」との事デス。

IBConsole の改修

まだやってるのですが、半分は IBX の調査です。一連の改修はその際に IBConsole をいじっていて見つかった問題を修正しているだけだったりします。

Firebird で IBX を使う

IBX で Firebird に接続しても大抵の事はできます。Firebird には Interbase 6 互換モードがありますので、動かない道理はないのです...ただ、ミドルウェアは別です。Interbase に特化した実装だと Firebird では動かなくなる事があります。

マトモに Firebird を扱えるようにするには IBX を修正する必要があります。とりあえず 3 + 1 ファイルをいじれば OK です。なお、ソースは XE8 のものですが、2009 以降ならどれも同じようなものです。

[IBX.IBSQL.pas]
function TIBXSQLVAR.GetCharsetSize: Integer;
begin
{
  case SQLVar.SQLSubtype and $FF of
    0, 1, 2, 10, 11, 12, 13, 14, 19, 21, 22, 39,
    45, 46, 47, 50, 51, 52, 53, 54, 55, 58 :  Result := 1;
    5, 6, 8, 44, 56, 57, 64 : Result := 2;
    3 :
    begin
      // System Tables incorrectly state they are in Unicode_Fss character set but they are not
      if SQLVar.RelName.StartsWith('RDB$') or
         (SQLVar.SqlLen mod 3 <> 0) then
        Result := 1
      else
        Result := 3;
    end;
    59 : Result := 4;
    else
      Result := 1;
  end;
}
  case SqlVar.SqlSubtype and $FF of
     5// SJIS_0208
     6// EUCJ_0208
     8// UNICODE_BE / UCS2BE (InterBase)
    44// KSC_5601
    56// BIG_5
    57// GB_2312
    64// UNICODE_LE / UCS2LE (InterBase)
    67// GBK (Firebird)
    68// CP943C (Firebird)
      result := 2;
    3// UNICODE_FSS
      begin
        // System Tables incorrectly state they are in Unicode_Fss character set but they are not
        if SqlVar.RelName.StartsWith('RDB$'or (SqlVar.SqlLen mod 3 <> 0then
          result := 1
        else
          result := 3;
      end;
     4// UTF8 (Firebird)
    59// UTF8 / UTF_8 (InterBase)
    69// GB18030 (Firebird)
      result := 4;
  else
    result := 1;
  end;
end;

最初の修正点はコレです。UTF8 をマトモに使えるようにします。InterBase と Firebird では UTF8 の Charset ID が異なるので、

このような問題が発生します。

対応表を見れば一目瞭然ですが、IBX を InterBase と Firebird で両対応しようとしてもできない文字コードがあります。

こればかりは仕方ないので、上記の修正案では InterBase 用にしてあります。

[IBX.IBDatabase.pas]
procedure BuildDPBConstants;
begin

  ...

  CodePages.Add('DOS775'775);      {do not localize} // 15
  CodePages.Add('DOS858'858);      {do not localize} // 16
  CodePages.Add('DOS862'862);      {do not localize} // 17
  CodePages.Add('DOS864'864);      {do not localize} // 18
  CodePages.Add('ISO8859_2'28592); {do not localize} // 22
  CodePages.Add('ISO8859_8'1255);  {do not localize} // 38
  CodePages.Add('DOS866'866);      {do not localize} // 48
  CodePages.Add('DOS869'1255);     {do not localize} // 49
  CodePages.Add('BIG_5'950);       {do not localize} // 56
  CodePages.Add('WIN1255'1255);    {do not localize} // 58
  CodePages.Add('WIN1256'1256);    {do not localize} // 59
  CodePages.Add('WIN1257'1257);    {do not localize} // 60
  CodePages.Add('KOI8R'20866);     {do not localize} // 63
  CodePages.Add('WIN1258'1258);    {do not localize} // 65
  CodePages.Add('GBK'936);         {do not localize} // 67
  CodePages.Add('TIS620'874);      {do not localize} // 66
  CodePages.Add('CP943C'932);      {do not localize} // 68
  CodePages.Add('GB18030'54936);   {do not localize} // 69
end;

次の修正では、文字コード変換用のコードページを追加しています。これを追加しておかないと Firebird で追加された文字コードが変換されません。

[IBX.IBExtract.pas]
function TIBExtract.ExtractDDL(Flag: Boolean; TableName: String) : Boolean;
begin

  ...

  if TableName <> '' then
  begin
    if not ExtractListTable(TableName, '', true) then
      Result := false;
  end
  else
  begin
    ListCreateDb;
//    ListEUAUsers;
//    ListEncryptions;
//    if ConnectAsOwner then
//      BuildConnectString;
    ListFilters;
    ListFunctions;
    ListDomains;
    ListAllTables(flag);
    ListIndex;
    ListForeign;
    ListGenerators;
    ListSubscriptions;
    ListViews;
    ListCheck;
    ListException;
    ListProcs;
    ListTriggers;
    ListGrants;
  end;

  ...

end;

多分、あまり必要のない修正です。IBExtract を使う際に上記箇所をコメントアウトしておかないと非互換性のためにエラーで止まります。

最後の +1 の修正は IBX の高速化です。気持ち程度のものなので過度な期待はしないでください。

オブジェクトインスペクタのようなもの

IBConsole のオプション設定にはオブジェクトインスペクタのようなものがありますが、これは TStringGrid / Tpanel / TComboBox で構成されています。

これをどうしても置き換えたかったのです。というのもコレ、オブジェクトインスペクタ的な動作をさせるために数百行使っており、中にはユニットの大半をオブジェクトインスペクタ的な動作をさせるためのコードが占めているものもあります。すべてのユニットにロジックをベタ書きって...ムダじゃね?

さてこれを代替するには何が適当であるのかを考えてみる事にしました。まずは JvInspector です。JvInspector は JVCL に含まれるコンポーネントで、まるっきりオブジェクトインスペクタです。

TEdit と TJvInspector を貼り、

  JvInspector1.AddComponent(Edit1, '', True);

と書くだけで Edit1 のプロパティを持ってきます。実行時に JvInspector1 で Text プロパティをいじると Edit1 の内容も変化します。まぁ、当たり前と言えば当たり前ですが、オブジェクトインスペクタや JvInspector は RTTI をビジュアル化したものなのです。

では、環境設定のようなものに JvInspector を使うにはどうすればいいのでしょう?これも簡単で、クラスを作ればいいのです。

type
  TDatabaseDialect = (Dialect1, Dialect2, Dialect3);
  TPageBuffers = 0..131000;
  TSweepInterval = 0..200000;
  TYesNo = (Yes, No);

  TDatabaseProp = class
  private
    FDialect: TDatabaseDialect;
    FForcedWrites: TYesNo;
    FReadOnly: TYesNo;
    FPageBuffers: TPageBuffers;
    FSweepInterval: TSweepInterval;
  published
    property Dialect: TDatabaseDialect read FDialect write FDialect;
    property ForcedWrites: TYesNo read FForcedWrites write FForcedWrites;
    property PageBuffers: TPageBuffers read FPageBuffers write FPageBuffers;
    property ReadOnly: TYesNo read FReadOnly write FReadOnly;
    property SweepInterval: TSweepInterval read FSweepInterval write FSweepInterval;
  end;

クラスと言ってもプロパティを published に配置しただけのものです。

type
  TForm1 = class(TForm)
    ...
  private
    { Private 宣言 }
    Prop: TDatabaseProp;
  public
    { Public 宣言 }
  end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  Prop := TDatabaseProp.Create;
  JvInspector1.AddComponent(Prop, '', True);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Prop.Free;
end;

このようにしてやれば...

環境設定として使えます。でも、この方法にはちょっと問題があります。常にプロパティ名でソートされてしまうのです。(表示されている) プロパティ名自体は DisplayName で変更できるのですが、日本語化したとしても常に (元々の) プロパティ名で並んでしまいます。A01_PROP1 みたいにしてプレフィクスに連番を付ければ並びはしますが...。

別の方法として、複数の変数を登録する方法があり、こちらだと順番の問題を回避できます。

uses
  ..., TypInfo;

type
  TDatabaseDialect = (Dialect1, Dialect2, Dialect3);
  TPageBuffers = 0..131000;
  TSweepInterval = 0..200000;
  TYesNo = (Yes, No);

  TForm1 = class(TForm)
    ...
  private
    { Private 宣言 }
    FDialect: TDatabaseDialect;
    FForcedWrites: TYesNo;
    FReadOnly: TYesNo;
    FPageBuffers: TPageBuffers;
    FSweepInterval: TSweepInterval;
  public
    { Public 宣言 }
  end;


procedure TForm1.FormCreate(Sender: TObject);
  { Inspector_AddItem BEGIN }
  procedure Inspector_AddItem(Inspector: TJvInspector; AName, ADisplayName: string; ATypeInfo: PTypeInfo; Address: Pointer; ATag: Integer);
  begin
    with TJvInspectorVarData.New(Inspector.Root, AName, ATypeInfo, Address) do
      begin
        DisplayName := ADisplayName;
        Tag := ATag;
      end;
  end;
  { Inspector_AddItem END }
begin
  with JvInspector1 do
    begin
      Root.SortKind := iskNone;
      Clear;
    end;
  Inspector_AddItem(JvInspector1, 'ForceWrite'   , '強制書き込み'            , TypeInfo(TYesNo)          , @FForcedWrites , 0);
  Inspector_AddItem(JvInspector1, 'SweepInterval''スイープ間隔'            , TypeInfo(TSweepInterval)  , @FSweepInterval, 1);
  Inspector_AddItem(JvInspector1, 'Dialect'      , 'データベースダイアレクト', TypeInfo(TDatabaseDialect), @FDialect      , 2);
  Inspector_AddItem(JvInspector1, 'ReadOnly'     , '読み取り専用'            , TypeInfo(TYesNo)          , @FReadOnly     , 3);
  Inspector_AddItem(JvInspector1, 'PageBuffers'  , 'ページバッファ'          , TypeInfo(TPageBuffers)    , @FPageBuffers  , 4);
end;

実行結果はこうなります。

ではこの線で進めてみましょう。フォントと色をどうにかしたいですね。これには Inspector 用 Painter を使うのですが、TJvInspectorBorlandPainter を使うと旧 IDE の配色になってしまうので、TJvInspectorDotNETPainter を使います。ただ、フォントをいくつも設定しなくてはならないので面倒です。なので、動的作成します。

  TForm1 = class(TForm)
    ...
  private
    ...
    InspectorPainter: TJvInspectorDotNETPainter;
  public
    { Public 宣言 }
  end;


procedure TForm1.FormCreate(Sender: TObject);

  ...

  InspectorPainter := TJvInspectorDotNETPainter.Create(Self);
  with InspectorPainter do
    begin
      JvInspector1.Painter := InspectorPainter;
      CategoryFont.Assign(JvInspector1.Font);
      HideSelectFont.Assign(JvInspector1.Font);
      NameFont.Assign(JvInspector1.Font);
      SelectedFont.Assign(JvInspector1.Font);
      ValueFont.Assign(JvInspector1.Font);
      ValueFont.Color := clNavy;
      SelectedColor := $00E0E0E0;
    end;
end;

このような記述にすれば、JvInspector1 に指定したフォントと同じサイズで描画されます。JvInspector1 のフォントをメイリオ (10pt) に、ItemHeight を 22 にして実行してみます。

ほーら、それっぽくなった。ここまでやったら左側のアクティブインジケータを付けたいですね。イベント名では想像しにくいですが、オーナードローは Painter の OnSetItemColors で行う事ができます。

procedure TForm1.InspectorPainter_SetItemColors(
  Item: TJvCustomInspectorItem; Canvas: TCanvas);
var
  PreNameRect: TRect;
  s: string;
begin
  if (Item = JvInspector1.Selected) then
    begin
      if (not Item.Expanded) and (not Item.HasViewableItems) then
        begin
          Canvas.Font.Assign(InspectorPainter.SelectedFont);
          Canvas.Font.Style := [fsBold];
          PreNameRect := Item.Rects[iprItem];
          PreNameRect.Left := PreNameRect.Left + 6;
          PreNameRect.Top  := PreNameRect.Top;
          s := #$00BB;
          Canvas.TextRect(PreNameRect, s, [tfLeft, tfSingleLine, tfVerticalCenter]);
        end;
    end;
end;

こんな感じでしょうかね?

あら、奥さんバッチリじゃないですかー!スイープ間隔等に範囲外の値を入力された時のエラー表示までやった全コードが以下になります。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, JvComponentBase,  JvInspector, JvExControls, TypInfo;

type
  TDatabaseDialect = (Dialect1, Dialect2, Dialect3);
  TPageBuffers = 0..131000;
  TSweepInterval = 0..200000;
  TYesNo = (Yes, No);

  TForm1 = class(TForm)
    JvInspector1: TJvInspector;
    procedure FormCreate(Sender: TObject);
    procedure InspectorPainter_SetItemColors(
      Item: TJvCustomInspectorItem; Canvas: TCanvas);
    procedure JvInspector1ItemValueError(Sender: TObject;
      Item: TJvCustomInspectorItem; ExceptObject: Exception);
  private
    { Private 宣言 }
    InspectorPainter: TJvInspectorDotNETPainter;
    FDialect: TDatabaseDialect;
    FForcedWrites: TYesNo;
    FReadOnly: TYesNo;
    FPageBuffers: TPageBuffers;
    FSweepInterval: TSweepInterval;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
  { Inspector_AddItem BEGIN }
  procedure Inspector_AddItem(Inspector: TJvInspector; AName, ADisplayName: string; ATypeInfo: PTypeInfo; Address: Pointer; ATag: Integer);
  begin
    with TJvInspectorVarData.New(Inspector.Root, AName, ATypeInfo, Address) do
      begin
        DisplayName := ADisplayName;
        Tag := ATag;
      end;
  end;
  { Inspector_AddItem END }
begin
  with JvInspector1 do
    begin
      Root.SortKind := iskNone;
      Clear;
    end;
  Inspector_AddItem(JvInspector1, 'ForceWrite'   , '強制書き込み'            , TypeInfo(TYesNo)          , @FForcedWrites , 0);
  Inspector_AddItem(JvInspector1, 'SweepInterval''スイープ間隔'            , TypeInfo(TSweepInterval)  , @FSweepInterval, 1);
  Inspector_AddItem(JvInspector1, 'Dialect'      , 'データベースダイアレクト', TypeInfo(TDatabaseDialect), @FDialect      , 2);
  Inspector_AddItem(JvInspector1, 'ReadOnly'     , '読み取り専用'            , TypeInfo(TYesNo)          , @FReadOnly     , 3);
  Inspector_AddItem(JvInspector1, 'PageBuffers'  , 'ページバッファ'          , TypeInfo(TPageBuffers)    , @FPageBuffers  , 4);

  InspectorPainter := TJvInspectorDotNETPainter.Create(Self);
  with TJvInspectorDotNETPainter.Create(Self) do
    begin
      JvInspector1.Painter := InspectorPainter;
      CategoryFont.Assign(JvInspector1.Font);
      HideSelectFont.Assign(JvInspector1.Font);
      NameFont.Assign(JvInspector1.Font);
      SelectedFont.Assign(JvInspector1.Font);
      ValueFont.Assign(JvInspector1.Font);
      ValueFont.Color := clNavy;
      SelectedColor := $00E0E0E0;
      OnSetItemColors := InspectorPainter_SetItemColors;
    end;
end;

procedure TForm1.JvInspector1ItemValueError(Sender: TObject;
  Item: TJvCustomInspectorItem; ExceptObject: Exception);
begin
  ShowMessage(ITem.DisplayName + ' の値が不正です');
end;

procedure TForm1.InspectorPainter_SetItemColors(
  Item: TJvCustomInspectorItem; Canvas: TCanvas);
var
  PreNameRect: TRect;
  s: string;
begin
  if (Item = JvInspector1.Selected) then
    begin
      if (not Item.Expanded) and (not Item.HasViewableItems) then
        begin
          Canvas.Font.Assign(InspectorPainter.SelectedFont);
          Canvas.Font.Style := [fsBold];
          PreNameRect := Item.Rects[iprItem];
          PreNameRect.Left := PreNameRect.Left + 6;
          PreNameRect.Top  := PreNameRect.Top;
          s := #$00BB;
          Canvas.TextRect(PreNameRect, s, [tfLeft, tfSingleLine, tfVerticalCenter]);
        end;
    end;
end;
end.

フォームに貼ってあるのは TJvInspector だけです。

続・オブジェクトインスペクタのようなもの

で、IBConsole に JvInspector を使うのはやめました...不採用です。前のトピックで書いたでしょう?「まずは」って (^^;A

不採用の理由は "ローカライズできないから" です。JvInspector は RTTI のビジュアライザなので、オブジェクトの published プロパティを列挙するのには向いています。ですが、クラスを使うとソートされちゃうし、名前はともかく値をローカライズできません

技術者だけが使うようなツールだったらそれでもいいかもしれませんが、IBConsole のプロパティのような用途には向いていないと思います。それに、ローカライズできたとしてもコード量は逆に増えてしまう気がします。本来の目的はリファクタリングなので、コードが長くなってしまっては本末転倒なのです。

で、結局何を使ったかと言うと TValueListEditor です。「結局それかい!」なんですが、何故最初にこちらをやらなかったかと言うと、

とか、アクティブ行の色を変えたり、名前と値でフォントの色を変えたいとかあったからです。

最終的にはなんとかなったんですけどね。3列ある [パラメータの入力] ウィンドウはどうしようか悩みました。

TValueListEditor で無理矢理3列にするのはそんなに難しくはないんですが、やった結果が思ったようにならないんですねぇ...これが。仕方がないのでこちらは TStringGrid 単体でやりました (継承クラスは作りました)。StringGrid でコンボボックスを使う方法のサンプルは以下にあります。

TStringList を TValueListEditor に似せるのはちょっとだけ面倒でしたけど、それなりにうまくできたようです。

そして...

一方、本家 IBConsole では表形式のオプション設定をやめた。

ちょっと待てやコラ!! (゚Д゚)


2015/11/03

IBLite (Windows 版) の残念な仕様

XE7 から Windows 版も無料で使えるようになった IBLite。

100MB までという制限はあるものの、それなりに使い勝手がいいと思っていました...が、かなり残念な仕様がある事が判明しました。接続が1 セッションというのは知っていましたが、まさか全アプリでとは思いませんでした。

簡単に言うと A と B という別のアプリでそれぞれ "別の" IBLite を使っていた場合でも、A が起動中だと B は使えないのです。同じ DB を開こうとした場合じゃありませんよ?別の DB をそれぞれのアプリで開くこともできないのです...なにこのク○仕様

誰かが作ったアプリとバッティングする可能性があるので、実質 Windows では IBLite は使えません。使えるには使えますが、IBLite 使用アプリは排他利用という事になります。何でこんな謎仕様を (伝統的に) 盛り込むのでしょう。IBLite って組み込み向けじゃないの?全く意味が解りません。

...という事で、Windows で組み込みに使うのなら Firebird Embedded か、InterBase To-Go (有償) または SQLite を使いましょう...InterBase の構造を知るのには使えますけど、そんな使い方をするのはごく少数だろうなぁ。


2015/11/26

RAD Studio 10 Seattle Update 1

10 Seattle の Update 1 がリリースされました。但し、アップデートサブスクリプション加入者専用です。

スプラッシュがちょっと変わりました。

StringGrid のバグが解消されています。

[緊急開催] 第31回 エンバカデロ・デベロッパーキャンプ

来月 10 日に緊急開催です。

See Also:

Delphi Advent Calendar 2015

年末恒例の Advent Calendar です。

Advent Calendar とは

技術系のアドベント・カレンダーとは、12/1 ~ 12/25 日まで、1日1人が各々のブログやウェブページに、技術やテクニックを紹介するイベントです。
もちろん、ここは Delphi の Advent Calendar ですので、VCL や FireMonkey の使い方や、IDE の小技とか、Object Pascal にまつわる話など、Delphi に関わる話なら何でも構いません!

基本的には1日1人が担当しますが、人数が満たない場合は、1人が複数の日にちを担当しても構いません。

Qiita が予約投稿に対応したようなので、当日お忙しい方でも事前に記事を登録して参加する事ができます!

See Also:

IBConsole の改修

未だにチマチマとやっています。スプラッシュやアイコンも変わっています。

See Also:

Strawberry Linux Da Vinci 32U

とある目的のために仕入れました。

まぁ、頑丈なUSB フットスイッチを作りたかっただけなのですが (w

ペダルスイッチと LED を接続しただけという、電子工作と呼ぶにはおこがましい程の初歩的なものですが、かなり実用性があります。

この USB フットスイッチは Windows 10 の仮想デスクトップ切り替えに使っています。大量にファイルやアプリを開いているとタスクバーだけでは管理しにくいので、仮想デスクトップにファイルやアプリを振り分ける事で作業効率を上げています。SNS アプリ (やそれを開いているブラウザ) を別のデスクトップにまとめて置いておくと断捨離ができて仕事中に気が散る事も少なくなります。

See Also:

Visual Studio Code

Microsoft 謹製、無料で使えるコードエディタ。ベータ版に到達したとかで、そこそこ使えるようです。プラグインで Object Pascal にも対応します。

Object Pascal 用のプラグイン (Omni Pascal) を導入する手順は以下の通り。

  1. Visual Studio Code を起動
  2. [Open View | Command Palette...]
  3. コマンドパレットに "ext install Omni Pascal" と入力
  4. 出てきた候補の右下にあるインストールボタンを押してインストール
  5. インストールが完了すると通知メッセージが表示されるのでそれまで待つ
  6. Visual Studio Code を再起動
  7. [File | Preferences | User Settings]
  8. 右側のエディタ (settings.json) の {} の中に以下の二行を追加する。
        "objectpascal.delphiInstallationPath": "(Delphi のインストールパス)",
        "objectpascal.searchPath": "(Delphi のソースパス)"


    パス区切り文字は \\ のようにエスケープする事。

もちろん Unicode 対応で、Shift_JIS 等の ANSI にも対応しているので、ロシア語やドイツ語で書かれた Delphi のソースコードを文字化けさせる事無く UTF-8 に保存したりもできます。

See Also:


 BACK   古いのを読む   新しいのを読む