Clipoli 0.0.1

Windows アプリ作りの為に新しいノートを買うか迷っている。
が、よく考えたら VirtualBox 仮想マシンの Vista があるじゃないか。
Aero が使えず遅いので全然使わなかったから存在を忘れる所だった。

これを使えばホストの Ubuntu で使っているマジェスタッチで打てる。
あぁキーボード入力が楽々!

しかもコイツには Visual Studio 2008 pro をインストールしてある。
Debug が楽々、コード保管で楽々、リファクタで楽々、何もかもが楽々!
流石はアップグレードに六万円も払っただけはある、これぞ統合開発環境。
テキストエディタだけで開発をしばらくやった後で触るとマジで神アプリ。

でも基本的にこんなものをインストールしないで作れるアプリにしたい。
ので bat の W クリックだけでビルドできるようにアプリ作成を続ける。

とにかく clipoli というタスクトレイ常駐するアプリだ。
当面はランチャとクリップボードにテキストを入れる機能を本格的に。
カスタマイズが簡単にできるように何かのファイルからデータを入れる。

よし、INI ファイルだ。

あぁ Opera 使いの性。

Opera 屋以外はアドオンと考えそう…

ま、仕様をアッサリ決められるかどうかがプロと凡人の違い。
なんだけど後悔することのほうが多いのは書くまでもない。

[Launcher]
メモ帳=C:\Windows\notepad.exe
端末=C:\Windows\System32\cmd.exe
MSDN=http://msdn.microsoft.com/ja-jp/library/

[Clipboard]
けいおん=あずにゃん
俺妹=きりの
えむえむ=みおたん
声優=全部同じ人だった...

みたく INI を作り読み込んでメニューの作成という流れでとりあえずいこう。
C# での INI 読み書きは以前作っているから簡単だ。

UTF-8 の ini 読み書き

と思ったけど今見ると書いていることが少々古い…
てか MessageBox をココで使うと WindowsForm だと using が足らない。
Python で作ったの同様に間違いは例外を投げるように変更して書き換えなきゃ。

そんなことより、このアプリの場合はこんな複雑なコードいらないヤン…

iniread.cs

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

namespace IniFile
{
	public struct Ini
	{
		public string section {get; set;}
		public string key {get; set;}
		public string value {get; set;}
	}
	public static class IniReader
	{
		public static IEnumerable<Ini> ReadLines(string path)
		{
			if (File.Exists(path))
			{
				string section = "";
				using (var sr = new StreamReader(path, Encoding.UTF8))
				{
					string buf;
					while ((buf = sr.ReadLine()) != null)
					{
						if (buf == "") continue;
						if (buf[0] == ';') continue;
						int len = buf.Length;
						if (len > 2 && buf[0] == '[' && buf[len - 1] == ']')
						{
							section = buf.Substring(1, len - 2);
						}
						else if (section == "")
						{
							//何もしない
						}
						else
						{
							int lPos = buf.IndexOf('='); //見つからない場合は -1
							if (lPos > 0)
							{
								Ini ini = new Ini();
								ini.section = section;
								ini.key = buf.Substring(0, lPos);
								ini.value = buf.Substring(lPos + 1, len - lPos - 1);
								yield return ini;
							}
						}
					}
				}
			}
		}
	}
}

これで十分だった、struct を yield で戻せば必要な値は取れるし。
んでコレを展開するコードを。

mainsrc.cs

using System;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection; // MethodInfo
using IniFile;

namespace Clipoli
{
	class TrayIcon
	{
		private NotifyIcon _icon;
		private ContextMenuStrip _menu;
		public TrayIcon()
		{
			_menu = new ContextMenuStrip();
			int i = 0;
			foreach (var ini in IniReader.ReadLines("default.ini"))
			{
				// Load Launcher
				if (ini.section == "Launcher")
				{
					_menu.Items.Add(ini.key, null, on_launcher);
					_menu.Items[i].Name = ini.value;
					i++;
				}
				// Load Clipboard
				else if (ini.section == "Clipboard")
				{
					_menu.Items.Add(ini.key, null, on_clipboard);
					_menu.Items[i].Name = ini.value;
					i++;
				}
				else
				{
					MessageBox.Show("現在 Launcher と Clipboard 以外のセクションは無効です");
				}
			}
			_menu.Items.Add("-");
			_menu.Items.Add("バージョン情報", null, on_about);
			_menu.Items.Add("-");
			_menu.Items.Add("終了", null, on_exit);
			// create tray icon
			_icon = new NotifyIcon();
			_icon.ContextMenuStrip = _menu;
			_icon.Icon = Icon.ExtractAssociatedIcon("clipoli.exe");
			_icon.Text = "Description Text";
			_icon.Visible = true;
			_icon.MouseUp += on_click;
		}

		private void on_click(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Left)
			{
				MethodInfo mi = typeof(NotifyIcon).GetMethod("ShowContextMenu",
								BindingFlags.Instance | BindingFlags.NonPublic);
				mi.Invoke(_icon, null);
			}
		}
		private void on_launcher(object sender, EventArgs e)
		{
			try
			{
				string s = (sender as ToolStripMenuItem).Name;
				System.Diagnostics.Process.Start(s);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}
		private void on_clipboard(object sender, EventArgs e)
		{
			try
			{
				string s = (sender as ToolStripMenuItem).Name;
				Clipboard.SetText(s);
				System.Media.SystemSounds.Beep.Play();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}
		private void on_about(object sender, EventArgs e)
		{
			var about = new About(_icon.Icon);
			about.ShowDialog();
		}
		private void on_exit(object sender, EventArgs e)
		{
			_icon.Visible = false;
			Application.Exit();
		}
	}
	class __main__
	{
		[STAThread]
		public static void Main(string[] args)
		{
			var n = new TrayIcon();
			Application.Run();
		}
	}
}

ContextMenu は ContextMenuStrip に変更したほうが扱いやすいみたいだったので。

その ContextMenuStrip.Name プロパティに文字列を入れるというウソみたいなことをやった。
だってこれで動くもの、sender から簡単に文字列が得られるし。

トレイアイコンの左クリックでメニューを出す方法は下記をパクった。
NotifyIconの左クリックでコンテキストメニューを表示させる:Gushwell’s C# Dev Notes

見えない Form をアクティブにするなんて方法も見つけたけど上手くいかなかった。
というか、こんなに強引なのに一番簡単かつ短いコードで終わるという。

想像していたより遥かに短いコードでこれだけ実装できてしまった。
私的にはもうコレで十分、ini の書き方が解らない人なんていないだろうし。
後は何を付けよう、というわけでまとめて zip

clipoli001.zip

しかしマジで Windows マシンはどうしよう?
キーボードでノートを選ぶなら東芝 R730 か Lenovo X201s がよさそうだが…
11.6 型の今でも持ち運びする時に少々大きいし…
1366×768 は正直狭いのでせめて 1600×900 以上にしたいけど…
かといってデスクトップでは使用歩度から考えて邪魔だし…
ブルーレイの再生をしたくなったら動画再生支援機能に問題が無い Windows のほうが…
結局決まらないのでミニノート 7 と仮想マシンの XP, Vista のままになりそう。