タブ(TabPage)毎に異なるコンテキストメニューを開く

ここには、タブをマウスの右ボタンで押し、そのボタンを離したときにタブに応じたコンテキストメニューを開くC#ソースコードを載せてある。このコンテキストメニューの開き方はVisual Studioでのそれに近い。

TabPageに直接コンテキストメニューを設定すると、上のタブ部分での右クリックに反応してくれない。

だからTabControlにコンテキストメニューを設定しないといけない。しかし、タブ毎に異なるコンテキストメニューを設定するメンバは無いし、クリックされたタブを簡単に取得できるメンバも無い。
ウィンドウメッセージなら、クリックされたタブが分かるらしいけど、ドトネで作ってるんだから、ドトネの中でやりたい。

そしたら以下のようなプログラムになった。短いし、要所要所でコメント入れてるのですぐ読めると思う。おおざっぱな流れは

  1. tabControl.MouseDownでクリックしたタブを取得
  2. tabControl.MouseUpでマウス右ボタンだったなら明示的にコンテキストメニューを開くよう指示
  3. contextMenuStrip.Openingで、タブがクリックされていたなら、タブにあった項目を用意してコンテキストメニューを開く

以下ソースコード

using System;
using System.Windows.Forms;
using System.ComponentModel;

public class Form1 : Form
{
    TabControl tabControl = new TabControl();
    TabPage    tabPage1   = new TabPage();
    TabPage    tabPage2   = new TabPage();

    ContextMenuStrip contextMenuStrip    = new ContextMenuStrip();
    ToolStripMenuItem toolStripMenuItem1 = new ToolStripMenuItem();
    ToolStripMenuItem toolStripMenuItem2 = new ToolStripMenuItem();

    // クリックしたタブページ。タブがクリックされた瞬間だけタブページへの参照を入れる。それ以外の時はnull
    TabPage clickedTabPage = null;

    public Form1()
    {
	// コンテキストメニューの項目
	toolStripMenuItem1.Text = "toolStripMenuItem1";
	toolStripMenuItem2.Text = "toolStripMenuItem2";
	toolStripMenuItem1.Click += delegate (object sender, EventArgs e)
	{
	    MessageBox.Show(sender.ToString());
	};
	toolStripMenuItem2.Click += delegate (object sender, EventArgs e)
	{
	    MessageBox.Show(sender.ToString());
	};

	// マウスクリックしたタブをthis.clickedTabPageに取得
	tabControl.MouseDown += delegate (object sender, MouseEventArgs e)
	{
	    this.clickedTabPage = null;
	    for (int i = 0; i < tabControl.TabCount; i++)
	    {
		if (tabControl.GetTabRect(i).Contains(e.X, e.Y))
		{
		    this.clickedTabPage = (TabPage)tabControl.GetControl(i);
		    tabControl.SelectedTab = this.clickedTabPage;
		}
	    }
	};

	// マウスの右ボタンが離された時にコンテキストメニューを開くよう指示
	tabControl.MouseUp += delegate (object sender, MouseEventArgs e)
	{
	    if (this.clickedTabPage != null)
	    {
		if (e.Button == MouseButtons.Right)
		{
		    contextMenuStrip.Show((TabControl)sender, e.Location);
		}
	    }
	};

	// コンテキストメニューを開く直前にコンテキストメニューの項目を組み立てる
	contextMenuStrip.Opening += delegate (object sender, CancelEventArgs e)
	{
	    ContextMenuStrip menu = (ContextMenuStrip)sender;

	    if (this.clickedTabPage != null)
	    { // タブがクリックされていたならコンテキストメニューを開く
		toolStripMenuItem1.Text = this.clickedTabPage.Text + " : " + "toolStripMenuItem1";
		toolStripMenuItem2.Text = this.clickedTabPage.Text + " : " + "toolStripMenuItem2";

		menu.Items.Clear();
		menu.Items.Add(toolStripMenuItem1);
		menu.Items.Add(toolStripMenuItem2);

		this.clickedTabPage = null;
	    }
	    else
	    { // タブがクリックされてないならコンテキストメニューを開かない
		e.Cancel = true;
	    }
	};

	// 最初に項目が追加しておかないと、初回だけコンテキストメニューが出ない。何で?
	contextMenuStrip.Items.Add(new ToolStripMenuItem());

	// タブの表示文字列
	tabPage1.Text = "tabPage1";
	tabPage2.Text = "tabPage2";

	// tabControlにタブとコンテキストメニューを設定
	tabControl.TabPages.Add(tabPage1);
	tabControl.TabPages.Add(tabPage2);
	tabControl.ContextMenuStrip = contextMenuStrip;

	this.Controls.Add(tabControl);
    }

    public static void Main()
    {
	Application.Run(new Form1());
    }
}

フィールドclickedTabPageは、TabControlのサブクラスに含めた方が適切かな

urxvtで任意のキーでセレクションのペースト

urxvt-unicode (urxvt) でのセレクション(PRIMARY)のペーストはShift-Insertに割り当てられている。このキーバインドホームポジションから遠く押しにくいので、他のキーバインドでペーストできるようにする。

問題点

バージョン9.07のurxvtは、PRIMARYのペーストしかできないうえ、ペーストのキーバインドがShift-Insertから変えられないっぽい(ハードコーディングされてる)

方法

urxvtはperlで機能拡張できる。perlを使ってxsel*1コマンドを呼び出し、セレクションの内容を端末に書き出す。

ここでの設定

  • perlのファイルは/home/willowlet/.urxvt/にあるとする。(もちろんここは各ユーザ名に変更する所)
  • キーバインドは以下の通り
Alt-v
PRIMARYのペースト
Ctrl-Alt-v
CLIPBOARDのペースト

ここではAlt-vと表記しているが、設定ファイル中ではM-vとする。

以下設定ファイル
~/.Xresources

URxvt.perl-lib: /home/willowlet/.urxvt/
URxvt.perl-ext-common: default,x-selection
URxvt.keysym.M-v: perl:x-selection:paste_primary
URxvt.keysym.C-M-v: perl:x-selection:paste_clipboard

~/.urxvt/x-selection

#! perl

sub paste_primary {
    my ($self) = @_;
    my $o = $self->locale_encode($self->selection);
    # 自urxvtに選択箇所があるとxselがフリーズするっぽいので場合分け
    if ($o) {
	$self->tt_write($o);
    } else {
	open my $fh, '-|', qw/xsel/ or die $!;
	while (<$fh>) {
	    $self->tt_write($_);
	}
	close $fh or warn "status: $?";
    }

    ()
}

sub paste_clipboard {
    my ($self) = @_;
    open my $fh, '-|', qw/xsel -b/ or die $!;
    while (<$fh>) {
	$self->tt_write($_);
    }
    close $fh or warn "status: $?";

    ()
}

sub on_user_command {
    my ($self, $cmd) = @_;

    if ($cmd eq "x-selection:paste_primary") {
	#$self->tt_write('[p]');
	$self->paste_primary;
    }

    if ($cmd eq "x-selection:paste_clipboard") {
	#$self->tt_write('[c]');
	$self->paste_clipboard;
    }

    ()
}

独り言

perlで拡張できるurxvtはフリーダム。どういう事ができるかは man urxvtperl とか http://linux.die.net/man/3/urxvtperl に書いてある。

URxvt.keysym.*: command: の使い方がよく分からない。

*1:xsel-conrad

VMware Server 2.xのサポート期限は2011/06/30

VMware Server 2.xのサポート期限が近い。1.xに至っては既に終了している。

Version 2.x
2011/06/30
Version 1.x
2010/03/23

http://www.vmware.com/support/policies/lifecycle/general/index.html#policy_server

以下によれば、VMware Serverは終わるから、ESXiかPlayerに移行してね、とのこと。

End of General Support for VMware Server 2.0 is 2011-06-30, users
should plan to upgrade to the newest release of either ESXi or
VMware Player.

http://www.vmware.com/security/advisories/VMSA-2010-0007.html

これからVMware Serverを使う方は一考した方が良いだろう。ただしESXiに移行する場合はIntelNICが無いとダメとかの制約があるらしい事に気をつけないといけない。


うちのサーバでもVMware Server 2.x使ってるから、ESXiに移る計画たてようかなぁ。

窓使いの憂鬱使いの憂鬱

Windows以外のOSを使おうとする時の最大の障壁は窓使いの憂鬱が無いこと。
FreeBSD上のXで使える窓使いの憂鬱的なソフトはあるのか。

私家版 窓使いの憂鬱 Linux & Mac (Darwin) 対応版

http://members.at.infoseek.co.jp/hattoushin_uma/
configureできない。変なエラー吐いて止まる。

checking whether the Boost::Regex library is available... yes
configure: error: Could not link against  !

調べるの面倒だし、他にも障害が出てきそうだからこれは止めよう。

xmodmap

http://x68000.q-e-d.net/~68user/unix/pickup?xmodmap
http://www.manpagez.com/man/1/xmodmap/
http://d.hatena.ne.jp/atasatamatara/20090612/1244786385

keycodeにおける5〜8個目のkeysymの意味が載ってるページ発見。
Some hints about xmodmap(1) and the X11 keyboard model
http://www.in-ulm.de/~mascheck/X11/xmodmap.html#format

5〜8番目のkeysymは、1〜4番目のと切り替えて使う。
切り替え方は、ISO_Next_Groupと言うkeysymを割り当てたキーを2回押すこと。

例)HHKB Professional JPの変換キーをISO_Next_Groupにする

keycode 129 = ISO_Next_Group
keysym a = a A b B c C d D

で、変換キーを2回押すと文字グループが変更される。
するとaキーでcが入力される。
C d Dの入力はA b Bと同様で、ShiftとMode_switchの組み合わせで入力できる。
さらに変換キーを2回押すと元のa A b Bの文字グループに戻る。

この機能はXKB extensionを使ってるらしい。

XKB extension

http://homepage.mac.com/hre.i/ebug-20080920-xkb.html
http://en.wikipedia.org/wiki/Xkb

かなり柔軟性があるっぽい。大変そうだから今度調べよう。

DXライブラリで任意のフォントを使用する

はじめに

ここではDXライブラリでフォントファイル(.ttfなど)をWindowsにインストールせずそのまま利用する方法を紹介する。この方法では既存のDXライブラリにある文字列描画関数をそのまま利用する事ができる。

目次

背景

バージョン3.00eのDXライブラリでは、文字列描画に使用するフォントを変更する手段として2種類の関数が用意されている。1つはChangeFont関数、もう1つはCreateFontToHandle関数だ。
しかし、通常これらの関数で指定できるフォントはWindowsにインストールされたフォントのみである。そのため、DXライブラリユーザ*1側で用意したフォントを使用するためには一工夫必要だ。

準備

以下の環境で正しく動作するのを確認した。

この環境、このバージョンで話を進める。
動作環境においてWindows2000以降の機能を使うので、Windows 95, 98, NTでは動作しないと思われる。これらに対応する方法については後述する。

方法

まずWin32APIのAddFontResourceExでフォントファイルを読み込んでおく。次にChangeFont関数などで読み込ませたフォントのフォント名を指定して、適当な文字列描画関数で文字列を出力する。最後に、プログラム終了前にRemoveFontResourceExでフォントをアンロード*2する。

日向梓さんのあずきフォントを使用するサンプルを以下に示す。あずきフォントのファイル(azuki.ttf)をプロジェクトのフォルダ*3に置いておくこと。
ChangeFontに指定するフォント名を調べるには、フォントファイルを開いて「書体名」を見ればよい。

サンプルコード

#include "DxLib.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
			 LPSTR lpCmdLine, int nCmdShow )
{
	ChangeWindowMode(TRUE);	// ウィンドウモードで表示
	if (DxLib_Init() == -1)	// DXライブラリ初期化処理
		 return -1;	// エラーが起きたら直ちに終了

	// ********** フォントのロード **********
	LPCSTR font_path = "azuki.ttf"; // 読み込むフォントファイルのパス
	if (AddFontResourceEx(font_path, FR_PRIVATE, NULL) > 0) {
	} else {
		// フォント読込エラー処理
		MessageBox(NULL,"フォント読込失敗","",MB_OK);
	}

	// ********** ここから読み込んだフォントを利用できる **********
	SetFontSize(36);	// 見やすさのため、フォントサイズを大きく

	DrawString(10, 200, "この文字はMS ゴシックです。", GetColor(255, 255, 255));

	ChangeFont( "あずきフォント", DX_CHARSET_DEFAULT ) ;
	DrawString( 10 , 240, "この文字はあずきフォントです。", GetColor( 255 , 255 , 255 ) );

	WaitKey() ;		// キーの入力待ち(『WaitKey』を使用)

	// ********** フォントのアンロード **********
	if (RemoveFontResourceEx(font_path, FR_PRIVATE, NULL)) {
	} else {
		MessageBox(NULL, "remove failure", "", MB_OK);
	}

	DxLib_End() ;		// DXライブラリ使用の終了処理

	return 0 ;		// ソフトの終了
}

実行画面

1行目がMS ゴシック、2行目があずきフォントになっている。

フォントファイルの置き場所

読み込むフォントファイルのパスには普通の相対パス絶対パスも指定できる。もちろんフォルダの区切り記号「\」は2個並べないといけない。

	font_path = "azuki.ttf"; // 一種の相対パス
	font_path = "fonts\\azuki.ttf"; // 相対パス
	font_path = "C:\\Documents and Settings\\user\\My Documents\\Visual Studio 2008\\Projects\\AddFontResource on DxLib\\fonts\\azuki.ttf"; // 絶対パス

絶対パスは使わない方がいいだろう。プログラムにフォントを同梱するならば相対パスを使用するべきだ。なぜなら、プログラムのユーザは、そのフォルダをどこに置くかわからないからだ。

欧文フォントを使うには

ChangeFont関数の第2引数のデフォルトはDX_CHARSET_SHFTJISとなっており、フォントの文字セットをシフトJIS文字セットにする。このため、欧文フォントは正しく適用できない。欧文フォントでも使えるようにするには、DX_CHARSET_DEFAULTを渡してデフォルト文字セットを使う様にすればよいようだ。また、日本語フォントを使用する場合であってもDX_CHARSET_DEFAULTを渡して問題無いようだ。

Windows 95, 98, NTに対応させる
上で使用したAddFontResourceExはWindows 2000以降でしか使えない。Windows 95などでは代わりにAddFontResourceを使用すればよい。
ただしAddFontResourceで読み込んだフォントは、このプログラム以外のプログラムからも使用できてしまう。そのため、フォントのライセンスにより注意しなければいけないだろう。

#include "DxLib.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
			 LPSTR lpCmdLine, int nCmdShow )
{
	ChangeWindowMode(TRUE);	// ウィンドウモードで表示
	if (DxLib_Init() == -1)	// DXライブラリ初期化処理
		 return -1;	// エラーが起きたら直ちに終了

	// ********** フォントのロード **********
	LPCSTR font_path = "azuki.ttf"; // 読み込むフォントファイルのパス
	if (AddFontResource(font_path) > 0) {
		PostMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
	} else {
		// フォント読込エラー処理
		MessageBox(NULL,"フォント読込失敗","",MB_OK);
	}

	// ********** ここから読み込んだフォントを利用できる **********
	SetFontSize(36);	// 見やすさのため、フォントサイズを大きく

	DrawString(10, 200, "この文字はMS ゴシックです。", GetColor(255, 255, 255));

	ChangeFont( "あずきフォント", DX_CHARSET_DEFAULT ) ;
	DrawString( 10 , 240, "この文字はあずきフォントです。", GetColor( 255 , 255 , 255 ) );

	WaitKey() ;		// キーの入力待ち(『WaitKey』を使用)

	// ********** フォントのアンロード **********
	if (RemoveFontResource(font_path)) {
		PostMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
	} else {
		MessageBox(NULL, "remove failure", "", MB_OK);
	}

	DxLib_End() ;		// DXライブラリ使用の終了処理

	return 0 ;		// ソフトの終了
}

さらにPostMessageを使ってる事に注意したい。ここではウィンドウメッセージをブロードキャストする。このメッセージを適切なウィンドウプロシジャで処理しなければフォントを有効にできない。メッセージ処理のもっとも簡単な方法は、このサンプルのようにDXライブラリにまかせる事だろう。つまりDxLib_Init()とDxLib_End()の間でPostMessageすればよい。

仕組み

DXライブラリはフォントデータの生成のとき、内部でWin32APIのCreateFontを呼んでいる。そこで、CreateFontで任意のフォントファイルからのフォントを使用できるようにしてやればよい。そのためにはAddFontResourceExでフォントファイルを読み込ませる。これでDXライブラリで任意のフォントを使用できるようになるのである。

まとめ

DXライブラリにおいて、任意のフォントを使用するためにはAddFontResourceExでフォントファイルを読み込ませる。これで、DXライブラリの関数ChangeFontやCreateFontToHandleで読み込ませたフォントを指定できるようになる。

DXライブラリ側でこの機能を用意してくれないかな。

参考

*1:DXライブラリを用いたプログラムをを作る人

*2:読み込んだフォントを無効にする

*3:実行プログラムのカレントディレクト

PS3にFedora 12インストール

Fedora 12のインストール

PS3に入れてたFedora 10がEOLになってた。Fedora 12へfedora-releaseパッケージでアップデートしたら、なんか画面が出てこなくなってしまった。調査するのは面倒なので新規にインストールしてしまおう。

基本的にここの手順に従ってインストールした。
PS3にFedora 12を「軽量」インストールする

  • petitbootの新しいバージョンが出てた。ps3-petitboot-09.11.30-cui.bld
  • ネットワークの設定ではIPを固定にした。/etc/sysconfig/network-scripts/ifcfg-eth0と/etc/sysconfig/networkの編集。
  • 起動するサービスを最小限に絞った。ただしsshdは起動するようにした。
  • デスクトップ関連はインストールしなかった。sshで操作するつもり。
  • Cell SDK 3.1をインストールした。
  • RPM Fusionリポジトリはインストールしなかった。

あと、manとかman-pages-jaとかlvとかzshとかscreenとかopenssh-clientsとかvim-commonとかvim-enhancedとかsubversionもインストールした。
WOLできるように/etc/rc.localに以下を追加

ethtool -s eth0 wol g

telnet(ssh)でFedoraを起動する

WOLなり電源スイッチなりで起動したら、インストールしたFedoraが自動で起動するようにしたい。PS3にはキーボードもマウスも接続しておきたくない。*1
今のバージョン(09.11.30)のpetitbootは自動起動に対応していない。そのため、petitbootが起動してからは人間が操作してやらないとFedoraの起動まで進まない。petitbootはtelnetsshでのログインができるので、これを使って起動処理を進める。

方法
  1. まず、petitbootは起動するとDHCPIPアドレスを決定する。だからそのIPアドレスを調べる。
  2. telnetでログインする。(ユーザ名もパスワードも聞かれない)*2
  3. そのままシェルでpetitbootコマンドを起動する。
  4. すると、ビデオ出力で見るのと同じpetitbootのメニューが出るので、お好みの項目を選んで起動。

これで選んだ項目での起動が進むので、telnetでの操作は終わり。


コミットログを見ると、そのうち自動起動に対応してくれそうに見えるなぁ。
なんかジョイスティック(PS3コントローラ)への対応もやってるようだけど、よくわからない。

*1:本当はそのためのキーボードもマウスも持ってないだけ

*2:sshログインしたい場合は、telnetログイン後にpasswdでパスワードを設定する。するとsshが有効になり、telnetは無効になる。と書いてあった

TortoiseSVN 1.6.6でSSH接続の時ポート指定したい

以下の様なURLの中にポート番号を指定する方法では駄目

svn+ssh://example.com:12345/path/to/repo

こちらの方法でできた。

C:\Documents and Settings\[ユーザーID]\Application Data\Subversion\configファイルの[tunnels]セクションに次のように記述する.

hoge = "C:\Program Files\TortoiseSVN\bin\TortoisePlink.exe" -ssh -l [ユーザーID] -P 10220 -pw [パスワード]

このように記述しておくことで,svn+sshではなくsvn+hogeとすることができる.

http://www.santalinux.net/blog2/?p=22

これ使えばsvnサーバ毎に自由に設定できるね。
PuTTY使えばもっと楽にできたような気がしなくもないけど…。