雑記 - otherwise

最近はDQ10しかやっていないダメ技術者がちまちまと綴る雑記帳

カウントダウンタイマー祭りへのエントリー(保険版)

本当はもっと変なところに力を入れたやつを作成中なんだけど、期限に間に合うかとても不安なので、テスト用に作ったやつを先に載せておきます。
面倒だったので Visual Studio を使わずに全部秀丸で書いちゃいました。
一応、 C# 3.0 向け。

  • CountdownTimerLite.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Jp.Mkns.Products.CountdownTimerLite {
  class TimerSetting {
    public int ID { get; set; }
    public string Text { get; set; }
    public int Hours { get; set; }
    public int Minutes { get; set; }
    public int Seconds { get; set; }
  }

  class HotKey {
    public int ID { get; set; }
    public int Key { get; set; }
    public int SpecialKeys { get; set; }
    public Action Execute { get; set; }
  }

  class TimerViewer : Form {
    private Label label;
    private Timer timer;
    private ContextMenuStrip contextMenu;
    private ToolStripMenuItem controlMenuItem;
    private ToolStripMenuItem settingMenuItem;
    private ToolStripMenuItem settingPlus4SecondsMenuItem;
    private ToolStripMenuItem exitMenuItem;
    private ToolStripMenuItem controlStartMenuItem;
    private ToolStripMenuItem controlPauseMenuItem;
    private ToolStripMenuItem controlClearMenuItem;

    private List<TimerSetting> timerSetting;
    private List<HotKey> hotkeys;
    private DateTime targetDate;
    private DateTime pauseDate;
    private bool? isActive;

    public TimerViewer() {
      this.InitializeComponent();

      this.timerSetting = new List<TimerSetting>() {
        new TimerSetting() { ID =   5, Text =   "5 分", Hours = 0, Minutes =  5, Seconds = 0 },
        new TimerSetting() { ID =  10, Text =  "10 分", Hours = 0, Minutes = 10, Seconds = 0 },
        new TimerSetting() { ID =  15, Text =  "15 分", Hours = 0, Minutes = 15, Seconds = 0 },
        new TimerSetting() { ID =  20, Text =  "20 分", Hours = 0, Minutes = 20, Seconds = 0 },
        new TimerSetting() { ID =  30, Text =  "30 分", Hours = 0, Minutes = 30, Seconds = 0 },
        new TimerSetting() { ID =  50, Text =  "50 分", Hours = 0, Minutes = 50, Seconds = 0 },
        new TimerSetting() { ID =  60, Text =  "60 分", Hours = 1, Minutes =  0, Seconds = 0 },
        new TimerSetting() { ID =  70, Text =  "70 分", Hours = 1, Minutes = 10, Seconds = 0 },
        new TimerSetting() { ID =  75, Text =  "75 分", Hours = 1, Minutes = 15, Seconds = 0 },
        new TimerSetting() { ID =  90, Text =  "90 分", Hours = 1, Minutes = 30, Seconds = 0 },
        new TimerSetting() { ID = 120, Text = "120 分", Hours = 2, Minutes =  0, Seconds = 0 }
      };
      this.RegistTimerSetting();

      this.hotkeys = new List<HotKey>() {
        new HotKey() { ID = 0x0101, Key = (int)Keys.S, SpecialKeys = MOD_CONTROL | MOD_SHIFT, Execute = this.StartTimer },
        new HotKey() { ID = 0x0102, Key = (int)Keys.L, SpecialKeys = MOD_CONTROL | MOD_SHIFT, Execute = this.PauseTimer },
        new HotKey() { ID = 0x0103, Key = (int)Keys.C, SpecialKeys = MOD_CONTROL | MOD_SHIFT, Execute = this.ClearTimer },
        new HotKey() { ID = 0x0109, Key = (int)Keys.Q, SpecialKeys = MOD_CONTROL | MOD_SHIFT, Execute = this.Close      }
      };
      this.RegistHotKey();
      this.isActive = null;
    }

    protected override void WndProc(ref Message m) {
      int param;
      base.WndProc(ref m);
      if (m.Msg == WM_HOTKEY) {
        param = (int) m.WParam;
        var target = from data in this.hotkeys where data.ID == param select data;
        foreach (var hotKey in target) {
          hotKey.Execute();
        }
      }
    }

    private void InitializeComponent() {
      this.label = new Label();
      this.contextMenu = new ContextMenuStrip();
      this.controlMenuItem = new ToolStripMenuItem();
      this.settingMenuItem = new ToolStripMenuItem();
      this.settingPlus4SecondsMenuItem = new ToolStripMenuItem();
      this.exitMenuItem = new ToolStripMenuItem();
      this.controlStartMenuItem = new ToolStripMenuItem();
      this.controlPauseMenuItem = new ToolStripMenuItem();
      this.controlClearMenuItem = new ToolStripMenuItem();

      this.label.Location = new Point(0, 0);
      this.label.AutoSize = false;
      this.label.Size = new Size(121, 31);
            this.label.Font = new System.Drawing.Font("メイリオ", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
      this.label.TextAlign = ContentAlignment.MiddleCenter;
      this.label.Text = @"00:00:00";
      this.label.KeyDown += (sender, e) => MessageBox.Show("Label KeyDown");
      this.label.MouseDoubleClick += (sender, e) => this.Close();
      this.label.ContextMenuStrip = this.contextMenu;

      this.controlMenuItem.Text = "タイマー(&T)";

      this.settingMenuItem.Text = "時間(&C)";

      this.settingPlus4SecondsMenuItem.Text = "5秒前からカウント開始する(&P)";
      this.settingPlus4SecondsMenuItem.Checked = true;
      this.settingPlus4SecondsMenuItem.Click += (sender, e) => this.settingPlus4SecondsMenuItem.Checked = !this.settingPlus4SecondsMenuItem.Checked;

      this.exitMenuItem.Text = "終了(&Q)";
      this.exitMenuItem.Click += (sender, e) => this.Close();

      this.controlStartMenuItem.Text = "開始(&S)";
      this.controlStartMenuItem.Click += (sender, e) => this.StartTimer();
      this.controlStartMenuItem.Enabled = true;

      this.controlPauseMenuItem.Text = "一時停止(&P)";
      this.controlPauseMenuItem.Click += (sender, e) => this.PauseTimer();
      this.controlPauseMenuItem.Enabled = false;

      this.controlClearMenuItem.Text = "クリア(&C)";
      this.controlClearMenuItem.Click += (sender, e) => this.ClearTimer();
      this.controlClearMenuItem.Enabled = false;

      this.contextMenu.Items.AddRange(
        new ToolStripMenuItem[] {
          this.controlMenuItem,
          this.settingMenuItem,
          this.settingPlus4SecondsMenuItem,
          this.exitMenuItem
        });
      this.controlMenuItem.DropDownItems.AddRange(
        new ToolStripMenuItem[] {
          this.controlStartMenuItem,
          this.controlPauseMenuItem,
          this.controlClearMenuItem
        });

      this.Location = new Point(0, 0);
      this.Size = new Size(121, 31);
      this.FormBorderStyle = FormBorderStyle.None;
      this.Text = "タイマー";
      this.TransparencyKey = this.BackColor;
      this.Opacity = 1;
      this.StartPosition = FormStartPosition.Manual;
      this.ShowInTaskbar = false;
      this.TopMost = true;
      this.Activated += (sender, e) => this.Opacity = 1;
      this.Deactivate += (sender, e) => this.Opacity = 0.6;
      this.MouseDoubleClick += (sender, e) => this.Close();
      this.Closed += (sender, e) => this.UnregistHotKey();

      this.Controls.Add(this.label);

      this.timer = new Timer();
      this.timer.Interval = 50;
      this.timer.Tick += (sender, e) => this.Timer_Tick();
    }

    private void RegistTimerSetting() {
      ToolStripMenuItem item;
      this.settingMenuItem.DropDownItems.Clear();
      foreach (TimerSetting setting in this.timerSetting) {
        item = new ToolStripMenuItem();
        item.Text = setting.Text;
        item.Checked = false;
        item.Click += (sender, e) => this.ChangeTimer((ToolStripMenuItem)sender);
        item.Tag = setting.ID;
        this.settingMenuItem.DropDownItems.Add(item);
      }
      ((ToolStripMenuItem)this.settingMenuItem.DropDownItems[0]).Checked = true;
    }

    private void RegistHotKey() {
      foreach (var hotKey in this.hotkeys) {
        RegisterHotKey(this.Handle, hotKey.ID, hotKey.SpecialKeys, hotKey.Key);
      }
    }

    private void UnregistHotKey() {
      foreach (var hotKey in this.hotkeys) {
        UnregisterHotKey(this.Handle, hotKey.ID);
      }
    }

    private void StartTimer() {
      TimeSpan pause;
      DateTime nowDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
      int targetId = 0;
      if (this.isActive == null) {
        this.targetDate = nowDate;
        this.pauseDate = nowDate;
        foreach (ToolStripMenuItem item in this.settingMenuItem.DropDownItems) {
          if (item.Checked) {
            targetId = (int)item.Tag;
            break;
          }
        }
        if (targetId == 0) {
          return;
        }
        var target = from data in this.timerSetting where data.ID == targetId select data;
        var targetCollection = target.ToArray();
        this.targetDate = this.targetDate.AddHours(targetCollection[0].Hours).AddMinutes(targetCollection[0].Minutes).AddSeconds(targetCollection[0].Seconds);
        if (this.settingPlus4SecondsMenuItem.Checked) {
          this.targetDate = this.targetDate.AddSeconds(5);
        }
        this.label.ForeColor = Color.Black;
      } else if (this.isActive == true) {
        return;
      } else {
        pause = this.targetDate - pauseDate;
        this.targetDate = nowDate.AddTicks(pause.Ticks);
      }
      this.timer.Start();
      this.isActive = true;
      this.ChangeControlMenu();
    }

    private void PauseTimer() {
      if (this.isActive == false) {
        return;
      }
      this.pauseDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
      this.timer.Stop();
      this.isActive = false;
      this.ChangeControlMenu();
    }

    private void ClearTimer() {
      if (this.isActive == true) {
        return;
      }
      this.label.Text = "00:00:00";
      this.label.ForeColor = Color.Black;
      this.isActive = null;
      this.ChangeControlMenu();
    }

    private void ChangeTimer(ToolStripMenuItem target) {
      if (this.isActive != null) {
        this.PauseTimer();
        this.ClearTimer();
      }
      foreach (ToolStripMenuItem item in this.settingMenuItem.DropDownItems) {
        item.Checked = false;
      }
      target.Checked = true;
    }

    private void ChangeControlMenu() {
      if (this.isActive == null) {
        this.controlStartMenuItem.Enabled = true;
        this.controlPauseMenuItem.Enabled = false;
        this.controlClearMenuItem.Enabled = false;
      } else if (this.isActive == true) {
        this.controlStartMenuItem.Enabled = false;
        this.controlPauseMenuItem.Enabled = true;
        this.controlClearMenuItem.Enabled = false;
      } else {
        this.controlStartMenuItem.Enabled = true;
        this.controlPauseMenuItem.Enabled = false;
        this.controlClearMenuItem.Enabled = true;
      }
    }

    private void Timer_Tick() {
      DateTime nowDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
      TimeSpan span = this.targetDate - nowDate;
      if (span.Ticks <= 0)
      {
        this.label.ForeColor = Color.Red;
        span = span.Duration();
      }
      this.label.Text = String.Format("{0:00}:{1:00}:{2:00}", span.Hours, span.Minutes, span.Seconds);
    }

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

    #region win32 DLL import
    const int MOD_ALT     = 0x0001;
    const int MOD_CONTROL = 0x0002;
    const int MOD_SHIFT   = 0x0004;
    const int WM_HOTKEY   = 0x0312;

    [DllImport("user32.dll")]
    extern static int RegisterHotKey(IntPtr HWnd, int ID, int MOD_KEY, int KEY);

    [DllImport("user32.dll")]
    extern static int UnregisterHotKey(IntPtr HWnd, int ID);
    #endregion
  }
}
> csc.exe /t:winexe CountdownTimerLite.cs
> CountdownTimer.exe

で左上に「 00:00:00 」な表示が出ます。
後は、ホットキーを登録しているので、

Ctrl + Shift + S
カウントダウン開始
Ctrl + Shift + L
一時停止
Ctrl + Shift + C
一時停止状態のときにタイマークリア
Ctrl + Shift + Q
終了

って感じでいけます。
ちなみに、数値上で右クリックするとメニューが出るので、そっちからも操作も可能。
カウントダウンする時間もメニューで選択可能です。
(面倒なのでスクリーンショットは省略。あくまでも「保険」用なので悪しからず。)