CPoint point_data;というのがありますが、これは点(の座標)を表すクラスCPointのインスタンスpoint_dataをドキュメント(つまりデータを扱うクラス)が持つという意味でした。NekoView.cppに定義のあるCNekoView::OnMouseMoveの中で、このpoint_dataにその時のマウスの座標を記録させ、ウインドウが再描画されるときには、そのとき呼び出される特別な関数CNekoView::OnDrawで、point_dataの位置(だけ)に「猫」という字を書かせたのでした。
CPoint data[100];などとやると、点の場所を100まで記録できますが、それ以上は無理です。この数を非常に大きくしておけば、良いかもしれませんが、どうも無駄が多くて気持ちが悪いですね。
CArray<int, int> h;とすれば良いのです。<int, int>は整数の配列だよ、という意味です。(なんで<や>があるんだろう。intがふたつあるのはなぜかな、と思ったらヘルプを見てください。ここではこれでゆるしてくださいね。)配列なのに[100]みたいなのがないぞ、と思うかもしれません。これがCArrayのみそなんです。普通の
int x[100];では、要素の数が100と決められていて、後からその数を大きくすることはできません。しかし、CArrayのhははじめに大きさを決めないで、要素が増えると(必要に応じて)勝手に大きくなってくれるのです。その詳しい使い方はまた後でやりましょう。
class CNekoDoc : public CDocumentの直前に、
class CNekoStroke { public: CArray<CPoint, CPoint> m_points; void DrawNekoStroke(CDC* pDC); };と入れてください。このクラスはCPointを要素に持つCArrayのインスタンスm_pointsをデータメンバに持ち、DrawNekoStrokeをパブリックなメンバ関数に持つクラスです。繰り返しますが、m_pointsがマウスの動いた点(CPoint)を格納する配列です。(その配列の大きさははじめに決めなくても、要素が増えると自然に大きくなっていくのでした。)DrawNekoStrokeは引数に受け取ったpDC(デバイスコンテキスト(へのポインタ))を使って「猫」と書いていく関数です。
///////////////////////////////////////////////////////////////////////////// // CNekoDocの直前に、
void CNekoStroke::DrawNekoStroke(CDC* pDC) { for(int i=0; i<m_points.GetSize(); i++) pDC->TextOut(m_points[i].x, m_points[i].y, "猫"); }としてください。ちょっと難しいですか?まず、CArrayであるm_pointsにはたくさんのCPointが格納されていると思ってください。(後でそのようにします。)
m_points.GetSize()で知ることができます。このGetSizeはCArrayのメンバ関数で、実際に格納されている要素の数を戻す関数なのです。また、i番の要素は普通の配列のようにm_points[i]と表されます。以上で、上のコードの意味はわかると思います。(pDCやTextOutがわからない人はちょっと復習してください。)この関数の使い方は後で考えましょう。
CPoints point_data;を
CNekoStroke data;に直してください。CNekoStrokeのインスタンスdata(の中のm_points)が、マウスの位置CPointを格納する場所になるのです。
void CNekoView::OnMouseMove(UINT nFlags, CPoint point) { //マウスをキャプチャしていなければ、何もしない if(GetCapture()!=this) return; CNekoDoc* pDoc=GetDocument(); pDoc->data.m_points.Add(point); CClientDC dc(this); dc.TextOut(point.x, point.y, "猫"); }と直してください。もちろん、ポイントは
pDoc->data.m_points.Add(point);です。pDocの中のdataの中のm_pointsを取ってきて、その関数Addを使っているのがわかるでしょうか。(pDocはドキュメント、つまりCNekoDoc(のインスタンス)へのポインタで、dataはCNekoDocのデータメンバのCNekoStroke、m_pointsはCNekoStrokeのデータメンバのCArrayなのでした。)AddはCArrayのメンバ関数で、上のようにすると、m_pointsに新たな要素としてpointが格納されるのです。(m_pointsの大きさは何もしなくてもAddとするだけで自動的に大きくなっていきます。)これで、マウスの位置が記録されてしまうのです。簡単ですね。(ただ、pDoc->data.m_pointsという、何々の何々の何々、、みたいなコードはわかりづらいかもしれません。これは後の方で改良するかもしれません。)
void CNekoView::OnDraw(CDC* pDC) { CNekoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDoc->data.DrawNekoStroke(pDC); }と直してください。これで、ウインドウの大きさを変えたりしても、常に、OnDrawが働いて「猫」の軌跡が再描画されるようになりました。
#include <afxwin.h> // MFC のコアおよび標準コンポーネント #include <afxext.h> // MFC の拡張部分 #include <afxtempl.h>のように、#include <afxext.h>の後あたりが良さそうです。
実は、ここまでで、ちょっとおまけができています。というのは、もう、印刷可能なんです。(実は前回からでしたが。)実はOnDrawという関数は印刷のときにも使われるのです。逆に言うとOnDrawをちゃんと書けば、印刷もできるのです。適当に、マウスで猫という字を書いたら、メニューかボタンで印刷してみてください。
ちょっと残念ですが、ウインドウの画面で見るのと、印刷された紙の上で見るのでは、縮尺が違っていて、思ったように印刷されていないと思います。この縮尺を調節する方法ももちろんありますが、この講座では省略します。(例えば、もう少し後にチュートリアルを見てください。)
class CNekoStroke { public: CString str; //コンストラクタで、strに「猫」を代入 //CStringを使うと下のように書ける CNekoStroke(){str="猫";} CArray<CPoint, CPoint> m_points; void DrawNekoStroke(CDC* pDC); };とし、DrawNekoStrokeの定義を
void CNekoStroke::DrawNekoStroke(CDC* pDC) { for(int i=0; i<m_points.GetSize(); i++) pDC->TextOut(m_points[i].x, m_points[i].y, str); }とし、CNekoViewのOnMouseMoveを
void CNekoView::OnMouseMove(UINT nFlags, CPoint point) { //マウスをキャプチャしていなければ、何もしない if(GetCapture()!=this) return; CNekoDoc* pDoc=GetDocument(); pDoc->data.m_points.Add(point); CClientDC dc(this); dc.TextOut(point.x, point.y, pDoc->data.str); }と直してください。こうすれば、猫を犬に変更したい場合、CNekoStrokeのコンストラクタをstr="猫";からstr="犬";に書き換えれば良いことになります。元気な人はちょっとやってみてください。