プリント基板設計・シミュレーション

TOP > アポロレポート > コラム > 第75回 プログラミングについて『On Save File』
コラム
2024/07/22

第75回 プログラミングについて『On Save File』

アポロレポート

 今回も前回に続いて簡易テキストエディタを作っていきましょう。

 まず前回のソースコード。

 #include <afxwin.h>
 #include <afxdlgs.h>
 #include "resource.h"

 class MyApp : public CWinApp
 {
  public:
   virtual BOOL InitInstance();
 };

 class MyWnd : public CFrameWnd
 {
  private:
   CEdit *edit_box;
   char current_file_name[_MAX_PATH];

  public:
   MyWnd();

   afx_msg void OnOpen();
   afx_msg void OnSize(UINT nType,int cx,int cy);
       void LoadFile(char *file_name);

   DECLARE_MESSAGE_MAP()
 };

 class MyApp MyApp;

 BOOL MyApp::InitInstance()
 {
  m_pMainWnd = new class MyWnd;

  m_pMainWnd->SetIcon(LoadIcon(ID_MYAPP_ICON),TRUE);

  return TRUE;
 }

 MyWnd::MyWnd()
 {
  RECT rect;

  edit_box=NULL;

  Create(NULL,"",WS_OVERLAPPEDWINDOW | WS_VISIBLE ,rectDefault,NULL,
      "MYAPP_MENU");

  GetClientRect(&rect);

  edit_box=new CEdit();

  edit_box->Create(
        WS_CHILD | WS_VISIBLE | WS_DLGFRAME | WS_HSCROLL | WS_VSCROLL
       | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
       rect,this,ID_EDIT_BOX);

  ::SetFocus(edit_box->m_hWnd);
 }

 BEGIN_MESSAGE_MAP(MyWnd,CFrameWnd)
  ON_COMMAND(IDM_OPEN,OnOpen)
  ON_WM_SIZE()
 END_MESSAGE_MAP()

 afx_msg void MyWnd::OnOpen()
 {
  if(edit_box->GetModify()){
   if(MessageBox(
      "Text was modified !\n"
      "Save ?",
      "Open error",MB_YESNO)==IDYES){
       ;
   }
  }

  CFileDialog FileDlg(
         TRUE,"txt",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
         "Text files (*.txt)|*.txt|All files (*.*)|*.*||",this);

  if(FileDlg.DoModal()==IDOK){
   LoadFile(FileDlg.GetPathName().GetBuffer(_MAX_PATH));
  }
 }

 afx_msg void MyWnd::OnSize(UINT nType,int cx,int cy)
 {
  RECT rect;

  if(edit_box==NULL)return;

  GetClientRect(&rect);

  edit_box->MoveWindow(&rect);
 }

 void MyWnd::LoadFile(char *file_name)
 {
 #define MAX_BUF 60000

  FILE *fp;
  char buf[MAX_BUF];
  int lbuf;
  char data[513];
  int ldata;

  fp=fopen(file_name,"rb");
  if(fp==NULL){
   sprintf(data,"--- Can not open ---\n%s",file_name);
   MessageBox(data,"Open error",MB_OK);
   return;
  }

  edit_box->SetSel(0,-1);
  edit_box->Clear();

  strcpy(current_file_name,file_name);

  buf[0]='\0';
  lbuf=0;

  while(1){
   if(fgets(data,sizeof(data),fp)==NULL)break;
   if(data[0]==0x1a)break;

   ldata=strlen(data);

   if(data[ldata-1]==0x1a)data[--ldata]='\0';

   if(ldata+lbuf>MAX_BUF-1)break;

   strcpy(buf+lbuf,data);
   lbuf+=ldata;
  }

  if(lbuf>2){
   if(buf[lbuf-2]=='\r' && buf[lbuf-1]=='\n')buf[lbuf-2]='\0';
  }

  edit_box->ReplaceSel(buf);
  edit_box->SetSel(0,0);
  edit_box->SetModify(FALSE);

  fclose(fp);

 #undef MAX_BUF
 }

 だんだんと長くなってきましたが、がんばりましょう。今回はまずセーブ機能を加えましょう。Open の機能を付け加えたときと同じに、MyWnd クラスの宣言に、

 afx_msg void OnSave();

を、メッセージマップに

 ON_COMMAND(IDM_SAVE,OnSave)

を追加します。また、エディットボックスが変更されているときにもセーブできるように、OnSaveFile 関数もMyWnd クラスの宣言に追加しておきます。

 int OnSaveFile();

 前回までは、OnOpen でセーブする関数を呼び出すコードを書く予定だったところにOnSaveFile() を次のように追加します。

 afx_msg void MyWnd::OnOpen()
 {
  if(edit_box->GetModify()){
   if(MessageBox(
      "Text was modified !\n"
      "Save ?",
      "Open error",MB_YESNO)==IDYES){
    if(!OnSaveFile())return;      //ここに追加。
   }
  }
       :
       :
 }

 OnSave() は、

 afx_msg void MyWnd::OnSave()
 {
  OnSaveFile();
 }

と単純に OnSaveFile を呼び出すようにし、OnSaveFile でファイルの指定を行なうようにします。このように OnSaveFile() 関数をわざわざ別にしたのは、OnOpen でファイルをセーブしようとしてファイルの指定をキャンセルしたときに新しくファイルをオープンしないようにするためです。

 int MyWnd::OnSaveFile()
 {
  CFileDialog FileDlg(
         FALSE,"txt",current_file_name,
         OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
         "Text files (*.txt)|*.txt|All files (*.*)|*.*||",this);

  if(FileDlg.DoModal()==IDOK){
   SaveFile(FileDlg.GetPathName().GetBuffer(_MAX_PATH));
   return 1;
  }

  return 0;
 }

OnSaveFile は特に説明する必要はないかも知れません。オープンのときに TRUE にしていた第1引数を FALSE にし、第3引数に現在開いているファイル名 current_file を指定して、ディフォルトのファイル名にしているところが違っているところです。
 この関数はファイルの指定がキャンセルされたときには0を、それ以外には1を返すようにしていますので、OnOpen はこの戻り値で判断すればいいようになっています。

 次に SaveFile() です。

 void MyWnd::SaveFile(char *file_name)
 {
 #define MAX_BUF 60000
  FILE *fp;

  char buf[MAX_BUF];
  int lbuf;
  char data[512];
  int ldata;

  int total_line,line;

  fp=fopen(file_name,"wb");
  if(ferror(fp)){
   sprintf(data,"--- Can not open ---\n%s",file_name);
   MessageBox(data,"Open error",MB_OK);
   return;
  }

  total_line=edit_box->GetLineCount();

   buf[0]='\0';
  lbuf=0;

  for(line=0;line<total_line;line++){
   ldata=edit_box->GetLine(line,data,sizeof(data));
   if(total_line==1 && ldata==0)break;
   if(ldata+lbuf>MAX_BUF-3)break;

   data[ldata++]='\r'; data[ldata++]='\n'; data[ldata]='\0';

   strcpy(buf+lbuf,data);
   lbuf+=ldata;
  }

  fprintf(fp,"%s",buf);

  fclose(fp);

  edit_box->SetModify(FALSE);

 #undef MAX_BUF
 }

ちょっと長くなってしまいましたが、エディットボックスの内容全部をまとめてファイルに書くようにしています。エディットボックスから1行を読み込んだ後に <CR><LF>を追加しているのは、この edit_box->GetLine() は文字列の最後にはヌルも何も付けてくれないからです。ファイルをセーブした後は、エディットボックスの変更フラグをオフにしておきます。

 これで、ファイルのロードとセーブの機能が付きましたので、ついでに Exit も付けましょう。これまでの手順と殆ど似ていますが、ちょっとだけ違うことがあります。
メッセージマップには、

 ON_COMMAND(IDM_EXIT,OnClose)

と、

 ON_WM_CLOSE()

を追加します。OnClose() というのは WM_CLOSE メッセージを受け取ったときに呼び出される基本クラスの関数で、ウインドウの右上の×印のボタンを押したときに発生するメッセージです。IDM_EXIT はメニューの Exit を選択したときに発生するメッセージWM_COMMAND のIDです。この2つのメッセージは基本的には同じ処理を行ないたいので、OnClose() を共通に使用しようという訳です。

 MyWnd クラスの宣言には、

 afx_msg void OnClose()

を追加します。そして OnClose() 本体は、

 afx_msg void MyWnd::OnClose()
 {
  if(edit_box->GetModify()){
   if(MessageBox(
      "Text was modified !\n"
      "Save ?",
      "Open error",MB_YESNO)==IDYES){
     if(!OnSaveFile())return;
   }
  }

  CFrameWnd::OnClose();
 }

とします。エディットボックスの内容が変更されているか否かをチェックし、必要ならばセーブします。そして、その後は基本クラスと同じでいいので、
 
 CFrameWnd::OnClose();

を呼び出します。これは MyWnd の基本クラス CFrameWnd のメンバー関数 OnClose() を呼び出すことを意味します。

 最後にプログラム全体。

 #include <afxwin.h>
 #include <afxdlgs.h>
 #include "resource.h"
 #include "dummy.h"

 class MyApp : public CWinApp
 {
  public:
   virtual BOOL InitInstance();
 };

 class MyWnd : public CFrameWnd
 {
  private:
   CEdit *edit_box;
   char current_file_name[_MAX_PATH];

  public:
   MyWnd();

   afx_msg void OnOpen();
   afx_msg void OnSave();
   afx_msg void OnClose();
   afx_msg void OnSize(UINT nType,int cx,int cy);
       void LoadFile(char *file_name);
       int OnSaveFile();
       void SaveFile(char *file_name);

  DECLARE_MESSAGE_MAP()
 };

 class MyApp MyApp;

 BOOL MyApp::InitInstance()
 {
  m_pMainWnd = new class MyWnd;

  m_pMainWnd->SetIcon(LoadIcon(ID_MYAPP_ICON),TRUE);

  return TRUE;
 }

 MyWnd::MyWnd()
 {
  RECT rect;

  edit_box=NULL;
  current_file_name[0]='\0';

  Create(NULL,"",WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
      rectDefault,NULL,"MYAPP_MENU");

  GetClientRect(&rect);

  edit_box=new CEdit();
  edit_box->Create(
        WS_CHILD | WS_VISIBLE | WS_DLGFRAME | WS_HSCROLL | WS_VSCROLL
       | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL ,
       rect,this,ID_EDIT_BOX);

  ::SetFocus(edit_box->m_hWnd);
 }

 BEGIN_MESSAGE_MAP(MyWnd,CFrameWnd)
  ON_COMMAND(IDM_OPEN,OnOpen)
  ON_COMMAND(IDM_SAVE,OnSave)
  ON_COMMAND(IDM_EXIT,OnClose)

  ON_WM_SIZE()
  ON_WM_CLOSE()
 END_MESSAGE_MAP()

 afx_msg void MyWnd::OnSize(UINT nType,int cx,int cy)
 {
  RECT rect;

  if(edit_box==NULL)return;

  GetClientRect(&rect);

  edit_box->MoveWindow(&rect);
 }

 afx_msg void MyWnd::OnOpen()
 {
  if(edit_box->GetModify()){
   if(MessageBox(
       "Text was modified !\n"
       "Save ?",
       "Open error",MB_YESNO)==IDYES){
    if(!OnSaveFile())return;
   }
  }

  CFileDialog FileDlg(
         TRUE,"txt",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
         "Text files (*.txt)|*.txt|All files (*.*)|*.*||",this);

  if(FileDlg.DoModal()==IDOK){
   LoadFile(FileDlg.GetPathName().GetBuffer(_MAX_PATH));
  }
 }

 void MyWnd::LoadFile(char *file_name)
 {
 #define MAX_BUF  60000

  FILE *fp;
  char buf[MAX_BUF];
  int lbuf;
  char data[513];
  int ldata;

  fp=fopen(file_name,"rb");
  if(fp==NULL){
   sprintf(data,"--- Can not open ---\n%s",file_name);
   MessageBox(data,"Open error",MB_OK);
   return;
  }

  edit_box->SetSel(0,-1);
  edit_box->Clear();

  strcpy(current_file_name,file_name);

   buf[0]='\0';
  lbuf=0;

  while(1){
   if(fgets(data,sizeof(data),fp)==NULL)break;
   if(data[0]==0x1a)break;

   ldata=strlen(data);

   if(data[ldata-1]==0x1a)data[--ldata]='\0';

   if(ldata+lbuf>MAX_BUF-1)break;

   strcpy(buf+lbuf,data);
   lbuf+=ldata;
  }

  if(lbuf>2){
   if(buf[lbuf-2]=='\r' && buf[lbuf-1]=='\n')buf[lbuf-2]='\0';
  }

  edit_box->ReplaceSel(buf);
  edit_box->SetSel(0,0);
  edit_box->SetModify(FALSE);

  fclose(fp);

 #undef MAX_BUF
 }

 afx_msg void MyWnd::OnSave()
 {
  OnSaveFile();
 }

 int MyWnd::OnSaveFile()
 {
  CFileDialog FileDlg(
          FALSE,"txt",current_file_name,
          OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
          "Text files (*.txt)|*.txt|All files (*.*)|*.*||",this);

  if(FileDlg.DoModal()==IDOK){
   SaveFile(FileDlg.GetPathName().GetBuffer(_MAX_PATH));
   return 1;
  }

  return 0;
 }

 void MyWnd::SaveFile(char *file_name)
 {
 #define MAX_BUF  60000
  FILE *fp;

  char buf[MAX_BUF];
  int lbuf;
  char data[512];
  int ldata;

  int total_line,line;

  fp=fopen(file_name,"wb");
  if(ferror(fp)){
   sprintf(data,"--- Can not open ---\n%s",file_name);
   MessageBox(data,"Open error",MB_OK);
   return;
  }

  total_line=edit_box->GetLineCount();

   buf[0]='\0';
  lbuf=0;

  for(line=0;line<total_line;line++){
   ldata=edit_box->GetLine(line,data,sizeof(data));
   if(total_line==1 && ldata==0)break;
   if(ldata+lbuf>MAX_BUF-3)break;

   data[ldata++]='\r'; data[ldata++]='\n'; data[ldata]='\0';

   strcpy(buf+lbuf,data);
   lbuf+=ldata;
  }

  fprintf(fp,"%s",buf);

  fclose(fp);

  edit_box->SetModify(FALSE);

 #undef MAX_BUF
 }

 afx_msg void MyWnd::OnClose()
 {
  if(edit_box->GetModify()){
   if(MessageBox(
      "Text was modified !\n"
      "Save ?",
      "Open error",MB_YESNO)==IDYES){
    if(!OnSaveFile())return;
   }
  }

  CFrameWnd::OnClose();
 }

 これで初期の目標は達成しましたが、次回からはもう少しこのプログラムに機能を追加する予定です。

 ではまた次回。


そのお悩み、
アポロ技研に話してみませんか?

アポロ技研でワンランク上の物創りへ!
そのお悩み、アポロ技研に話してみませんか?