/ 最近 .rdf 追記 編集 設定 本棚

脳log[20080604] 現在適用されているファイルタイプを表示/変更するツールバーボタン



2008年06月04日 (水) 状態表示系のボタンが好き。でも基本はツールバー非表示。

最終更新: 2010-07-05T02:44+0900

[SakuraEditor] 現在適用されているファイルタイプを表示/変更するツールバーボタン

既存のコード(主に検索ボックス)の切り貼りで、とりあえず機能するものに。

スクリーンショット(一部)

ソース面の難はニンともしがたいが機能面の問題くらいは何とかしたいところ。以下、わかっている問題点。

  • 検索ボックスより右に表示できない。(済み)
  • リストにフォーカスがある状態では(検索ボックスと同様に)共通設定で割り当てたキーボードショートカットが効かない。(済み)
  • マウスオーバーでツールチップが出ない。(対応しない)
  • 頭文字を利用した選択。(Jを押すと Jで始まるアイテムを順番に選択する機能)(済み)
  • ツールバーを非表示から表示にしたとき、現在のタイプ別設定を反映していない(2010-06-29追記&修正済み)。

 追記@2008-06-05: 「検索ボックスより右に表示できない。」を修正した。

switch-caseで break忘れというまぬけぶりが原因だった。

フォント(HFONT)の削除漏れも修正した。

ツールチップはリストの上下数ピクセルで出ていた。(役に立たない!でもマウスオーバーでツールチップが出てもうるさいだけなのでそのまま)

 追記@2008-06-05: リストのキーボード操作「頭文字を利用した選択」を有効に。

他に、WM_PAINTで行っていた処理を、本来あるべき位置である WM_COMMAND(CBN_SELCHANGE)で行うように変更した。

キーボードアクセラレータを有効にするのは難しい。矢印や PageUp/PageDownキーまでが登録されているから、単純に有効にするとアクセラレータにそれらの入力を奪われてリストの操作が行えなくなる。

 追記@2008-06-14: キー割り当てを有効に。

ドロップダウンリストにフォーカスがあるときでもエディタのキーボードショートカットを有効にした。優先順位は

  1. リストの操作(↑, ↓, ←, →, PageDown, PageUp, Home, End, F4, Alt+↓で全部かな?)
  2. キーボードショートカット (↑, ↓, ←, →, PageDown, PageUp, Home, End, F4を使わないものと、Tab、Shift+Tab以外のもの)
  3. 頭文字を利用したリストアイテムの選択

なぜ一部のキーボードショートカットを無効にするかというと、リストの先頭のアイテムが選択されているときに Homeを押してもリストはこの入力を処理しない(すでに先頭が選択されているから)のだが、リストの選択位置に依存してキーの意味が変わってしまう(リストの先頭を選択↔キャレットを行頭に移動)のは不自然だと思うから。同様のキーをわかっている分だけ無効にした結果が上記リストの 2。モディファイアキーの状態やリストの選択位置を考慮して、もっと多くのキー入力をキーボードショートカットとして渡す余地はある(が、こんなアドホックな対処にこれ以上の手間をかけるのもどうかと)。


 SakuraEditorに欲しい機能

 ウィンドウのリサイズに追従する「現在のウィンドウ幅で折り返し」

追従しないことにむしろ驚いた。ブラウザにできてテキストエディタにできないはずがない。

 標準入力から編集テキストを読む機能

ファイル名も与えられていた場合は、それを上書き保存先にしてくれると尚良し。長めの出力をコマンドプロントからエディタに流して読んだりできる。

 svn diff hoge.rb | sakura --stdin "hoge.rb.diff"

 追記@2009-07-19: アップデート > SakuraEditor(Toolbar-FileTypeBox)_rev2.diff

Set/GetWindowLongPtr(hwnd, GWLP_USERDATA, ) の代わりに Set/Get/RemoveProp(hwnd, TEXT("MyData"), ) を使うようにした。

参考> http://hilbert.elcom.nitech.ac.jp/~taki/program.html

 1. そもそもあった疑問: GWLP_USERDATAはウィンドウクラスに属するのかウィンドウに属するのか?

以下のものを見つけた。

  • Set/GetClassLongPtr()
  • WNDCLASSEX.cbClsExtra
  • WNDCLASSEX.cbWndExtra

答え。GWLP_USERDATAはウィンドウに属し、その領域は WNDCLASSEX.cbWndExtraの値に従って確保される。

 2. ぎゃっ! WNDCLASSEX.cbWndExtraなんて変更してなかった。

いったいどこを読み書きしていたんだろう? クラッシュしなかったのは Windowsが俺みたいなうっかりもののために余白を空けていたから?

言い訳。msdnのこの記述は非常に誤解を招きやすいと思う。予め 32ビットだけメモリが確保されてると勘違いしても仕方ないじゃない?

GWLP_USERDATA | ウィンドウに関連付けられた 32 ビット値を取得します。この 32 ビット値は、ウィンドウを作成したアプリケーションで使用する目的で各ウィンドウが持っているものです。この値の初期値は 0 です。

現状の理解で、この「32 ビット値」の出所は、読み書き(Get/Set)の単位が 4バイトだというところから。LONG=32ビットだから。

ここで、LONG_PTRが 64ビットのとき、GetWindowLongPtr(,GWLP_USERDATA)は 64ビット値を取得するのでは?ドキュメントあってる?という新たな疑問。

 3. いずれにしろ

GWLP_USERDATAのように汎用的な場所から取得した値を恐る恐る CMainToolbar* にキャストするより、特別な名前をつけた場所に保存した値をキャストする方がまだ安心できる、という理由で改訂した。


ありふれた課題のようで GWLP_USERDATA を検索するだけで似たような話が見つかる。最初にリンクを張ったページは理解しやすかったが ATLのは難しい。これから読む > WTL/ATLのメッセージマップ実現のしくみ


GWLP_USERDATAと WNDCLASSEX.cbWndExtraが別物の可能性。


別物。GWLP_USERDATAは負のオフセット(-21)。cbWndExtraで確保した領域には 0から始まるオフセット(バイト単位)でアクセスする。

GWLP_USERDATAの有効な領域はポインタのサイズに関係なく、ドキュメント通りの 32ビットなのかについては winuser.hを見てもよくわからない。GWLP?_USERDATA(-21)から GWL_EXSTYLE(-20)まで差が 1しかないし……。GWLP?_* というのはオフセットでなく特別扱いされる値で、Windowsのソースを見ないと何もわからない、ということ? GWLP_USERDATAにポインタを格納できるのとできないのの差は大きいからハッキリ 64ビットサイズだと知りたい。

/*
 * Window field offsets for GetWindowLong()
 */
#define GWL_WNDPROC         (-4)
#define GWL_HINSTANCE       (-6)
#define GWL_HWNDPARENT      (-8)
#define GWL_STYLE           (-16)
#define GWL_EXSTYLE         (-20)
#define GWL_USERDATA        (-21)
#define GWL_ID              (-12)

#ifdef _WIN64

#undef GWL_WNDPROC
#undef GWL_HINSTANCE
#undef GWL_HWNDPARENT
#undef GWL_USERDATA

#endif /* _WIN64 */

#define GWLP_WNDPROC        (-4)
#define GWLP_HINSTANCE      (-6)
#define GWLP_HWNDPARENT     (-8)
#define GWLP_USERDATA       (-21)
#define GWLP_ID             (-12)

 追記@2010-06-29: ツールバーを非表示から表示にしたとき、現在のタイプ別設定を反映していないのを修正

// まだファイルタイプは取得できないっぽい。常に基本(0)になる。
//::SendMessageAny( m_hwndFileTypeBox, CB_SETCURSEL, m_pOwner->GetDocument().m_cDocType.GetDocumentType().GetIndex()+1, 0 );
::SendMessageAny( m_hwndFileTypeBox, CB_SETCURSEL, 0, 0 );

とコメントアウトしていた部分を復活させて

::SendMessageAny( m_hwndFileTypeBox, CB_SETCURSEL, m_pOwner->GetDocument().m_cDocType.GetDocumentType().GetIndex(), 0 );

ちょっと修正した(謎の+1を取り除いた)だけ。

アプリケーションの起動と同時にツールバーが作成されるときであれば、コメントの内容は正しい。アプリケーションが起動してファイルが読み込まれた後でツールバーが表示する設定に変更されたとき(このときもツールバーが作成される)は、そうではなかった。