フォントタグを実現しよう
第6週が終了する頃には、少しはタグによるHTMLエディタらしくなってきていると思います。各自、よく使用するであろうと思われるタグを挿入するコードのコーディングはできているはずです。しかしリンクタグ、フォントタグ、イメージタグだけはただ挟み込むだけじゃないタグなのでまだコーディングできていないはずです。(厳密に言うと改行<BR>や水平線<HR>タグも挟み込むだけじゃないいんですけどね)これらのタグは、タグの間にオプションを書き込むことによって、意味を成します。例えばリンクタグ<A HREF>は、リンク先の指定を行わないと意味がないですしね。
そこでそこで今回は、フォントタグのコーディングを行っていこうと思います。
18.フォントの種類、色、大きさを指定するダイアログ、ダイアログクラスを作成する。
手っ取り早く言ってしまえば、第6-2章の最後でもお見せした、右のダイアログを作成してしまいます。このダイアログを作ることによって、こんなことが可能になります。
1.文字の色や大きさを指定したい文字列をハイライト(選択)する。
2.メニューで[挿入]-[フォント]を押す。
3.ダイアログが開く
ユーザが色やフォントの種類、文字の大きさなどを指定します。
4.指定されたオプションに基づいてタグを生成する。
5.できたタグで挟み込んで終了
このようにすることでよりユーザインタフェースの高いものになることが分かりますね。ユーザは、文字の色などを指定するだけで、あとはPCがタグを全て打ち込んでくれるので非常に便利になります。
それでは早速リソースを編集していきましょう。
1.ダイアログを新規追加する。
IDは、IDD_DLG_FONTなどとしておきます。
2.以下の図のようにコントロールを配置します。
IDは自由ですが、今回は図のようなIDで説明していきます。
3.IDC_FONT_EDT_COL、IDC_FONT_EDT_FONT、IDC_FONT_EDT_SIZEを読み取り専用に設定する。
コントロールを右クリックし、プロパティを表示させます。[スタイル]の読み取り専用をチェックします。こうすることでエディットボックスが灰色になります。
ダイアログベース(MDIやSDI形式でないもの)のリソースを編集して実行すると、すぐに反映されますが、今回のソフトのようにSDI形式でダイアログを追加しただけでは、このダイアログは使用することすらできません。(使うこともできなくはないですが、非常に使いにくいためです)
ではどうやって使用するか…ということになりますが、このダイアログを操作するためのclassを作成すれば、普通のMFCクラスのように使用することができそうです。
でもクラスを最初から作るのは面倒ですし、大体ダイアログを作成するクラスなんてそう簡単に作れるわけがないし…。そこでC++の継承という概念を使ってクラスを作ります。継承とは、あるクラスを丸ごと含んだ新しいクラスを作成することです。話し込むとかなり長くなるので、ここではダイアログを例にとって説明します。MFCには、ダイアログを作成・管理するクラスとしてCDialogクラスが用意されていますが、私たちが作成するダイアログクラスには、このCDialogクラスの機能はもちろん、この自作クラスだけの独自の機能(関数)があるのが望ましいでしょう。そこで新しく作るダイアログはCDialogを継承すると非常に便利です。
継承の基になるクラス(例でいうとCDialogクラス)を基本クラス、そして継承によって作成したクラスを継承(後継)クラスなどと言います。
変なたとえ話ですが、自分は攻撃魔法の使える魔法使いだとします。ここで転職して、格闘家になるとします。するとこの格闘家は、攻撃魔法の使える格闘家になりますよね。継承とはこんな感じを指します。

話は長くなりましたが、ダイアログのクラスを作成します。
1.リソースタブのIDD_DLG_FONTをハイライトする。
2.ClassWizardを起動する。
2.メニューの[表示]-[ClassWizard]で行えます。
3.メッセージが出てくるが、新規にクラスを作成するほうを選ぶ。
4.クラスの新規作成をする。
クラス名:CDlgSetFont(フォント設定ダイアログクラスの意味です)
基本クラス:CDialog
5.ワークスペースのClassViewにCDlgSetFontができていれば○。
ダイアログを追加して、コーディングに使えるようにするためには、ダイアログをクラスにしておく必要があるのを忘れてはいけません。これは要チェックです。
19.メンバ変数を割り当てる
先ほど作成したダイアログのコントロールを使いやすくするために、メンバ変数を割り当てます。メンバ変数を割り当てることで、コーディングの際に、割り当てられたコントロールを使用することができるからです。
1.クラスウィザードを起動し、メンバ変数タブをクリック
このとき、CDlgSetFontクラスが選択されていることを確認してください。
2.コントロールIDのIDC_FONT_CHECK_COLを選択し、変数の追加を押す
こんな画面が出てくるので、図のように入れてみてください。
3.2の操作を繰返し行う。
つけるべき変数名は、図を参考にしてください。(変数名は自由に変えても構いませんが、図の変数名をつけたとして以下は進めていきます)
--変数名のつけ方--
メンバだと分かるように"m_"をつけ、変数の型が分かるようにしておく(BOOL型だったらbをつけたり、整数型だったらn、CString型だったらstr)のが一般的です。
4.dlgsetfont.hを開き、以下のコードを追加する
/////////////////////////////////////////////////////////////////////////////
// CDlgSetFont ダイアログ

class CDlgSetFont : public CDialog
{
// コンストラクション
public:
	BOOL	m_bColor;	// ここを追加
	BOOL	m_bFont;	// ここを追加
	BOOL	m_bSize;	// ここを追加
	
	CDlgSetFont(CWnd* pParent = NULL);   // 標準のコンストラクタ 
これらは、ダイアログの中のコントロールに割り当てるのではなく、ダイアログクラス内部で使用するメンバ変数です。チェックボタンの状態を示すものです。(選択されているときにはtrue、チェックされていないときはfalseの値を持つようにあとでコーディングします)。
5.メンバの初期値を決める
dlgsetfont.cppのコンストラクタを以下のように書き換える。
CDlgSetFont::CDlgSetFont(CWnd* pParent /*=NULL*/)
	: CDialog(CDlgSetFont::IDD, pParent)
{
	m_bColor = false;
	m_bFont = false;
	m_bSize = false;
	//{{AFX_DATA_INIT(CDlgSetFont)
	m_strColor = _T("");
	m_strFont = _T("");
	m_nFontSize = 4;
	//}}AFX_DATA_INIT
}
19.ダイアログの動作を決める
ここではチェックボックスをによるコントロールの制御について考えていきます。具体的に言うと、チェックが入っていない場合に、ボタンやスピンコントロールを押しても動作させないような動作にします。
1.クラスウィザードを起動し、メッセージマップタブをクリック
2.IDC_FONT_CHECK_COLを選択し、メッセージにBN_CLICKEDを選択、関数の追加を押す。
3.同様のことをIDC_FONT_CHECK_SIZEとIDC_FONT_CHECK_FONTに関して行う。
追加した関数は、チェックボックスをクリック(すなわちチェックをつけたり消したりする)瞬間に呼ばれる関数です。
4.コードの編集で以下をコーディングする
void CDlgSetFont::OnFontCheckCol() 
{
	CEdit* pEdt = (CEdit*)GetDlgItem(IDC_FONT_EDT_COL);
	if(!m_bColor){
		pEdt->SetReadOnly(false);
		m_bColor = true;
	}
	else{
		m_bColor = false;
		pEdt->SetReadOnly(true);
	}

}

void CDlgSetFont::OnFontCheckFont() 
{
	if(!m_bFont)	
		m_bFont = true;
	else
		m_bFont = false;
}

void CDlgSetFont::OnFontCheckSize() 
{
	if(!m_bSize)
		m_bSize = true;
	else
		m_bSize = false;
}
まずはOnFontCheckFont()とOnFontCheckSize()から見ていきましょう。構文を見れば分かる通り、チェックボックスが押されたときに、現在のフラグm_b****を反転させているだけですね。これらのフラグの値によって、対応するボタンが押された時の処理を決めることで、ボタンを制御していきます。
OnFontCheckCol()の方は、少し複雑ですが、OnFontCheckFont()関数と似たようなことをやっているのはお気づきでしょうか?違うところは、pEdt->SetReadOnly(***);という関数があることですね。これらについてちょっと解説します。
CEdit* pEdt = (CEdit*)GetDlgItem(IDC_FONT_EDT_COL);
これは、IDからそのIDのクラス(ここではエディットボックス)のポインタを取得する関数です。これによって、コントロールのクラスを操作することができます。ここではCEditクラスのポインタを取得していますが、これは次に説明するSetReadOnly関数を実行するために使用します。
pEdt->SetReadOnly(false);
エディットボックスを読み取り専用にするかしないかを決めます。引数がtrueの時は、読み取り専用に、falseの時は通常の状態にします。コードを見れば分かると思いますが、チェックしたときのみ色指定のエディットボックスは、書き込むことができるようになるという仕組みです。
これでエディットボックスと現在の選択フラグの設定はできました。後はボタンの制御ができればいいですね。
1.クラスウィザードを起動する
このときCDlgSetFontが選択されているか確認しましょう
2.IDC_FONT_BTN_COLORを選択し、メッセージにBN_CLICKEDを指定し、関数の追加をする
3.IDC_FONT_BTN_FONTにも同様のことを行う
4.コードの編集で以下をコーディングする
void CDlgSetFont::OnSetFont() 
{
	if(!m_bFont){return;}
 	CFontDialog   cDlg(NULL, CF_SCREENFONTS|CF_INITTOLOGFONTSTRUCT,NULL,NULL);//FONTダイアログ
 	if (cDlg.DoModal() != IDOK){return ;}
	
	m_strFont = cDlg.GetFaceName();
	UpdateData(false);
}

void CDlgSetFont::OnSetColor() 
{
	if(!m_bColor){return;}

	m_strColor = GetColorString();
	UpdateData(false);
}
二つの関数において注目すべき点は、if(!m_bFont){return;}とif(!m_bColor){return;}です。つまりフラグが立っていない(trueでない)時は、何もしないで関数を抜けるという操作を行っています。これでチェックボックスでボタンをコントロールすることができていることが分かります。

またUpdateData(false);についてはお分かりだと思いますが、メンバ変数に保持されているデータを、割り当てられているコントロールに反映する関数です。

なんだか懐かしい関数GetColorString()が出てきましたね。この関数の動作確認がまだでしたね。ではこの関数とダイアログ全体の動作確認をしてみたいと思います。
1.リソースのメニューの[挿入]に、フォントを追加する。(リソースを編集)
もうリソースの編集方法はお分かりですね。リソースIDは、ID_INS_FONTとしておきましょう。
2.クラスウィザードを起動する。
このとき、クラスにCMyHtmlViewを選択してください。
3.ID_INS_FONTにメッセージBN_CLICKEDをバインド(選択して関数の追加をすること)する
4.以下のコードを記述する
// インクルードファイル ←インクルードしておく
#include "DlgSetFont.h"

//フォントの設定ダイアログ
void CMyHtmlView::OnInsFont() 
{
	CDlgSetFont cDlg;
	if(cDlg.DoModal()!=IDOK){return ;}
}
動作確認をしてみましょう。チェックボタンがうまく動作していますか?そしてチェックボタンの状態によってカラーのエディットボックスが白・灰色に変化するでしょうか?またカラーボタンもうまく色が選択できますか?例えば黒だったら#000000が表示されれば成功です。
気づいた人もいるかと思いますが、スピンコントロールがまだ未完成ですね。次はスピンコントロールの実装です。スピンコントロールの実装といってもそんなに大げさなことはしませんのでご安心を。

スピンコントロールは、増加または減少のキーがあるやつですね。CSpinButtonCtrlクラスが存在するのですが、ここでは別にクラスを作るほどでもないのでもう少し簡単な方法で実装していきます。具体的な動作はこんな感じです。
  1.  スピンコントロールの上下ボタンが押される(UDN_DELTAPOSメッセージが送られる)
  2.  UDN_DELTAPOSにバインドした関数が実行される(UDN_DELTAPOSをキャッチする)
  3.  「上」を押されていたら上記関数内部でCDlgSetFontクラスのメンバm_nFontSizeの値を1増やし、また「下」を押されていたら上記関数内部でCDlgSetFontクラスのメンバm_nFontSizeの値を1減らす。
  4. m_nFontSizeの値wをIDC_FONT_EDT_SIZEのエディットボックスに反映させる。
とやっていけばうまくいきそうです。では早速作業です。
1.クラスウィザードを起動(以下を選択しておく)
クラス:CDlgSetFont
ID:IDC_FONT_SPINSIZE
メッセージ:UDN_DELTAPOS
UDN_DELTAPOSはスピンコントロールが押された時に送られるメッセージです
2.関数の追加
3.以下をコーディングする。
//フォント サイズ スピンコントロール
void CDlgSetFont::OnFontSpinSize(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;

	// Flag
	if(!m_bSize){return;}

	// Up
	if(pNMUpDown->iDelta != 1){
		if(m_nFontSize != 7){
			m_nFontSize++;
			UpdateData(false);
		}
		else AfxMessageBox("フォントサイズは1〜7で指定してください");
	}
	// Down
	else{
		if(m_nFontSize != 1){
			m_nFontSize--;
			UpdateData(false);
		}
		else AfxMessageBox("フォントサイズは1〜7で指定してください");
	}		
	*pResult = 0;
}
では、いつもの通り解説していきます。
NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
これはおまじないだと思って構いません。アップダウンの情報を得るために必要な変数を定義したと思ってくれればいいと思います。
if(!m_bSize){return;}
フラグ、"サイズ"のチェックボックスがチェックされていない場合はreturn;が実行されるようになります。チェックされているときのみだけ、これ以降のコードを実行するために書かれています。
if(pNMUpDown->iDelta != 1){
		if(m_nFontSize != 7){
			m_nFontSize++;
			UpdateData(false);
		}
		else AfxMessageBox("フォントサイズは1〜7で指定してください");
}
pNMUpDown->iDeltaの値が1以外のときは、スピンコントロールのキーが押されています。つまりフォントサイズをあげるときの命令に対応していますのでm_nFontSize++;でサイズを上げ、その情報を即座にコントロールに反映させています。しかしHTMLの性質上フォントサイズは1〜7なので、現在のフォントサイズm_nFontSizeが7の時は、これ以上サイズをあげてはいけないのでelse以降を実行します。
else{
		if(m_nFontSize != 1){
			m_nFontSize--;
			UpdateData(false);
		}
		else AfxMessageBox(IDS_ERROR_FONT);
}
pNMUpDown->iDeltaの値が1のときは、スピンコントロールのキーが押されています。つまりフォントサイズをさげるときの命令に対応していますのでm_nFontSize++;でサイズをさげ、その情報を即座にコントロールに反映させています。しかしHTMLの性質上フォントサイズは1〜7なので、現在のフォントサイズm_nFontSizeが1の時は、これ以上サイズを下げてはいけないのでelse以降を実行します。
*pResult = 0;
こいつもおまじないみたいなものですね(笑)
さて実行してみてください。これで全てのコントロールが動作していると思います。もし動作しないようだったら、いま一度コードを確かめてみましょう。

20.タグ生成コードを作成する
早いものでもう20まで来てしまいました。少しずつソフトを作ることが楽しくなっていただければいいのですが、ここからが山場ですからね〜(笑)

まずは、先ほど少し書いたCMyHtmlView::OnInsFont()関数ですが、以下のように書き直していきます。
//フォントの設定ダイアログ
void CMyHtmlView::OnInsFont() 
{
	CString strSel;		//選択されている文字列

	CDlgSetFont cDlg;
	if(cDlg.DoModal()!=IDOK){return ;}

	/* 現在選択されている文字列を取得 */
	CEdit& cEdt = GetEditCtrl();
	cEdt.Copy();
	strSel = GetClipBoard();

	cDlg.MakeTag(strSel);		//タグの生成
	PasteString(cDlg.strTag);	//作成したタグ付文字列をペースト
	SetFocus();		//フォーカスを設定する
}
 ここで実行すると、cDlg.MakeTag(strSel);PasteString(cDlg.strTag);でエラーが出ます。何故ならCDlgSetFontクラスにはMakeTagというメンバ関数とstrTagというメンバ変数が存在しないからです。まずこれらのメンバを追加していきます。
1.DlgSetFont.hを開いて以下のように編集する
/////////////////////////////////////////////////////////////////////////////
// CDlgSetFont ダイアログ

class CDlgSetFont : public CDialog
{
// コンストラクション
public:
	BOOL	m_bColor;
	BOOL	m_bFont;
	BOOL	m_bSize;

	void MakeTag(CString str);	// ここを追加
	CString strTag;		// ここを追加

	CDlgSetFont(CWnd* pParent = NULL);   // 標準のコンストラクタ
2.DlgSetFont.hを開き、ファイルの最後に以下のコードを追加する
// CDlgSetFont::MakeTag(CString)  タグの生成
void CDlgSetFont::MakeTag(CString strIns)
{
	CString strTagA,strTagB,cTemp;
	strTagA = "<FONT";
	if(m_bSize){
		cTemp.Format("%d",m_nFontSize);
		strTagA += " SIZE=";
		strTagA += cTemp;
	}
	if(m_bColor){
		strTagA += " COLOR=\"";
		strTagA += m_strColor;
		strTagA += "\"";
	}
	if(m_bFont){
		strTagA += " FACE=\"";
		strTagA += m_strFont;
		strTagA +="\"";
	}
	strTagA += ">";

	strTagB = "</FONT>";
	strTag = strTagA + strIns + strTagB;
}
3.実行する
実行してみます。このときフォントタグでしっかり文字列がはさめるかどうか確認してください。またチェックを入れたところだけ、オプションがしっかり書かれるかどうかも確認しておきましょう。
m_b***は、先ほどから何回も書いている通りに、チェックボックスにチェックが入っている時にtrue,入っていない時にはfalseになるように設定してあるフラグです。チェックが入っている=m_b***が"真(true)"=FONTタグオプションをつける、としてプログラムを組んでいます。コードの解読はそんなに難しいものではないと思います。もっとよいコーディング方法があると思いますので、みなさんが一番いいと思うコーディングをしていただけたらうれしいです。

最後に実行例を紹介してこの章を終わりにしたいと思います。

実行した結果


おまけですが、HTMLは、色コード"#○○○○○○"でなくて、直接色指定できる色(純色)があり、図のようにredと書き込むこんでもうまくいきます。実行例の3行目の色指定は、直接色の名前を書いた例です。色コードも色名も文字列には代わりがないので、こんなこともできるんです。


[Next]
[Previous]
[Home]