第275章 複数ファイルを選択する


前章までは書庫にファイルを追加するとき1回に 一つのファイルしか追加できませんでした。今回は一度に複数のファイルを追加できるようにします。 また、ステータスバーを付けてツールバーのボタンや、メニュー項目をポイントしたとき その説明を表示するようにしました。



メニュー項目を選択すると、その説明がステータスバーに表示されます。 また、一番右のパーツに著作権表示が出ていますが、ウィンドウを小さくすると 全部が表示されません。この時この部分をポイントすると著作権表示が ツールチップとして表示されます。(全部が表示されているときはツールチップは出ない)



左の図のように複数のファイルを一度に選択できます。

複数ファイルを選択するには、コントロールキーを押しながら1つずつ選択するか、 シフトキーを押連続した並びのファイルを選択します。



では、プログラムを見てみましょう。

// unlha04.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "書庫の新規作成(&N)...", IDM_NEW MENUITEM "書庫を開く(&O)...", IDM_OPEN MENUITEM SEPARATOR MENUITEM "書庫にファイルを追加(&A)...", IDM_ADD MENUITEM "書庫からファイルを削除(&D)...", IDM_DEL MENUITEM SEPARATOR MENUITEM "解凍(&E)...", IDM_EXTRACT MENUITEM SEPARATOR MENUITEM "自己解凍書庫の作成(&S)...", IDM_SFX MENUITEM SEPARATOR MENUITEM "終了(&X)...", IDM_END END POPUP "表示(&V)" BEGIN MENUITEM "ツールバー(&T)", IDM_TOOL, CHECKED MENUITEM "ステータスバー(&S)", IDM_STATUS, CHECKED END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDIRDLG DIALOG DISCARDABLE 0, 0, 141, 151 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ディレクトリ指定" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,130,50,14 PUSHBUTTON "キャンセル",IDCANCEL,84,130,50,14 LISTBOX IDC_LIST1,7,31,126,90,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP LTEXT "Static",IDC_STATIC1,7,7,127,19 END ///////////////////////////////////////////////////////////////////////////// // // Toolbar // MYTOOLBAR TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON IDM_ADD BUTTON IDM_EXTRACT BUTTON IDM_SFX END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYTOOLBAR BITMAP DISCARDABLE "bmp00001.bmp" ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE DISCARDABLE BEGIN IDS_NEW "新規の書庫を作ります" IDS_OPEN "書庫を開きます" IDS_DELETE "書庫からファイルを削除します" IDS_ADD "書庫にファイルを追加します" IDS_EXTRACT "書庫からファイルを解凍します" IDS_SFX "自己解凍書庫を作ります" END

メニューに「ステータスバー」(IDM_STATUS)が加わりました。他は同じです。

// unlha04.cpp #ifndef STRICT #define STRICT #endif #define ID_LISTVIEW 100 #define ID_TOOLBAR 101 #define ID_REBAR 102 #define ID_STATIC 103 #define ID_TOOLMENU 104 #define ID_STATUSBAR 105 // unlha32.hでtime_tを使うためtime.hが必要です #include <time.h> // sprintf()を使う関係上stdio.hが必要です #include <stdio.h> #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "unlha32.h" #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDirProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MyAdd(HWND, int); BOOL MyOpen(HWND, int); BOOL MySetColumn(HWND); BOOL MyExtract(HWND); BOOL MyDel(HWND hWnd); BOOL MySFX(HWND hWnd); char szClassName[] = "unlha04"; //ウィンドウクラス char szWindowTitle[] = "猫でもわかるアーカイバ [%s]"; HINSTANCE hInst; char szCmdLine[1024]; char szLZHFile[MAX_PATH], szOrgFile[1024 * 10], szTitle[MAX_PATH]; char szDir[MAX_PATH]; HWND hList, hTool, hRb; BOOL bTool = TRUE, bStatus = TRUE; //Toolbar, Statusbarを表示するかどうか TBBUTTON tbBtn[] = { {STD_FILENEW, IDM_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {STD_FILEOPEN, IDM_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {STD_DELETE, IDM_DEL, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0} }; TBBUTTON tbLHA[] = { {0, IDM_ADD, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {1, IDM_EXTRACT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {2, IDM_SFX, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0} };

ステータスバーのID定義が増えました。

szOrgFileの配列を大きくしました。これは、ファイルを複数選択したとき すべてのファイル名がここに格納されるのである程度大きくしておく必要があります。

ステータスバーを表示するかどうかのフラグ(bStatus)が増えました。

WinMain, InitApp, InitInstanceの各関数に変更はありません。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; INITCOMMONCONTROLSEX ic; static HWND hStatic1, hStatic2, hStatusbar; RECT rc; static int nRbH, nStatusH; REBARINFO ri; REBARBANDINFO rbi; TBADDBITMAP ta; int nStdID; MENUITEMINFO mi; static HMENU hMenu; LPARAM mylp; LPTOOLTIPTEXT lptip; LPNMHDR lpnmhdr; int sb_parts[3] = {230, 300, -1}; char szBuffer[256]; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES; InitCommonControlsEx(&ic); hStatic1 = CreateWindow("static", "", WS_CHILD | WS_VISIBLE | SS_SUNKEN, 0, 0, 0, 0, hWnd, (HMENU)ID_STATIC, hInst, NULL); hStatic2 = CreateWindow("static", "", WS_CHILD | WS_VISIBLE | SS_SUNKEN, 0, 0, 0, 0, hWnd, (HMENU)ID_STATIC, hInst, NULL); hRb = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, "", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | RBS_VARHEIGHT | RBS_BANDBORDERS, 0, 0, 0, 0, hWnd, (HMENU)ID_REBAR, hInst, NULL); hTool = CreateToolbarEx(hRb, WS_CHILD | WS_VISIBLE | CCS_NORESIZE | CCS_NODIVIDER | CCS_NOMOVEY | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS, ID_TOOLBAR, 3, HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tbBtn, 3, 0, 0, 0, 0, sizeof(TBBUTTON)); hStatusbar = CreateStatusWindow(WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS, "", hWnd, ID_STATUSBAR); SendMessage(hStatusbar, SB_SETPARTS, (WPARAM)3, (LPARAM)sb_parts); SendMessage(hStatusbar, SB_SETTEXT, 2 | 0, (LPARAM)"粂井康孝 制作・著作"); SendMessage(hStatusbar, SB_SETTIPTEXT, (WPARAM)2, (LPARAM)"粂井康孝 制作・著作"); ta.hInst = NULL; ta.nID = (UINT)LoadBitmap(hInst, "MYTOOLBAR"); nStdID = SendMessage(hTool, TB_ADDBITMAP, (WPARAM)3, (LPARAM)&ta); tbLHA[0].iBitmap += nStdID; tbLHA[1].iBitmap += nStdID; tbLHA[2].iBitmap += nStdID; SendMessage(hTool, TB_ADDBUTTONS, (WPARAM)3, (LPARAM)tbLHA); memset(&ri, 0, sizeof(REBARINFO)); ri.cbSize = sizeof(REBARINFO); SendMessage(hRb, RB_SETBARINFO, 0, (LPARAM)&ri); memset(&rbi, 0, sizeof(REBARBANDINFO)); rbi.cbSize = sizeof(REBARBANDINFO); rbi.fMask = RBBIM_TEXT | RBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE; rbi.fStyle = RBBS_CHILDEDGE | RBBS_GRIPPERALWAYS; rbi.lpText = "ファイル"; rbi.hwndChild = hTool; rbi.cxMinChild = 0; rbi.cyMinChild = 23; rbi.cx = 120; SendMessage(hRb, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbi); hList = CreateWindowEx(0, WC_LISTVIEW, "", WS_CHILD | WS_VISIBLE | LVS_REPORT, 0, 0, 0, 0, hWnd, (HMENU)ID_LISTVIEW, hInst, NULL); ListView_SetExtendedListViewStyle(hList, LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); MySetColumn(hList); break; case WM_SIZE: if (bTool) { GetWindowRect(hRb, &rc); nRbH = rc.bottom - rc.top; } else nRbH = 0; if (bStatus) { GetWindowRect(hStatusbar, &rc); nStatusH = rc.bottom - rc.top; } else nStatusH= 0; MoveWindow(hStatic1, 0, 0, LOWORD(lp), 2, TRUE); SendMessage(hRb, WM_SIZE, wp, lp); MoveWindow(hStatic2, 0, nRbH + 2, LOWORD(lp), 2, TRUE); MoveWindow(hList, 0, nRbH + 4, LOWORD(lp), HIWORD(lp) - nRbH - 4 - nStatusH, TRUE); InvalidateRect(hList, NULL, TRUE); SendMessage(hStatusbar, WM_SIZE, wp, lp); break; case WM_MENUSELECT: if (bStatus == FALSE) break; switch (LOWORD(wp)) { case 0: strcpy(szBuffer, "書庫の操作など"); break; case 1: strcpy(szBuffer, "表示切り替え"); break; case IDM_NEW: LoadString(hInst, IDS_NEW, szBuffer, sizeof(szBuffer)); break; case IDM_OPEN: LoadString(hInst, IDS_OPEN, szBuffer, sizeof(szBuffer)); break; case IDM_ADD: LoadString(hInst, IDS_ADD, szBuffer, sizeof(szBuffer)); break; case IDM_EXTRACT: LoadString(hInst, IDS_EXTRACT, szBuffer, sizeof(szBuffer)); break; case IDM_DEL: LoadString(hInst, IDS_DELETE, szBuffer, sizeof(szBuffer)); break; case IDM_SFX: LoadString(hInst, IDS_SFX, szBuffer, sizeof(szBuffer)); break; case IDM_END: strcpy(szBuffer, "このアプリケーションを終了します"); break; case IDM_TOOL: strcpy(szBuffer, "ツールバーの表示・非表示を切り替えます"); break; case IDM_STATUS: strcpy(szBuffer, "ステータスバーの表示・非表示を切り替えます"); break; default: strcpy(szBuffer, ""); break; } SendMessage(hStatusbar, SB_SETTEXT, 0 | 0, (LPARAM)szBuffer); break; case WM_NOTIFY: lpnmhdr = (LPNMHDR)lp; if (lpnmhdr->hwndFrom == hTool && lpnmhdr->code == TBN_HOTITEMCHANGE) SendMessage(hStatusbar, SB_SETTEXT, 0 | 0, (LPARAM)""); switch (lpnmhdr->code) { case TTN_NEEDTEXT: lptip = (LPTOOLTIPTEXT)lp; lptip->hinst = hInst; switch (lptip->hdr.idFrom) { case IDM_NEW: lptip->lpszText = MAKEINTRESOURCE(IDS_NEW); LoadString(hInst, IDS_NEW, szBuffer, sizeof(szBuffer)); SendMessage(hStatusbar, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)szBuffer); break; case IDM_OPEN: lptip->lpszText = MAKEINTRESOURCE(IDS_OPEN); LoadString(hInst, IDS_OPEN, szBuffer, sizeof(szBuffer)); SendMessage(hStatusbar, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)szBuffer); break; case IDM_DEL: lptip->lpszText = MAKEINTRESOURCE(IDS_DELETE); LoadString(hInst, IDS_DELETE, szBuffer, sizeof(szBuffer)); SendMessage(hStatusbar, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)szBuffer); break; case IDM_ADD: lptip->lpszText = MAKEINTRESOURCE(IDS_ADD); LoadString(hInst, IDS_ADD, szBuffer, sizeof(szBuffer)); SendMessage(hStatusbar, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)szBuffer); break; case IDM_EXTRACT: lptip->lpszText = MAKEINTRESOURCE(IDS_EXTRACT); LoadString(hInst, IDS_EXTRACT, szBuffer, sizeof(szBuffer)); SendMessage(hStatusbar, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)szBuffer); break; case IDM_SFX: lptip->lpszText = MAKEINTRESOURCE(IDS_SFX); LoadString(hInst, IDS_SFX, szBuffer, sizeof(szBuffer)); SendMessage(hStatusbar, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)szBuffer); break; default: return DefWindowProc(hWnd, msg, wp, lp); } default: return DefWindowProc(hWnd, msg, wp, lp); } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_ADD: //iOpt 1..追加 //iOpt 0..新規 MyAdd(hWnd, 1); break; case IDM_NEW: MyAdd(hWnd, 0); break; case IDM_OPEN: //iOpt 0...新規オープン MyOpen(hWnd, 0); break; case IDM_EXTRACT: MyExtract(hWnd); break; case IDM_DEL: MyDel(hWnd); break; case IDM_SFX: MySFX(hWnd); break; case IDM_TOOL: memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; if (bTool) { mi.fState = MFS_UNCHECKED; SetMenuItemInfo(hMenu, IDM_TOOL, FALSE, &mi); bTool = FALSE; ShowWindow(hRb, SW_HIDE); } else { mi.fState = MFS_CHECKED; SetMenuItemInfo(hMenu, IDM_TOOL, FALSE, &mi); bTool = TRUE; ShowWindow(hRb, SW_NORMAL); } GetClientRect(hWnd, &rc); mylp = MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top); SendMessage(hWnd, WM_SIZE, (WPARAM)SIZE_RESTORED, mylp); break; case IDM_STATUS: memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; if (bStatus) { mi.fState = MFS_UNCHECKED; SetMenuItemInfo(hMenu, IDM_STATUS, FALSE, &mi); bStatus = FALSE; ShowWindow(hStatusbar, SW_HIDE); } else { mi.fState = MFS_CHECKED; SetMenuItemInfo(hMenu, IDM_STATUS, FALSE, &mi); bStatus = TRUE; ShowWindow(hStatusbar, SW_NORMAL); } GetClientRect(hWnd, &rc); mylp = MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top); SendMessage(hWnd, WM_SIZE, (WPARAM)SIZE_RESTORED, mylp); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hStatusbar); DestroyWindow(hStatic1); DestroyWindow(hStatic2); DestroyWindow(hTool); DestroyWindow(hRb); DestroyWindow(hList); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

親ウィンドウのプロシージャです。

WM_CREATEメッセージが来たら各コントロールウィンドウを作ります。 ステータスバーはCreateStatusWindow関数で作りました。この時ウィンドウスタイルに SBT_TOOLTIPSを加えて全部が表示されないときにツールチップが出るようにしました。 詳しくは第203章を参照してください。

WM_SIZEメッセージが来たら各コントロールウィンドウの位置、大きさを調整します。

さて、メニュー項目がマウス等で選択されている場合、この説明をステータスバーに表示するには どうしたらよいのでしょうか。これはWM_MENUSELECTメッセージを処理すれば簡単です。

LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

WM_MENUSELECTメッセージはメニューのオーナー・ウィンドウにユーザーが メニューアイテムを選択したときに送られます。 wParamの下位ワード値には、サブメニューのIDが入ります。または、選択が ドロップダウンメニューとかサブメニューの時はこれらのインデックスが入ります。

このプログラムでは0は「ファイル」、1は「表示」に相当します。

メニュー項目が選択されたらszBufferに説明文を格納します。ストリングテーブルにある ものはLoadString関数で呼び出し、無いものは直接strcpy関数で格納します。 格納後SB_SETTEXTメッセージでステータスバーに表示します。

WM_NOTIFYメッセージが来たらTBN_HOTITEMCHANGE通知メッセージを処理します。

TBN_HOTITEMCHANGE lpnmhi = (LPNMTBHOTITEM) lParam;

これは、ツールバーから送られます。ホットアイテムやハイライトアイテムが変化したときに 送られてきます。

これが来たらステータスバーの説明を消します。と、言うのもこれを処理しないと ツールバーボタンのの説明がステータスバーに表示されたあと、マウスを他のコントロールなどに 移動したときボタンの説明が残ってしまうからです。

ツールバーボタンのツールチップが出たときに、ステータスバーにも同じ文章を 表示するようにしました。

メニューからIDM_STATUSが選択されたとき、メニュー項目にチェックを付けたりはずしたり してステータスバーを表示にしたり非表示にしたりするのは、ツールバーの時と同じです。

プログラム終了時にステータスバーも破棄しておきます。

BOOL MyAdd(HWND hWnd, int iOpt) { OPENFILENAME ofn; char szBuf[1024]; char *lpstrPtr; int nLen; if (strcmp(szLZHFile, "") == 0 || iOpt == 0) { MessageBox(hWnd, "書庫ファイル名を付けてください", "新規書庫ファイル", MB_OK); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.lpstrTitle = "書庫ファイルの新規作成"; ofn.lpstrFile = szLZHFile; ofn.nMaxFile = sizeof(szLZHFile); ofn.lpstrFileTitle = szTitle; ofn.nMaxFileTitle = sizeof(szTitle); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "LZH FILES(*.lzh)\0*.lzh\0\0"; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_CREATEPROMPT; ofn.lpstrDefExt = "lzh"; if(!GetSaveFileName(&ofn)) { return FALSE; } } MessageBox(hWnd, "書庫に追加するファイルを選択してください", "ファイル選択", MB_OK); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER | OFN_HIDEREADONLY; ofn.lpstrTitle = "書庫に格納するファイル"; ofn.lpstrFile = szOrgFile; ofn.nMaxFile = sizeof(szOrgFile); ofn.lpstrFilter = "Text File (*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0"; ofn.lpstrDefExt = "txt"; if (!GetOpenFileName(&ofn)) { return FALSE; } lpstrPtr = szOrgFile; while(1) { nLen = strlen(lpstrPtr); lpstrPtr += (nLen + 1); strcpy(szCmdLine, "a "); strcat(szCmdLine, "\""); strcat(szCmdLine, szLZHFile); strcat(szCmdLine, "\""); strcat(szCmdLine, " "); strcat(szCmdLine, "\""); strcat(szCmdLine, szOrgFile); if (lpstrPtr[0] != '\0') { if (!strchr(lpstrPtr, '\\')) { strcat(szCmdLine, "\\"); strcat(szCmdLine, lpstrPtr); } } strcat(szCmdLine, "\""); Unlha(hWnd, szCmdLine, szBuf, sizeof(szBuf)); MyOpen(hWnd, 1); if (lpstrPtr[0] == '\0') { break; } } wsprintf(szBuf, szWindowTitle, szTitle); SetWindowText(hWnd, szBuf); return TRUE; }

複数ファイル選択を許可するにはOPENFILENAME構造体のFlagsメンバに OFN_ALLOWMULTISELECTを加えておきます。この時OFN_EXPLORERを加えないと 旧式のスタイルのダイアログが出てきます。また、ファイル名の格納のされ方が 異なります。エクスプローラスタイルの時は複数選択ファイルはlpstrFileメンバに

X:\dir1\dir2\0file1.txt\0file2.txt\0abc.exe\0\0

のように格納されます。一方旧スタイルでは

X:\dir1\dir2 file1.txt file2.txt abc.exe

のようにディレクトリやファイル名の間にスペースが入ります。

また、ユーザーが1つしかファイルを選択しなかった場合はヌル文字やスペースの区切りは入りません。

これがプログラム上結構面倒くさいです。今回のプログラム例は必ずしも効率がよくないので もっと工夫してみてください。

MyOpen, MySetColumn, MyExtract, MyDel, MyDirProc, MySFXの各関数に変更はありません。

あと、アイコンを付けたりunlha32.dllが返すテキストをステータスバーに表示するなどして 改良してみてください。
[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 12/Jun/2000 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。