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="犬";に書き換えれば良いことになります。元気な人はちょっとやってみてください。