root/TextView.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. IMPLEMENT_DYNCREATE
  2. CTextView
  3. BEGIN_MESSAGE_MAP
  4. AssertValid
  5. Dump
  6. OnInitialUpdate
  7. Font
  8. OnChar
  9. GetCurrentPath
  10. GetCurrentRawHeader
  11. GetCurrentSubject
  12. GetCurrentFrom
  13. GetCurrentTo
  14. GetCurrentCc
  15. GetCurrentDate
  16. GetCurrentMsgID
  17. GetCurrentBody
  18. GetCurrentHeader
  19. GetCurrentCT
  20. GetCurrentCTE
  21. GetCurrentReplyTo
  22. GetCurrentText
  23. IsPgpMime
  24. OnLink
  25. DisplayPart
  26. Update
  27. Update2
  28. OnRButtonDown
  29. OnPopupCopy
  30. OnPopupSelall
  31. OnPopupBrowser
  32. OnPopupEditor
  33. OnSetFocus
  34. SetFontEx
  35. SetFontColorAndLink
  36. ReDisplay
  37. GetFontEx
  38. Clear

/*
 * Copyright (C) 2002-2003 chik, s.hiranaka
 * For license terms, see the file COPYING in this directory.
 */

// TextView.cpp : インプリメンテーション ファイル
//

#include "stdafx.h"
#include "Pochy.h"
#include "TextView.h"
#include "FetchMail.h"
#include "CodeConvert.h"
#include "mainfrm.h"
#include "sendmail.h"
#include "PassPhraseDlg.h"
#include "HeaderInfo.h"
#include <Shlwapi.h> // StrStrI プロジェクトの設定のリンクに"Shlwapi.lib"を追加
#include "quoted-printable.h"
#include "base64.h"
//#include "_regex.h"
#include "lib.h"
#include "direct.h"                     // _mkdir
#include "Gpg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CTextView

IMPLEMENT_DYNCREATE(CTextView, CRichEditView)

CTextView::CTextView()
{
        m_current_part = -1;
}

CTextView::~CTextView()
{
}


BEGIN_MESSAGE_MAP(CTextView, CRichEditView)
        //{{AFX_MSG_MAP(CTextView)
        ON_WM_CHAR()
        ON_WM_RBUTTONDOWN()
        ON_COMMAND(ID_TEXTVIEW_COPY, OnPopupCopy)
        ON_COMMAND(ID_TEXTVIEW_SELALL, OnPopupSelall)
        ON_COMMAND(ID_TEXTVIEW_BROWSER, OnPopupBrowser)
        ON_COMMAND(ID_TEXTVIEW_EDITOR, OnPopupEditor)
        ON_WM_SETFOCUS()
        //}}AFX_MSG_MAP
        ON_NOTIFY_REFLECT_EX(EN_LINK, OnLink)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTextView 描画

void CTextView::OnDraw(CDC* pDC)
{
        CDocument* pDoc = GetDocument();
}

/////////////////////////////////////////////////////////////////////////////
// CTextView 診断

#ifdef _DEBUG
void CTextView::AssertValid() const
{
        CRichEditView::AssertValid();
}

void CTextView::Dump(CDumpContext& dc) const
{
        CRichEditView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CTextView メッセージ ハンドラ

void CTextView::OnInitialUpdate()
{
        CRichEditView::OnInitialUpdate();
        CPochyApp *app = (CPochyApp *)AfxGetApp();

        CRichEditCtrl& r = GetRichEditCtrl();

 /*   HWND hwnd = r.m_hWnd;
    int x = ::SendMessage (hwnd, EM_GETLANGOPTIONS, 0, 0);
    x &= ~(IMF_AUTOFONT);
    ::SendMessage(hwnd, EM_SETLANGOPTIONS, 0, x);*/

        LOGFONT lf;
        LPBYTE pData;
        UINT nSize;
        if(app->GetProfileBinary("TextViewFont", "LogFont", &pData, &nSize)){
                ASSERT(nSize == sizeof(LOGFONT));
                ::CopyMemory(&lf, pData, sizeof(LOGFONT));
                delete [] pData; 
        }else{
//              ::GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf);
                lf.lfHeight = -16;
                lf.lfWidth = 0;
                lf.lfEscapement = 0;
                lf.lfOrientation = 0;
                lf.lfWeight = 400;
                lf.lfItalic = 0;
                lf.lfUnderline = 0;
                lf.lfStrikeOut = 0;
                lf.lfCharSet = 128;
                lf.lfOutPrecision = 3;
                lf.lfClipPrecision = 2;
                lf.lfQuality = 1;
                lf.lfPitchAndFamily = 49;
                strcpy(lf.lfFaceName, "MS ゴシック");
        }
        this->SetFontEx(lf);

        r.SetBackgroundColor(FALSE, RGB(
                                app->GetProfileInt("TextViewColor", "BkColorR", DEF_BKG_COLOR_R),
                                app->GetProfileInt("TextViewColor", "BkColorG", DEF_BKG_COLOR_G),
                                app->GetProfileInt("TextViewColor", "BkColorB", DEF_BKG_COLOR_B)));
        r.SetReadOnly(TRUE);

        this->SetFontColorAndLink();

//      if(設定で折り返しなしになっていたら以下を実行)
//      m_nWordWrap = WrapNone;
//      WrapChanged(); // 折り返しなし
//      r.SetOptions(ECOOP_OR,ECO_AUTOHSCROLL);
}

void CTextView::Font()
{
        CRichEditCtrl& r = GetRichEditCtrl();
        CHARFORMAT cf;
        DWORD dwMask = r.GetDefaultCharFormat(cf);

        LOGFONT lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "\0"};
        if(cf.dwEffects & CFE_STRIKEOUT)
                lf.lfStrikeOut = 1;
        if(cf.dwEffects & CFE_UNDERLINE)
                lf.lfUnderline = 1;
        if(cf.dwEffects & CFE_ITALIC)
                lf.lfItalic |= 1;
        if(cf.dwEffects & CFE_BOLD)
                lf.lfWeight = FW_BOLD;
        else
                lf.lfWeight = FW_NORMAL;
        strcpy(lf.lfFaceName, cf.szFaceName);
        lf.lfPitchAndFamily = cf.bPitchAndFamily;
        lf.lfCharSet = cf.bCharSet;
        lf.lfHeight = cf.yHeight * 4/3 * 1/20;

        CFontDialog fd(&lf, CF_EFFECTS | CF_SCREENFONTS | CF_BOTH, NULL, this);
        fd.m_cf.rgbColors = cf.crTextColor;
        if(fd.DoModal() == IDOK){
                int minilen = min(strlen(lf.lfFaceName), LF_FACESIZE);
                strncpy(cf.szFaceName, lf.lfFaceName, minilen);

                cf.bPitchAndFamily = lf.lfPitchAndFamily;
                cf.bCharSet = lf.lfCharSet;

                cf.yHeight = fd.m_cf.iPointSize * 2;
                cf.crTextColor = fd.m_cf.rgbColors;
                cf.dwEffects &= ~CFE_AUTOCOLOR;

                cf.dwMask = CFM_BOLD | CFM_CHARSET | CFM_COLOR | CFM_FACE | CFM_ITALIC | CFM_SIZE | CFM_OFFSET | CFM_STRIKEOUT | CFM_UNDERLINE;
                if(lf.lfStrikeOut)
                        cf.dwEffects |= CFE_STRIKEOUT;
                else
                        cf.dwEffects &= ~CFE_STRIKEOUT;
                if(lf.lfUnderline)
                        cf.dwEffects |= CFE_UNDERLINE;
                else
                        cf.dwEffects &= ~CFE_UNDERLINE;
                if(lf.lfItalic)
                        cf.dwEffects |= CFE_ITALIC;
                else
                        cf.dwEffects &= ~CFE_ITALIC;
                if(lf.lfWeight > FW_NORMAL)
                        cf.dwEffects |= CFE_BOLD;
                else
                        cf.dwEffects &= ~CFE_BOLD;

                r.SetDefaultCharFormat(cf);
                Invalidate(TRUE);
        }
        TRACE("height=%d\n",cf.yHeight);
}

void CTextView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
        CPochyApp* app = (CPochyApp*)AfxGetApp();
        CMainFrame *mf = (CMainFrame*)app->m_pMainWnd;

        if(mf->Char(nChar))
                return;

        switch(nChar){
                case '<': // 実装予定
                        break;
                case '>': // 実装予定
                        break;
                default:
                        break;
        }
}

CString CTextView::GetCurrentPath()
{
        return m_path;
}

CString CTextView::GetCurrentRawHeader()
{
        return m_md.m_hi.GetAllDecoded();
}

CString CTextView::GetCurrentSubject()
{
        return m_md.m_hi.GetSubject();
}

CString CTextView::GetCurrentFrom()
{
        return m_md.m_hi.GetFrom();
}

CString CTextView::GetCurrentTo()
{
        return m_md.m_hi.GetTo();
}

CString CTextView::GetCurrentCc()
{
        return m_md.m_hi.GetCc();
}

CString CTextView::GetCurrentDate()
{
        return m_md.m_hi.GetDate();
}

CString CTextView::GetCurrentMsgID()
{
        return m_md.m_hi.GetMsgID();
}

CString CTextView::GetCurrentBody()
{
        return m_body;
}

CString CTextView::GetCurrentHeader()
{
        return m_header;
}

CString CTextView::GetCurrentCT()
{
        return m_md.GetContentType(m_current_part);
}

int CTextView::GetCurrentCTE()
{
        return m_md.GetEncodingType(m_current_part);
}

CString CTextView::GetCurrentReplyTo()
{
        return m_md.m_hi.GetReplyTo();
}

CString CTextView::GetCurrentText()
{
        return m_text;
}

BOOL CTextView::IsPgpMime()
{
        return g_cstr_compare(m_md.m_hi.GetProtocol(), "application/pgp-encrypted");
}

void CTextView::OnLink(NMHDR* in_pNotifyHeader, LRESULT* out_pResult)
{
        *out_pResult = 0;
        CPochyApp *app = (CPochyApp *)AfxGetApp();
        CMainFrame *mf = (CMainFrame*)app->m_pMainWnd;
        CString current_account = mf->m_pAcntV->GetCurrentAccountName();

        ENLINK * pLink = (ENLINK*)in_pNotifyHeader;
        if(pLink->msg == WM_LBUTTONDBLCLK && m_onlink != pLink->lParam){
                // 二重起動を防ぐために現在のマウスのクリックされたポイントを保存しておく
                m_onlink = pLink->lParam; // 苦肉の策

                // クリックされたリンクの文字列を取得
                GetRichEditCtrl().SetSel(pLink->chrg);
                CString link = GetRichEditCtrl().GetSelText();

                if(link.Find("mailto:") == 0||link.Find("Mailto:") == 0){
                        // initialization.
                        app->m_me.Initialize();
                        // setting To
                        if(link.Find("?", 7) != -1) // mailto:hoge@hoge.com?subject=PGP%20Key&Body=Please%20send%20keyとか
                                // subject, body is not implemented.
                                app->m_me.SetTo(link.Mid(7, link.Find("?", 7)-7));
                        else
                                app->m_me.SetTo(link.Mid(7));
                        // setting From
                        CString path = app->m_app_path+"\\"+current_account+"\\account.ini";
                        CString address;
                        GetPrivateProfileString("mailbox", "address", "", address.GetBuffer(BUF_LENGTH), BUF_LENGTH, path);
                        address.ReleaseBuffer();
                        app->m_me.SetFrom(address);

                        // getting signature.
                        CString signature;
                        g_file2cstring(app->m_app_path+"\\"+current_account+"\\signature", signature);
                        // set main text.
                        app->m_me.AddText(signature);
                        // draftframe
                        app->CreateDraftFrame(mf->m_pAcntV->GetCurrentAccountName());
                }else{
                        ShellExecute(this->GetSafeHwnd(), _T( "open" ), link, NULL, NULL, SW_NORMAL);
                }
        }
}

void CTextView::DisplayPart(int part)
{
        CPochyApp *app = (CPochyApp *)AfxGetApp();
        CStringArray body;
        CString message;
        CString header;

        m_current_part = part;
        m_md.GetBody(part, body);
        g_cstra2cstr(body, message, g_cstra_getsize(body));

        if(m_md.GetFileName(part).IsEmpty()){
                // in case of base64 in text/plain or text/html.
                if(m_md.GetEncodingType(part) == BASE64){
                        message.Remove('\r');
                        message.Remove('\n');
                        char* in_base64 = message.GetBuffer(0);
                        char* out_base64 = new char[message.GetLength()];
                        int len = from64tobits(out_base64, in_base64, strlen(in_base64));
                        out_base64[len] = 0;
                        message.Empty();
                        message = out_base64;
                        delete out_base64;
                }
                // in case of quoted-printable in text/plain or text/html
                if(m_md.GetEncodingType(part) == QUOTED_PRINTABLE){
                        // fromQPtobitsの実装があやしい
                        // implementation of fromQPtobits is weird.
                        char* in_qp = message.GetBuffer(0);
                        char* out_qp = new char[message.GetLength()+10]; // これではbufferの容量が足りないみたいで時々これが原因でお亡くなりになることが。。。 
                        // ↑の+10はfromQPtobitsの最後で挿入されるNULL文字文 
                        // +10 mean fromQPtobits insert NULL last.
                        // CString の GetBuffer はNULL文字は含まないのか???? 
                        // not clear that CString::GetBuffer not include NULL ?????
                        int len = fromQPtobits(out_qp, in_qp, strlen(in_qp));
                        out_qp[len] = 0;
                        message.Empty();
                        message = out_qp;
                        delete out_qp;
                }
                CCodeConvert cc(message);
                message = cc.ToSjis();
        }else{ // in case of attachment file.
                if(GetCurrentCTE() == SEVEN_BIT){
                        CCodeConvert cc(message);
                        message = cc.ToSjis();
                }
                else if(GetCurrentCTE() == EIGHT_BIT){
                        CCodeConvert cc(message);
                        message = cc.ToSjis();
                }else{
                        message.Format("添付ファイル\r\n%s", m_md.GetFileName(part));
                }
        }

        m_body = message;

        // 表示用ヘッダの準備
        // prepare for heaer info displayed on textview.
        if(!m_md.m_hi.GetTo().IsEmpty())
                header += "To: "+m_md.m_hi.GetTo()+"\r\n";
        if(!m_md.m_hi.GetCc().IsEmpty())
                header += "Cc: "+m_md.m_hi.GetCc()+"\r\n";
        if(!m_md.m_hi.GetBcc().IsEmpty())
                header += "Bcc: "+m_md.m_hi.GetBcc()+"\r\n";
        if(!m_md.m_hi.GetFrom().IsEmpty())
                header += "From: "+m_md.m_hi.GetFrom()+"\r\n";
        if(!m_md.m_hi.GetSubject().IsEmpty())
                header += "Subject: "+m_md.m_hi.GetSubject()+"\r\n";
        if(!m_md.m_hi.GetDate().IsEmpty())
                header += "Date: "+m_md.m_hi.GetDate()+"\r\n";
        if(!m_md.m_hi.GetReplyTo().IsEmpty())
                header += "Reply-To: "+m_md.m_hi.GetReplyTo()+"\r\n";
        if(!m_md.m_hi.GetXmailer().IsEmpty())
                header += "X-Mailer: "+m_md.m_hi.GetXmailer()+"\r\n";

        m_header = header;

        message = header+"\r\n"+message;
        m_text = message;

        this->SetWindowText("");
        this->SetWindowText(message);

/*      CHARFORMAT cf;
        GetRichEditCtrl().SetSel(0, header.GetLength());
        GetRichEditCtrl().GetSelectionCharFormat(cf);
        cf.dwMask = CFM_COLOR|CFM_BACKCOLOR;
        cf.crTextColor = RGB(
                app->GetProfileInt("TextViewHdColor", "TxtColorR", DEF_HEADER_INFO_COLOR_R),
                app->GetProfileInt("TextViewHdColor", "TxtColorG", DEF_HEADER_INFO_COLOR_G),
                app->GetProfileInt("TextViewHdColor", "TxtColorB", DEF_HEADER_INFO_COLOR_B));
        
        GetRichEditCtrl().SetSelectionCharFormat(cf);
        GetRichEditCtrl().HideSelection(TRUE, FALSE);
        GetRichEditCtrl().SetSel(0, 0);*/
        this->SetFontColorAndLink();
}

void CTextView::Update(CString path)
{
        CPochyApp *app = (CPochyApp *)AfxGetApp();
        CMainFrame *mf = (CMainFrame*)app->m_pMainWnd;
        CListCtrl &lc = mf->m_pMultiV->GetListCtrl();

        m_path = path;

        // after getting 0st element of MULTIPARTSTRUCT in mimedecode class, and display.
        if(path.IsEmpty()){ // if empty, not display. 
                this->Clear();
        }else{
                if(!m_md.DoIt(path)){ // analyze mime structure of mail.
                        this->Clear();
                        return;
                }
                DisplayPart(0); // anyway, 0st element of MULTIPARTSTRUCT is on display.

                // pgp/mime or not
                if(IsPgpMime()&&app->m_gpg_enable)
                        mf->m_button_gpg_dec = TRUE;
                // in case of being not compatible to pgp/mime 
                else if((-1 != GetCurrentBody().Find("-----BEGIN PGP MESSAGE-----"))&&app->m_gpg_enable)
                        mf->m_button_gpg_dec = TRUE;
                else
                        mf->m_button_gpg_dec = FALSE;

                // setting multipartview
                lc.DeleteAllItems();
                int n;
                if(1 < m_md.HowManyPart()){ // in case of two or more than tree multipart.
                        n = 0;
                        CString enc_type;
                        while(n < m_md.HowManyPart()){
                                lc.InsertItem(n, m_md.GetContentType(n));
                                switch(m_md.GetEncodingType(n)){
                                case BASE64:
                                        enc_type = "base64";
                                        break;
                                case QUOTED_PRINTABLE:
                                        enc_type = "quoted-printable";
                                        break;
                                case SEVEN_BIT:
                                        enc_type = "7bit";
                                        break;
                                }
                                lc.SetItemText(n, 1, enc_type);
                                if(!m_md.GetFileName(n).IsEmpty()){
                                        lc.SetItemText(n, 2, m_md.GetFileName(n));
                                }
                                n++;
                        }
                        mf->HideMultiPartView(FALSE);
                        // 0番目のアイテムを選択状態にする
                        // setting 0th item selected.
                        lc.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
                }
                else if(!m_md.GetFileName(0).IsEmpty()){ // in case of only one attachment file.
                        lc.InsertItem(0, m_md.GetFileName(0));

                        mf->HideMultiPartView(FALSE);
                        // setting item of 0 selected.
                        // 0番目のアイテムを選択状態にする
                        lc.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
                }
                else{ // in case of not multipart, nor number of multipart is one, text/html
                        // multipartでない、もしくはmultipartの数が1、もしくはtext/htmlのみの場合
                        mf->HideMultiPartView(TRUE);
                }
                mf->m_pMultiV->ReArrange();
                mf->ReArrangeSplit();
        }
}

void CTextView::Update2(CString mail)
{
        CPochyApp *app = (CPochyApp *)AfxGetApp();
        CMainFrame *mf = (CMainFrame*)app->m_pMainWnd;
        CListCtrl &lc = mf->m_pMultiV->GetListCtrl();

        // 以下mimedecodeのMULTIPARTSTRUCTの0番目の要素を取得して表示
        if(mail.IsEmpty()){ // 空の場合は何も表示しない
                this->Clear();
        }else{
                if(!m_md.DoIt2(mail)){ // メールのmime構造を分析する
                        // メールが壊れていた場合はそのまま表示する。
                        this->Clear();
                        return;
                }
                DisplayPart(0); // 取りあえず、MULTIPARTSTRUCTの0番目の要素を表示

                // pgp/mimeかどうか
                if(IsPgpMime()&&app->m_gpg_enable)
                        mf->m_button_gpg_dec = TRUE;
                // pgp/mime非対応のpgpメール
                else if(-1 != GetCurrentBody().Find("-----BEGIN PGP MESSAGE-----")&&app->m_gpg_enable)
                        mf->m_button_gpg_dec = TRUE;
                else
                        mf->m_button_gpg_dec = FALSE;

                // multipartview の設定
                lc.DeleteAllItems();
                int n;
                if(1 < m_md.HowManyPart()){ // multipart2個以上の場合
                        n = 0;
                        CString enc_type;
                        while(n < m_md.HowManyPart()){
                                        lc.InsertItem(n, m_md.GetContentType(n));
                                        switch(m_md.GetEncodingType(n)){
                                        case BASE64:
                                                enc_type = "base64";
                                                break;
                                        case QUOTED_PRINTABLE:
                                                enc_type = "quoted-printable";
                                                break;
                                        case SEVEN_BIT:
                                                enc_type = "7bit";
                                                break;
                                        }
                                        lc.SetItemText(n, 1, enc_type);
                                        lc.SetItemText(n, 2, m_md.GetFileName(n));
                                n++;
                        }
                        mf->HideMultiPartView(FALSE);
                        // 0番目のアイテムを選択状態にする
                        lc.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);

                }
                else if(!m_md.GetFileName(0).IsEmpty()){ // 添付ファイル1個だけの場合
                        lc.InsertItem(0, m_md.GetFileName(0));
                        mf->HideMultiPartView(FALSE);
                        // 0番目のアイテムを選択状態にする
                        lc.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
                }
                else{ // multipartでない、もしくはmultipartの数が1、もしくはtext/htmlのみの場合
                        mf->HideMultiPartView(TRUE);
                }
                mf->m_pMultiV->ReArrange();
                mf->ReArrangeSplit();
        }
}

void CTextView::OnRButtonDown(UINT nFlags, CPoint point) 
{
        POINT pt;

        PostMessage(WM_NULL, 0, 0); // this is point
        GetCursorPos(&pt);
        CMenu menu;

        menu.LoadMenu(IDR_TEXTVIEW_POPUP);

        // 文字列が選択されているかどうか確認
        CRichEditCtrl &r = GetRichEditCtrl();
        if(r.GetSelText().IsEmpty())
                menu.EnableMenuItem(ID_TEXTVIEW_COPY, MF_GRAYED /* 無効 */);
        else
                menu.EnableMenuItem(ID_TEXTVIEW_COPY, MF_ENABLED /* 有効 */);

        // htmlかtextかbinaryか判断する

        CMenu *pPopup = menu.GetSubMenu(0);
        pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, (int)pt.x, (int)pt.y, this);
        CRichEditView::OnRButtonDown(nFlags, point);
}

void CTextView::OnPopupCopy() 
{
        // 選択されている文字列をクリップボードにコピーする
        CRichEditCtrl &r = GetRichEditCtrl();
        r.Copy();
}

void CTextView::OnPopupSelall() 
{
        // select all
        CRichEditCtrl &r = GetRichEditCtrl();
        r.SetSel(0, -1);
}

void CTextView::OnPopupBrowser() 
{
        CPochyApp *app = (CPochyApp *)AfxGetApp();
        CStringArray body;
        this->m_md.GetBody(this->m_current_part, body);

        CString buf;
        g_cstra2cstr(body, buf, g_cstra_getsize(body));

        if(this->m_md.GetEncodingType(this->m_current_part) == BASE64){
                buf.Remove('\r');
                buf.Remove('\n');
                char* in_base64 = buf.GetBuffer(0);
                char* out_base64 = new char[buf.GetLength()];
                int len = from64tobits(out_base64, in_base64, strlen(in_base64));
                out_base64[len] = 0;
                buf.Empty();
                buf = out_base64;
                delete out_base64;
        }
        // in case of quoted-printable in text/plain or text/html
        else if(this->m_md.GetEncodingType(this->m_current_part) == QUOTED_PRINTABLE){
                // fromQPtobitsの実装があやしい
                // implementation of fromQPtobits is weird.
                char* in_qp = buf.GetBuffer(0);
                char* out_qp = new char[buf.GetLength()+10]; // これではbufferの容量が足りないみたいで時々これが原因でお亡くなりになることが。。。 
                // ↑の+10はfromQPtobitsの最後で挿入されるNULL文字文 
                // +10 mean fromQPtobits insert NULL last.
                // CString の GetBuffer はNULL文字は含まないのか???? 
                // not clear that CString::GetBuffer not include NULL ?????
                int len = fromQPtobits(out_qp, in_qp, strlen(in_qp));
                out_qp[len] = 0;
                buf.Empty();
                buf = out_qp;
                delete out_qp;
        }

        CString msg_id;
        // use Message-ID for file name
        msg_id = m_md.m_hi.GetMsgID();
        msg_id = g_ma(msg_id);
        msg_id.Replace(".", "_");

        if(!g_is_there(app->m_app_path+"\\temp")) // if temp directory not exist, create
                _mkdir(app->m_app_path+"\\temp");

        g_cstring2file(buf, app->m_app_path+"\\temp\\"+msg_id+".html");
        ShellExecute(NULL, "open", app->m_app_path+"\\temp\\"+msg_id+".html", NULL, NULL, SW_SHOW);
}

void CTextView::OnPopupEditor() 
{
        CPochyApp *app = (CPochyApp *)AfxGetApp();
        CString body = GetCurrentHeader()+"\r\n"+GetCurrentBody();
        CString msg_id;

        // user Message-ID for file name
        msg_id = m_md.m_hi.GetMsgID();
        msg_id = g_ma(msg_id);
        msg_id.Replace(".", "_");

        if(!g_is_there(app->m_app_path+"\\temp")) // if temp directory not exist, create
                _mkdir(app->m_app_path+"\\temp");

        FILE* file = fopen(app->m_app_path+"\\temp\\"+msg_id+".txt", "wb");
        fwrite(body.GetBuffer(0), sizeof(char), body.GetLength(), file);
        fclose(file);

        CString file_path = app->m_app_path+"\\temp\\"+msg_id+".txt";
        CString editor_path = app->GetProfileString("Editor", "Path", "notepad");
        if(editor_path.IsEmpty())
                editor_path = "notepad";
        file_path = "\""+file_path+"\"";
        // ファイルpathをsetting.iniで指定されたeditorで開く
        ShellExecute(NULL, NULL, editor_path, file_path, NULL, SW_SHOWNORMAL);
}

void CTextView::OnSetFocus(CWnd* pOldWnd) 
{
        CRichEditView::OnSetFocus(pOldWnd);
}

void CTextView::SetFontEx(LOGFONT &lf)
{
        CRichEditCtrl &r = GetRichEditCtrl();

        m_font.DeleteObject();
        if(m_font.CreateFontIndirect(&lf))
                r.SetFont(&m_font);
        this->SetFontColorAndLink();
}

void CTextView::SetFontColorAndLink()
{
        CRichEditCtrl &r = GetRichEditCtrl();
        CPochyApp* app = (CPochyApp*)AfxGetApp();

        // clickable url を有効にする
        long lENM = r.GetEventMask();
        lENM |= ENM_LINK;
        r.SetEventMask(lENM);
        BOOL bEnable = 1;
        ::SendMessage(m_hWnd, EM_AUTOURLDETECT, bEnable, 0);

        // 文字の属性を設定
        CHARFORMAT2 cf;
        r.GetDefaultCharFormat(cf);
        cf.dwMask = CFM_COLOR | CFM_PROTECTED | CFM_FACE | CFM_SIZE | CFM_CHARSET | CFM_SUBSCRIPT;
        cf.crTextColor = RGB(
                app->GetProfileInt("TextViewColor", "TxtColorR", DEF_TXT_COLOR_R),
                app->GetProfileInt("TextViewColor", "TxtColorG", DEF_TXT_COLOR_G),
                app->GetProfileInt("TextViewColor", "TxtColorB", DEF_TXT_COLOR_B));
        cf.dwEffects = CFE_PROTECTED | CFE_SUBSCRIPT;
//      cf.bCharSet = CHINESEBIG5_CHARSET;
//      strcpy(cf.szFaceName, "MingLiU");//cf.szFaceName
//      r.SendMessage(EM_SETCHARFORMAT,(WPARAM)SCF_ALL,(LPARAM)&cf);
        r.SetSel(0, -1);
        r.SetSelectionCharFormat(cf);

        CString mail;
        r.GetWindowText(mail);
        r.SetSel(0, mail.Find("\r\n\r\n")+2);
        r.GetSelectionCharFormat(cf);
        cf.dwMask = CFM_COLOR | CFM_BACKCOLOR;
        cf.crTextColor = RGB(
                app->GetProfileInt("TextViewHdColor", "TxtColorR", DEF_HEADER_INFO_COLOR_R),
                app->GetProfileInt("TextViewHdColor", "TxtColorG", DEF_HEADER_INFO_COLOR_G),
                app->GetProfileInt("TextViewHdColor", "TxtColorB", DEF_HEADER_INFO_COLOR_B));
        r.SetSelectionCharFormat(cf);
        r.HideSelection(TRUE, FALSE);
        r.SetSel(0, 0);

        // 行間を調整する
//      PARAFORMAT2 pf;
//      pf.cbSize = sizeof(pf);
//      pf.dwMask = PFM_LINESPACING | PFM_SPACEAFTER;
//      pf.dySpaceAfter = 0;
//      pf.bLineSpacingRule = 4;
//      pf.dyLineSpacing = 210;
//      r.SetSel(0,-1);
//      r.SendMessage(EM_SETPARAFORMAT,0,(LPARAM)&pf);
//      r.SetSel(0,0);
}

void CTextView::ReDisplay()
{
        if(m_current_part != -1)
                DisplayPart(m_current_part);
}

CFont *CTextView::GetFontEx()
{
        return &m_font;
}

void CTextView::Clear()
{
        CPochyApp* app = (CPochyApp*)AfxGetApp();
        CMainFrame *mf = (CMainFrame*)app->m_pMainWnd;

        this->SetWindowText("");
        this->m_current_part = 0;
        this->m_body.Empty();
        this->m_current_part = -1;
        this->m_display_header.Empty();
        this->m_header.Empty();
        this->m_mail.Empty();
        this->m_multipart_header.Empty();
        this->m_path.Empty();

        mf->HideMultiPartView(TRUE);
}

/* [<][>][^][v][top][bottom][index][help] */