月別アーカイブ: 2008年5月

Gtk#

Gtk# とやらで Linux の GUI 作りを試してみる。
実体は思いっきりというか単なる Gtk+ のラッパーらしいんだが…

とりあえず Gtk# 2.0 プロジェクトを新規で作成する。
static void Main だけの雛形コードが作成された。
そのままビルドすればとりあえずウインドは表示されるようである。

とりあえずボタンを押すとメッセージを表示する初心者アプリでも。
色々探したが情報少なすぎ、見つけたコードを色々試してみる。

Gtk.MessageDialog 関数を使えばいいようだとまでは解ったのだが…
一番迷ったのは引数の一番最初に指定する Gtk.Window をどう指定するか。
それ以外はコード補完機能のおかげでなんとなく解ったがココでどうしても詰まる。

後で解ったんだけどメインの class では static メソッドは Main 一つにすべし!

GtkSharp – Mono

ココのスクリーンショットを見ると RAD での GUI デザインできそうなんだが…
方法が解らない、情報が全く見つからない、まあコードでなんとかしよう。

CodeProject: A google search application using Gtk#. Free source code and programming help

Glade なんてのがあるんだ。
「追加と削除」から Glade3 をインストール。
でもよく解らない…今回は下の方にあるコードを見てなんか作ってみよう。

using System;
using Gtk;
//using Glade; //後で

namespace gtkApp2
{
    class Test
    {
        private Window win;

        public static void Main (string[] args)
        {
            new Test(args);
        }
        public Test(string[] args)
        {
            Application.Init();
            win = new MainWindow();
            win.DeleteEvent += new DeleteEventHandler(win_DeleteEvent);
            Button btn = new Button();
            btn.Label = "水色の";
            btn.Clicked += new EventHandler(btn_Clicked);
            win.Add(btn);
            win.Show ();
            btn.Show();
            Application.Run ();
        }
        private void win_DeleteEvent(object o, DeleteEventArgs args)
        {
            Application.Quit();
            args.RetVal = true;
        }
        private void btn_Clicked(object sender, EventArgs e)
        {
            MessageDialog dlg = new MessageDialog(
                          this.win,
                          Gtk.DialogFlags.Modal,
                          Gtk.MessageType.Info,
                          Gtk.ButtonsType.Ok,
                          "セーラー服");
            dlg.Run();
            dlg.Destroy();
        }
    }
}

で、ウインドサイズに広がったボタンを押すとメッセージダイアログが出る。
というしょーもないアプリを作るのに三日も使った情けないプログラマーであった。

Gtk ってレイアウトという概念があるんだね。
ボタンがウインドサイズになった時に思わず「おまえは WPF か!」と思ったけど
よく考えたら Java にもレイアウタがあったっけか。

そうか、インターフェイスデザインはそういう方向に向かっているんだなぁと。
Windows も WPF でやっと時代に追いついたってことだね。

まあなんとか Gtk# はどういうもんか解ってきたぞと。

OPENFILENAME は古い 2

SHCreateItemFromParsingName についてだが3番目の引数に

IShellItem *psi = NULL;
SHCreateItemFromParsingName(szDoc, NULL, IID_IShellItem, (LPVOID *)&psi);

なんてありがちな IID を付けたらすんなり通ってしまった。
しかしどこを探しても引数が3つなのは何故?
まあこれで IFileDialog 関連はなんとかなりそうだ。

自前関数とかそのままだけーがこんな感じで。

/*
 * (*.mpg|*.mpeg) を (*.mpg;*.mpeg) に変換
 * セミコロンって INI の仕様ではコメントなのよね
 */
BOOL SemicolonReplace(LPWSTR _Dst, LPWSTR _Src)
{
	for (int i=0; i<256; i++)
	{
		if (_Src[i] == '\0') {
			_Dst[i] = '\0';
			return TRUE;
		} else if (_Src[i] == '|') {
			_Dst[i] = ';';
		} else {
			_Dst[i] = _Src[i];
		}
	}
	return FALSE;
}

void CCinemaWindow::DoOpenDlg()
{
	IFileDialog *pDlg = NULL;
	IShellItem *psi = NULL;
	HRESULT hr;

	hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_IFileDialog, (LPVOID *)&pDlg);
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer IFileDialog Create");
		pDlg->Release();
		return;
	}
	//拡張子フィルタの登録
	wchar_t filter[3][256];
	SemicolonReplace(filter[0], IniSetting.szExt01);
	SemicolonReplace(filter[1], IniSetting.szExt02);
	SemicolonReplace(filter[2], IniSetting.szExt03);
	COMDLG_FILTERSPEC filterspec[] =
	{
		{IniSetting.szExtA, filter[0]},
		{IniSetting.szExtB, filter[1]},
		{IniSetting.szExtC, filter[2]}
	};
	hr = pDlg->SetFileTypes(3, filterspec);
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer SetFileTypes1");
		pDlg->Release();
		return;
	}
	//フィルタの選択インデックス変更(先頭は 1 である)
	hr = pDlg->SetFileTypeIndex((UINT)IniSetting.nExtNo + 1);
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer SetFileTypeIndex");
		pDlg->Release();
		return;
	}
	//初期選択フォルダ
	if (fname::FileExists(PlayFileInfo.FullPath))
	{
		hr = SHCreateItemFromParsingName(PlayFileInfo.FilePath, NULL, IID_IShellItem, (LPVOID *)&psi); //IID_PPV_ARGS(&si)
	} else if (fname::DirectryExists(IniSetting.szSFolder)) {
		hr = SHCreateItemFromParsingName(IniSetting.szSFolder, NULL, IID_IShellItem, (LPVOID *)&psi);
	} else {
		wchar_t szDoc[256];
		SysUtils::get_MyDocumentString(szDoc);
		hr = SHCreateItemFromParsingName(szDoc, NULL, IID_IShellItem, (LPVOID *)&psi);
	}
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer CreateItemFromParsingName");
		psi->Release();
		pDlg->Release();
		return;
	}
	hr = pDlg->SetFolder(psi);
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer SetFolder");
		psi->Release();
		pDlg->Release();
		return;
	}
	psi->Release();
	psi = NULL;
	//ダイアログの表示
	hr = pDlg->Show(hWnd);
	if (SUCCEEDED(hr))	// FAILED ではキャンセルを拾ってしまう
	{
		wchar_t *pFileName = NULL;
		hr = pDlg->GetResult(&psi);
		if (FAILED(hr)) {
			DoMessage(hWnd, L"Errer GetResult");
			psi->Release();
			pDlg->Release();
			return;
		}
		hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pFileName);
		if (FAILED(hr)) {
			DoMessage(hWnd, L"Errer GetFileName");
			psi->Release();
			pDlg->Release();
			CoTaskMemFree(pFileName);
			return;
		}
		UINT nIndex;
		hr = pDlg->GetFileTypeIndex(&nIndex);
		if (FAILED(hr)) {
			DoMessage(hWnd, L"Errer GetFileTypeIndex");
			psi->Release();
			pDlg->Release();
			CoTaskMemFree(pFileName);
			return;
		}
		hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pFileName);
		if (FAILED(hr)) {
			DoMessage(hWnd, L"Errer GetDisplayName");
			psi->Release();
			pDlg->Release();
			CoTaskMemFree(pFileName);
			return;
		}
		//完走したので処理
		int nExtNo = (int)nIndex - 1;
		if (IniSetting.nExtNo != nExtNo)
		{
			SetPlayFileInfo(L"c:\\a\\a"); //拡張子が無いのでクリアになる
			SetListupMenu(nExtNo, TRUE);  //本体のセレクトを変更
		}
		DirectA->SetURL(hWnd, pFileName);
		//end
		CoTaskMemFree(pFileName);
		psi->Release();
	}
	pDlg->Release();
}

長いよぉ!だから COM は…

を選択したら本体のリストアップ選択も変更したかっただけなのに。

さて、問題は XP 版をどうするかだ…又 #ifdef で分けるしかないか。
こっちに都合がイイように仕様変更しまくったのだどうしよう…

OPENFILENAME は古い

C# ばかりやっていたら頭が腐りそうなので今日は慣れた C++ を。

今回は Cinema にリストアップ拡張子選択機能を付けて更新しようと考えた。
ついでに開くダイアログを改造していたのであるが…

OPENFILENAME 構造体に lpfnHook を指定したら古いダイアログになっちまった。
なんでじゃい!

CFileDialog::m_ofn

なんてこった、GetOpenFileName 関数はこんなに変わっていたのか。
このサポートされなくなったメンバを使わなければコンパイル時に新しいのに置き換えられる。
ということみたい。

ExpLZH の保存ダイアログが古いのとかが気になっていたのだがそういうことだったか。
でも秀丸とかはカスタマイズしているのに Vista スタイルになっているよな?
何か方法があるのだろう、調べてみよう。

で。

どうやら Vista 型式のダイアログは COM の IFileDialog を使うようである。
何故 COM なんだ?と文句を書いてもしゃーないので作るしかない。

MSDN じゃ全然解らない!サンプルにも見あたらない!
日本語で探しても解りにくいので「ウエブ全体」でググる。

CodeProject: Vista Goodies in C++: Using the New Vista File Dialogs. Free source code and programming help

流石に老舗のココは解りやすい。
CComPtr なので ATL での解説だけどなんとかなるだろうと書いてみる。

何故だろう?
SHCreateItemFromParsingName の引数がどこを探しても3つなんですけど…
私の Visual Studio では4つあるんですけど、デフォルト引数でもないし…

あぁ更に解らない!ということで更新は又今度。

C# で UTF-8 の INI

さて、STL で作った IniFile8 クラスを C# で作り替えているんだが。

C#のジェネリック -OKWave

ココとかを参考に std::pair 互換クラスを作ろうかとも。
でも今はよくワカンネ、別の方法で実装できるならそれでいいし。

意地でも同じ方法を行いたいのではなく「こういう処理がしたい」なので。
この言語にて最適な方法を探ったほうが強引に同じにするより自然だろうと。

StreamWriter クラス (System.IO)

んー、BOM は強制的に書き込まれてしまうということなんですね。
別に変換して ASCII で書き込むという方法もあるけどまあいいか。

文字列がnull(Nothing)か空文字列かを簡単に判定するには?[2.0のみ、C#、VB] ? @IT

うっ!そういえばそうだ、考えていなかった。
IniFile8 STL 版は空文字チェックなんてしていない。

C# でキーが見つからない場合は関数で null を返すようにしてテスト。
どうやらそういう使い方だけなら null チェックだけで済みそうだ。

C++ は試しに Cinema の設定で「リストアップ拡張子」を空にしてみる。
再起動すると…あぁやっぱり初期値の復活だ。

if ( (s.empty()) && (0 != s.length()) )

でチェックすればいいかな?
しかし C# のコードを探して C++ 版の不具合を見つけるとはなんとも。
やはり色々なアプリを平行して作るとイイことあるよ。

ということで。

UTF-8 の ini 読み書き

というコードがなんとかできた。

using IniFile

/* 略 */

private void Window_Closed(object sender, EventArgs e)
{
    IniFile8 ini = new IniFile8(@"D:\\00_Temp\\test.ini");
    ini.WriteInteger("Window", "Left", (int)this.Left);
    ini.WriteInteger("Window", "Top", (int)this.Top);
    ini.WriteString("Window", "Title", this.Title);
    ini.Save();
}

みたいなコードで使える。

Ubuntu で C# と C++

まてよ、mono の実行エンジンはインストールされているのかな?
とりあえず Vista でコンソールアプリを作り Ubuntu に持って行って実行してみる。
java じゃないんだからコンソールでないと動くはずがない。

まあ結果は動く、軽く調べるとどうやら実行エンジンは標準搭載らしい。
標準状態では C 言語さえコンパイルできないんだから C# は当然ムリか。

Mono 1.9.1のインストール – Programming/Mono – 総武ソフトウェア推進所

なんかイッパイ入れなきゃいけないみたいですけど…
そういえば Visual Studio 2008 のインストールは二時間掛かったっけか。
たった二分でインストールできた mono Develop で同等なわけがない。

特に Linux の場合 GUI アプリは gtk+ か Qt とか様々なわけで。
Windows 版のようにまとめて一気にインストールはそりゃ無謀かと納得。

2008-02-03 – ままならない日記

とりあえず mono-gmcs だけをインストール。
結果はコンパイルできた、よしよし、後は気が向いたら(ぉい!)

ちなみに、コード補完は C# でしかやってくれない。
思わず Ctrl+Space を押して SCIM を…略。

それより知らなかったのだがソースコードは UTF-8 で保存されるんだね。
ローケルのとうりになるのか、こりゃ日本語の扱いが更に面倒そう。
だけどコードページ 932 (つまり Shift-JIS)の呪縛が無いのは嬉しいね。

ということは…

やはり search.ini なんかも普通に char 配列で読み書きできるわな。
Firefox も Opera も設定が全部 UTF-8 なわけだよ、よく解った。
Windows も…バイナリ配布が普通の Windows では無理だと解っているけど。

C# テストが目的だったけど C++ コンパイラとして使うのも悪くないなコレ。
続く…かもしれない。