ざ つ だ ん 。 (12/09/01~)
12/09/01
・
EULA Change: No Client/Server in XE3 Pro. Not even 3rd Party. の顛末 (その1)
このサイトは XE3 のリーク情報を載せているブログで、かいつまんで話すと、
Professional 版ではリモート接続できない
XE3 でEULA が変更になった
Professional 版ではADO 接続とかのリモート接続もダメ
XE2 までのユーザは今まで通りだけど、XE3 からは CLIENT/SERVER PACK を購入するか、Enterprise 以上の SKU でないと DB へリモート接続不可
...てな、話が書いてあるエントリがありました。これが騒ぎになって、
公式フォーラム で喧々諤々となりましたとさ。
元々のブログを読んでみると、"EULA 変更"と言っている根拠 (元ソース) となっているのは、とある BBS の "Changes in the Delphi EULA" というトピックのようでした (フォーラムの方は二次以降のソースなので、あまり読む気にもなりませんでした)。
"Changes in the Delphi EULA" を読んで思った事は、
という事でした。そこで、
EMBT: 「XE3 の Partner DVD 締め切るよー」
投稿者: 「ウチの DBX ドライバヨロー」
EMBT: 「...ってお前、Pro では DBX のリモート接続できんよ。」
投稿者: 「(゚Д゚)ハァ?」
EMBT: 「XE2 のでいいから EULA 読んでみ?」
投稿者: 「...DBX の一部なんか含んでないし!」
EMBT: 「一部含んでるだろ、DBX4 のデリゲートドライバなんだから。」
投稿者: 「そんなんワカンネーよ!客に今更何て言えばいいんだよ!」
EMBT: 「(...確認しろよ)わかったわかった、既に販売中の XE2 までのドライバは黙認すっから。でも XE3 からは絶対ダメだからね。」
投稿者: 「EMBT が EULA 変えやがった!」
ブログ主: 「EMBT が EULA 変えやがった!...って書いてあるぞー!みんなー!」
「大体、こんな感じの話じゃないの?」と僕は推測しました。僕の twitter のツイート はこの推測が元になっています。どんなツイートをしたかと言うと...
@ht_deko んー、これって "Pro 版の DBX ライセンス" の件が (DBX4 ドライバ作ってる) サードパーティに通達されたという事でしょうかね?実際の EULA 読んでみない事にはワカラナイですけど。
@ht_deko Pro 版に DBX4 ドライバを同梱しないという制限はグレード的にアリだと思うけど、例えば DBX4 ドライバを自作したとしてもリモート接続不可ってのはちょっと理不尽だと思った... 4 年前に。
@ht_deko クラサバパックの事に言及しているという事は DBX4 の話だろうから、"Pro 版の DBX ライセンス" の件を知っていたユーザには何の変更もないって事じゃなかろうか?もしそうなら「何を今更」感があるなぁ。実際の EULA 読んでみない事にはワカラナイけど。
@ht_deko ブログの内容もなんとなく伝聞形ですし、ADO のくだりがありますが CreateObject() 等で ADO 接続する事をどうやって禁止できるというのでしょうね?「拡大解釈してないか?」とちょっと思っちゃいます。
では、「何故このような推測をしたのか?」ですが、3 年前の出来事が頭にあった からです。
知らないヒトのためにちょっと解説すると、RAD Studio 2010 の時に Firebird の DBX ドライバが追加されました。RTM の数週間前から 「2010 では DBX による Firebird サポートが行われる」 と公式に発表がなされていましたが、"Professional 版はリモート接続禁止はおろかドライバすら提供されない" という事実は製品発売時点で初めて判明しました。FTer だった僕も A7M さんもこの事を知りませんでした。
ぶっちゃけ、FTer であろうと "RTM 前に (ちゃんとした) EULA なんか読めない" って事です。SKU 毎の違いなんかも RTM 前には知り得ません 。つまり、"EULA 自体は変わってないけど解釈に齟齬が生じた" としか思えなかったのです。
もちろん、8/28 が RTM 当日だった (その日に EULA を読む事が出来た) 可能性もありますけど、もしそうだったとしても Partner DVD の構成で大混乱が起きるハズですから、"土壇場で EULA が変更された事を通知する" なんて事はやらないハズです。普通に考えれば、相当前に EULA 変更通知を 3rd Party に行うでしょうしね。
・
EULA Change: No Client/Server in XE3 Pro. Not even 3rd Party. の顛末 (その2)
公式フォーラム はその後しばらく盛り上がりを見せます..."荒れた" とも言いますが。
そしてフォーラムに新しいスレッド が立ちます。そして、ここに XE3 の EULA の一部が投下されました。
ADDITIONAL LICENSE TERMS APPLICABLE TO STARTER EDITION
In the event Licensee has obtained a Starter Edition license the following terms hereby apply. Licensee may use the Product to develop software (i) for which Licensee does not charge directly or indirectly a fee or receive other consideration including but not limited a license fee, a service fee, a development fee, a consulting fee, a subscription fee, a support fee, a hosting fee or the like and (ii) for which Licensee only charges fees (including but not limited a license fee, a service fee, a development fee, a consulting fee, a subscription fee, a support fee, a hosting fee or the like) that cumulatively, on an annual basis, do not exceed USD$1000. In the event any (for profit or non-profit) company elects to license the software then (i) the total company revenues may not exceed USD$1000 and (ii) the total number of Starter Edition licenses deployed may not exceed 5.
ADDITIONAL LICENSE TERMS APPLICABLE TO SOFTWARE LICENSED FOR EDUCATIONAL USE
In the event Licensee has obtained an educational license the following terms apply. Licensee may exercise Licensee's rights under this Agreement to use the Product and to create Works solely for Licensee's own personal use in providing or receiving instruction within the limited scope of guided computer programming and/or software training courses in which Licensee are a direct and personal participant, either as student or instructor ("Courses"). Licensee may only reproduce, distribute and use Works, in source or object code form, to other participants of the Courses and then only for educational or training purposes. Licensee may not use the Products or Works created with the products for any commercial, business, governmental or institutional purpose of any kind, except to the extent Licensee are an instructor teaching a Course. All rights not specifically granted to Licensee herein are retained by Licensor.
ADDITIONAL LICENSE TERMS APPLICABLE TO RAD STUDIO, DELPHI AND C++BUILDER, PROFESSIONAL AND PROFESSIONAL ACADEMIC EDITIONS
In the event Licensee has obtained a RAD Studio, Delphi or C++Builder Professional, or Professional Academic product license then the following terms apply.
Subject to the terms and conditions of this Agreement, Licensor grants to Licensee as the licensed user of the Product the limited right to use that portion of the Product identified as "dbExpress", in executable form only, to access a local database installed on the same machine as the Work. Licensee may not use that portion of the Product identified as "dbExpress" in association with a database located on a different machine other than the machine on which the Works are installed.
CLIENT/SERVER PACK
If licensee has purchased a Client/Server Pack, the Licensee of RAD Studio,
Delphi, or C++Builder XE3 Professional Edition ("Product") may deploy that portion of the Product identified as "dbExpress" and dbExpress
enterprise database drivers, in executable form only, to enable client server database access. Embarcadero may deliver the Product identified
as "Enterprise," however Licensee is licensed to use only the "Professional"
edition features plus "dbExpress" and the Enterprise dbExpress database drivers
in a client/server configuration. Licensee may evaluate the n-Tier DataSnap
functionality included in the Enterprise Product delivered, but may not deploy
or redistribute DataSnap.
読むと解りますが、これは XE2 の EULA に "CLIENT/SERVER PACK" 用の条文が追加されただけです。で、またもやツイートしました。
@ht_deko 最終版の前は XE2 の EULA にクラサバパック関連の条文を追加したものだったけれど "dbExpress" という一文が抜けていた箇所があった...ってトコなのでわ?
@ht_deko 前ツイートの通りだったにしても、クラサバパックが DBX4 関連製品だというのをちゃんと理解していれば勘違いも起きるハズはないと思いますが...一連の騒動の件はあのブログを書いた奴がアホウです。反省しやがれ。
@ht_deko どのみち (多分) NDA 違反なのだから、EULA 全文をブログにアップすればよかったのに (w そしたら、「お前、バカじゃね?」って誰かがツッコミ入れて終わってた話だと思うんですよねー。
結論: XE3 の EULA は XE2 と変わらない。
教訓: 明確な元ソースがない場合は話半分で聞け。
12/09/02
・
Delphi VCL Tips
忙しさにかまけてアナウンスしていませんでしたが、ひとつ追加されています。
Delphi-ML の質問 "[delphi-users:2599] dbfファイルを読み込む" に対する回答です。
・
Delphi FireMonkey Tips
忙しさにかまけてアナウンスしていませんでしたが、幾つか追加されています。
サンプルソースコード を見る限りでは、XE3 の FM2 だとちょっと違うトコもあるようですが。
・
Delphi XE3 および C++Builder XE3 の新機能 (DocWiki)
公開されたようですので、ちょっとじっくり読んでみたいと思います。
関連して上記二つも。EDN に XE3 の EULA (End User License Agreement) もアップされています
12/09/03
・
XE3 の DocWiki を読んで判明した事、再確認できた事。
とりあえずつらつらと (だいたい) 原典付きで。
個人的に気になる点は大体こんなトコ。C++Builder の変更点は少ない ので、第22回デブキャン【T2】セッション で語られていた事は実現できていないようだ...64bit コンパイラも含めて。
・
FireMonkey 2 (XE3) についての感想
XE2 の FireMonkey で足りないと感じていた機能がほぼ網羅された。来年初に発売予定の Microsoft Surface (Pro) を含むスレート PC プログラミングには最適な開発環境になったかもしれない。てか、それをウリにすればいいのに。てか、スレート PC バンドル製品出せばいいのに。ONKYO TW3A-A31 シリーズ とかオススメ。
タッチおよびジェスチャと仮想キーボードが実装された事により、スレート PC プログラミングが実用的なものになった。オーディオが実装された事により、Flash みたいな事ができるようになった。ビデオとセンサー&ロケーションが実装された事により、AR (仮想現実) が比較的簡単に実現できるようになった。スバラシイ!
・
iOS 開発がオミットされた件 (XE3) についての感想
このサイトでは以前から 「現時点では iOS に注力すべきではない」 とか、「C++Builder には FPC に相当するコンパイラが存在しない」 とか書いてたしなぁ...。"ARM コンパイラを自社開発している" というアナウンスがあったので、このツールチェインによる手法は将来的には無くなる方向にある事は目に見えていたし。まさか XE3 で早々にツールチェインできなくなるとは思わなかったけれど。
個人的にはスマホやタブレット開発は別製品とまではいかなくともある程度分離すべきだと思う。デスクトップアプリをスマホアプリにしたりその逆をやって失敗したのが Windows Mobile ですよね?操作性が異なるものを無理にまとめるべきではないと思う。マルチプラットフォームのフレームワークに大きな足枷が付くだけだし...かと言って、Smart Mobile Studio のようなものでお茶を濁すのも勘弁してください。コレでお茶を濁すようならツールチェインの方がまだマシです。
これまた個人的には 「FireMonkey もあっていいけど "VCL for iOS" のようなネイティブ開発環境がいいなぁ」 と思っております。
・
Metropolis UI アプリケーションのライブタイル (XE3) についての感想
これが有用かどうかは置いといて、面白い技術だと思う。ただ、疑問点がない訳ではない。
"Metroスタイル・アプリの開発者が知るべき3つのこと(@IT)" にあるように、"Modern UI (Metro) アプリ" は Windows Store で配布しなくてはならないが、問題は Embarcadero.Metropolis.LiveTileProxy.exe がどうやって配布されるのか? という事だ。
Embarcadero.Metropolis.LiveTileProxy.exe は Embarcadero が Windows Store にアップすれば済む話なのか?それとも個別にアップしなくてはならないのか?Windows Store を経由しない場合、サイドローディングを行わなくてはならないが、その場合には Windows 8 Enterprise が必要 となってしまう。この辺りがどうなっているのかが記されたドキュメントは未だ存在しないようだ。
追記: サイドローディングの方法に関しては "Metropolis UI 用ライブ タイルの開発環境のセットアップ" に記述があった...んーちょっと面倒かな (^^;A
・
SkRegExp 1.3.2 リリース
小宮さん作の正規表現ライブラリ SkRegExp の version 1.3.2 がリリースされました。
・
RAD STUDIO XE3 トライアル版 リリース
XE3 のトライアル版がリリースされました。
当方ではトライアル版をインストールした際、「上限に達した」旨のメッセージが出て "機能制限付き 14 日トライアル" になってしまいましたので、カスタマーサポート から上限を更新してもらいました。
・
XE3 製品情報
XE3 の製品情報が公開されました。
SQLite は Professional 版で (ローカル接続) 利用可能なようです。BDE 代替の手段が増えました! (^o^)/
・
Delphi VCL Tips
XE3 に合わせて更新しました。
EULA の件の日本語訳が載っています。
・
Delphi FireMonkey Tips
XE3 に合わせて更新しました。
コンポーネントは地道に増えてますね。
12/09/04
・
第 23 回エンバカデロデベロッパーキャンプ 開催!
UStream によるライブ配信 も行われます...午前の部のライブ配信がないのが少々残念ですが。感想は後程。
・
XE3 ショップリンク
XE3 を購入できるショップへのリンクです。恒例ですね。
特別バージョンアップ (2009~XE2 ユーザー向け) 2012/09/27 まで
Delphi XE3 Professional ESD
¥44,625 (税込)
¥47,250 (税込)
¥47,250 (税込)
¥47,250 (税込)
Delphi XE3 Enterprise ESD
¥139,230 (税込)
¥147,420 (税込)
¥147,420 (税込)
¥147,420 (税込)
Delphi XE3 Ultimate ESD
¥210,630 (税込)
¥223,200 (税込)
¥223,020 (税込)
¥223,020 (税込)
Delphi XE3 Architect ESD
¥246,330 (税込)
¥260,820 (税込)
¥260,820 (税込)
¥260,820 (税込)
C++Builder XE3 Professional ESD
¥44,625 (税込)
¥47,250 (税込)
¥47,250 (税込)
¥47,250 (税込)
C++Builder XE3 Enterprise ESD
¥139,230 (税込)
¥147,420 (税込)
¥147,420 (税込)
¥14,742 (税込)
C++Builder XE3 Ultimate ESD
¥210,630 (税込)
¥223,020 (税込)
¥223,020 (税込)
¥223,020 (税込)
C++Builder XE3 Architect ESD
¥246,330 (税込)
¥260,820 (税込)
¥260,820 (税込)
¥260,820 (税込)
RAD Studio XE3 Professional ESD
¥74,970 (税込)
¥79,380 (税込)
¥79,380 (税込)
¥79,380 (税込)
RAD Studio XE3 Enterprise ESD
¥192,780 (税込)
¥204,120 (税込)
¥204,120 (税込)
¥204,120 (税込)
RAD Studio XE3 Ultimate ESD
¥264,180 (税込)
¥279,720 (税込)
¥279,720 (税込)
¥279,720 (税込)
RAD Studio XE3 Architect ESD
¥299,880 (税込)
¥317,520 (税込)
¥317,520 (税込)
¥317,520 (税込)
RAD XE3 Bonus Pack を貰えます。
2012/10/31 迄にユーザ登録をすると DVDインストールメディア を貰えます。
特別バージョンアップの期間を過ぎると、バージョンアップ対象は 2010~XE2 ユーザとなります。正確には 2009 ユーザがバージョンアップ版を購入できるのは 2012/12/31 迄 となります。
ジャンプアップキャンペーン (1~2007 ユーザー及び Starter Edition ユーザー向け) 2012/12/26 まで
Delphi XE3 Professional ESD
¥78,960 (税込)
¥78,960 (税込)
¥83,895 (税込)
¥78,960 (税込)
Delphi XE3 Enterprise ESD
¥198,240 (税込)
¥198,240 (税込)
¥210,630 (税込)
¥198,240 (税込)
Delphi XE3 Ultimate ESD
¥299,040 (税込)
¥299,040 (税込)
¥317,730 (税込)
¥299,040 (税込)
Delphi XE3 Architect ESD
¥349,440 (税込)
¥349,440 (税込)
¥371,280 (税込)
¥349,440 (税込)
C++Builder XE3 Professional ESD
¥78,960 (税込)
¥78,960 (税込)
¥83,895 (税込)
¥78,960 (税込)
C++Builder XE3 Enterprise ESD
¥198,240 (税込)
¥198,240 (税込)
¥210,630 (税込)
¥198,240 (税込)
C++Builder XE3 Ultimate ESD
¥299,040 (税込)
¥299,040 (税込)
¥317,730 (税込)
¥299,040 (税込)
C++Builder XE3 Architect ESD
¥349,440 (税込)
¥349,440 (税込)
¥371,280 (税込)
¥349,440 (税込)
RAD Studio XE3 Professional ESD
¥124,320 (税込)
¥124,320 (税込)
¥132,090 (税込)
¥124,320 (税込)
RAD Studio XE3 Enterprise ESD
¥282,240 (税込)
¥282,240 (税込)
¥299,880 (税込)
¥282,240 (税込)
RAD Studio XE3 Ultimate ESD
¥283,040 (税込)
¥383,040 (税込)
¥406,980 (税込)
¥383,040 (税込)
RAD Studio XE3 Architect ESD
¥433,440 (税込)
¥433,440 (税込)
¥460,530 (税込)
¥433,440 (税込)
※価格にポイント等は反映されていません。また、価格は変動する事がありますのでリンク先を必ず確かめるようにして下さい。
以前に掲載していた OSJ オンライン さんは残念ながら 9/18 で閉店だそうです。
・
Delphi の製品情報
Delphi XE3 情報を更新しました。
12/09/05
・
RAD Studio XE3 での旧製品ライセンス
XE 以降恒例ですが、XE3 を購入すると旧製品を利用する事ができます (Starter / アカデミック除く)。
Delphi XE2 / XE / 2010 / 2009 / 2007 R2 / 7
C++Builder XE2 / XE / 2010 / 2009 / 2007 R2 / 6
Prism XE2 / XE / 2011 / 2010 / 2009
RadPHP XE2 / XE
しばらく Delphi 単体製品の購入が続いた方には RAD Studio がお買い得だと思います。
・
エンバカデロ (標準) 製品価格表
RAD Studio XE3 を含む エンバカデロの製品価格表 が更新されました。実は以前から Delphi の製品情報 の左ペインに "Price List" があったのですけど...。製品価格表を読んでいて思ったのですが、Archtect 版の "インストールメディア (DVD) 付き製品" はなくなったのですね。Ultimate 同様 ESD のみのようです。
製品価格表には載っていませんが、XE3 の EULA に Client/Server Pack が明記されているという事はそのうち XE3 用の Client/Server Pack が発売されるのでしょうか?てか、最初から商品ラインナップにあればいいのになぁ。
12/09/06
・
RAD Studio XE3 サポート KB より
トレンドマイクロェ...。
また、XE3 のインストーラが何時まで経っても終わらないと思ったらタスクバーを確認してみて下さい。ヘルプ (Document Explorer) セットアップが裏に回っている事があります。
・
XE3 のトライアル版が「登録の上限」と言われて製品登録できない場合には?
事前に EDN にログインしておくといいと思います。EDN アカウント作成、編集、ログイン方法については "ディスカッションフォーラムの使い方" を参考にしてみて下さい。
まず、製品登録(使用許諾)ファイルの回復 を試してみて下さい。これでうまく行かなかった場合 (例えば XE3 トライアルの登録コードの番号が 0 になっているとか) には以下を試してみて下さい。
ご質問フォーム (https://support.embarcadero.com/forms/ ) に行きます。
入力欄を埋めます。
[ご質問の送信] ボタンを押します。
メールで上限更新の通知が送られてきたら、再度製品登録を行ってみてください。
・
Embarcadero のデモの神様について トゥギャってみた。
やりとりが面白かったのでトゥギャってみました。
...なんか林さんが罠にはめられた感がありますね。登場人物でマトモなのは DH 山本さんだけってのが、また。・゜・(/Д`)・゜・。
余談ですが、アテナの杖は "ニケの杖" で、アテナの随神であるニケの変化だとか。こちら で右手の上に乗ってるのがニケ神。ナイキとも言いますね ...有名ですが、あの Nike のロゴはニケの翼が由来です。
12/09/07
・
DocWiki 内カスタム検索
Embarcadero 公式フォーラムでイロイロありまして 、"Embarcadero RAD Studio DocWiki 内カスタム検索" を作ってみました。
例えば、RAD Studio 2010 の検索 を利用すると、XE 以降で追加された新しい関数やメソッドはヒットしなくなります。"その時点での機能" や "バージョン間の差異" を調べる事ができるという訳です。ヘルプが DocWiki ベースになったのは 2010 からですから、2009 以前の検索はできません。2007 は 付属のオンラインヘルプ (MS-Help2 / Help Update 4 相当) の元になった HTML が公開されている ので、そこを検索対象としています (DocWiki ベースではありません)。
「DocWiki メインページ の左側から各バージョンへ行けるじゃん?」 と言われそうですね...確かに左側の "以前のバージョン" から各バージョンのトピックには行けますし、リンクを辿る分には問題ありません (VCL を辿ると最新版かも...)。ですが、左上の検索を使った時点で最新版 RAD Studio のリファレンスになってしまうのです ...大抵は最新版でも問題はないのですが。
過去に作ったカスタム検索はこちらとなります。
イロイロな語句での検索を試してみてください。ブラウザのブックマークに入れておくと便利かもしれません。
12/09/08
・
AnsiStrings.AnsiPos が不正な値を返す (XE のみ)
何故なのかよく解りませんが...
uses
..., AnsiStrings;
var
Sub, Src: AnsiString;
begin
// Case 1:
Sub := #$82 #$A4 ; // Japanese Hiragana 'u'
Src := #$82 #$A0 #$82 #$A2 #$82 #$A4 #$82 #$A6 #$82 #$A8 ; // Japanese Hiragana 'a' 'i' 'u' 'e' 'o'
ShowMessage(IntToStr(AnsiStrings.AnsiPos(Sub, Src)));
// Case 2:
Sub := AnsiString('う' ); // Japanese Hiragana 'u'
Src := AnsiString('あいうえお' ); // Japanese Hiragana 'a' 'i' 'u' 'e' 'o'
ShowMessage(IntToStr(AnsiStrings.AnsiPos(Sub, Src)));
// Case 3:
Sub := #$95 #$5C ; // Japanese 'Hyou'
Src := #$8C #$76 #$8F #$57 #$95 #$5C ; // Japanese 'SyuuKeiHyou'
ShowMessage(IntToStr(AnsiStrings.AnsiPos(Sub, Src)));
// Case 4:
Sub := AnsiString('表' ); // Japanese 'Hyou'
Src := AnsiString('集計表' ); // Japanese 'SyuuKeiHyou'
ShowMessage(IntToStr(AnsiStrings.AnsiPos(Sub, Src)));
end ;
このコードは Case 1 と Case 2、Case 3 と Case 4 は同じ結果になるハズです。コントロール文字列で記述しているか、文字定数で記述しているかの違いしかありません。AnsiString な AnsiPos() なので、結果はバイト位置で返ります。すべて 5 が返ってくるハズです。
...が、実際には
Case 1: 5
Case 2: 7
Case 3: 5
Case 4: 0
このような結果になります。XE のみで発生し、2010 以前や XE2 以降では発生しません。
XE で AnsiString の処理をされる場合にはご注意を。修正は望み薄だなぁ...MECSUtils に実装しようかなぁ。
12/09/09
・
続・AnsiStrings.AnsiPos が不正な値を返す (XE のみ)
RTL をざっと眺めてみたけれど不審な点は見当たらない。イロイロやってて気付いたのは、以下のコードならうまくいくという事だった。
uses
..., AnsiStrings;
var
Sub, Src: AnsiString;
uSub, uSrc: UnicodeString;
begin
// Case 2:
uSub := 'う' ; // Japanese Hiragana 'u'
uSrc := 'あいうえお' ; // Japanese Hiragana 'a' 'i' 'u' 'e' 'o'
Sub := AnsiString(uSub);
Src := AnsiString(uSrc);
ShowMessage(IntToStr(AnsiStrings.AnsiPos(Sub, Src)));
// Case 4:
uSub := '表' ; // Japanese 'Hyou'
uSrc := '集計表' ; // Japanese 'SyuuKeiHyou'
Sub := AnsiString(uSub);
Src := AnsiString(uSrc);
ShowMessage(IntToStr(AnsiStrings.AnsiPos(Sub, Src)));
end ;
いよいよ訳がわかんねぇ...。XE の何かがおかしくなってる (自分の環境だけで起きる現象) としか思えないので、再インストールを試みるとしよう。なお、Windows のロケールがおかしくなっているというオチでない事は確認済デス。
・
続々・AnsiStrings.AnsiPos が不正な値を返す (XE のみ)
あ゛ 。完全アンインストール後に再インストールしてテストしたら再現しないや。変な拡張とかライブラリ入れた覚えはないんだけど?とりあえず、再インストール直後に問題は出ないので QC は取り下げとこう。
少なくとも Delphi XE のバグではない事は証明されたけれど、コンポーネントやらライブラリやら突っ込んで元の環境に戻してどうなるかだな...って、再現しないし! 元と違いが無いと思える位には環境を構築してみたけど、ちゃんとすべて 5 を返す。しまった!変な値を返す奴のバイナリを保存しとけばよかったーーー!アレさえあれば何がおかしかったのかの判断材料になったかもしれないのに!!
...えーっと。お騒がせして申し訳ありませんでした m(_ _)m
12/09/10
・
Pos() と AnsiPos() と PosEx()
ある文字列から部分文字列を検索するには Pos() を使います。
var
Index: Integer;
Src: String ;
begin
Src := 'あいうえお' ;
Index := Pos('う' , Src);
ShowMessage(IntToStr(Index));
end ;
このコードを ANSI 版 Delphi で実行すると 5 が表示され、Unicode 版 Delphi で実行すると 3 が表示されます。Unicode 版 Delphi で Ansi 処理を行うために、
var
Index: Integer;
Src: AnsiString ;
begin
Src := 'あいうえお' ;
Index := Pos('う' , Src);
ShowMessage(IntToStr(Index));
end ;
このように書き換えてみました。ですが、結果はやっぱり 3 が表示されてしまいます。これは 'う' という文字列定数が UnicodeString と解釈され、Unicode 版のオーバーロード関数が呼ばれてしまうからです。
これを回避するには、
var
Index: Integer;
Src: AnsiString;
begin
Src := 'あいうえお' ;
Index := Pos(AnsiString( 'う' ) , Src);
ShowMessage(IntToStr(Index));
end ;
var
Index: Integer;
Src, Sub: AnsiString;
begin
Src := 'あいうえお' ;
Sub := 'う' ;
Index := Pos(Sub, Src);
ShowMessage(IntToStr(Index));
end ;
文字列定数をキャストするか、AnsiString の変数を渡すようにします。
Pos() の ANSI 版はマルチバイトを考慮しないため、いわゆる "0x5C 問題" が発生します。
var
Index: Integer;
Src, Sub: AnsiString;
begin
Sub := '\' ;
Src := '表1\Sheet1.xls' ;
Index := Pos(Sub, Src);
ShowMessage(IntToStr(Index));
end ;
このコードでは 4 が表示されるのを期待しますが、実行すると ANSI 版 / Unicode 版いずれの Delphi でも 2 が表示されます。'表' の文字は 0x955C なので、2 バイト目の 0x5C にヒットしてしまう のです。
これを回避するには、マルチバイトを考慮してくれる AnsiPos() を使います。
var
Index: Integer;
Src, Sub: AnsiString;
begin
Sub := '\' ;
Src := '表1\Sheet1.xls' ;
Index := AnsiPos( Sub, Src) ;
ShowMessage(IntToStr(Index));
end ;
ANSI 版 Delphi では期待通りに 4 が表示されます...が、Unicode 版 Delphi では 3 が表示されてしまいます。何故でしょうか?
実は Unicode 版 Delphi の AnsiString 用 AnsiPos() は SysUtils ではなく AnsiStrings に存在するのです。つまり、SysUtils にある UnicodeString 版の AnsiPos() が呼ばれてしまっている のです。正しくは以下のようなコードを書かなくてはなりません。
uses
..., AnsiStrings ;
var
Index: Integer;
Src, Sub: AnsiString;
begin
Sub := '\' ;
Src := '表1\Sheet1.xls' ;
Index := AnsiStrings. AnsiPos(Sub, Src);
ShowMessage(IntToStr(Index));
end ;
こうする事で確実に AnsiStrings にある AnsiString 版の AnsiPos() が呼ばれるようになります。
Pos() / AnsiPos() は一致した部分文字列の位置を返してくれますが、開始位置を指定して検索する事ができません。開始位置を指定するには StrUtils.PosEx() を使います (Delphi 7 以降で利用可能)。Unicode 版 Delphi の AnsiString 版は AnsiStrings.PosEx() です。
uses
..., AnsiStrings ;
var
Index: Integer;
Src, Sub: AnsiString;
begin
Sub := '\' ;
Src := 'C:\ABC\DEF\GHI.TXT' ;
Index := AnsiStrings.PosEx( Sub, Src, 4 ) ;
ShowMessage(IntToStr(Index));
end ;
4 文字 (バイト) 目から検索しますので、7 が表示されます。
...しかしながら AnsiPosEx() という関数はどこにも存在しない のですよ。
・
"ANSI 版 Delphi" と "Unicode 版 Delphi" と名前空間
Unicode では "0x5C問題" を始め、多くのマルチバイト関連の問題は起きないので、Unicode 版 Delphi で UnicodeString だけ扱っていれば Pos() を使おうが AnsiPos() を使おうが結果は同じになります。日本語があっても PosEx() も使えますしね。
ですが、ANSI 版 Delphi や、Unicode 版 Delphi で AnsiString を扱おうとすると、"Ansi~()" と名の付く関数を使う必要があったり、PosEx() が使えなかったりします。別の名前空間に同名関数があったりする のも厄介ですね。
Pos()
-
SysUtils.Pos()
SysUtils.Pos()
SysUtils.Pos()
AnsiPos()
-
SysUtils.AnsiPos()
SysUtils.AnsiPos()
AnsiStrings.AnsiPos()
PosEx()
-
StrUtils.PosEx()
StrUtils.PosEx()
AnsiStrings.PosEx()
AnsiPosEx()
-
-
-
-
ANSI 版 Delphi から Unicode 版 Delphi へマイグレーションし、AnsiString で扱っていたものを UnicodeString で扱うようにした場合にはとても楽に移行できる事が解りますね。Unicode 版 Delphi で AnsiString の処理をする場合は...uses し忘れによるバグとかで軽く死ねそうです。ANSI アプリケーションでいいのなら、素直に Delphi 2007 を使う事をオススメします。
・
MECSUtils ver1.55 リリース
だが、しかし!たまーに、AnsiString 用の "AnsiPosEx()" が欲しい事があるんじゃい! ...という動機で MECSUtils に AnsiPosEx() を実装しました。「AnsiPosEx() 欲しい」 という QC を入れて 4 年が経つ けど、流石にもう見込み薄なので。
MecsAnsiPosEx() 以外はその実装過程で作られた副産物です。未保証ですが、Delphi 7 でコンパイルできる事を確認しているので、PosEx() の使えない Delphi 6 とかでも MecsAnsiPosEx() が使えると思います。
・
TurboPower LockBox v2 for Delphi 2009-XE3, Win32 & OSX
Turbo Power シリーズの暗号化コンポーネント群。先月調査したのは SourceForge のもの だったけれど、それとは違う ver 2 の XE3 対応版 (OSX にも対応!) が CodeCentral にアップされていた。
Blowfish
DES/3DES
Rijndael (AES)
RSA
MD5
SHA-1
v3 よりはサポートされる種類が少ないようだ。LockBox v2 のマニュアルは http://sourceforge.net/projects/tplockbox/files/TurboPower LockBox/LockBox version 2/2.07/ から DL できる。
12/09/11
・
Delphi XE3 とセンサー&ロケーション
既報の通り Delphi XE3 には FireMonkey のツールパレットに [Sensors] カテゴリがあり 、センサー&ロケーションが使えるようになっている。
FireMonkey から TLocationSensor と TMotionSensor コンポーネントが使えるのだが、恐らくこれは Windows の場合には Windows 7 以降のセンサー &ロケーション API を使っている。
先のデブキャン で David.I が 「GeoSense を使えば物理的な GPS がなくともロケーションを取得できる」 と言っていたから間違いないだろう。
裏を返せば、PC に物理的なセンサーが搭載されていても、Windows 7 対応のドライバが存在しない...例えば特定のアプリケーションからのみ利用される専用ドライバを使っているとか、シリアルポートで通信するようなセンサーは利用できないという事になる。
・
Geosense
Windows 7 以降用 GPS 仮想ドライバ。
GPS を物理的に搭載していなくとも、Wi-Fi 等を使っておおまかな位置情報を取得できる。32bit / 64bit 版があるので、どちらかをインストールし、コントロールパネルの [位置センサーとその他のセンサー] でセンサーを有効にする事で利用可能となる。
例えば 64bit OS に64bit ドライバをインストールすれば、32bit / 64bit アプリケーションから Geosense を利用できる。
・
センサーのサンプルプロジェクト (Delphi XE3)
サンプルプロジェクトはサンプルフォルダの "FireMonkey\LocationDemo" にある (最新のサンプルソースコードの取得方法はこちら )。Geosense を有効にした状態でこのサンプルを動作させてみた。
センサーマネージャのチェックボックスを ON にし、センサーをアクティブにするチェックボックスを ON にすると、このように位置 (緯度/経度) 情報が正しく取得できた。そこで実際のソースコードを眺めてみることに。センサーマネージャからセンサーをリストして選択して有効にしてるのか...ちょっと面倒臭いなぁ...ん?
僕はここで "ある事" に気付き、フォームを確認してみた。
キタ━━━━(゚∀゚)━━━━!!!!
先程つぶやいていたデータを取得する方法は、まさにセンサー&ロケーション API でデータを得る手順そのものであり、このフォームには TLocationSensor が貼り付けられていない。つまりこのサンプルは FMX ではなく RTL を使ったセンサーのサンプル という事になる。じゃ、ソースコード中で使われている TSensorManager のユニットスコープは何なんだ...
System.Sensors キタ━━━━(゚∀゚)━━━━!!!!
ユニットスコープが "System" で始まるという事は、VCL からも FMX からも利用可能 な事を意味する...もうお解かりだろうが XE3 では VCL からもセンサーが使える という事に他ならない。
FMX のソースを VCL 用に書き直してみる。System.Sensors を uses し、チェックボックスの IsChecked を Checked にする程度でいいハズだ。
キタ━━━━(゚∀゚)━━━━!!!!
...という事で、結論として Delphi XE3 ではセンサー&ロケーションは FireMonkey だけではなく VCL からも使えます。 DocWiki が追い付いてないようなのでドキュメントがありませんが、使うのはそんなに難しくありません...って、アレ?TLocationSensor と TMotionSensor はどうやって使うんだ? (^^;A
・
センサーのサンプルプロジェクト with Google Map (Delphi XE3)
緯度経度が取れるという事は、Google Map でその位置へジャンプできるという事だ。
var
URL: string ;
begin
URL := Format('https://maps.google.co.jp/maps?q=%s,%s&z=14&hl=ja&output=embed' ,
[etLatitude.Text, etLongitude.Text]);
WebBrowser1.Navigate2(URL);
end ;
q=緯度 ,経度
z=ズーム
hl=言語
output=表示モード
先程の VCL のサンプルに TWebBrowser とボタンを貼って、Google Map を表示してみたのがコレ。
あまり意味はないのかもしれないが、なかなか面白い。
12/09/12
・
既報 (その1): Delphi XE3 のフォームデザイナは埋め込みデザイナのみ
Delphi XE3 は[ツール | オプション] から ▶環境オプション▶VCL デザイナ を見ても "埋め込みデザイナ" が見当たらない。
但し、機能が死んでいる訳ではなく UI が存在しないだけのようだ。IDE 起動前に以下のレジストリを追加すれば埋め込みデザイナはオフ...つまり、フローティングデザイナになる。
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\10.0\Form Design]
"Embedded Designer"="False"
元 (埋め込みデザイナ) に戻すにはこちら。
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\10.0\Form Design]
"Embedded Designer"="True"
もちろん、これをやったとて FireMonkey でフローティングデザイナが使える訳ではない。フローティングデザイナが使えるのは VCL のみ。
・
既報 (その2): Professional 版で SQLite が使えない
...いや、本来ライセンス的には使えるのです。インストーラのミスだか何のミスだか解りませんが、"動かない" という意味で使えないようです。
BDE 難民救済手段となり得るのですから、早めに使えるようにして欲しいトコロですね。ちなみに CodeCentral には D6 / D7 用の SQLite DBX ドライバ があります。他のバージョンでは動かない可能性が大ですけどね。
・
既報 (その3): IDE の起動が遅い
RAN さんのトコ によると違法コピー対策との事。激遅なのは PC 起動後の初回起動時だけな感じはしますが...。
素人考えかもしれませんが、Windows のスタートアップに "XE3 のすべての DLL をロードしてアンロードするだけのプログラム" を仕込んで事前に実行させるようにすれば、いざ XE3 を使うという時になった際の起動速度が幾分稼げる気がします。
・
XE3 DLL Preloader
書いてみた。
program xe3preload;
{$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}
{$WEAKLINKRTTI ON}
uses
System.Masks,
System.SysUtils,
System.IOUtils,
Winapi.Windows;
{$R *.res}
var
FileName: String ;
DLLWnd: THandle;
begin
System.NoErrMsg := True;
for FileName in TDirectory.GetFiles('C:\Program Files\Embarcadero\RAD Studio\10.0' ,
'*.dll' ,
TSearchOption.soAllDirectories) do
begin
if MatchesMask(ExtractFileName(FileName), 'dbkw64*.dll' ) then
Continue;
if MatchesMask(ExtractFileName(FileName), 'dcc64*.dll' ) then
Continue;
try
DLLWnd := LoadLibrary(PWideChar(FileName));
if DLLWnd > 0 then
FreeLibrary(DLLWnd);
except
end ;
end ;
end .
短いのでソース公開。気休め程度には XE3 の起動時間を短縮できるかもしれない。但し、理屈上 "PC 起動後、初回の XE3 起動" しか早くならない。また、プリローダ起動後に数多くのアプリケーションを起動して XE3 を起動すると効果が望めない事がある (OS のキャッシュを期待したプリローダだから)。2 回目以降の起動が遅いのは...どうしようもないかも。
XE3 のパスは 64bit OS だと "Program Files (x86)" な事に注意!で、細川さんが修正を加えた のがこちら 。
・
第23回 エンバカデロ・デベロッパーキャンプ - セッション資料ダウンロード
公開された模様です。じっくり確認したいと思います。togetter もありますよ。
最近、デブキャン公式まとめ (デベロッパーキャンプ・アーカイブ) が出来たので、あまり有難味がないですが、"88.とりあえず、デベロッパーキャンプの資料を読んでみようか。(Delphi VCL Tips)" も更新されています。
・
XE3 DLL Preloader を実測してみた
とりあえず効果がワカンナイので実測してみる事にした。PC 起動後 3 分間 PC が落ち着くまで待ち、初回の XE3 起動にかかる時間を計測する事にした。
結果: 有意な差はみられない。
細川さんのプリローダを使ったのだが、dwFlags が LOAD_LIBRARY_AS_DATAFILE であれ 0 であれ差はなかった。プリローダで有意差がないとすると、"本当に DLL チェックに時間が掛かっている" のかもしれない。他にやれそうな事はと言えば [コントロールパネル] から "インデックスのオプション" で XE3 のフォルダをインデックス対象にする位か...これまた気休めだろうけど。
XE3 に限らず、XE2 や XE も二回目の起動の方が高速な事を考えると、"BDS.exe /preload" みたいなオプション (サイレントで起動してすぐに終了する) があれば、スタートアップに登録することもできて便利なのかもしれない。
12/09/13
・
XE3 Preloader - 「RAD Studio XE3 の IDE 起動時間をどうにかしたい!」 をトゥギャってみた。
その後の経緯です。
「OS の (何らかの) キャッシュ機構に作用する事を狙う」 という考え方はどれも同じですが、やはり bds.exe そのものを実際に走らせると効果絶大なようです。
その代わり、当然ながら bds.exe をプリロードする場合にはプリロード時に時間が掛かります。@onimaro2010 (aka au2010 ) さんの事前照合方式だと、プリロード時間は短いですが、初回 IDE 起動速度は 2 回目以降の起動速度よりもやや遅くなります。この辺りはトレードオフなので、ご自分の環境に合わせてお好きな方を使うといいと思います。
・
ちょっといじった XE3 Preloader
大した修正ではないのですが、細川さんの XE3 Preloader をちょっといじってサイレント動作可能なようにしてみました。そのままコンパイルすればサイレントなウィンドウアプリケーションになり、Xe3Preloader.dpr の {$DEFINE VERBOSE} のコメントを取ってコンパイルすれば細川さんのと同じ動作 (コンソールアプリケーション) になります。
program Xe3Preloader;
{$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}
{$WEAKLINKRTTI ON}
// {$DEFINE VERBOSE} // <- ここのコメントを外せばコンソールアプリになります
{$IFDEF VERBOSE}
{$APPTYPE CONSOLE}
{$ELSE}
{$R *.res}
{$ENDIF}
uses
Winapi.Windows,
Winapi.Messages,
...
ウィンドウアプリケーションの場合、メッセージは OutputDebugString() で吐かれますので、イザという時は外部デバッガからも追えます。アーカイブにはバイナリが 2 種類 (コンソール/ウィンドウアプリ) 収録されています。もちろんソースコード付きです。
・
ちょっといじった XE3 DLL Preloader (with WinVerify Trust)
大した修正ではないのですが、@onimaro2010 さんの XE3 DLL Preloader をちょっといじってサイレント動作可能なようにしてみました。そのままコンパイルすればサイレントなウィンドウアプリケーションになり、Xe3Preloader.dpr の {$DEFINE VERBOSE} のコメントを取ってコンパイルすれば @onimaro2010 さんのと同じ動作 (コンソールアプリケーション) になります。
program Xe3dllPreloader;
{$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}
{$WEAKLINKRTTI ON}
// {$DEFINE VERBOSE} // <- ここのコメントを外せばコンソールアプリになります
{$IFDEF VERBOSE}
{$APPTYPE CONSOLE}
{$ELSE}
{$R *.res}
{$ENDIF}
uses
System.SysUtils,
System.IOUtils,
...
ウィンドウアプリケーションの場合、メッセージは OutputDebugString() で吐かれますので、イザという時は外部デバッガからも追えます。アーカイブにはバイナリが 2 種類 (コンソール/ウィンドウアプリ) 収録されています。もちろんソースコード付きです。XE3 Preloader と exe 名がカブるので、こちらは "xe3dll preloader.exe" に改名してあります。
・
さらに改良された XE3 Preloader (by @pik)
細川さんが "できるだけオリジナルの環境に影響を与えないように bds.exe をプリロードする XE3 Preloader" を公開されました。
仕様...というか、理屈的なものは togetter にて twitter でのやりとりを確認して下さい。プリロード専用のレジストリを用いて起動するため、プリロード中に XE3 を起動する事があってもオリジナル環境に悪影響を及ぼす事がありません。
bds.exe に /rpreload スイッチが追加されていますので、初回のプリローダ処理時間は長いです。これは "preload" というレジストリエントリが初回実行時に作られるためです。次回以降のプリローダ実行時間は従来と変わらなくなります。初回起動は手動でコマンドライン版 (バーボーズ版) を走らせて動作チェックするといいのかもしれませんね。
12/09/14
・
FireMonkey で仮想キーボードを有効にするには? (XE3)
FMX.Platform を uses し、VKAutoShowMode 変数の値を設定します。
uses
..., FMX.Platform;
procedure TForm1.FormCreate(Sender: TObject);
begin
VKAutoShowMode := vkasAlways;
end ;
vkasDefinedBySystem システム設定に依る
vkasNever 仮想キーボードを表示しない
vkasAlways 仮想キーボードを常に表示する
表示する仮想キーボードの種類は入力コントロールの KeyboardType プロパティで指定します。
vktDefault 標準キーボード
vktNumbersAndPunctuation 数字と記号のキーボード
vktNumberPad テンキーパッド
vktPhonePad 電話用キーボード
...って、
思いっきり iPhone 準拠じゃねーか!
∧_∧
⊂(#・д・)
/ ノ∪
し―-J |l| |
人ペシッ!!
__
\ \
 ̄ ̄
Windows だとシステム標準の "スクリーン キーボード" が出てくるので、仮想キーボードの種類の指定は無意味です。位置もサイズ指定できない (そりゃ、やろうと思えばできるでしょうが) のでイマイチ使い勝手がよくないと思います。
XE3 では iOS サポートがなくなっているので、この機能は微妙ですね。FireMonkey なのだから、システム非依存の...それこそ VCL の TTouchKeyboard みたいなのが欲しいトコロです。理由については実際にスレート PC で "スクリーン キーボード" を使ってみると解ると思います。それでも "出ないよりはマシ" ですけど。
・
Platform 変数 (XE3)
あり?XE3 では Platform 変数 (FMX.Platform.Platform) なくなったのか?
てか、TPlatform 自体が存在しないような。"XE3 向けに行われた FireMonkey の変更点 (DocWiki)" に廃止されたものも書いておいて欲しいなぁ。FMX.Forms.TScreen (そして Screen 変数) にはディスプレイサイズを特定できるようなプロパティはないので、Platform 変数がなくなるとマズい気がする。XE2 には Platform.GetScreenSize() があった のに...?
前のトピックに関連して。XE2 には Platform.ShowVirtualKeyboard() と Platform.HideVirtualKeyboard() というメソッドがあったのですが...いや、もちろん XE2 では動作しなかったのですが、一度も陽の目を見る事なく消えてしまいました。
...本当に TPlatform はないのか?XE2 の TPlatform (DocWiki) を見てもらえば解るけど、GetTick() も GetClipboard() / SetClipboard() も使えない事になっちゃうけど?
・
SkRegExp version 1.4 へ
SkRegExp 新版の投入が予定されているようです。
・
TPlatformServices と TPlatformService (XE3)
ぬう...そういう事か。細川さんのツイート と "And the prize for ugliest API goes to… (Delphi Haven)" を読んで理解した。
例えば Clipboard を使うには、
// 現在のクリップボードの内容を Edit1 に表示
var
ClipboardService: IFMXClipboardService;
begin
ClipboardService := IFMXClipboardService(TPlatformServices.Current.GetPlatformService(IFMXClipboardService));
Edit1.Text := ClipboardService.GetClipboard.AsString;
end ;
// Edit1 の内容をクリップボードに入れる
var
ClipboardService: IFMXClipboardService;
begin
ClipboardService := IFMXClipboardService(TPlatformServices.Current.GetPlatformService(IFMXClipboardService));
ClipboardService.SetClipboard(Edit1.Text);
end ;
こうなる、と。確かにコード記述の冗長っぽさがちょっとヤだな。
Delphi Haven の記事には解決方法も書いてあって 、高度なレコード型でクラスメソッド作っとけば (ユニットはこちら )、
unit uPlatformService;
interface
uses
FMX.Platform;
type
TPlatformService = record
class function Available<IntfType: IInterface>(out Service: IntfType): Boolean; static ;
class function Get<IntfType: IInterface>: IntfType; static ;
end ;
implementation
uses
System.TypInfo;
class function TPlatformService.Available<IntfType>(out Service: IntfType): Boolean;
var
Guid: TGUID;
begin
Guid := PTypeInfo(TypeInfo(IntfType)).TypeData.Guid;
Result := TPlatformServices.Current.SupportsPlatformService(Guid, IInterface(Service));
end ;
class function TPlatformService.Get<IntfType>: IntfType;
var
Guid: TGUID;
begin
Guid := PTypeInfo(TypeInfo(IntfType)).TypeData.Guid;
Result := IntfType(TPlatformServices.Current.GetPlatformService(Guid));
end ;
end .
こんな感じに書けたり、
uses
..., uPlatformService;
// 現在のクリップボードの内容を Edit1 に表示
var
ClipboardService: IFMXClipboardService;
begin
ClipboardService := TPlatformService.Get<IFMXClipboardService>;
Edit1.Text := ClipboardService.GetClipboard.AsString;
end ;
// Edit1 の内容をクリップボードに入れる
var
ClipboardService: IFMXClipboardService;
begin
ClipboardService := TPlatformService.Get<IFMXClipboardService>;
ClipboardService.SetClipboard(Edit1.Text);
end ;
変数なしで記述してもそんなに長くならない
// 現在のクリップボードの内容を Edit1 に表示
Edit1.Text := TPlatformService.Get<IFMXClipboardService>.GetClipboard.AsString;
// Edit1 の内容をクリップボードに入れる
TPlatformService.Get<IFMXClipboardService>.SetClipboard(Edit1.Text);
...という事らしいです。FMX.Platform にあるのは TPlatformServices で、高度なレコード型の方は TPlatformService です。最初、記事 読んでて混乱しちゃいました (^^;A
・
グローバル変数 (XE3)
従来、非推奨だったグローバル変数 が XE3 では "削除されていて" 使えなくなっています。TFormatSettings の使い方に関しては、ナイスタイミング (?) で山本隆さんが記事を書いてらっしゃいます。
GJ! (^-^)b
12/09/15
・
OutputDebugString() をトレースする
何でもいいので Windows 用のデバッガを用意すれば、大抵のデバッガは OutputDebugString() で吐かれた文字列を表示する事ができます。
OutputDebugString() をトレースするだけなら、インストール不要で使える DebugView がお手軽です。
画像は 細川さんの XE3 Preloader を実行して確認してみたトコロです (ウィンドウアプリケーション版を実行しています)。
DebugView は "管理者として実行" で実行し、
OS が 32bit の場合: [Capture | Capture Win32] にチェックが入っているか確認する。
OS が 64bit の場合: [Capture | Capture Global Win32] にもチェックが入っているか確認する。
キャプチャ対象のチェックを確認してから監視対象のアプリケーションを起動します。
アプリケーションはデバッグビルドである必要はありません。
32bit / 64bit アプリケーションいずれも取得できます。
Delphi IDE を含め、DebugView の他にデバッガが起動しているとキャプチャできない事があります。
リモートデスクトップ操作からだとキャプチャできないかもしれません。
お客さんトコにノート PC 持ち込めないとか、リモートデバッガすら利用できない時には役に立つかも知れません。転ばぬ先の OutputDebugString()
Delphi IDE 上での OutputDebugString() を使ったデバッグ手法に関しては 第21回 エンバカデロ・デベロッパーキャンプ【T2】「実践!Delphiデバッグテクニック」 を参照して下さい。
VIDEO
※ フルスクリーンで再生する事をオススメします。
12/09/16
・
TMS Smooth Control Pack (無償版) について
2009 / 2010 / XE では、TMS Software の TMS Smooth Control Pack が貰えました (XE2 用の "無償版 TMS Smooth Control Pack" は提供されませんでした)。
2009 の場合は期間限定ではありましたが、いずれもキャンペーン期間内にエンバカデロ製品を購入していなくとも、ダウンロードは可能でした。
これら TMS Smooth Control Pack は現在 CodeCentral から DL できません。
つまり、過去のプロジェクトに TMS Smooth Controls Pack を利用していた場合、プロジェクトを XE2 や XE3 へアップデートするには製品版を購入するしかなく、開発環境をフッ飛ばして再インストールするハメになった時でもアーカイブを所持していなければこれまた製品版を購入するしかありません。
但し、過去に CodeCentral から 無償版 TMS Smooth Control Pack をDL したのであれば、条件付ですが TMS Software のサイトからアップデータ (インストーラ) を DL する事ができます。
2009: DL 不可 (アップデータ提供終了)
2010: 製品登録から二年以内であれば、まだ DL 可能かも?
XE: 現時点では DL 可能
TMS の製品ライフサイクルは以下のようになっています (いずれか)。
Full version cycle (version X.Y to X+1.Y)
Maximum 2 years
私の場合、TMS Software のサイトからダウンロード可能だったのは XE 用だけでした。
・
TMS Smooth Control Pack (無償版) の DL について
TMS Software からのアップデータ入手方法は以下の通りです。
TMS Software へ行く。
左側の LOGIN からログインする
過去に CodeCentral から TMS Smooth Control Pack を DL していれば、登録コードが書かれたメールが送られてきている。もし、登録コードが判らないのなら、左下の Forgot code? を押し、
メールアドレスを入力して [Verify] ボタンを押せば、登録コードがメールで送られてくる。
DL 可能であれば、以下の画像のようになる。
Registered version のリンクからアップデータ (インストーラ) を DL する。
DL 不能であれば、製品名が表示されないか、以下の画像のようになる。
この例では登録日より 2 年を超過しているので expired になっている (アップデータ入手期間満了)。
必要な方はお早めに入手する事をオススメします。
12/09/17
・
Delphi FireMonkey Tips
地道に更新しております。
FM2 用の記述が追加されていたりします。
・
FireMonkey のフォーム (HD / 3D) でクリック / ダブルクリックイベントを検出してみる
"Fire Monkey 3D で作成した Shape を回転させるには? (Delphi Q&A)" のネタです。FireMonkey のフォームには OnClick / OnDblClick イベントがありません (XE2 の初版には OnKeyDown イベントすらありませんでした) が、これをどうにかしてみようという趣旨です。
FireMonkey HD / 3D 用の Click / DblClick を判定するユニットを用意してみました (FMX.BaseForm.pas)。
unit FMX.BaseForm;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
{$IFDEF MSWINDOWS}
Winapi.Windows,
{$ENDIF}
{$IFDEF MACOS}
MacAPI.Foundation, MacAPI.AppKit,
{$ENDIF}
FMX.Types, FMX.Forms;
type
{ TBaseFormHD }
TBaseFormHD = class (TForm)
procedure Timer_Timer(Sender: TObject);
private
{ private 宣言 }
FTimer: TTimer;
FStartPoint: TPointF;
FEndPoint: TPointF;
FIntervalOver: Boolean;
FFinished: Boolean;
FMouseUpCount: SmallInt;
FMouseClickEvent: TNotifyEvent;
FMouseDblClickEvent: TNotifyEvent;
function IsMouseOnControl(X, Y: Single): Boolean;
public
{ public 宣言 }
constructor Create(AOwner: TComponent); override ;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
published
{ published 宣言 }
property OnClick: TNotifyEvent read FMouseClickEvent write FMouseClickEvent;
property OnDblClick: TNotifyEvent read FMouseDblClickEvent write FMouseDblClickEvent;
end ;
{ TBaseForm3D }
TBaseForm3D = class (TForm3D)
procedure Timer_Timer(Sender: TObject);
private
{ private 宣言 }
FTimer: TTimer;
FStartPoint: TPointF;
FEndPoint: TPointF;
FIntervalOver: Boolean;
FFinished: Boolean;
FMouseUpCount: SmallInt;
FMouseClickEvent: TNotifyEvent;
FMouseDblClickEvent: TNotifyEvent;
function IsMouseOnControl(X, Y: Single): Boolean;
public
{ public 宣言 }
constructor Create(AOwner: TComponent); override ;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
published
{ published 宣言 }
property OnClick: TNotifyEvent read FMouseClickEvent write FMouseClickEvent;
property OnDblClick: TNotifyEvent read FMouseDblClickEvent write FMouseDblClickEvent;
end ;
const
CLICK_JUDGE_DIST = 4 ; // クリック判定用
DOUBLE_CLICK_TIME = 500 ; // ダブルクリック判定時間 (規定)
implementation
function CalcDist(a, b: TPointF): Single;
begin
result := Sqrt(Sqr(a.X - b.X) + Sqr(a.Y - b.Y));
end ;
{ TBaseFormHD }
constructor TBaseFormHD.Create(AOwner: TComponent);
begin
inherited ;
FMouseUpCount := 0 ;
FIntervalOver := False;
FFinished := False;
FTimer := TTimer.Create(Self);
FTimer.Enabled := False;
FTimer.Interval := DOUBLE_CLICK_TIME;
{$IFDEF MSWINDOWS}
FTimer.Interval := GetDoubleClickTime;
{$ENDIF}
{$IFDEF MACOS}
// OSX 用の処理
{$ENDIF}
FTimer.OnTimer := Timer_Timer;
end ;
procedure TBaseFormHD.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
try
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) and ((FMouseUpCount = 0 ) or FFinished) then
begin
FStartPoint := PointF(X, Y);
FIntervalOver := False;
FFinished := False;
if Assigned(FMouseDblClickEvent) then
FTimer.Enabled := True;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseFormHD.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
begin
try
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
FEndPoint := PointF(X, Y);
Inc(FMouseUpCount);
if not Assigned(FMouseDblClickEvent) then
FIntervalOver := True;
if FIntervalOver then
Timer_Timer(FTimer);
end ;
finally
inherited ;
end ;
end ;
function TBaseFormHD.IsMouseOnControl(X, Y: Single): Boolean;
begin
result := (IControl(ObjectAtPoint(ClientToScreen(PointF(X, Y)))) <> nil );
if result then
begin
FTimer.Enabled := False;
FMouseUpCount := 0 ;
end ;
end ;
procedure TBaseFormHD.Timer_Timer(Sender: TObject);
begin
FIntervalOver := True;
if FMouseUpCount = 0 then
Exit;
FTimer.Enabled := False;
if CalcDist(FStartPoint, FEndPoint) < CLICK_JUDGE_DIST then
begin
if (FMouseUpCount > 1 ) then
begin
if Assigned(FMouseDblClickEvent) then
OnDblClick(Self);
end
else
begin
if Assigned(FMouseClickEvent) then
OnClick(Self);
end ;
end ;
FFinished := True;
FMouseUpCount := 0 ;
end ;
{ TBaseForm3D }
constructor TBaseForm3D.Create(AOwner: TComponent);
begin
inherited ;
FMouseUpCount := 0 ;
FIntervalOver := False;
FFinished := False;
FTimer := TTimer.Create(Self);
FTimer.Enabled := False;
FTimer.Interval := DOUBLE_CLICK_TIME;
{$IFDEF MSWINDOWS}
FTimer.Interval := GetDoubleClickTime;
{$ENDIF}
{$IFDEF MACOS}
// OSX 用の処理
{$ENDIF}
FTimer.OnTimer := Timer_Timer;
end ;
procedure TBaseForm3D.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
begin
try
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) and ((FMouseUpCount = 0 ) or FFinished) then
begin
FStartPoint := PointF(X, Y);
FIntervalOver := False;
FFinished := False;
if Assigned(FMouseDblClickEvent) then
FTimer.Enabled := True;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseForm3D.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
begin
try
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
FEndPoint := PointF(X, Y);
Inc(FMouseUpCount);
if not Assigned(FMouseDblClickEvent) then
FIntervalOver := True;
if FIntervalOver then
Timer_Timer(FTimer);
end ;
finally
inherited ;
end ;
end ;
function TBaseForm3D.IsMouseOnControl(X, Y: Single): Boolean;
begin
result := (IControl(ObjectAtPoint(ClientToScreen(PointF(X, Y)))) <> nil );
if result then
begin
FTimer.Enabled := False;
FMouseUpCount := 0 ;
end ;
end ;
procedure TBaseForm3D.Timer_Timer(Sender: TObject);
begin
FIntervalOver := True;
if FMouseUpCount = 0 then
Exit;
FTimer.Enabled := False;
if CalcDist(FStartPoint, FEndPoint) < CLICK_JUDGE_DIST then
begin
if (FMouseUpCount > 1 ) then
begin
if Assigned(FMouseDblClickEvent) then
OnDblClick(Self);
end
else
begin
if Assigned(FMouseClickEvent) then
OnClick(Self);
end ;
end ;
FFinished := True;
FMouseUpCount := 0 ;
end ;
end .
FireMonkey HD / 3D 用の Click / DblClick を判定するユニットを用意してみました。
仕様は下記の通りです。
クリック / ダブルクリックは左マウスボタンを対象とする。
ダブルクリック時間は Windows の場合のみシステムから持ってくる (手抜)。それ以外の場合は規定で 500ms。
ダブルクリック時間以内に 2 回以上 MouseDown / MouseUp が繰り返されたら OnDblClick イベントが発生。
ダブルクリック時間以内に 1 回 MouseDown / MouseUp が行われたら OnClick イベントが発生。
MouseDown 時点のマウス位置と MouseUp 時点のマウス位置の二点間の距離が 4px 以上離れていればクリック / ダブルクリックとみなさない。
(フルスクリーンアプリケーションだとクリックをキャンセルする方法がないため)
MouseDown 状態のままダブルクリック時間が経過した後に MouseUp が行われたら MouseUp の時点で OnClick イベントを発生させる。
MouseUp がフォームの外で行われても正常動作するようにする。
使い方はこんな感じです。
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Platform, FMX.BaseForm ;
type
TForm1 = class (TBaseFormHD )
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Form1Click(Sender: TObject);
procedure Form1DblClick(Sender: TObject);
private
{ private 宣言 }
public
{ public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.fmx}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
OnClick := Form1Click; // クリックイベントをアサイン
OnDblClick := Form1DblClick; // ダブルクリックイベントをアサイン
end ;
procedure TForm1.Form1Click(Sender: TObject);
begin
ShowMessage('Clicked!' );
end ;
procedure TForm1.Form1DblClick(Sender: TObject);
begin
ShowMessage('Double Clicked!' );
end ;
end .
コードは FireMonkey HD 用なので TBaseFormHD を継承していますが、FireMonkey 3D を使うときは TBaseForm3D を継承して下さい。OS ネイティブのイベントを用いない "擬似クリック / ダブルクリック" イベントですが、それっぽい動きはすると思います。
BaseFormHD / BaseForm3D とかの名前でフォームを作る
FMX.BaseForm の内容をコピペ
フォームをプロジェクトに追加
継承して使う
...って使い方が楽かもしれません。バグがあったらゴメンナサイ。
12/09/18
・
ssDouble じゃいかんのか? (from 2ch)
...できれば twitter とかでツッコミ入れておくれよー。
さて。つまりは 「このようなコードでいいんちゃうんか?」 という事だと思います (或いは Delphi-ML Tips のコレ )。
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
if ssDouble in Shift then
ShowMessage('Double Clicked!' )
else
ShowMessage('Clicked!' );
end ;
しかしながらこれはうまく動作しません。MouseUp イベントでは Shift に ssDouble が入ってくることはない からです。ssDouble が検出できるのは MouseDown イベントの時です。
今度は MouseDown イベントに書いてみましょう。
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
if ssDouble in Shift then
ShowMessage('Double Clicked!' )
else
ShowMessage('Clicked!' );
end ;
これまたうまく動作しません。初回 MouseDown 時にもこのイベントが発生してしまいますので、ダブルクリックを判定できません。加えて言えば、クリック判定はマウスボタンを離した時 に行われますから、その意味でもマズい事になります。例えば VCL アプリケーションで TButton をマウス左ボタンで押下したまま TButton からマウスカーソルを外してマウスボタンを離す事でクリックをキャンセルできますが、MouseDown イベントでのみ検出しようとするとこのキャンセル動作を実現できません。
では、ダブルクリックの判定を MouseDown でやって、クリックを MouseUp で処理してみます。
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
if ssDouble in Shift then
ShowMessage('Double Clicked!' );
end ;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
ShowMessage('Clicked!' );
end ;
これもダメです。最初の MouseUp でクリック判定されてしまうのでダブルクリックを検出できません。
...つまり、クリックとダブルクリックの両方を判定する場合には "ダブルクリック時間を経過するまで待つ必要がある" という事になるのです。片方だけの場合なら検出は簡単ですし、「ダブルクリックの判定には ssDouble 使え」 というのは正しいと思います。
しかしながら、今回のような場合には ssDouble を判定に使おうが使うまいがコードの複雑さに変わりはないように思えるのですが...いかがでしょうか?私の思慮が足りないだけで、"実は ssDouble を使ってもっと簡単に記述できる方法がある" のでしたら教えて下さい m(_ _)m
・
ちなみに...
元ネタ にはこう書いてあって、
# VCL の挙動とは異なりますが、
# この後にダブルクリック判定を組み込むのが楽になります。
"FireMonkey のフォーム (HD / 3D) でクリック / ダブルクリックイベントを検出してみる" はその伏線 (?) を回収するための記事でした。
・
改定
ダブルクリックイベントハンドラが割り当てられていない場合にはダブルクリック時間を待たないように "FireMonkey のフォーム (HD / 3D) でクリック / ダブルクリックイベントを検出してみる" のコードを修正してみました。
・
改定 (その2)
おっと。フォームの MouseUp / MouseDown イベントはフォーム上に貼られたコントロールをクリックしても発生するのか。と言う訳で、マウスボタン押下位置にコントロールが存在する場合にはクリック/ダブルクリックイベントを発生させないようにコードを修正してみました。
なお、FireMonkey のフォームに OnClick イベントが存在しない件は QC に入っています。
Open になっているので将来のバージョンで実装されるのかもしれません。
12/09/19
・
FireMonkey のフォーム (HD / 3D) でクリック / ダブルクリックイベントを検出してみる (with GetTick)
タイマーを使わない "GetTick 版" のコードも貼っておきます。
unit FMX.BaseForm;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
{$IFDEF MSWINDOWS}
Winapi.Windows,
{$ENDIF}
{$IFDEF MACOS}
MacAPI.Foundation, MacAPI.AppKit,
{$ENDIF}
FMX.Types, FMX.Forms, FMX.Platform;
type
TClickType = (ctNone, ctClick, ctDblClick);
{ TBaseFormHD }
TBaseFormHD = class (TForm)
private
{ private 宣言 }
FDoubleClickTime: Extended;
FStartPoint: TPointF;
FStartTime: Extended;
FMouseUpCount: SmallInt;
FMouseClickEvent: TNotifyEvent;
FMouseDblClickEvent: TNotifyEvent;
function IsMouseOnControl(X, Y: Single): Boolean;
public
{ public 宣言 }
constructor Create(AOwner: TComponent); override ;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
published
{ published 宣言 }
property OnClick: TNotifyEvent read FMouseClickEvent write FMouseClickEvent;
property OnDblClick: TNotifyEvent read FMouseDblClickEvent write FMouseDblClickEvent;
end ;
{ TBaseForm3D }
TBaseForm3D = class (TForm3D)
private
{ private 宣言 }
FDoubleClickTime: Extended;
FStartPoint: TPointF;
FStartTime: Extended;
FMouseUpCount: SmallInt;
FMouseClickEvent: TNotifyEvent;
FMouseDblClickEvent: TNotifyEvent;
function IsMouseOnControl(X, Y: Single): Boolean;
public
{ public 宣言 }
constructor Create(AOwner: TComponent); override ;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
published
{ published 宣言 }
property OnClick: TNotifyEvent read FMouseClickEvent write FMouseClickEvent;
property OnDblClick: TNotifyEvent read FMouseDblClickEvent write FMouseDblClickEvent;
end ;
const
CLICK_JUDGE_DIST = 4 ; // クリック判定用
DOUBLE_CLICK_TIME = 500 ; // ダブルクリック判定時間 (規定)
implementation
function Get_Tick: Extended;
{$IF FireMonkeyVersion >= 17.0}
begin
result := IFMXTimerService(TPlatformServices.Current.GetPlatformService(IFMXTimerService)).GetTick;
end ;
{$ELSE}
begin
result := Platform.GetTick;
end ;
{$IFEND}
function CalcDist(a, b: TPointF): Single;
begin
result := Sqrt(Sqr(a.X - b.X) + Sqr(a.Y - b.Y));
end ;
{ TBaseFormHD }
constructor TBaseFormHD.Create(AOwner: TComponent);
begin
inherited ;
FMouseUpCount := 0 ;
FDoubleClickTime := DOUBLE_CLICK_TIME / 1000 ;
{$IFDEF MSWINDOWS}
FDoubleClickTime := GetDoubleClickTime / 1000 ;
{$ENDIF}
{$IFDEF MACOS}
// OSX 用の処理
{$ENDIF}
end ;
procedure TBaseFormHD.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
try
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if (FMouseUpCount = 0 ) then
begin
FStartPoint := PointF(X, Y);
FStartTime := Get_Tick;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseFormHD.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
var
ClickType: TClickType;
begin
try
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
Inc(FMouseUpCount);
ClickType := ctNone;
if Assigned(FMouseDblClickEvent) then
begin
if (FMouseUpCount > 1 ) then
ClickType := ctDblClick
else
begin
while ((Get_Tick - FStartTime) < FDoubleClickTime) do
begin
if (FMouseUpCount > 1 ) then
Exit;
Application.ProcessMessages;
end ;
if (FMouseUpCount = 1 ) then
ClickType := ctClick;
end ;
end
else
ClickType := ctClick;
if (ClickType <> ctNone) then
begin
if (CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST) then
case ClickType of
ctClick:
if Assigned(FMouseClickEvent) then
OnClick(Self);
ctDblClick:
if Assigned(FMouseDblClickEvent) then
OnDblClick(Self);
end ;
FMouseUpCount := 0 ;
end ;
end ;
finally
inherited ;
end ;
end ;
function TBaseFormHD.IsMouseOnControl(X, Y: Single): Boolean;
begin
result := (IControl(ObjectAtPoint(ClientToScreen(PointF(X, Y)))) <> nil );
if result then
FMouseUpCount := 0 ;
end ;
{ TBaseForm3D }
constructor TBaseForm3D.Create(AOwner: TComponent);
begin
inherited ;
FMouseUpCount := 0 ;
FDoubleClickTime := DOUBLE_CLICK_TIME / 1000 ;
{$IFDEF MSWINDOWS}
FDoubleClickTime := GetDoubleClickTime / 1000 ;
{$ENDIF}
{$IFDEF MACOS}
// OSX 用の処理
{$ENDIF}
end ;
procedure TBaseForm3D.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
try
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if (FMouseUpCount = 0 ) then
begin
FStartPoint := PointF(X, Y);
FStartTime := Get_Tick;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseForm3D.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
var
ClickType: TClickType;
begin
try
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
Inc(FMouseUpCount);
ClickType := ctNone;
if Assigned(FMouseDblClickEvent) then
begin
if (FMouseUpCount > 1 ) then
ClickType := ctDblClick
else
begin
while ((Get_Tick - FStartTime) < FDoubleClickTime) do
begin
if (FMouseUpCount > 1 ) then
Exit;
Application.ProcessMessages;
end ;
if (FMouseUpCount = 1 ) then
ClickType := ctClick;
end ;
end
else
ClickType := ctClick;
if (ClickType <> ctNone) then
begin
if (CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST) then
case ClickType of
ctClick:
if Assigned(FMouseClickEvent) then
OnClick(Self);
ctDblClick:
if Assigned(FMouseDblClickEvent) then
OnDblClick(Self);
end ;
FMouseUpCount := 0 ;
end ;
end ;
finally
inherited ;
end ;
end ;
function TBaseForm3D.IsMouseOnControl(X, Y: Single): Boolean;
begin
result := (IControl(ObjectAtPoint(ClientToScreen(PointF(X, Y)))) <> nil );
if result then
FMouseUpCount := 0 ;
end ;
end .
FireMonkey Tips の "経過時間を得るには?" にもあるように、GetTick() の使い方が XE2 / XE3 で異なる事に注意が必要です。コード量的には TTimer 版 と大差ないですね。
Windows / Mac OSX で動作する。
XE2 / XE3 で動作する。
OnDblClick イベントハンドラが割り当てられている場合:
ダブルクリックは 2 回目の MouseUp により最短で検出される。ダブルクリック時間終了を待たない。
シングルクリックはダブルクリック時間経過後に検出される。
OnDblClick イベントハンドラが割り当てられていない場合:
シングルクリックは初回の MouseUp により最短で検出される。ダブルクリック時間終了を待たない。
マウスカーソルがコントロールの上にある場合には OnClick / OnDblClick イベントは発生しない。
シングルクリックキャンセルが可能。
TTimer 版 も GetTick 版も仕様的には同じです。
12/09/20
・
さらに FireMonkey のフォーム (HD / 3D) でクリック / ダブルクリックイベントを検出してみる
先日までのコードではダブルクリックキャンセルが行えた。
Windows の標準動作的にはダブルクリックのキャンセルは存在しないし、ダブルクリックキャンセルを使う事はまずないだろう。それよりは少しでもダブルクリックのレスポンスを向上させるべきだと思い、ダブルクリック判定を MouseDown で行い、最適なタイミングで OnDblClick() イベントを起こすようにしてみた。折角なので ssDouble も使ってみた。
仕様変更。
Windows / Mac OSX で動作する。
XE2 / XE3 で動作する。
OnDblClick イベントハンドラが割り当てられている場合:
ダブルクリックは 2 回目の MouseDown により最短で検出される。ダブルクリック時間終了を待たない。
シングルクリックはダブルクリック時間経過後に検出される。
OnDblClick イベントハンドラが割り当てられていない場合:
シングルクリックは初回の MouseUp により最短で検出される。ダブルクリック時間終了を待たない。
OnClick / OnDblClick イベントハンドラがいずれも割り当てられていない場合:
マウスカーソルがコントロールの上にある場合には OnClick / OnDblClick イベントは発生しない。
シングルクリックキャンセルが可能 (4px 以上移動して MouseUp)。
まずは TTimer 版
// -----------------------------------------------------------------------------
// OnClick / OnDblClick を備えたベースフォーム
// TTimer 版
// -----------------------------------------------------------------------------
unit FMX.BaseForm;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
{$IFDEF MSWINDOWS}
Winapi.Windows,
{$ENDIF}
{$IFDEF MACOS}
MacAPI.Foundation, MacAPI.AppKit,
{$ENDIF}
FMX.Types, FMX.Forms;
type
{ TBaseFormHD }
TBaseFormHD = class (TForm)
procedure Timer_Timer(Sender: TObject);
private
{ private 宣言 }
FTimer: TTimer;
FStartPoint: TPointF;
FIntervalOver: Boolean;
FIsMouseUp: Boolean;
FMouseClickEvent: TNotifyEvent;
FMouseDblClickEvent: TNotifyEvent;
function IsMouseOnControl(X, Y: Single): Boolean;
procedure GenerateClickEvent;
public
{ public 宣言 }
constructor Create(AOwner: TComponent); override ;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
published
{ published 宣言 }
property OnClick: TNotifyEvent read FMouseClickEvent write FMouseClickEvent;
property OnDblClick: TNotifyEvent read FMouseDblClickEvent write FMouseDblClickEvent;
end ;
{ TBaseForm3D }
TBaseForm3D = class (TForm3D)
procedure Timer_Timer(Sender: TObject);
private
{ private 宣言 }
FTimer: TTimer;
FStartPoint: TPointF;
FIntervalOver: Boolean;
FIsMouseUp: Boolean;
FMouseClickEvent: TNotifyEvent;
FMouseDblClickEvent: TNotifyEvent;
function IsMouseOnControl(X, Y: Single): Boolean;
procedure GenerateClickEvent;
public
{ public 宣言 }
constructor Create(AOwner: TComponent); override ;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
published
{ published 宣言 }
property OnClick: TNotifyEvent read FMouseClickEvent write FMouseClickEvent;
property OnDblClick: TNotifyEvent read FMouseDblClickEvent write FMouseDblClickEvent;
end ;
const
CLICK_JUDGE_DIST = 4 ; // クリック判定用
DOUBLE_CLICK_TIME = 500 ; // ダブルクリック判定時間 (規定)
implementation
function CalcDist(a, b: TPointF): Single;
begin
result := Sqrt(Sqr(a.X - b.X) + Sqr(a.Y - b.Y));
end ;
{ TBaseFormHD }
constructor TBaseFormHD.Create(AOwner: TComponent);
begin
inherited ;
FTimer := TTimer.Create(Self);
FTimer.Enabled := False;
FTimer.Interval := DOUBLE_CLICK_TIME;
{$IFDEF MSWINDOWS}
FTimer.Interval := GetDoubleClickTime;
{$ENDIF}
{$IFDEF MACOS}
// OSX 用の処理
{$ENDIF}
FTimer.OnTimer := Timer_Timer;
end ;
procedure TBaseFormHD.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
try
if not (Assigned(FMouseClickEvent) or Assigned(FMouseDblClickEvent)) then
Exit;
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if (ssDouble in Shift) then
begin
FTimer.Enabled := False;
if CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST then
if Assigned(FMouseDblClickEvent) then
OnDblClick(Self);
end
else
begin
FStartPoint := PointF(X, Y);
if Assigned(FMouseDblClickEvent) then
begin
FIntervalOver := False;
FIsMouseUp := False;
FTimer.Enabled := True;
end ;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseFormHD.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
begin
try
if not (Assigned(FMouseClickEvent) or Assigned(FMouseDblClickEvent)) then
Exit;
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST then
begin
if (not Assigned(FMouseDblClickEvent)) or FIntervalOver then
GenerateClickEvent
else
FIsMouseUp := True;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseFormHD.Timer_Timer(Sender: TObject);
begin
FIntervalOver := True;
FTimer.Enabled := False;
if FIsMouseUp then
GenerateClickEvent;
end ;
function TBaseFormHD.IsMouseOnControl(X, Y: Single): Boolean;
begin
result := (IControl(ObjectAtPoint(ClientToScreen(PointF(X, Y)))) <> nil );
if result then
FTimer.Enabled := False;
end ;
procedure TBaseFormHD.GenerateClickEvent;
begin
FIntervalOver := False;
FTimer.Enabled := False;
if Assigned(FMouseClickEvent) then
OnClick(Self);
end ;
{ TBaseForm3D }
constructor TBaseForm3D.Create(AOwner: TComponent);
begin
inherited ;
FTimer := TTimer.Create(Self);
FTimer.Enabled := False;
FTimer.Interval := DOUBLE_CLICK_TIME;
{$IFDEF MSWINDOWS}
FTimer.Interval := GetDoubleClickTime;
{$ENDIF}
{$IFDEF MACOS}
// OSX 用の処理
{$ENDIF}
FTimer.OnTimer := Timer_Timer;
end ;
procedure TBaseForm3D.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
try
if not (Assigned(FMouseClickEvent) or Assigned(FMouseDblClickEvent)) then
Exit;
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if (ssDouble in Shift) then
begin
FTimer.Enabled := False;
if CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST then
if Assigned(FMouseDblClickEvent) then
OnDblClick(Self);
end
else
begin
FStartPoint := PointF(X, Y);
if Assigned(FMouseDblClickEvent) then
begin
FIntervalOver := False;
FIsMouseUp := False;
FTimer.Enabled := True;
end ;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseForm3D.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
begin
try
if not (Assigned(FMouseClickEvent) or Assigned(FMouseDblClickEvent)) then
Exit;
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST then
begin
if (not Assigned(FMouseDblClickEvent)) or FIntervalOver then
GenerateClickEvent
else
FIsMouseUp := True;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseForm3D.Timer_Timer(Sender: TObject);
begin
FIntervalOver := True;
FTimer.Enabled := False;
if FIsMouseUp then
GenerateClickEvent;
end ;
function TBaseForm3D.IsMouseOnControl(X, Y: Single): Boolean;
begin
result := (IControl(ObjectAtPoint(ClientToScreen(PointF(X, Y)))) <> nil );
if result then
FTimer.Enabled := False;
end ;
procedure TBaseForm3D.GenerateClickEvent;
begin
FIntervalOver := False;
FTimer.Enabled := False;
if Assigned(FMouseClickEvent) then
OnClick(Self);
end ;
end .
次に GetTick 版。
// -----------------------------------------------------------------------------
// OnClick / OnDblClick を備えたベースフォーム
// GetTick 版
// -----------------------------------------------------------------------------
unit FMX.BaseForm;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
{$IFDEF MSWINDOWS}
Winapi.Windows,
{$ENDIF}
{$IFDEF MACOS}
MacAPI.Foundation, MacAPI.AppKit,
{$ENDIF}
FMX.Types, FMX.Forms, FMX.Platform;
type
{ TBaseFormHD }
TBaseFormHD = class (TForm)
private
{ private 宣言 }
FDoubleClickTime: Extended;
FStartPoint: TPointF;
FStartTime: Extended;
FDoubleClicked: Boolean;
FMouseClickEvent: TNotifyEvent;
FMouseDblClickEvent: TNotifyEvent;
FIsMouseUp: Boolean;
FIntervalOver: Boolean;
function IsMouseOnControl(X, Y: Single): Boolean;
procedure GenerateClickEvent;
public
{ public 宣言 }
constructor Create(AOwner: TComponent); override ;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
published
{ published 宣言 }
property OnClick: TNotifyEvent read FMouseClickEvent write FMouseClickEvent;
property OnDblClick: TNotifyEvent read FMouseDblClickEvent write FMouseDblClickEvent;
end ;
{ TBaseForm3D }
TBaseForm3D = class (TForm3D)
private
{ private 宣言 }
FDoubleClickTime: Extended;
FStartPoint: TPointF;
FStartTime: Extended;
FDoubleClicked: Boolean;
FMouseClickEvent: TNotifyEvent;
FMouseDblClickEvent: TNotifyEvent;
FIsMouseUp: Boolean;
FIntervalOver: Boolean;
function IsMouseOnControl(X, Y: Single): Boolean;
procedure GenerateClickEvent;
public
{ public 宣言 }
constructor Create(AOwner: TComponent); override ;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override ;
published
{ published 宣言 }
property OnClick: TNotifyEvent read FMouseClickEvent write FMouseClickEvent;
property OnDblClick: TNotifyEvent read FMouseDblClickEvent write FMouseDblClickEvent;
end ;
const
CLICK_JUDGE_DIST = 4 ; // クリック判定用
DOUBLE_CLICK_TIME = 500 ; // ダブルクリック判定時間 (規定)
implementation
function Get_Tick: Extended;
{$IF FireMonkeyVersion >= 17.0}
begin
result := IFMXTimerService(TPlatformServices.Current.GetPlatformService(IFMXTimerService)).GetTick;
end ;
{$ELSE}
begin
result := Platform.GetTick;
end ;
{$IFEND}
function CalcDist(a, b: TPointF): Single;
begin
result := Sqrt(Sqr(a.X - b.X) + Sqr(a.Y - b.Y));
end ;
{ TBaseFormHD }
constructor TBaseFormHD.Create(AOwner: TComponent);
begin
inherited ;
FDoubleClickTime := DOUBLE_CLICK_TIME / 1000 ;
{$IFDEF MSWINDOWS}
FDoubleClickTime := GetDoubleClickTime / 1000 ;
{$ENDIF}
{$IFDEF MACOS}
// OSX 用の処理
{$ENDIF}
end ;
procedure TBaseFormHD.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
try
if not (Assigned(FMouseClickEvent) or Assigned(FMouseDblClickEvent)) then
Exit;
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if (ssDouble in Shift) then
begin
FDoubleClicked := True;
if CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST then
if Assigned(FMouseDblClickEvent) then
OnDblClick(Self);
end
else
begin
FStartPoint := PointF(X, Y);
FStartTime := Get_Tick;
FDoubleClicked := False;
FIsMouseUp := False;
FIntervalOver := False;
while ((Get_Tick - FStartTime) < FDoubleClickTime) do
begin
Application.ProcessMessages;
if FDoubleClicked then
Exit;
end ;
FIntervalOver := not FDoubleClicked;
if FIsMouseUp then
GenerateClickEvent;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseFormHD.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
begin
try
if not (Assigned(FMouseClickEvent) or Assigned(FMouseDblClickEvent)) then
Exit;
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST then
begin
if (not Assigned(FMouseDblClickEvent)) or FIntervalOver then
GenerateClickEvent
else
FIsMouseUp := True;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseFormHD.GenerateClickEvent;
begin
FDoubleClicked := False;
if Assigned(FMouseClickEvent) then
OnClick(Self);
end ;
function TBaseFormHD.IsMouseOnControl(X, Y: Single): Boolean;
begin
result := (IControl(ObjectAtPoint(ClientToScreen(PointF(X, Y)))) <> nil );
end ;
{ TBaseForm3D }
constructor TBaseForm3D.Create(AOwner: TComponent);
begin
inherited ;
FDoubleClickTime := DOUBLE_CLICK_TIME / 1000 ;
{$IFDEF MSWINDOWS}
FDoubleClickTime := GetDoubleClickTime / 1000 ;
{$ENDIF}
{$IFDEF MACOS}
// OSX 用の処理
{$ENDIF}
end ;
procedure TBaseForm3D.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
try
if not (Assigned(FMouseClickEvent) or Assigned(FMouseDblClickEvent)) then
Exit;
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if (ssDouble in Shift) then
begin
FDoubleClicked := True;
if CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST then
if Assigned(FMouseDblClickEvent) then
OnDblClick(Self);
end
else
begin
FStartPoint := PointF(X, Y);
FStartTime := Get_Tick;
FDoubleClicked := False;
FIsMouseUp := False;
FIntervalOver := False;
while ((Get_Tick - FStartTime) < FDoubleClickTime) do
begin
Application.ProcessMessages;
if FDoubleClicked then
Exit;
end ;
FIntervalOver := not FDoubleClicked;
if FIsMouseUp then
GenerateClickEvent;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseForm3D.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
begin
try
if not (Assigned(FMouseClickEvent) or Assigned(FMouseDblClickEvent)) then
Exit;
if IsMouseOnControl(X, Y) then
Exit;
if (Button = TMouseButton.mbLeft) then
begin
if CalcDist(FStartPoint, PointF(X, Y)) < CLICK_JUDGE_DIST then
begin
if (not Assigned(FMouseDblClickEvent)) or FIntervalOver then
GenerateClickEvent
else
FIsMouseUp := True;
end ;
end ;
finally
inherited ;
end ;
end ;
procedure TBaseForm3D.GenerateClickEvent;
begin
FDoubleClicked := False;
if Assigned(FMouseClickEvent) then
OnClick(Self);
end ;
function TBaseForm3D.IsMouseOnControl(X, Y: Single): Boolean;
begin
result := (IControl(ObjectAtPoint(ClientToScreen(PointF(X, Y)))) <> nil );
end ;
end .
以前のコードでは二回目の MouseUp までをダブルクリック時間として処理していたので、
ダブルクリック時間に "二回目の MouseDown -> MouseUp の時間" が含まれているので、実質 "ダブルクリック判定時間が短い" 事になる
ダブルクリックキャンセルが可能なため、ダブルクリック時に二回目の MouseUp を待たなくてはならない (最短での処理ではない)。
という問題があった。現状では考えられるすべてのパターンで最短 (最速という意味ではない) でクリック / ダブルクリック判定が行われる...このテのアルゴリズムを考えるのは面白いな。ちなみに Microsoft のクリック / ダブルクリック判別サンプルは "方法 : クリックとダブルクリックを識別する" にある。
・
ダブルクリックのキャンセルは存在しない
シングルクリックは MouseDown▽ -> MouseUp▲ と、MouseUp のタイミングで行われる。MouseDown のままマウスカーソルを移動させて MouseUp を行うとシングルクリックキャンセルを行う事ができる。
ダブルクリックは "二回 (ダブル) のクリック" なので MouseDown▽ -> MouseUp△ -> MouseDown▽ -> MouseUp▲ と、二回目の MouseUp のタイミングでダブルクリックになる...訳じゃなく 、ダブルクリック判定は MouseDown▽ -> MouseUp△ -> MouseDown▼ -> MouseUp△ この二回目の MouseDown のタイミング で行われる。よって、二回目の MouseDown のままマウスカーソルを移動させて MouseUp してもダブルクリックキャンセルは行えない。
例えば、デスクトップにある任意のアイコンを 1.5 回クリック (Down, Up, Down) すると、二回目の MouseDown でダブルクリック判定され、押しっぱなしのマウスボタンをどこで離そうがダブルクリック動作に影響しない事が解る。
12/09/21
・
Delphi / C++Builder XE3 Starter Edition について
XE3 Starter Edition には幾つかの注意点があります。
次回のバージョンアップ版は Starter Edition のみ購入できる。Q&A には "Starterエディションユーザー向けアップグレード価格" との記述があるが、"ジャンプアップキャンペーン" の事だと思われる。
Starter Edition バージョンアップ版は Delphi / C++Builder 以外の開発ツールを所持していても購入できる。
Visual Studio Express Edition も例外ではない。
ソースコードが付属しない。
個人または 5 人以下の企業/組織で、
Delphi / C++Builder で開発したアプリケーションの収益が US1000$ / 年を超えてはいけない。
5 セットまでしか購入できない (5 ユーザー / サブネット)。
64bit アプリケーションの開発はできない。
Mac OSX の開発はできない。
データベース、帳票系のコンポーネントはほぼ使えない。
FireMonkey は使えるが、Windows プラットフォーム向け 32bit アプリケーションしか作れない。
Visual Live Binding は利用できない。
過去のバージョンの Delphi / C++Builder は利用できない (貰えない)。
特別バージョンアップ及びジャンプアップキャンペーン対象外なので、RAD XE3 Bonus Pack は貰えない。
これらの制限を購入前によく検討して下さい。
Delphi XE3 Starter Edition
C++Builder XE3 Starter Edition
Starter Edition に関する詳しい情報については上記資料で確認できます。
・
XE3 Starter Edition ショップリンク
恒例のショップリンクです。
Delphi XE3 Starter ESD
¥17,955 (税込)
¥17,955 (税込)
¥17,955 (税込)
¥17,955 (税込)
Delphi XE3 Starter ESD (アップグレード)
¥13,965 (税込)
¥13,965 (税込)
¥13,965 (税込)
¥14,700 (税込)
C++Builder XE3 Starter ESD
¥17,955 (税込)
¥17,955 (税込)
¥17,955 (税込)
¥17,955 (税込)
C++Builder XE3 Starter ESD (アップグレード)
¥13,965 (税込)
¥13,965 (税込)
¥13,965 (税込)
¥14,700 (税込)
※価格にポイント等は反映されていません。また、価格は変動する事がありますのでリンク先を必ず確かめるようにして下さい。
12/09/22
・
TSingleHelper / TDoubleHelper / TExtendedHelper そして TStringHelper (XE3)
TSingleHelper / TDoubleHelper / TExtendedHelper は XE2 で実装された "浮動小数点用レコード" の代替となるヘルパーです。わざわざ高度なレコード型を使わなくとも、同等の事ができます。"同等" というのがミソなのですが、例えば、TSingleRec のコードを単純にヘルパーに置き換えられるかというとそうではありません。
var
F: Single;
I: Integer;
begin
F := -0.5 ;
// display the hexadecimal representation of the single precision floating-point value
// leftmost bytes are the most significant
for I := High(F.Bytes) downto Low(F.Bytes) do
Write (IntToHex(F.Bytes[I], 2 ) + ' ' );
Writeln;
// display the value
Writeln(FloatToStr(F));
// ...
end ;
これは XE2 の TSingleRec のドキュメントにあったサンプルソースコード をヘルパーで置き換えただけのものですが、コンパイルすると Bytes の High / Low の取得でエラーになってしまいます...大した事ではありませんが。
TStringHelper は TStringBuilder を使わなくとも、
var
s: string ;
begin
s := 'ABCDEFG' .Substring(2 , 3 );
s := s.ToLower;
Writeln(s);
end ;
このような記述が可能になるという事です。
しかしながら、このヘルパーが使えるのは String (UnicodeString) だけであり、AnsiString や WideString 用のヘルパーは用意されていません 。つまり、AnsiString や WideString は旧来の文字列操作関数群を使う必要があります。
加えて言えば、Copy 関数と同等のものは TStringHelper では SubString() ですが、TStringHelper.Copy() という同名ながら違う機能を持つメソッドもあります。関数とヘルパーのメソッドを混在させると訳がわからなくなるため、使い所が難しいかもしれません。逆に、"文字列の種類を混在させなければ使い勝手のいい機能" だとも思います...コード補完も利用できる訳ですから。
すべてのプリミティブ型 にヘルパーが用意されると使い勝手も変わってくるのだとは思いますが...。
See Also:
・
SkRegExp 1.4.1 リリース
小宮さん作の正規表現ライブラリ SkRegExp の version 1.4.1 がリリースされました。
12/09/23
・
Delphi FireMonkey Tips
XE3 に合わせて記事をあちこち変更してあります。どうでもいいけど、XE3 で 3D Shapes をフラットシェーディングにしたりワイヤーフレームにしたりするにはどうすればいいのだろう?
・
Apple Keyboard の分解清掃
メンドイ。ガワが透明アクリルなだけに、ゴミが入ると汚れが目立って仕方がない。
画像で上にあるのが Power Mac G4 の "Apple Pro Keyboard (M7803J/A)"、下は iMac 5.1 (17″Late 2006) の "Apple Pro Keyboard (M7803J/A)"。分解には、
精密ドライバー (+/- 両方あった方がいい)
六角レンチ (1.3mm)
が必要となる。六角レンチは 0.05 インチ (1.27mm) でもイケない事はないが、ネジが硬い場合にはネジ穴をなめてしまうかもしれない。ダイソーで探せばあるハズだ。
キーボード裏にある 5 本のネジ を外せばガワは外れる。見当たらないであろう 1 本は隠しネジとなっており、
こんなトコロにあるので表面のシールをカッター等で切り取る必要がある。
画像で上にあるのが "Apple Keyboard (M9034J/A)"、下にあるのが "Apple Wireless Keyboard (M9270J/A)"。M9270J/A の分解には、
も必要となる...どんだけメンドイんだよ。まぁ、Y字ドライバーは一本持っておけば任天堂製品の分解にも使えるしね。
分解清掃にあった方がいいものは、
バケツ (キートップをジャブジャブ洗うのに使う)
ハンドブロワ
食器用洗剤
界面活性剤入りの洗剤 (マイペットとかマジックリンとか)
歯ブラシ (使わなくなったのでいい)
シリコンスプレー
キッチンペーパー
綿棒
布巾
メガネ拭き (100均にある)
キートップは手で外れるのでキートップ外し用工具は要らないと思う。
キーボードを洗剤で洗うと必要な油分まで取れてしまいキーがスムーズに押下できなくなったりするので、シリコンスプレーがあるといい。綿棒に付けてキートップが収まる穴をクルっと撫でてやると動作がスムーズになる。
ガワがなまじアクリルなので、水洗いすると水アカが目立ってしまう。布巾で拭きとっても拭き残しがあったり、布巾の糸状のホコリが付着するので最後はメガネ拭きでキレイにするといい。外側は後でも拭けるので内側を重点的に。指紋にも気を付けて。アクリルは間違ってもティッシュで擦ってはいけない。ティッシュで擦ると擦り傷が付いてしまう。
分解清掃前の画像?あまりの汚さにドン引きしかねないので、最初から撮っておりませぬ (w
12/09/24
・
FireMonkey のメディア再生 (XE3)
XE2 では動画再生用コントロールである TVideo / TVideo3D がオミットされてしまいましたが、XE3 では TMediaPlayer として実装されています。
使い方は...説明するのがバカらしくなる程簡単なのですが、簡単にチュートリアルを。
FireMonkey HD アプリケーションを新規作成
TMediaPlayer 、TMediaPlayerControl 、TButon を一つずつ貼る。
MediaPlayerControl1 (TMediaPlayerControl ) の MediaPlayer プロパティに MediaPlayer1 (TMediaPlayer ) を指定。
Button1 (TButon ) の OnClick イベントハンドラに以下のように記述
procedure TForm1.Button1Click(Sender: TObject);
begin
MediaPlayer1.FileName := 'C:\Users\Public\Videos\Sample Videos\Wildlife.wmv' ;
MediaPlayer1.Play;
end ;
コンパイルして実行。
Windows 7 なら、ボタンを押せばサンプル動画が再生されるはずです。VCL 版の TMediaPlayer のように再生 GUI はありませんが、ボタンでチャチャっと作れば終わりです。TCornerButton とかで作れば多少見栄えがするかもしれません。
TAnimation とコレがあれば Flash みたいな事ができますね。
・
.delphi maniacs
デブキャン講師としておなじみ、シリアルゲームズ細川さんの Delphi 系ブログがオープンしました (^o^)/
12/09/25
・
Delphi の開発環境 (デバッグ環境) として使える Mac は?
XE2 以降で FireMonkey (またはコンソールアプリケーション) による OSX 開発が可能になりました。開発環境 (デバッグ環境) に使える Mac は簡単に言えば以下の条件を満たしている必要があります。
Intel Mac
OSX 10.6 (Snow Leopard) 以降の OS
Intel Mac であっても Tiger (10.4) や Leopard (10.5) では PAServer が動作しません。FireMonkey の正式サポートは Snow Leopard (10.6) 以降 です。なお、Snow Leopard (10.6) は Intel Mac であれば、どの機種でも動作します。
さて。ここに iMac 5.1 (17″Late 2006 / Tiger 搭載) と Leopard (10.5) がある訳だが...。
・
Delphi でコンパイルされたバイナリを PAServer を使わずに Leopard (10.5) 以前で無理矢理実行したらどうなるのか?
dyld: Library not loaded: @rpath/libcgunwind.1.0.dylib
Reason: no suitable image found.
のエラーを拝むだけ だと思います...何故そんな事が言えるのかって?
そりゃ、やってみたからさ (ToT)
・
Delphi / C++Builder でコンパイルされたバイナリを PAServer を使わずに Mac OSX 上で実行するには?
ビルドされたバイナリを OSX に普通にコピーしただけでは実行できません。
それでも、何らかの問題で PAServer が使えない場合には手動で実行できるようにしなくてはならないため、PAServer を使わずに Mac OSX でバイナリを実行する方法を書いておきます。
Windows で、ビルドされたバイナリがある場所 (.\OSX\Release) へ行く。
アプリケーション名が MyApp だとすると、以下のようにファイルを配置する。
[MyApp.app]
|
+-[Contents]
|
+-[MacOS]
| |
| +-MyApp
| |
| +-libcgcrtl.dylib <- $(BDS)\Redist\osx32 から持ってくる: C++Builder のみ
| |
| +-libcgstl.dylib <- $(BDS)\Redist\osx32 から持ってくる: C++Builder のみ
| |
| +-libcgunwind.1.0.dylib <- $(BDS)\Redist\osx32 から持ってくる
|
+-[Resources]
| |
| +-MyApp.icns
|
+-info.plist <- MyApp.info.plist をリネーム
MyApp.app フォルダごと OSX に持っていく (Ex.~/Delphi/ の下とかに)。
Finder から実行。
トップフォルダを "プロジェクト名.app " にして、指定のフォルダにファイルを配置するだけなので、解ってしまえば簡単です。PAServer を使えればもっと簡単です (w
12/09/27
・
FireMonkey でセンサーを使ってみる (XE3)
XE3 の FireMonkey には、
という 2 つのコンポーネントがツールパレット (コンポーネントツールバー) にあります。とりあえず簡単にこれらを使ってみる事にします。
・
TLocationSensor (XE3)
使い方は簡単です。
フォームに TLocationSensor を貼ります (LocationSensor1)。
フォームに TButton を貼ります (Button1)。
フォームに TLabel を 2 つ貼ります (Label1 / Label2)。
Button1 の OnClick() イベントハンドラを以下のように記述します。
procedure TForm1.Button1Click(Sender: TObject);
begin
LocationSensor1.Active := True;
end ;
LocationSensor1 の OnLocationChangedイベントハンドラを以下のように記述します。
procedure TForm1.LocationSensor1LocationChanged(Sender: TObject;
const OldLocation, NewLocation: TLocationCoord2D);
begin
Label1.Text := Format('緯度: %f' , [NewLocation.Latitude]);
Label2.Text := Format('経度: %f' , [NewLocation.Longitude]);
end ;
完成です。コンパイルして実行してみましょう。
ボタンを押すとセンサーが有効になり、現在位置の緯度と経度をラベルに表示します。GPS を持っていなくても、無線 LAN さえあれば 09/12 の雑談で紹介 した Geosense を使ってロケーションのテストができます。
・
TMotionSensor (XE3)
こちらも TLocationSensor と使い方は同じです。データが更新されると OnDataChanged イベントが飛んでくるので、そこで Sensor プロパティの各メンバを参照します (AccelerationX / AccelerationY / AccelerationZ など)。
ただ、モーションセンサーと一口に言っても、加速度センサー、ジャイセンサーなど色々種類があるため、取れるデータや参照すべきプロパティは様々です。それ故に、具体的なコードを書きようがないので、詳細は割愛させて頂きます。モーションセンサー (3 軸加速度センサーなど) が搭載されている PC をお持ちの方は是非チャレンジしてみて下さい。
・
More Sensors! (XE3)
実はツールパレット (コンポーネントツールバー) にコンポーネントとして存在しないだけで、FMX.Sensors には下記クラスが存在します。
試しに ALS (照度センサー) をテストしてみましょう。仮想 ALS については以前も記事にしているのでそちらを参照してください。最新の SDK には VirtualLightSensor.exe は含まれないようですのでご注意を。
照度センサーを調べるには TLightSensor を使います。順を追ったコードはメンドイので抜粋で。
uses
..., FMX.Sensors;
...
procedure FormCreate(Sender: TObject);
procedure btnActiveClick(Sender: TObject);
procedure DataChanged(Sender: TObject);
private
{ private 宣言 }
LightSensor: TLightSensor;
public
{ public 宣言 }
end ;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
// 照度センサーの生成とイベントハンドラ設定
LightSensor := TLightSensor.Create(Self);
LightSensor.OnDataChanged := DataChanged;
end ;
procedure TForm1.btnActiveClick(Sender: TObject);
begin
// センサーをアクティブに
LightSensor.Active := True;
end ;
procedure TForm1.DataChanged(Sender: TObject);
begin
// データが更新された
lblLUX.Text := Format('%f LUX' , [LightSensor.Sensor.Lux]);
end ;
フォームにはラベル (lblLUX) とボタン (btnActive) が貼ってあるだけです。
ボタンを押してセンサーをアクティブにしておき、VirtualLightSensor.exe で照度を変更すると、照度が LUX で表示されます。メチャメチャ簡単ですね。悩みまくった 挙句、「助けて!ブラザー!!」 してたのがウソのようです。
...それにしても、何故ツールパレット (コンポーネントツールバー) にコンポーネントとして登録してないのだろう?忘れてた?それともコンポーネントアイコン作るのがメンドウだったとか?
12/09/28
・
RAD Studio XE3 Hotfix 1-2-3
この "1-2-3" という表記で Lotus 1-2-3 (表計算ソフト) を思い出しても、魔道物語 1-2-3 (コンパイルの RPG。ぷよぷよのキャラはここからきている) を思い出しても、結構いいお歳の方なのではないだろうか。
それはともかく、RAD Studio XE3 の Hotix がリリースされている。特に "SQLite が Pro 版で使えない" という問題が修正されたのは朗報だと言える。
・
FireMonkey で "オミットされた!" コンポーネントと "次に実装される?" コンポーネント
TVideo / TVideo3D というコンポーネントが XE2 にはあったらしいのだが、RTM の時点で消滅してしまった。その名残は DocWiki に残っており、検索すると今でも一部ヒットする。ただ、XE3 で TMediaPlayer / TMediaPlayerControl が実装されたので、これらのコンポーネントはもう実装されないだろう。
で、アップデータ或いは次のバージョンでは VCL にある TWebBrowser と同等のブラウザコンポーネントが追加されると思う。実際問題、09/11 の雑談 のような事を FireMonkey でやろうとしても、組み込みブラウザがないために実現できなかったりするからだ。これではセンサー機能 (ロケーション) が充分に活かせない。
ブラウザコンポーネントが追加されるとしたら、おそらくは Chromium を使った物になるだろう。クラスプラットフォームなので、IE コンポを実装する訳にはいかないし、Mozilla の "組み込み Gecko" はサポートを終了 しているし、選択肢らしい選択肢が Chromium しかないのが現状だからだ。
Delphi 用の Chromium コンポーネントとしては、Delphi Chromium Embedded というプロジェクトが既にあり、FireMonkey 対応も進められている (SVN からソースを持ってくると XE2 用だが FireMonkey のサンプルプロジェクトもある)。そういった背景から、Chromium ベースのブラウザコンポが追加されると思う。
...さーて、もっともらしい事も長々と語った事だし、ネタばらしをするか。
実は FireMonkey のコンポーネントアイコンリソースに TWebBrowser と、TWebBrowserControl というのが残ってるのですよ。こんなのが >
・
More, More, Sensors! (XE3)
やっぱりメンドイので、ツールパレット (コンポーネントツールバー) に存在しないセンサークラスをコンポーネントとして登録するパッケージを作ってみた。
多分動くと思う。左から順に、
となる。コンポーネントアイコンは PowerPoint のクリップアートを適当に加工したものだから文句は言わないこと (w
追記: やっぱりアイコンに統一性がない&視認性が悪いので新しいのを作ってみた。
Windows 7 にあるセンサー用のリソースアイコンからのパクリ。アーカイブ内の FMX.SensorsEx.dcr.old は古いコンポーネントリソースファイルなので、要らなければ削除して構わないです。
12/09/29
・
SkRegExp 1.4.3 リリース
小宮さん作の正規表現ライブラリ SkRegExp の version 1.4.3 がリリースされました。
・
Listing the available sensors using RAD Studio XE3 and FireMonkey 2 (Sip from the Firehose)
twitter で RAN 氏に教えて貰った David.I のセンサーの記事。09/11 の記事で書いた LocationDemo の件 同様、この記事中のソースコードもまた VCL アプリケーションで動作する。
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Sensors;
type
TForm3 = class (TForm)
Button2: TButton;
Label2: TLabel;
Memo2: TMemo;
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
NumberOfSensors : integer;
public
{ Public declarations }
end ;
var
Form1: TForm3;
implementation
{$R *.dfm}
uses System.TypInfo;
procedure TForm3.Button2Click(Sender: TObject);
var
i : integer;
begin
// get list of found sensors - if any
TSensorManager.Current.Active := true;
NumberofSensors := TSensorManager.Current.Count;
Label2.Caption := 'Sensors: ' +IntToStr(NumberOfSensors);
Memo2.Lines.Clear;
for i := 0 to NumberOfSensors-1 do begin
Memo2.Lines.Add(
IntToStr(i)
+ ': '
+ TSensorManager.Current.Sensors[i].Name
+ '", Category: '
+ GetEnumName(System.TypeInfo(TSensorCategory),
Ord(TSensorManager.Current.Sensors[i].Category))
);
end ;
TSensorManager.Current.Active := false;
end ;
Label2.Text が Label2.Text になったのと uses にリストされているネームスペースが違うだけだ。
さて、センサーをリストアップするコードは以前 C++Builder で書いた 事がある...いわゆる 「助けて!ブラザー!!」 だ。この時のバイナリはまだ DL できる。ONKYO TW317A5 で、これら "センサーを列挙するアプリケーション" を実行してみる事にする。
上が C++Builder で SensorAPI を使った場合、下が Delphi XE3 の TSensorManager を使った場合だ。XE3 版では "G Sensor (Pegatron 三軸加速度センサー)" が列挙されていない事が判ると思う。
どうやら、ONKYO TW317A5 の加速度センサードライバは既知のセンサーカテゴリを返さない ようなのだ...つまりはドライバが悪いという事なのだが、逆に言えば TSensorManager は既知のカテゴリー以外のセンサーを列挙できないという事も判った。マルチプラットフォームの絡みなのだろうとは思う (実装しても OSX で使えない列挙方法だという事) けれど、カテゴリーは無視してすべてのセンサーを列挙して欲しいところ。センサーカテゴリとして "Unknown" を返せば問題ないハズなのだし。
まぁ、こんな行儀の悪いセンサーは SensorAPI で直接どうにかしてやればいいという話ではあるのだけれど...。
12/09/30
・
TSelectionPoint3D (XE2)
XE2 にはあって XE3 で廃止されたコンポーネント。3D shape を移動または回転させるのに使う。
簡単に使い方を説明すると、
Firemonkey HD アプリケーションを新規作成
TViewport3D (Viewport3D1) を貼って、プロパティを以下のように設定する。
Viewport3D1 に TCamera (Camera1) を貼って、邪魔にならない左上などに移動。
Viewport3D1 に TRountCube (RoundCube1) を貼って、プロパティを以下のように設定する。
Depth : 5
Height : 5
Width : 5
Viewport3D1 に TSelectionPoint3D (SelectionPoint3D_X) を貼って、プロパティを以下のように設定する。
Depth : 0.5
Height : 0.5
Width : 0.5
Kind : spRotation
Position.X : 0
Position.Y : 0
Position.Z : -5
WorkPlane.X : 1
WorkPlane.Y : 0
WorkPlane.Z : 0
Viewport3D1 に TSelectionPoint3D (SelectionPoint3D_Y) を貼って、プロパティを以下のように設定する。
Depth : 0.5
Height : 0.5
Width : 0.5
Kind : spRotation
Position.X : 5
Position.Y : 0
Position.Z : 0
WorkPlane.X : 0
WorkPlane.Y : 1
WorkPlane.Z : 0
Viewport3D1 に TSelectionPoint3D (SelectionPoint3D_Z) を貼って、プロパティを以下のように設定する。
Depth : 0.5
Height : 0.5
Width : 0.5
Kind : spRotation
Position.X : 0
Position.Y : -5
Position.Z : 0
WorkPlane.X : 0
WorkPlane.Y : 0
WorkPlane.Z : 1
ここまでの作業で以下の画像のようになるハズだ。軸の関係が解りにくいので、以前作った "右手座標系サンプル" の画像も並べておく。
RoundCube1 を回転させるコードは OnTrack イベントハンドラに記述する。
procedure TForm1.SelectionPoint3D_XTrack(Sender: TObject);
begin
RoundCube1.RotationAngle.X := -SelectionPoint3D_X.Angle;
end ;
procedure TForm1.SelectionPoint3D_YTrack(Sender: TObject);
begin
RoundCube1.RotationAngle.Y := -SelectionPoint3D_Y.Angle;
end ;
procedure TForm1.SelectionPoint3D_ZTrack(Sender: TObject);
begin
RoundCube1.RotationAngle.Z := -SelectionPoint3D_Z.Angle;
end ;
これを実行してみると、X 軸の選択ポイント (SelectionPoint3D_X) が動かせない 事が判る...軌道と一致しているため動かせないのだ。2D の座標で考えた場合、該当する 3D 座標は手前と奥の 2 地点存在する のだから。SelectionPoint3D_X の X 位置をほんの少し右か左へズラしてやれば動かせるようになる。もちろん、選択ポイントにオフセットを設定したとしても、"選択ポイントを移動させて 3D Shape を回転させた結果、軌道と一致してしまった場合" にはやはり選択ポイントは動かせなくなってしまう。
それと、このままだと各選択ポイントを動かしても他の選択ポイントは追従しない。例えば Y 軸の選択ポイント (SelectionPoint3D_Y) を動かしても X 軸の選択ポイント (SelectionPoint3D_X) は回らない。加えて、このサンプルは原点で動作させるからうまく動くのであって、RoundCube1 が原点を外れているとうまく動作しない。これを解決するには、TDummy を貼って、RoundCube1 と SelectionPoint3D_X / Y / Z を子として登録する...ローカル座標を一致させればいいのだ。しかしながら、TDummy の子にしてしまうと、選択ポイントは歪な動きをする。FMX のフォームデザイナのような動きはしない。
また、TSelectionPoint3D は一軸毎の回転であり、選択ポイントの位置や移動角度の値は各 TSelectionPoint3D のインスタンスが保持している。この仕様では、他の TSelectionPoint3D の値を参照していないため、3 軸が独立して回転するような UI だと ジンバルロック が起こってしまう。XE3 で TSelectionPoint3D が廃止された理由もこの辺りにあるのではないかと思う...それでも、"無いよりはあったほうがマシ" なのだがなぁ。
今回の元ネタは Disucussion Forum の "実行時に3Dオブジェクトをマウス操作するには? @ FireMonkey" です。