2013年10月9日水曜日

MoEに至るプロセス - PCの利用からバッファ理論へ


PCへ


  • 京大カード→PoIC

というプロセスを経て、やはりカードは無理だと思い、PCに移ることになった。そのときに考えていたのは、「PC特有の機能によって、死蔵という問題も解消されるのではないか」ということである。

例えば何か調べ物をしたい場合、Googleで適当な検索語を入れれば、自分に必要な情報が乗ってそうなリンクがずらっと並び、そこからいくつか開けばたいていの場合解決する。
はてなやwikiを読んでてよくわからない言葉があれば、その語のリンクをたどればその説明を見ることができ、自分にとって不明であることがすぐに明らかになる。
ニコニコ動画で興味のある動画に出会ったら、それにつけられているタグをクリックすれば関連する動画が羅列して表示される。

このように、ネットにおいてはデータが無数にあったとしても、それから自分にとって有用である情報をPC特有の機能でもって取得する方法が存在する。これを応用すれば、カードで試行錯誤していた「情報が増えることによるカオス化」など、容易に解決するのではないか、と思ったのだ。


カードをPCで再現する



カードでやってたことをPCで再現するなら、それはテキストファイルをため、それでできたデータベースに対して何らかの処理をする、ということになる。このときのデータがソフトに依存するようなものであった場合、あとで利用できなくなるおそれがあるからだ。
このテキストファイルに対してPC特有の機能を用いて、カードだと克服できなかった死蔵という問題を解決することになるのだが、それを実現する以前の段階として、カードで実現してたことをPCで再現することが必要になる。*1一見簡単そうなのだが、わりと難しい。

カードの「くる」を再現


カードにおいては、それを「くる」という操作を行っていた。この操作は、カードボックスにたまったカードのタイトルだけを見て関係するものを見つけ出す方法と、カードをめくっていってその本文をざっと見ることで、その内容を把握することで関係するものを見つけ出す方法の二つからなっている。
このことをPCで実現するために必要になるのは

  • タイトル一覧表示
  • 連動ビュー

の二つである。
「カードのタイトルを見る」に対応するのが、タイトル一覧表示である。一つのフォルダにメモファイルが入っているとすれば、そのタイトルが一覧表示される仕組みがあればいい。(デジタル知的生産術の急所-タイトルをどう実装するか
「カードの中身をざっと見る」に対応するのが連動ビューである。Windowsでは、通常ファイルの中身は表示されない。だが、いちいちそれをエディタで開いて、ということをして確認するのでは手間がかかりすぎる。ということで連動ビュー、つまりそのファイルにカーソルをあわせれば中身を表示してくれる機能が必要になる。

これについては、TextTree+ViewLinesだったり、howmだったり、PPx+スクリプトだったり、いくつかの方法がある。
これにより、カードで実現していた「最近のをざっとみて関係するのを抜き出す」は実現できる。次に問題になるのは、「データベース全体から関係するものを抜き出す」である。これの追求で立ち止まる。

PCの機能による関連メモの並列


  • Grepの利用
  • 関係するファイルに共通のタグを入れる
  • メモデータ本体とは別に情報管理

の三つの方法がある。*2

まずGrepだが、この方法で自分が今知りたいテーマについての情報を並列するのは難しい。そもそもテーマに関係する単語というのが存在するのかという問題がまずある。また、たとえあったとしてもそれによってアイデアメモの内から関連するものをすべて拾える保証というのがない。そして、この方法だとどうしても関係するメモ以外がヒットし、ノイズが大きくなる。
これは、Googleで何か調べものをするときの方法論からの類推である。何かを調べようというとき、それに関係していそうな単語で検索する。すると、いくつかのサイトがヒットするので、それをいくつか開きその情報を探すという具合だ。
だが、ここで検索するのはアイデアであり、それがGoogleの場合と大きく異なっている。Googleでヒットするのは情報としては完成したものであり、知りたい情報が載っている可能性もある。しかしアイデアメモというのはそれ以前の段階であり、これをいくつか集めてそれで初めて答えが出る、という性質のものである。だから、Grepによって自分の求める情報が手に入る可能性は低い。
また、アイデア発生装置としても期待できない。これは京大カードがこの用途に使えなかったのと同じ理由による。

次にファイル名をキーにする方法について。この場合、ファイル検索でヒットするように、次のようにタグをつけることになる。

【タグ】ファイル名.txt

重要度をスターなどで表す場合も原理的にはこれと同じであり、例えばファイル名に特別な記号をつけたりする。このようにして共通の語がファイル名に入るようにし、その語でファイル名検索をすれば関連するファイルが一覧表示されることになる。

ファイル名☆☆☆.txt

このときの問題点の一つ目は、持続性を持つタグをつけることができない、ということである。何かを思いついてアイデアメモを書いたとしても、それを思いついた当初はそれがなにについてのアイデアか、というカテゴライズなどできていない場合が大半である。だからどうしても、おおざっぱな区分か、あるいはそのときにたまたま思いついたタグを付けることになる。
おおざっぱなタグというのはおおざっぱゆえに役に立たない。そのときどきの問題関心は移り変わる故に、同じカテゴリーであっても関係のないものが含まれることになるからである。またずっと持続するような区分というのを見いだすのが難しい。タグの数を制限するだとか、タグ一覧を目にとまりやすい場所に貼っておくだとかいろいろしたが、結局は機能しなかった。
そのときに思いついたタグというのは、次にそれに関係するアイデアを思いつきメモにしたとしても、以前のタグを思い出せずやはりそのときたまたま思いついた別のタグを付けてしまう、という理由で役に立たない。
また、タグを付けても重要度での区別をつけることもできないという問題もある。あるメモを編集してその精度をあげても、タグを利用して表示するときには他の雑多な適当なメモと並列して表示され埋もれてしまう。同じタグを持つファイルが増えれば増えるほどこれは顕著になる。結局、カードボックスで有益なカードが他のカードが増えることで死んだのと同じように、同じタグを持つファイルの中で死んでしまうことになる。

「メモデータ本体とは別に情報管理」だが、これは覚えらんない人が紹介していた方法(文具としてのコンピュータを考える(2)-メモ(情報カード) - 覚えらんない人のためのオンラインソフト備忘録)。カード型データベースソフトを使い、一つのデータに対し一つの蔵書カードを作成。検索その他はこの蔵書カードを利用して行う。気になって調べはしたが、適当なカード型データベースソフトを探しても見つからなかったという理由であきらめる。

そして破綻へ


結局、メモをデータベース化してPC特有の機能で操作する、という試みは破綻した。
結果、最近のメモのみを一覧表示と連動ビューで表示し、関係するメモをいくつか見比べて、という操作のみが残ることになった。これは、最近作成した以前のメモというのはあってもなくてもいい、そこにかけた労力が無駄な死蔵したものになることを意味する。カードで行っていたのと同じことが起きたわけだ。そして、最近のデータのみを扱うのであれば、入力の手間その他を考えれば、カードでやってたのと比べどちらが効率がいいかというと微妙な問題である。


野口のバッファー理論


フローを扱う


PCに特有の機能を使っても問題は解決しない。やはり死蔵し、膨大にアイデアメモをためたとしてもそれを有効に使う方法はない。
これを突破するきっかけになったのが、野口のバッファー理論だった。これは、野口悠紀雄『「超」整理法〈3〉』において出てくる概念である。

知的生産活動で扱うデータは、決まりきった仕方では処理できない、という特性を持っている。それは、最初はどのカテゴリーに入るかも不明で、重要度も分からない。同じデータといっても取り扱うべき対象は様々である。そしてそれを扱う方法にも先例もモデルも無い場合が多く、決まりきった仕方で処理するということができない。
このような性質を持つデータに対しては、先例もモデルもある、処理が確立されているような定型的な仕事でとられてきた仕方では処理ができない。今までとは異なる発想に基づく処理システムを用意し、そこで処理すべきである。このような考えをする。
まず、対象がフロー(流れ)であることを明確に意識する必要がある。必要とされるのは、膨大な量のフローを制御するダイナミック(動的)な方法である。一定量のストック(蓄積)を管理するスタティック(静的)な対処法ではない。
これは自明のことである。しかし、従来の収納システムは、「内容がほぼ変わらないストックのための管理」のためのものだ。「大量のものが流入し、ストックの内容が短期間のうちに入れ替わってしまう」という認識は、殆どないのである。(野口悠紀雄『「超」整理法〈3〉』P21)

ではどう処理すべきかというと、「とりあえずおいておく場所(バッファー)」を用意し、そこに分類せずにデータを入れる。ある程度の時間がたてば、そこに放り込まれたデータは自然に醸成して区分とかもわかってくるから、そのときになって初めて処理をすればいい。

マゼラン的な仕事を扱うには、マニュアル遵守的な仕事とは異なる発想にもとづいて、処理システムを構築する必要がある。
とくに重要なのは、「バッファー」(buffer)だ。これは、「緩衝器」、つまり二つのもの、あるいは二つのプロセスの中間にあって、衝撃を受け止めるための装置である。
まず、外から入ってきたものや新しく作ったものを、簡単な手続きによって(できれば、殆ど手間をかけずに)、システムの中に受け入れる必要がある。整然とした収納でなくともよい。しかし、書類が紛失したり迷子になったりすることはないようにする。つまり、「とりあえず受け入れる」のである。これが、「受け入れバッファー」だ。
そして、不要と思われるものを必要なものから区分し、所要の措置や加工などを行い、次の段階に送る。
この際、確立された処理法はないのだから、本当に正しい処理をしたかどうかは分からない。やり直す必要があるかもしれない。そのため、完全でなくともよいから、一応の措置をする。一〇〇%処理を目論んで何もしないのではなく、とにかく一歩進める。「ゼロか完璧か」でなく、八割の処理をするのだ。これは、単なる先送りとは違う。「もっとも重要と思われること」は、行っておくのである。
そして、「多分必要ないだろう」と思われるものを、日常の仕事のジャマにならないようなところに置く。つまり「とりあえず捨てておく」のである。これが、「廃棄バッファー」だ。(野口悠紀雄『「超」整理法〈3〉』p41)

バッファーによる解決


知的生産関係のデータを扱うときに問題になるのは、それが処理しきれなくなるほどたまり、古いデータがノイズとなって全体が機能しなくなることである。そこで、バッファーを作り、時間の経過で勝手に洗練されることを利用して、常に有用な情報が取得できる状態を保つのだ。

  1. 最初は区分せず、とりあえずおく場所を用意してなんでもそこに放り込んでいく
  2. 半自動的にそれが保てるシステムを構築する

この二点を実現すれば、不定型なデータの処理が可能になる。これまで対処できなかったのは、不適当なシステムを使って処理していたからなのである。
従来の整理法では、情報をストックとして扱っている。つまり、将来に向かって価値が減少することを重視していない。したがって、「いかに保存するか」に重点があり、「いかに捨てるか」は、副次的なこととしか考えられていない。
情報が比較的少ない時代には、こうした考えが妥当したかもしれない。しかし、現代のように情報が増えてくると、基本的な考えを転換する必要がある。つまり、情報とは生まれ消えるものであり、一定の寿命を持ったフロー量である、と捉える必要があるのだ。
(野口悠紀雄『「超」整理法』P8)

データベースからバッファーへ


我々が蓄積したアイデアをうまく利用できなかったのは、データベースの規模の問題でも、データベースをうまく運用する方法に通じていないからでもない。そもそもの、データベースを作るという発想から間違っていたのだ。
個々のものの重要性がはじめからわかっており、区別がすでになされているようなものであればデータベースを築くことは有効だろう。そしてそれは、規模を増していけばそれだけ有用なものになるだろう。web上にある情報をGoogleなりwikiなりの機能を追求すれば、データが増大しても破綻しないシステムを構築することは可能だろう。

だが、アイデアのように、日々陳腐化し、重要度の区別などつけようがなく、最初から区分のしようがないデータを大量にためることには意味がないのである。それをもとにしてデータベースを築いたとしても、それは個々バラバラのスピードで陳腐化していく。それはだんだんとカオスで使いものにならないものになり、このこと自体はデータベースから有用なデータを抜き取る機能を追求しても解消されない。それよりは、バッファが半自動的に洗練され、不要なものが削ぎ落とされ、常に自身にとって有益な情報がある状態を保ち続けることが重要なのである。

バッファ理論の特徴


つまり、バッファ理論の特徴は以下の点にある。
  1. アイデアメモをためる先は、データベースではなくバッファである。それは時間の経過とともに陳腐化するから、常に自分にとって有用なものに更新する必要がある
  2. これは時系列を利用することで半自動的に可能になる
発想としてはPoICのタスクフォースと似ている。このタスクフォースとバッファ理論をPC上で実現しよう、と考えたことがMoEの基礎になっている。

MoEの原型


データベースを作る必要はない。だからメモが不要であればそれは捨ててしまってもかまわない。自由に組み替え、原本を改変しても全くかまわない。それでもいいから、とにかくずっと、全体が有用であり続ける状態をつくれ。

この発想を元にして、MoEの原型はできあがった。
必要なのは以下のことである。

1.メモの受け入れバッファーをはじめから用意する。これは、分類だとか重要度だとかそういったことをいっさい気にせず、放り込める場所にする

→これは実際は、これまでカード法の時点から実施してきたことである。そこでは基本的に、分類をせずつっこんできた。梅棹の意図したところは、「何でもつっこまれている状態であればくるときにそれだけ有効になる」と少し意味合いが違っているが、これは受け入れバッファーとしての役割をすでに果たしている。
ただ、そこでしていた、「質の高いもののみメモとして含める」「タグを打つ」といった、それがカオスになることを防ぐためにとっていた方法はしなくてもいいことになる。この時点では、なにも考えずメモを作れることが第一であるのだ。

2.それが秩序を保った状態を維持する

→これは、「そこから関連するもののみを取り出す」という仕方で維持を図る。PCで言えば、それを別フォルダに移動することをすればいい。
関係するファイルを探し、一つの箇所にまとめる過程がそのまま秩序を保つことにつながる。また、この過程は一度にやる必要性もない。関連するファイルをある程度までまとめて移動したあとでも、それを中断して後日続きをするというのは可能である。これはカードを使った方法ではできないことである。

また、同時に廃棄バッファーをあらかじめ用意しておき、そこに不要なものはすべて移動するようにする。こうすることによって、それらは一旦は目の前から消えることになり、受け入れバッファーのノイズが減ることになる。また、たとえそこに捨てたとしても、がんばれば(Grepなりなんなりを利用して)元に戻すことも可能である。ただ、今のHDDの容量からすればテキストメモの容量など微々たるものであるから、実際にこのフォルダにあるファイルを削除する必要はないだろう。
「とりあえず捨てる」ためのバッファーは、つぎの二つの条件を満たしていなければならない。
(1)目の前からは消えていること。
(2)しかし、必要になれば取り戻すことのできること。(野口悠紀雄『「超」整理法〈3〉』p55)

時間軸という観点から


これが利用しているのは、「時間がたてば書き出したメモも醸成され意味がわかってくる」という原理である。

人間はそのときどきでいろいろな問題に取り組んでいる。それは突発的なものであったり、あるいはずっとかかえているものであったりするだろう。それについての思いつき、対処法、方針、総括、感想といったものが、そのときどきにたまたま経験することをきっかけにして、無秩序にひらめく。ただ、それがどのような問題に関してのものなのか、といった全体的な見通しについては保たないままランダムに生じる。そうして、そのときにたまたま思いついたことを元にして何かの行為をし、それがたまたま失敗するか成功するかして、またそれについて忘却し、また同じ思いつきをしてというように、同じところをくるくると回るのである。
記憶があてにならないという事実も、「発見の手帳」をつかっているうちに、うかびあがってきた一つの「発見」であった。わたしは、自分自身の発見や着想をも、すぐにわすれてしまうのである。その証拠に、わたしの「発見の手帳」をよみかえしてみると、まったくおなじ内容の「大発見」がしばしばあらわれてくる。まえに一ぺん「発見」したことをわすれてしまって、またおなじことを「発見」してしまうのだ。
しかし、とにかくもそれが記録にとどめてあったことによって、無意味な二重発見をチェックすることもできるのである。もしこれが、なんにもかいてなかったら、わたしは毎日大発見をしたような気になっても、じっさいのわたしの知的活動の内容は、何年もまえとおなじところで足ぶみしているのかもしれないのだ。かいておきさえすれば、まえの発見が、つぎの発見のためのふみ石になって、しだいに巨大な構築物にまでつみあげることも可能なはずである。(梅棹忠夫『知的生産の技術』P27)

そこで、それをメモとして蓄積する。それ自体は、はじめは単体としてみればたいして価値のないものだろう。そうすれば、それが蓄積しそれを眺めたならば、それがどのような問題意識からきたものなのか、そしてそれについてどのような方針を思いついたのか、それは適当なものなのか、ということの判断というのがだんだんとついてくる。そこで、それを元にしてその関係するものを取り出し、整理をつけて、実際の行動に反映させ、思いつきでやっていたよりもより適切な行為をする。我々は現実において様々な問題に出会い、そのときどきでなんらかの対処をする。そのような実際の流れとは別に、それらを一旦、メモ群という別の場所にとりあえずおいておき、寝かせて、それを再び現実に反映させる。リアルの流れとはまた別の時間の流れというのを、別の場所に平行して作り出すのである。

何か思いついたことがあれば、とりあえずそれをメモにして受け入れバッファーに放り込む。そうしてあとは時間がたつのを待ち、適当に時間が過ぎて受け入れバッファーを見て、なんかこれ関係してそうだなというのがあればそれをまとめて操作をする。時間がたちすぎてもはや問題そのものが消滅し、アイデアの価値がなくなっていると判断したものについては、それを廃棄バッファーに捨てればいいだろう。受け入れバッファーのなかでもより古いファイルがそれに該当する可能性が高いので、見分けるのもそれほどは難しくないはずだ。

PCにバッファ理論を応用


メモをためるというだけなら、PCでやろうとカードでやろうと大差はない。しかし、それを編集し、移動し、活用するとった自由な操作をメモ単位で行おうとすれば、アナログなカードよりもPCのデータのほうがより適している。PCの利点はGrepだとかにあるのではない。メモという単位でデータを扱うのがより柔軟になる、ということにあるのである。

実際のシステムは次のようになる。

フォルダ構造とバッファ


まず受け入れバッファー用のフォルダを作る。テキストメモは、とりあえずここに保存する。
ついで、廃棄バッファー用のフォルダを用意する。これは、不要になったファイルを適当に移動するためのフォルダである。

また、カテゴリーフォルダを用意し、受け入れバッファーからメモを取り出して操作する際にはこのサブフォルダを使って行うようにする。このフォルダ内で編集その他の操作をし、不要になったと判断したメモは廃棄フォルダに移動する。

こうして
  1. 受け入れバッファー用のフォルダ
  2. カテゴリーフォルダ
  3. 廃棄バッファー用のフォルダ
の三つを作ればいいということになる。

ファイル移動による秩序の維持


再生産をする際には、受け入れバッファーから適宜、一覧ビューとタイトル表示を利用して、関連するファイルをカテゴリーフォルダに移動する。そして移動先で、編集なりなんなりの操作を行う。
これは同時に、受け入れバッファー内の秩序を保つ過程でもある。

以上の操作は、ファイラを用いてするべき範疇に入る。こうして、「ファイラを起点にして運用する」というように、PCを用いたメモの方法論の性質が定まることになる。

*1:このときは選択肢として以下のようなソフトがあった
  • howm
  • アウトラインプロセッサ
  • ChangeLog
  • 紙copi

*2:このときに利用していたソフト
  • Getia
  • xyzzyのGrep
  • VxEditorのGrep
  • DesktopHE
  • fenrir

2013年10月8日火曜日

MoEに至るプロセス-京大カードからPoICまで


まえおき


つかさです。
僕が京大カードに出会ってから、どういう経緯でMoEに至ったかを追ってみたいと思う。おおざっぱには

  • 京大カード→PoIC→(カードからPCデータへの移行)→「超」整理法→MoE

という過程で進んでいるのだが、試行錯誤や行ったり来たりとか同時並行だとかがあるのでもちろんスムーズに来たわけではない。
この種の技術にはある程度普遍性があると思うので、誰かの参考になればいいかなと思ってます。

京大カード


僕が知的生産術に手を出したのは、梅棹忠夫『知的生産の技術』を読んだのがきっかけだった。
「京大カード」あるいは「情報カード」と呼ばれるB6のカードを手に入れ、ここに思いついたことを書き込んでいく。


書式はこのように。日付、タイトル、本文から構成。また、一つのカードには一項目のことのみを記述するようにする。裏面は使わない。書けたら、そのカードをカードボックスに入れる。

カードが相当の枚数たまったら、それを「くる」ことによって発想を促す。自分が書いた個々のカードがランダムに現れる状況を作ることで、知的生産を実行するのだ。

たしかに個々のカードは、経験や知識の記録である。しかし、それをカードにしたのは、知識を分類して貯蔵するのが目的なのではない。何万枚ものカードも、死蔵していたのではなんにもならない。それは活用しなければならないのだ。カードを活用するとはどういうことか。それは、カードを操作して、知的生産の作業をおこなうこということである。
操作できるというところが、カードの特徴なのである。蓄積と貯蔵だけなら、ノートで十分だ。ノートにかかれた知識は、しばしば死蔵の状態におちいりやすいので、カードにしようというのではなかったか。カードの操作のなかで、いちばん重要なことは、くみかえ操作である。知識と知識とを、いろいろにくみかえてみる。あるいはならべかえてみる。そうするとしばしば、一見なんの関係もないようにみえるカードとカードのあいだに、おもいもかけぬ関連が存在することに気がつくのである。そのときには、すぐにその発見をもカード化しよう。そのうちにまた、おなじ材料からでも、くみかえによって、さらにあたらしい発見がもたらされる。これは、知識の単なる集積作業ではない。それは一種の知的創造作業なのである。カードは、蓄積の装置というよりはむしろ、創造の装置なのだ。(梅棹忠夫『知的生産の技術』P57)


このシステムは、カードが相当枚数たまることを前提としている。そしてカードがたまればそれだけ、有用性は高まるという思想を持っている。
くりかえし強調するが、カードは分類することが重要なのではない。くりかえしくることがたいせつなのだ。いくつかをとりだして、いろいろなくみあわせをつくる。それをくりかえせば、何万枚のカードでも、死蔵されることはない。(梅棹忠夫『知的生産の技術』P59)

ノートでも同じだが、カードはとくに、長年つづけてやらねば効果はすくない。いわば蓄積効果の問題なのだから、一時的におもいついてやってみても、なんのためにこんなことをするのか、わからぬうちにいやになる。(梅棹忠夫『知的生産の技術』P63)


京大カードの画期的な点


「アイデアは個々バラバラになっているほうが使いやすいのではないか」というアイデアが、京大カードの基礎にある。そしてこの発想は、一般に用いられている文房具とは潮流を異にするものである。

この手の技術について疎い人の場合、論文を書くなり何なりで知的生産をしたいというとき何をするかというと、ノートを利用するわけだ。ノートに思いつきを書き、それを元にして論文を作ろうとするだろう。だが、そのような試みはたいてい失敗する。ノートの場合、書き込んだアイデアを組み替えることができない。関係するもの同士を並列したいのなら、新しくノートを購入してそこに書き直すくらいしかない。が、これはそれに非常に手間がかかるという理由で、たいていは破綻する。
そこで、あらかじめアイデアを最初から個々バラバラで組み替え可能なようにしているのが、このカードシステムなのである。アイデアを個別に分離し、それを蓄積して利用する。このための具体的方法論を提出したという点で非常に画期的である。

また、これは論文を書くというのは所詮技術の問題にすぎない、それがうまくいかないのはそれがつたなかったからだ、というように言い切ったという点でも意義がある。この転換がなければ、論文が書けないのは自分の頭が悪いからであり、精神的に未だ未熟だからと総括して無意味な方法を続けることになっただろう。
技術というものは、原則として没個性的である。だれでもが、順序をふんで練習してゆけば、かならず一定の水準に到達できる、という性質をもっている。それに対して、研究だとか勉強とかの精神活動は、しばしばもっとも個性的・個人的ないとなみであって、普遍性がなく、公開不可能なものである、というかんがえかたがあるのである。それは、個性的な個人の精神の、奥ぶかい秘密の聖域でいとなまれる作業であって、他人にみせるべきものではない… …。
しかし、いろいろとしらべてみると、みんなひじょうに個性的とおもっているけれど、精神の奥の院でおこなわれている儀式は、あんがいおなじようなものがおおいのである。おなじようなくふうをして、おなじような失敗をしている。それなら、おもいきって、そういう話題を公開の場にひっぱりだして、おたがいに情報を交換するようにすれば、進歩もいちじるしいであろう。そういうようにしようではないか、というのが、このような本をかくことの目的なのである。(梅棹忠夫『知的生産の技術』P8)


京大カードの限界


このシステムを用いていると、途中で突き当たる壁がある。それは、「死蔵」という問題である。この問題は、カードを大量にためてそれを利用するシステムである限り、ずっと付きまとってくるものである。

このシステムは、取り扱うカードが少ないうちは問題なく機能する。たとえばA4数枚のレジュメだとか、本を読んでそれをまとめるくらいだったらいい。レジュメだったら、何か思いついたことなり調べたことなりを片っ端からカードに書いていき、あとでそれを並び変えて作成する。本を読んでそこで気づいたこと、思いついたことをカードに何枚か書きとめる。そうして読み終わったら、そのカードを並び替え、それを元に文章を書いていけばいい。漠然と本を読むなり思いつきなりだけでいきなり文章を書き始めるよりは、格段に質のいいものが短時間でできるはずだ。

必要なアイデアメモが取り出せない


だが、これは扱う対象が増え、複数の事柄について書いたカードが蓄積し、カードの枚数が増えていくと機能しなくなる。例えば何かのテーマについて書く必要があり、そのために以前に書いたメモを参考にしようと思って、カードをくるとしよう。すると出てくるのは、個々バラバラの事態について思いついたアイデアだ。そのテーマに関係の無いメモはノイズとなり、かつ過去へと遡れば遡るほど、賞味期限が切れて意味をなさなくなったアイデアのかけらに出会うことになる。

アイデアは、それを思いついた当初は意味を持っていたとしても、時間の経過によって劣化し意味のわからないものになる。結果、最近のカードから使えそうなアイデアをタイトルなどから関係してそうなもののみ抜き出し、それを元に考察をするだろう。これは、それらを一つのデータベースとしてためていることに意味がないということを示す。

発想支援装置にもならない


アイデアの発生装置としても役には立たない。たとえば僕が過去に使っていたカードボックスには、FPSでの特定マップの攻略アイデアと気になったレシピと何かの思いつきの断片と何かの記録とというようなものが混じっている。これらを見比べて何か意義のあることが思いつくことなどできるわけがない。

結局は、最近のカードをめくって関係するもののみを取り出してそれを持ち歩き、それを元にして発想をするということをしていた。これは結局、それ以前のカードが死蔵し、それの作成のためにかけた労力が無になったのと同じことである。

京大カードの次へ


複数の情報を並列することで発想する。これは、その対象が少なくとも相当練り上げられ、情報として洗練されているものでないと意味がない。アイデア段階のものがそのまま打ち捨てられた状態のものが並んでいたとしても、それはノイズにしかならないのである。

新しい刺激が欲しいのであれば、誰かに話を聞きにいくなり町に出て新しい経験をするなり誰かの本を読むなりすればいい。それを自分のアイデアで、データベースとしていちいち手間をかけて作成する意味はない。カードがたまればたまるだけ、それが有効に機能するという根幹にある思想自体が誤っているのではないかと思うのだ。アイデアを出す。それを個別に切り離せる形で保存する。ここまではうまくいく。アイデアはバラバラになっているほうが使いやすいというのは確かにそうだ。ただ、それをどのように運用するかという問題が、それを実際に大量にためたあとに現れるのである。

これへの対策としてPoICのタスクフォースがある。

PoICのタスクフォース


PoICというのは、京大カードを用いた方法論を体系的に整備したものである。
wiki形式になっており、梅棹以降の理論や、考案者の実際の試行錯誤に基づく修正などが盛り込まれており、非常にレベルが高い。梅棹は方法論としてはそれほど詳しくは書いてないので、実際にカードを使ってみたいなら、梅棹でだいたい思想をつかんで、このサイトをみてやり方を学ぶ、というようにするといいとおもう。

この過程の一つに、タスクフォースがある。それは、ためたカードを元に再生産をするプロセスである。

PoIC - 再生産する

広い場所を作り、そこにカードを並べていく。もし似たようなことについて扱っているカードがあれば、それは重ねる。このようにして、似たことについて扱っているカードのグループを作る。そうしてできたまとまりに、そのまとまりの内容を書いたふせんを貼る。

このようにしてできたまとまりを元にして文章を作る。その作業の途中で新しいことを思いつけば、新しくカードを書く。その作業で内容を反映されたカードのグループは、お役御免として、カードボックスとは別の場所にしまう。こうして、カードボックスの中身は、以前よりも精選されたアイデアになる。

タスクフォースの意義


その特徴は以下の二点である。

1.再生産という過程を組み込んだこと

京大カードの限界点を、「アイデアから再生産する過程」を意識的に作り出さなかったことに求める。そしてその方法論を、タスクフォースという形で具体的に提示しているのだ。
ちなみに梅棹だと、この過程はない。アイデアはひたすらたまっていく限りで、その内容をどうこうしようという発想はできないのである。

2.カードボックスの秩序を一定に保つ方法論を提示したこと

カードの死蔵が起こるのは、時間が経過しカードが増えることによってカードボックスがカオスになるからだ。そこで、カードをまとめ、束ね、廃棄し、という過程を組み込むことで、カードボックスの秩序を一定にしようとしている。*1


時系列でカードを蓄積していくと、自然の法則に従って、システムの中のエントロピー(情報の乱雑さ)は一方的に増えていきます。分類しない時系列で は、なおさらです。このままでは、PoIC は破綻しそうにも思えます。私自身、カードが増えるにしたがって、このまま行ったらどうなるのだろうか、と心配になったことがありました。
この自然の法則に逆らってエントロピーを減らそうとする場合、人間の「努力」が必要になります。図書館や博物館では、「つねに分類する努力」によってこれを実現しています。そのために、これらの公共施設では高いコスト(人件費、時間)を払っています。しかし、前述のように、PoIC では積極的に(?)検索・分類しません。では、どのようにしてシステムの破綻を防ぐのでしょうか。
答えは簡単で、やはり検索・分類するのです。従来の方法と違うのは、これが一番最後に来ることです。PoIC において、カードを書くのは、個人の知識のデータベースを構築することです。しかし、これはまだ準備段階です。PoIC の本当の目標は、このシステムを使って、新しい知恵・知識・成果を再生産することです。そうして初めて "Get things Done!" となります。(PoIC - 時系列スタック法)

タスクフォースの限界


僕はカード法で挫折したあと、この過程を組み込んだのだが、それでもやはり、死蔵という問題は解消されなかった。それは、タスクフォースの過程に手間がかかる、という理由による。

まず、広い場所を用意しなければならない。気楽に机の上程度の大きさでやるということはできない(ただ使うカードの大きさにも依拠するかもしれない。僕はB6を使っていた)。これを実現するには畳数枚程度の広さは必要になる。
また、この作業は一度に行わなければならない。そうそう何度も、カードをすべて調べて並び替えるということはできないし、どのカードとどのカードが内容がかぶってるかを判別するというのは、時間をあければ忘れてしまう。
ふせんを買ったりとか場所を作ったりとかいろいろ用意はしたが、結局は何回かやってやめてしまった。月に一度は日を決めてやろうだとか、まとまりを途中まで作ったままそのままの状態でまとめ、一時中断もできるようにするだとかいろいろ試みたが、結局やらなかった。
「カードボックス内の秩序を一定に保つ必要がある」この発想自体は正しいのかもしれない。だが、それを実現する方法の煩雑さによって、実際に機能するまでに至らないのである。実行するための負担が大きすぎるのだ。

そうしてここでも失敗し、やがてカードを使う方法をすて、PCでの方法を模索することになるのである。

…続く


*1:梅棹だと、カードは基本的にすべて残すことになる。それの対処として対応するのは、「カードを書くときには論文を書くようにする」、つまりアイデアとして半端なものをそもそも流入させないという方法である。
カードは、他人がよんでもわかるように、しっかりと、完全な文章でかくのである。「発見の手帳」についてのべたときに、豆論文を執筆するのだといったが、その原則はカードについてもまったく同じである。カードは、メモではない。(梅棹忠夫『知的生産の技術』P55)

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日とか、メモをしばらくの間書きためておいてあとでまとめて修正作業をする、というのだったらこの仕組はいいのだが、その後がね。捨てるという過程を組み込めない。