ScrollBoxのMouseWheelイベントをFormMouseWheelで捕まえる[VCL]

ScrollBoxのMouseWheelが捕まえられないときは、FormのMouseWheelイベントで捕まえる方法がある。

以上は、ScrollBoxがダイレクトにForm1にのっかっている時の例。
Form上のPanelの上にScrollBoxを配置しているときなどはもう一ひねり必要になる。

Unicodeのコード番号からStringを取得する

VclとFireMonkey共通。

“80CC”等、コードポイント(番号)の文字列をStringに変換するには、基本的には以下でOK。

ただし実際にはサロゲートペア用に割り当てられた番号(D800~DFFF)に配慮する必要がある。Androidで、サロゲートペア用の番号を指定するとアプリが落ちる場合もある(Android4.4.2で確認)
そこに配慮した関数。

サロゲートペア文字も取得するには、もう一手間が必要。
サロゲートペア用の番号は”20B9F”など5桁になる。
少し汎用にstd::wstringを使った関数。(注:C++Builderはwchar_tが2byte)

(注:C++Builder Berlin Update2では、Androidではwstring版の”wcstol”がバグっているようなので、さらに一手間かけてstring版の”strtol”を使う必要がある)

念のため、Stringに変換するには以下のように呼び出す。

(なおC++Builder BerlinではStringはUnicodeStringのtypedef)

C++Builder Berlin Update2 / Windows7(Sp1)で確認。

文字列に任意の角度で斜体をかける[VCL]

C++Builderで、文字に任意の角度で斜体をかけて描画する方法。
基本的に文字も点の集まりなので、その点の位置をずらして変形する。
具体的には、
 ①文字をパスでImage等のCanvasに描く
 ②パスから点の集合を取得する
 ③点の位置をずらした新たな点集合を作成
 ④変形用の点集合でCanvasに描く
という手順になる。

Vclにはパスを扱うクラスはないので、WindowsAPIを直接使うことになる。
以下、サンプル。FormにTrackBarとImageを置いた例。
(boost::shared_arrayを使っているが、生ポインタをnewしてももちろん動く)

こんな感じで変形する。

点の情報をずらすだけなので、さまざまな形に変形可能。
たとえば文字列に楕円が食い込んだように変形させる関数のサンプル。

こんな感じで変形する。

C++Builder Berlin Update2 / Windows7(Sp1)で確認。

続・Compositeパターンをshared_ptrで実装する…Clone/Assignの追加

Compositeパターンをshared_ptrで実装するの続き。

DeepCopyを作る”Clone”メソッドや、他のComponentから内容をコピーする”Assign”メソッドもあると便利。

Compositeもメンバ変数を保持することも想定して実装してみる。
もっとスマートなやり方もありそうだが、とりあえず、Compositeとその子クラスに非公開のデフォルトコンストラクタを実装する方式で。
(前回同様、c++11の機能を使っているが、c++11以前でもboost利用で同様の実装が可能。また行数省略のためすべてheaderに実装している。)

1.Componentクラス
Clone/Assign用の純粋仮想関数を追加する。

2.Compositeクラス
Clone/Assignメソッドを実装する。
Compositeの派生クラス用の純粋仮想関数doClone/doAssignも追加。
デフォルトConstructorはprotectedとする。
メンバ変数”name_”も追加、

3.ConcreteCompositeクラス
doClone/doAssignメソッドを実装する。非公開のコンストラクタも追加。
doCloneは非公開のデフォルトコンストラクタで作成したshared_ptrを返すだけ。

(不正なAssignの対応は何もしていないが、本来は例外を投げるなどのエラー処理が必要かも)

4.テストコード
適当に親子関係を作成した後、CloneとAssignを実行する。

実行結果
————————
name:c1/A: val == 3
 name:c2/B: str == child
  name:c3/B: str == child of child
   name:c4/A: val == 107
 name:c5/B: str == other child
–Clone–
name:c1/A: val == 3
 name:c2/B: str == child
  name:c3/B: str == child of child
   name:c4/A: val == 107
 name:c5/B: str == other child
–Before Assign–
name:assigned_compo/A: val == 0
–After Assign–
name:c1/A: val == 3
 name:c2/B: str == child
  name:c3/B: str == child of child
   name:c4/A: val == 107
 name:c5/B: str == other child
————————

ConcreteCompositeのメンバを追加・変更した場合、doAssignメソッドのみを書き換えればOK。

Compositeパターンをshared_ptrで実装する

かつて情報をネットで探したときに、断片的な情報はあるものの、分かりやすいサンプルが見付からなかったのでメモ。
Compositeパターンをshared_ptrで実装するのは、単にポインタをshared_ptrに置き換えるだけだと、循環参照が発生する。

ポイントは2つ。
 1.親(Parent)のポインタはshared_ptrでなくweak_ptrに置き換える
 2.shared_from_thisを使う

以下のコードはc++11以降のstl版だが、c++11以前でもboostで同様の実装が可能。必要なヘッダをincludeすれば”std::”を”boost::”に置き換えるだけでOK。
(説明を短くするために全てheaderに入れている)

まずComponentクラス。
メンバ”parent_”をweak_ptrで保持する。

(Executeは階層構造を出力するだけのテスト用Method)

つづいてCompositeクラス。
継承元に”std::enable_shared_from_this”を指定するのがポイント。
また”Add”で親をセットする際、”shared_from_this()”を使う。


CompositeのConcreteクラスを2つ作ってみる。ひとつはstd::string, もう一つはintをメンバに持つことにする。

実行してみる。適当に親子関係を作り、出力。

出力結果。
—————————————-
A: val == 3
 B: str == child
  B: str == child of child
   A: val == 107
 B: str == other child
—————————————-

生ポインタを使うのと比べ、解放処理が不要で便利。

TListViewのItemをソートする3方法[Vcl]

TListViewのソート方法については、ヘルプのコード例を見てもいまいち分からなかったので整理してみる。(Style:vsReportの場合)。
まだ正確に把握しきってない可能性はあるが…。

まず、ListViewでは以下の3方法のSortが可能。
 stText…いわゆる文字列ソート。ListItemのCaption順にソートする
 stData…ListItemに格納されたDataを比較する
 stBoth…上記両方でソート(たぶん)

1.stSort

これは単純。TListViewのSotrTypeプロパティにstTextを指定するだけ。コードを書く必要はない。

2.stData

こっちは、事前にListItemのDataプロパティに何かの値をセットしておく必要がある。
たとえばクラスのポインタとか。

実例として、各ItemのDataが、intメンバを持つクラスのポインタを保有している場合。(エラー処理は省略)

Compoare値が0より大きいか小さいか、でソート順が決まる。

3.stBoth

ここがちょっと分かりにくいパターン。
基本的に、まずデータを比較し、同じ場合はTextを比較するという手順を踏む(たぶん)
コード例。

4.実行時にTSortTypeを変えたい場合

アプリケーションのオプション設定等でSortTypeを変更したい場合は、ちょっと面倒。
ここで気になるのがListViewのOnCompareイベントの引数
 int Data
とは何だろうってこと。
ヘルプをざっと見た限りだと、こういう事らしい。
 Data == 0 なら、stTextかstNone
 Data != 0 なら、stDataかstBoth

ということで、仮に実行時にSortTypeを変更し、反映したい場合は、以下のようなコードになるはず。

うーん、このコードだと、そもそも引数のDataを利用する必要がないような…。
もっとスマートな方法がある気がする。

TListViewをGroup名順に並び替える[Vcl]

C++BuilderでTListViewをStyle:vsReportで使用している場合、Group名順に並び替えるには、GoupItemのIndexプロパティを変更するだけでいい。(GroupViewプロパティはTrue)

以下のコードでは、std::mapにGroup名とTListGroup(のポインタ)を格納して、自動的に名前順に並び替えている。

なお、このサンプルでは、名前のないGroupが先頭に来てしまう。名前のないGroupを最後にするには、Indexを明示的に付けなけれぱOK。自動的に最後になる。(正確に言うと自動的にカウントアップされたIndexが割り振られる)

以下、蛇足。

Indexプロパティって何?

ListViewにGroupを動的に追加する場合、GroupIDは自分で設定するけど、Indexは特に指定しない。
Indexプロパティは、GroupがAddされるたびに自動的に連番で振られる(らしい)。
ちなみに以下のようにループを回すと、GroupID順でなくIndex順にアクセスされる。(GroupID順だと思い込んでバグったことがある)

なおIndexを指定する場合、ダブりを気にする必要はない。

つまり

グループ0Index = 0
グループ1Index = 1
グループ2Index = 2

この「グループ2」のIndexを0に変更すると、以下のように全グルーブのIndexが割り振り直される。
グループ0Index = 1
グループ1Index = 2
グループ2Index = 0

なので、Groupの最後を先頭に持ってくるのは以下のコードですむ。

(なお”Group->Count-1″を超えるIndexを指定すると例外が投げられる。)

ここらへん、TListGroupsの継承元のTCollectionクラスあたりの仕様を把握していると直感的に分かるのかもしれないけど、ちょっと苦労した。

Windows7(64bit)/C++Builder Berlin(Update1)で確認