2013年8月20日火曜日

Win32APIで仮想リストビューを使ってみる

あいさつ


もともと、苦しんで覚えるC言語を読んで、試しに何か作ってみようと思ったのがWin32APIに手を出したきっかけだった。そのあと『猫でもわかるWindowsプログラミング』というのを買って読み、作ってみたのが前回のeClip風の何かと今回のfenrirとかEverything風の見た目の何か。



途中まではすいすいうまいこといってたのだが、なんかある程度自分で考えたり検索しまくったり時間をおいたりしないとわからなげな、深入りしそうな段階に入った気配がするのでいったん中断する。ただ本当にそのままほっといたら今までやったことを忘れてしまいそうなので、ここまでできたソースだけメモとしてブログにあげておきます。

コード


lview.cpp

#define ID_EDIT 101

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

#pragma comment (lib, "comctl32.lib")

typedef struct {
    TCHAR  filename[MAX_PATH];    // ファイル名
    DWORD size;                    // ファイルサイズ
    DWORD attr;                    // ファイルの属性
    TCHAR  type[256];            // 種類
    int   icon;                    // アイコン
    FILETIME writedate;            // 更新日
} filedata;

ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
HWND CreateListView(HWND hWnd);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK MyEditProc(HWND, UINT, WPARAM, LPARAM);
int DoFind(HWND hWnd);

TCHAR szClassName[] = TEXT("Cecil");  // ウィンドウクラス
HINSTANCE hInst;
HWND hMain;             // メインウィンドウのハンドル
HWND hSubList;
WNDPROC OrgEditProc;   // プロシージャアドレスを格納するための変数
static filedata *lpData = NULL;   // ファイル情報のリストを格納するための変数

// メイン
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;

    hInst = hCurInst;
    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow))
        return FALSE;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
        if (bRet == -1) {
            break;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}

// ウィンドウクラスの登録
ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;  // プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;      // インスタンス
    wc.hIcon = NULL;
    wc.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW),
                    IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;    // メニュー名
    wc.lpszClassName = szClassName;
    wc.hIconSm = NULL;

    return (RegisterClassEx(&wc));
}

// ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;

    hWnd = CreateWindow(szClassName, TEXT("Cecil"),
        WS_OVERLAPPEDWINDOW & ~WS_CAPTION, CW_USEDEFAULT,
        CW_USEDEFAULT, 500, 300, NULL, NULL, hInst, NULL);
    if (!hWnd)
        return FALSE;
    hMain = hWnd;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    static HWND edit;
    static HWND hList;

    switch (msg) {
        case WM_CREATE:
            hList = CreateListView(hWnd);
            hSubList = hList;
            ListView_SetItemState(hSubList, 0,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
            edit = CreateWindow(
                TEXT("EDIT") , NULL , 
                WS_CHILD | WS_VISIBLE | WS_BORDER ,
                0 , 0 , 0 , 0 , hWnd , (HMENU)ID_EDIT, hInst, NULL);
            // エディットコントロールをサブクラス化
            OrgEditProc = (WNDPROC)SetWindowLongPtr(
                edit, GWL_WNDPROC, (LONG)MyEditProc);
            break;
        case WM_NOTIFY:
            if (((LPNMHDR)lp)->code == LVN_GETDISPINFO) {
                TCHAR        szBuf[256];
                NMLVDISPINFO *lpDispInfo = (NMLVDISPINFO *)lp;

                switch(lpDispInfo->item.iSubItem){
                    case 0:{
                            if (lpDispInfo->item.mask & LVIF_TEXT) {
                                wsprintf(szBuf, lpData[lpDispInfo->item.iItem].filename);
                                lstrcpy(lpDispInfo->item.pszText, szBuf);
                            }
                            if (lpDispInfo->item.mask & LVIF_IMAGE) {
                                lpDispInfo->item.iImage = lpData[lpDispInfo->item.iItem].icon;
                            }        
                        }
                           break;
                    case 1:{
                            if (lpDispInfo->item.mask & LVIF_TEXT) {
                                wsprintf(szBuf, lpData[lpDispInfo->item.iItem].type);
                                lstrcpy(lpDispInfo->item.pszText, szBuf);
                            }
                            }
                            break;
                }
            }
            return 0;
        case WM_SIZE:    
            // ウィンドウサイズを調整
            MoveWindow(edit, 0, 0, LOWORD(lp), 30, TRUE);
            MoveWindow(hList, 0, 30, LOWORD(lp), HIWORD(lp), TRUE);
            break;
        case WM_SETFOCUS:
            SetFocus(edit);
            break;
        case WM_CLOSE:
             DestroyWindow(hList);
             DestroyWindow(hWnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}

// 仮想リストビューを作る
HWND CreateListView(HWND hWnd) {
    int i;
    HWND hList;
    HIMAGELIST           himl;
    SHFILEINFO           fileInfo;
    LVCOLUMN             column;
    INITCOMMONCONTROLSEX ic;
    DWORD dwStyle;

    ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
    ic.dwICC  = ICC_LISTVIEW_CLASSES;
    InitCommonControlsEx(&ic);
        
    hList = CreateWindowEx(0, WC_LISTVIEW, TEXT(""), WS_CHILD 
        | WS_VISIBLE | LVS_REPORT | LVS_OWNERDATA 
        | LVS_SHOWSELALWAYS | LVS_SINGLESEL, 
        0, 0, 0, 0, hWnd, (HMENU)ID_EDIT, hInst, NULL);
    dwStyle = ListView_GetExtendedListViewStyle(hList);
    dwStyle |= LVS_EX_FULLROWSELECT;
    ListView_SetExtendedListViewStyle(hList, dwStyle);
    column.mask    = LVCF_WIDTH | LVCF_TEXT;
    column.cx      = 300;
    column.pszText = TEXT("Name");
    ListView_InsertColumn(hList, 0, &column);
        
    column.mask    = LVCF_WIDTH | LVCF_TEXT;
    column.cx      = 200;
    column.pszText = TEXT("Type");
    ListView_InsertColumn(hList, 1, &column);

    himl = (HIMAGELIST)SHGetFileInfo((LPCTSTR)TEXT("C:\\"), 
        0, &fileInfo, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
    ListView_SetImageList(hList, himl, LVSIL_SMALL);

    i = DoFind(hList);

    ListView_SetItemCountEx(hList, i, LVSICF_NOINVALIDATEALL);

    return hList;
}

// 実行フォルダ配下のファイルを全て検索する
int DoFind(HWND hList)
{
    int             i = 0;
    int             count = 0;
    HANDLE hFind;
    WIN32_FIND_DATA fd;
    SHFILEINFO      fileInfo;
    LVITEM          item;

    /* ファイル数のカウント */
    hFind = FindFirstFile(TEXT("*.*"), &fd);
    do {

        if (lstrcmp(fd.cFileName, TEXT("..")) != 0 && lstrcmp(fd.cFileName, TEXT(".")) != 0) {
            count++;
        }
    } while(FindNextFile(hFind, &fd));

    /* カウント終了 */
    FindClose(hFind);

    /* 動的メモリ確保 */
    lpData = (filedata *)HeapAlloc(GetProcessHeap(), 0, count * sizeof(filedata));

    /* 最初のファイル検索 */
    hFind = FindFirstFile(TEXT("*.*"), &fd);

    do {

        if (lstrcmp(fd.cFileName, TEXT("..")) != 0 && lstrcmp(fd.cFileName, TEXT(".")) != 0) {
            SHGetFileInfo(fd.cFileName, 0, &fileInfo, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_TYPENAME);

            item.mask     = LVIF_TEXT | LVIF_IMAGE;
            item.iItem    = i;
            item.iSubItem = 0;
            // ファイルパス
            item.pszText  = fd.cFileName;
            lstrcpy(lpData[i].filename,fd.cFileName);
            // ファイルの属性
            lpData[i].attr = fd.dwFileAttributes;
            // 更新日
            lpData[i].writedate = fd.ftLastWriteTime;
            // ファイルタイプ
            lstrcpy(lpData[i].type, fileInfo.szTypeName);
            // アイコン    
            item.iImage   = fileInfo.iIcon;
            lpData[i].icon = fileInfo.iIcon;
            ListView_InsertItem(hList, &item);

            item.iSubItem = 1;
            item.pszText  = fd.cAlternateFileName;
            ListView_SetItem(hList, &item);

            i++;
        }
    } while(FindNextFile(hFind, &fd));
    /* 検索終了 */
    FindClose(hFind);

    return count;
}

// エディットボックスのプロシージャ
LRESULT CALLBACK MyEditProc(HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
{
    int i = 0 ,n;
    switch (msg) {
        case WM_KEYDOWN:
            if ( wp == VK_DOWN )
            {
                i = ListView_GetNextItem(hSubList,-1, LVNI_ALL | LVNI_SELECTED);

                if (i == ListView_GetItemCount(hSubList) - 1)
                {
                    ListView_SetItemState(hSubList,0,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
                    ListView_EnsureVisible(hSubList, 0, TRUE);
                }
                else
                {
                    ListView_SetItemState(hSubList,i + 1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
                    ListView_EnsureVisible(hSubList, i + 1, TRUE);
                }
                break;
            }
            else if ( wp == VK_UP )
            {
                i = ListView_GetNextItem(hSubList,-1, LVNI_ALL | LVNI_SELECTED);
                if (i == 0)
                {
                    ListView_SetItemState(hSubList,ListView_GetItemCount(hSubList) - 1,
                        LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
                    ListView_EnsureVisible(hSubList, ListView_GetItemCount(hSubList) - 1, TRUE);
                }
                else
                {
                    ListView_SetItemState(hSubList,i - 1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
                    ListView_EnsureVisible(hSubList, i - 1, TRUE);
                }
                break;
            }
            else if ( wp == VK_ESCAPE )
            {
                DestroyWindow(hEdit);
                DestroyWindow(hSubList);
                DestroyWindow(hMain);
                break;
            }
            else if ( wp == VK_RETURN )
            {
                int nItem;

                nItem = ListView_GetNextItem(
                        hSubList, -1, LVNI_ALL | LVNI_SELECTED);
                ShellExecute(NULL, TEXT("open"), lpData[nItem].filename, NULL, NULL, SW_SHOW);
                break;
            }
            return 0;
        case WM_CHAR:           // 文字入力が発生したとき
            for (n = 0; n < ListView_GetItemCount(hSubList); n++){
                ;
            }
            break;
    }

    return CallWindowProc(OrgEditProc, hEdit, msg, wp, lp);
}

説明とか


Visual C++ 2008 Express Editionのインストールの「cppファイルにコードを書く」過程で、上のコードをコピペしたら多分動くと思う。

コントロールの作成


エディットボックスとリストコントロールを作成。
リストコントロールは仮想リストビューを使っている。データの管理と描画を分離するのがこっちで、処理が早いらしい。

エディットボックスのキーバインド


エディットボックスでカーソル上下をしたら、リストコントロールのカーソルが上下するように設定している。
また、Enterを押したらデフォルトの実行。Escでプログラムを閉じる。

アイテムの登録


実行ファイルのあるフォルダのパスを取得し、表示させている。

次にやること


  • ファイルから一行ずつ読み込み
  • ある文字列にある文字列が含まれているかどうかを調べる

あたりです。また気が向いたら続きしよう。

2013年8月19日月曜日

Win32APIでListBoxを使ってみる

挨拶


つかさです。
Win32APIでリストボックスを使ったサンプル。勉強のために、eClipっぽいのとfenrirっぽいのの二つを作ってみたのですがそのひとつ。



今日はこのようなどっかで見た気がしないでもないGUIを作って、エディットボックスでカーソル移動できるまで…といっても試しに作ってみただけなので、次回はおそらくないです。
ちなみに同じことをPythonでやったのがwxPythonでAA管理ツールを作ろう!第二回 ListBoxの操作です。参考までに。

コード


lbox.cpp

#define ID_EDIT 101

#include <windows.h>
#include <windowsx.h>

LPCTSTR strText[] = {
    TEXT("tukasa") ,
    TEXT("kagami") ,
    TEXT("konata") ,
    TEXT("homuhomu") ,
    TEXT("madoka")
};

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK MyEditProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);

TCHAR szClassName[] = TEXT("SETZER2");  // ウィンドウクラス
HINSTANCE hInst;
HWND hMain;             // メインウィンドウのハンドル
HWND hSubsortList;
WNDPROC OrgEditProc;   // プロシージャアドレスを格納するための変数
int CursorDown(HWND hEdit, HWND hsortList);
int CursorUp(HWND hEdit, HWND hsortList);

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;

    hInst = hCurInst;
    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow))
        return FALSE;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
        if (bRet == -1) {
            break;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}

// ウィンドウクラスの登録
ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;  // プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;      // インスタンス
    wc.hIcon = NULL;
    wc.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW),
                    IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;    // メニュー名
    wc.lpszClassName = szClassName;
    wc.hIconSm = NULL;

    return (RegisterClassEx(&wc));
}

// ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;

    hWnd = CreateWindow(szClassName, TEXT("SETZER2"),
        WS_OVERLAPPEDWINDOW & ~WS_CAPTION, CW_USEDEFAULT,
        CW_USEDEFAULT, 230, 300, NULL, NULL, hInst, NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    hMain = hWnd;
    return TRUE;
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    static HWND sortList, edit;
    int i;

    switch (msg) {
        case WM_CREATE:
            sortList = CreateWindow(TEXT("LISTBOX"), TEXT(""),
                WS_CHILD | WS_VISIBLE | LBS_STANDARD,
                0, 0, 0, 0, hWnd, (HMENU)ID_EDIT, hInst, NULL);
            hSubsortList = sortList;
            for (i = 0 ; i < 5 ; i++)
                SendMessage(sortList , LB_ADDSTRING , 0 , (LPARAM)strText[i]);
            SendMessage(sortList , LB_SETCURSEL , 0 , 0);
            edit = CreateWindow(
                TEXT("EDIT") , NULL , 
                WS_CHILD | WS_VISIBLE | WS_BORDER ,
                0 , 0 , 0 , 0 , hWnd , (HMENU)ID_EDIT, hInst, NULL);
            // エディットコントロールをサブクラス化
            OrgEditProc = (WNDPROC)SetWindowLongPtr(
                edit, GWL_WNDPROC, (LONG)MyEditProc);
            break;
        case WM_SIZE:    
            // ウィンドウサイズを調整
            MoveWindow(edit, 0, 0, LOWORD(lp), 25, TRUE);
            MoveWindow(sortList, 0, 25, LOWORD(lp), HIWORD(lp), TRUE);
            break;
        case WM_SETFOCUS:
            SetFocus(edit);
            break;
        case WM_CLOSE:
             DestroyWindow(sortList);
             DestroyWindow(hWnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}

// エディットボックスのプロシージャ
LRESULT CALLBACK MyEditProc(HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg) {
        case WM_KEYDOWN:
        if( wp == VK_DOWN )
        {
            CursorDown(hEdit, hSubsortList);
            break;
        }
        else if ( wp == VK_UP )
        {
            CursorUp(hEdit, hSubsortList);
            break;
        }
        else if ( wp == VK_ESCAPE )
        {
            DestroyWindow(hEdit);
            DestroyWindow(hSubsortList);
            DestroyWindow(hMain);
        }
    }
    return CallWindowProc(OrgEditProc, hEdit, msg, wp, lp);
}

// リストボックスのカーソルを上に
int CursorUp(HWND hEdit, HWND hsortList)
{
    int i;

    i = SendMessage(hsortList , LB_GETCURSEL , 0 , 0);
    if( i == 0 )
    {
        SendMessage(hsortList , LB_SETCURSEL , SendMessage(hsortList , LB_GETCOUNT , 0 , 0) - 1 , 0);
    }
    else
    {
        SendMessage(hsortList , LB_SETCURSEL , i - 1 , 0);
    }
    return 0;
}

// リストボックスのカーソルを下に
int CursorDown(HWND hEdit, HWND hsortList)
{
    int i;

    i = SendMessage(hsortList , LB_GETCURSEL , 0 , 0);
    if( i == SendMessage(hsortList , LB_GETCOUNT , 0 , 0) - 1 )
    {
        SendMessage(hsortList , LB_SETCURSEL , 0 , 0);
    }
    else
    {
        SendMessage(hsortList , LB_SETCURSEL , i + 1 , 0);
    }
    return 0;
}

解説


前回の記事の「cppファイルにコードを書く」過程で、上のコードをコピペしたら多分動くと思う。
エディットボックスでカーソル上下したらそれに連動して、リストボックスの選択カーソルが上下するようになってます。またEscを押したらプログラムを終了します。

Win32APIは、メッセージを受け取ったらそれにあわせて何か動作をするという仕組みらしい。コードは長いが、処理の中心はWndProcとかMyEditProcとかなので、そこらへんを追っていけばだいたいわかるかなと。ウィンドウの登録とかウィンドウの生成とかはどのプログラムでも同じなので、あまり気にしなくてもいいらしい。

2013年8月17日土曜日

Visual C++ 2008 Express Editionのインストール

あいさつ


つかさです。
Win32APIをしばらくいじってたので忘れないうちにそのメモ。今回は環境のインストールから実行ファイルを作成するまで。

インストール


Visual C++ 2008 Express Editionをインストール。場所はNonSoft - Visual Studio 2008 Expressのダウンロードとインストールを参考にした。
ネットを利用してインストールする方法と、ディスクイメージを落としてインストールする方法の二つがあるらしい。
ディスク版だと使用期限とか聞かれなくていいらしいので、僕はこっちを利用した。

プロジェクトの作り方


まずはプロジェクトを作る。

メニューバーから

  • ファイル→新規作成→プロジェクト

新しいプロジェクトダイアログが出るので、Win32プロジェクトを選択。また適当なプロジェクト名を入力する。





あとは「次へ」をクリックすればいいが、「アプリケーションの設定」で

  • 追加オプション→空のプロジェクト

にチェックを入れる。これで、「ソースファイル」「ヘッダーファイル」「リソースファイル」の三つの空フォルダが作成される。



実行ファイルの作り方


ソースフォルダを右クリックして

  • 追加→新しい項目

を選択。C++ファイル(.cpp)を選択し、適当な名前を入力して「追加」を押す。




追加されたcppファイルにコードを書く。

書き終えたら、メニューバーから

  • デバッグ→デバッグなしで開始

を選択。エラーがなければ、コンパイルして実行ファイルを開いてくれる。

2013年7月15日月曜日

PPxで外部ソフト起動メニュー

つかさです。
PPxから外部ソフトを起動する方法。



いつも僕はこのランチャの用途にはfenrirを用いるのだが、それができない場合のためにメモしておく。

やり方


以下を編集して取込。Ctrl+Lで外部ソフト起動用のメニューが表示されます。

M_exec = { ** comment **
AzPainter = %Ob D:\bin\azpt212\AzPainter2.exe
EasyShot = %Ob D:\bin\EasyShot\EasyShot.exe
emacs = %Ob D:\bin\emacs\bin\runemacs.exe
Kifu for Win = %Ob "C:\Program Files (x86)\Kifu for Windows V7\KifuW.exe"
MPC = %Ob D:\bin\MPC-HC\mpc-hc.exe
SETZER = %Ob D:\bin\SETZER\SETZER.exe
}

KC_main = { ; PPcメイン窓
^L ,%M_exec
}

カーソル位置のパスを投げるだとか引数をつけるだとかそういうのはまったく関係なくただ起動するだけのメニューです。ひねりとかそういうの全く無さすぎてあれなんだけどたまにはいいよね
ポイントは起動したいパスの前に%Obをつけることと、パスに空白がある場合は""でくくるということです。

他のやり方


後、
%Ob D:\bin\azpt212\AzPainter2.exe

は
*launch D:\bin\azpt212\AzPainter2.exe
*start D:\bin\azpt212\AzPainter2.exe
%Z D:\bin\azpt212\AzPainter2.exe

と書いても同じ事が(おそらく)できるのでこっちのが直感的に分かりやすそうだという人は試してもいいかもしれない。僕も使い始めのころはこの%ってなんだろうとかOって何の略だろうとか無駄に考えてはまってた気がする。何が違うか気になる人はHelpを見て研究しましょう。

2013年7月12日金曜日

ChangeLogをPPxで閲覧する

やってみた感想


ChangeLogでいくつか文章を読みながら、それのメモを書くということをやってみたがかなりいい。プリントの余白に書き込んでいた時みたいに、書き込む量を気にしないですむのでどんどん書いていける。
気になる箇所があれば、C-x 4 aでChangeLog.txtを開き、メモをする。書き終えたらC-x 0で閉じる。

PPvを並列する


次に、どうやってこのメモを活用するかという話になる。
やりたいのは、本文とこのメモとを見比べる環境を作ること。そのときには、対応箇所の一致ができればなおよい。

見出しをGrepでまとめるとか一々ChangeLogを手動で編集し直すとかそれ以前にメモの書式を変えるだとかいくつかやりようはあると思うが、単純にPPvを左右に並べるだけでいいのではないかと思った。




一々これを手動でやるのも面倒なので、PPv上で実行すれば

  1. 同フォルダのChangelog.txtを別IDのPPvで開き
  2. 右にくっつける
コマンドを作る。以下をカスタマイザーで編集して取込。
KV_main = { ; PPvメイン窓
\C ,%Obi %0\PPVW.EXE -bootid:Z  %1\ChangeLog.txt %: *fitwindow %N,%NVZ,6
}

PPvで文章を閲覧してて、そのメモを見たいという場合は、Shift+Cを押せば右にそのメモが表示されるので、見比べることができます。
PPXWIN.DLLが必要なので、ない場合はTORO's Libraryからダウンロードしましょう。

メモの反映作業


PPvで閲覧時に



Shfit+CをPPv上で押して、ChangeLog.txtを表示する



編集するときは、PPv上でEを押してエディタで開き、メモを元にして修正する。メモを書くときにどの見出しと対応しているかわかるようにしておいて、個々の見出しごとに修正するのがいい。

修正し終えたら、最後にメモをざっと見て反映させてない箇所が残ってないかの確認をする。

特定の章だけを抜き出したい


ChangeLogを使うと、時系列でどんどん書き連ねることになる。だが、実際にメモとして活用したいときは、カテゴリーごとにまとまっていたほうがいい。ではどうやってそれを実装するかということになる。

これも単純に、PPvでクリップボードの内容を表示させて対処すればいいのではないかと思った。

PPvをアクティブにしてCtrl+Vを押すと、クリップボードの内容を表示することができるのでこれを利用。先のChangeLogの閲覧時、その一部だけを表示したいとしましょう。




例えばこの黄色く囲んだ箇所だけを表示したいなら、範囲選択してCtrl+C、Ctrl+Vとすると


このようになる。

で、同じカテゴリーのことがいろいろな場所に散らばっている場合のをまとめて一覧表示させたいなら、それらをクリップボードに追加コピーしてPPvで表示させればいい。
そのためのスクリプトを用意。

ClipAdd.js

//!*script

PPx.Clipboard = PPx.Clipboard + "\n\n" + PPx.Extract("%*selecttext");

PPvの選択範囲をクリップボードに追加コピーするスクリプトです。
このファイルをScriptフォルダに保存してから、以下を編集して取込。

KV_main = { ; PPvメイン窓
&C ,*script %0\Script\ClipAdd.js
}

これで、Alt+Cで、選択範囲をクリップボードに追加コピーできるようになる。

例えば、以下のファイルの黄色く囲んだ箇所のみを表示したい場合は



最初の範囲を選択してCtrl+Cでコピー



次の範囲を選択してAlt+Cで追加コピー


PPv上でCtrl+V


単純すぎるというか原始的すぎるような気がしないでもないが、とりあえずこれでやってみる。

思ったこととか


プリントを使っていた時は、反映作業をするときはPCの横に書き込み済みのプリントを書見台とかで立てかけて、それを見ながらということをしてた。それと比べるとこっちのが色々とスムーズですね。プリントを使うメリットは、PCの前でなくても作業ができる、ことくらいしかなさそう。

今、ネックになるんじゃないかなーっと思ってるのは、ChangeLogの古いメモがずっと残されること。
例えばプリントだったら、それを参照して本文を修正してしまったら、後は書き残したことが無いかざっとメモを閲覧したあと、プリントごと捨ててしまうということをしている。反映された以上、そのメモはもう使うことはないしこの方がいい。残っていてもノイズにしかならない。

だが、今回のChangeLogだとずっとそれが残ってしまう。2日とか3日とか、メモをしばらくの間書きためておいてあとでまとめて修正作業をする、というのだったらこの仕組はいいのだが、その後がね。捨てるという過程を組み込めない。

2013年7月2日火曜日

ChangeLogでメモ?

問題意識


一つの文章をどう作っていくか、という問題がある。
メモをためることはMoEの手法を使えばできるが、それを材料にしてどのように一つの文章を作っていくか、ということについてはしっくりくるものはできてなかった。

今、僕がやっているのはプリントを介する方法である。

  • 文書データをプリントアウトする
  • そのプリントに書き込みをする
  • 元のデータに反映させる

ざっとこのようなことをしていた。
この方法の不満点は以下の点。

  • プリントを介すること。まず思いついたことを紙に書き込んだ後にデータにするのだから、二度手間である
  • それまでにためたメモデータの利用ができない。プリントを使って考察する環境において、参照することが基本できないから

この後者については、今までは関係しそうなメモのみをClipMarkFile.jsでまとめてプリントアウトして、参考にしたりはしてたが、あまり使えなかった。

PCの制限


じゃあPC上でやれよと思うかもしれないが、それでだいたい失敗してきたからこのようなことをしているのである。たいていは、集中が続かない、という仕方で破綻した。
なぜだろうかと考えたが、それは「文章を読みながらメモをする」という作業が必須なのに今までそれを実装してなかったからじゃないか、と思い至った。

プリントを使う場合は、基本、その文章を読み、そこで思いついたことを余白に書き込み、あとでそれから何か文章を作り直すということをしている。

  • 文章を読む→思いつきをメモする→それをいくつか溜める→それらを元に新たに文章を作り直す

という過程をとっている。
だがPCにおいては、これを実装していないため、

  • 文章を読む→新たに文章を作り出す

このようにメモを作って溜める過程を飛ばして、いきなり手がかりもない状態で文章を思いつき、作り出せということになる。このことがそもそも無理で、結局破綻してたのではないかと思うのだ。

今の枠組み


今のところシステムとして用意しているのは、howm形式のメモである。これが、この用途には適していないのではないか。

まず、今の形式では、本来の目的である保存のためのメモと、文章の作成過程での思いつきを書くためのメモを区別できない。
また、この用途のためにはhowmメモの仕組みは大掛かり過ぎる。別ファイルを開き、タイトルを書いて閉じて、という作業はもともとしていた文章を読むという行為をぶったぎってしまう。

一つのファイルを再利用して、何度も編集して使う、というのであればそのファイルを探す手間が生じるし、複数のファイルに分割してやるのではそれらをまとめて見る手軽な手段がない。

つまり、今までのhowmでやってたメモとは違った種類のメモが要請されてたのに、そのように認識せずそれに対応したものを用意していなかったのが問題なのではないかと思うのだ。

要請されるもの


今までのメモが

  • 保存して再利用するためのメモ

だとすれば、ここで要請されるのは

  • 一時的な、文章を読んでいるときに思いついたことを書くためのメモ

である。

この用途に即したものを実装したいわけであるが、要請されるのはざっと以下のようになるだろう。

  • 文章とメモファイルの対応
  • どの箇所についてのメモなのかの参照が容易
  • 文章を読む作業を妨げない。すぐ開け、すぐ閉じれる
  • 全体についてのメモと個々の箇所についてのメモとが分離できる

ChangeLog?


一番この用途に近いのは、ChangeLogではないかと思う。これはプログラムの変更履歴を書く形式の一つらしい。僕は今までは、プログラム作成時に思いつきを吐き出すのにこれを利用していた。

僕が使っているのはJunk ScriptsのChangelog.l

  • C-x 4 a

で新たな項目が追加される仕組み。

だが、これだと

  • どの箇所についてのメモなのかの参照が容易
  • 章ごとにメモを並列することができない。時系列のみ

の二点では求めるものとは違っている。
というわけで、しばらくはこの方面で何か工夫してできないかを考えてみたい。とりあえずEmacsのChangeLogを試してみようかな。

これがPC上でできれば、プリントを使う際に必ずネックになり、構造的に乗り越え不可そうな
  • 書き込みによる視認の低下
  • 元の文章が書き込みが増えると読みにくくなる
  • 余白の大きさに規定される

などは克服されるので、考えてみる価値はありそうかなと思っている。

2013年6月26日水曜日

現在窓を反対窓と同パスに

あふのように

O→現在窓を反対窓と同パス
Shfit+O→反対窓を現在窓と同パス

にするようにする。
カスタマイザーを開いて以下を編集して取込

KC_main = { ; PPcメイン窓
O ,%j%2
\O = @'='
}