private bool WriteReport(DateTime timeToRecord, string message, int powerOnDuration = -1, int workDuration = -1)
 {
     if(silentMode)
     {
         // デバッグモード
         return true;
     }
     try
     {
         string[] values = {
             timeToRecord.ToString("yyyy/MM/dd HH:mm:ss"),
             message,
             FormatDuration(powerOnDuration),
             FormatDuration(workDuration),
         };
         System.IO.StreamWriter st = new System.IO.StreamWriter(
             GetReportFilePath(),
             true,
             new UTF8Encoding(false)
         );
         st.WriteLine(string.Join("\t", values));
         st.Close();
     }
     catch(Exception e)
     {
         messageToShow = () => OpenBalloon("ファイルへの書き込みに失敗したよ!\n" + e.ToString(), alerting: true);
         return false;
     }
     return true;
 }
        private void timer_Tick(object sender, EventArgs e)
        {
            if (frame != 0)
            {
                CheckModifierKeys();

                if (messageToShow != null)
                {
                    // メッセージの表示が予約されている
                    messageToShow();
                    messageToShow = null;
                    return;
                }

                if (IsTalking())
                {
                    // しゃべっている間の制御は行わない
                    if (talkingDuration > 0)
                    {
                        if (--talkingDuration <= 0)
                        {
                            CloseBalloon();
                        }
                    }
                    return;
                }
            }

            AnimationDefinition def = scenario.GetAnimationDefinition(animationState);
            // 通常のフレーム再生
            if(frame == 0)
            {
                // フレーム 0 は特別な扱い。
                // 継続時間を決定する。
                duration = random.Next(def.durationFramesMin, def.durationFramesMax);
                frame = 1;
                talkingImage = def.talkingImage;
                if (IsTalking())
                {
                    imageFile = talkingImage;
                }
            }
            if(--duration <= 0)
            {
                int possibility = random.Next(100);
                foreach(AnimationTransition trans in def.transitions)
                {
                    possibility -= trans.possibility;
                    if(possibility < 0)
                    {
                        animationState = trans.state;
                        break;
                    }
                }
                frame = 0;
                return;
            }

            while (!ProcessFrame(def)) ;
            ++frame;
        }
        public MainWindow()
        {
            InitializeComponent();
            // デザイナのプロパティをこのオブジェクトにバインド
            this.DataContext = this;
            // 本来、親の DataContext だけに設定すればいいはずだが、
            // 最初の表示までは DataContext が作られないとかなんとか。
            this.ContextMenu.DataContext = this;

            silentMode = ((Keyboard.Modifiers & ModifierKeys.Shift) != 0);

            if (Properties.Settings.Default.version != System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString())
            {
                Properties.Settings.Default.Upgrade();
            }
            if (Properties.Settings.Default.version != null && Properties.Settings.Default.version != "")
            {
                // 設定から復旧
                reportFile = Properties.Settings.Default.reportFile;
                menuTopmostChecked = Properties.Settings.Default.topmost;
                watchModifiers = Properties.Settings.Default.watchModifiers;
                Left = Properties.Settings.Default.X;
                Top = Properties.Settings.Default.Y;
            }
            else
            {
                reportFile = "勤務記録.txt";
                menuTopmostChecked = true;
                watchModifiers = true;
                GotoHome();
            }
            isRegisterStartup = CheckRegisterStartup();

            balloonWindow = new BalloonWindow();
            CloseBalloon();

            InitNotifyIcon();

            timer = new System.Windows.Threading.DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(33.3);
            timer.Tick += timer_Tick;
            timer.Start();

            state = MinidamState.Unknown;
            StopWork();

            startTime = DateTime.Now;
            WriteReport(startTime, "起動したよ");
            if(silentMode)
            {
                messageToShow = () => OpenBalloon("デバッグモード", alerting: true);
            }
        }
 private void StartWork()
 {
     if(state == MinidamState.Working)
     {
         return;
     }
     messageToShow = () => OpenBalloon("はたらくよ!!", 30);
     Title = "働いてるよ! - 働くミニダム";
     state = MinidamState.Working;
     animationState = AnimationState.WorkingNormal;
     frame = 0;
     workStartTime = DateTime.Now;
     WriteReport(workStartTime, "おしごとはじめ");
 }
 private void StopWork()
 {
     if (state == MinidamState.Idle)
     {
         return;
     }
     if (state == MinidamState.Working)
     {
         messageToShow = () => OpenBalloon("今日のおしごと\nおわり!", 30);
         DateTime workStopTime = DateTime.Now;
         WriteReport(workStopTime, "おしごとおわり", workDuration: (int)((workStopTime - workStartTime).TotalSeconds));
     }
     Title = "働くミニダム";
     state = MinidamState.Idle;
     animationState = AnimationState.IdleDoNothing;
     frame = 0;
 }
 private void MenuItemQuit_Click(object sender, RoutedEventArgs e)
 {
     if (IsWorking)
     {
         messageToShow = () => OpenBalloon("まだ働いてるよ!", 30);
         return;
     }
     Close();
 }
 private void MenuItemOpenReport_Click(object sender, RoutedEventArgs e)
 {
     if(System.IO.File.Exists(GetReportFilePath()))
     {
         System.Diagnostics.Process.Start(GetReportFilePath());
     }
     else
     {
         messageToShow = () => OpenBalloon("勤務ファイルがないよ!", alerting: true);
     }
 }
 private void CloseBalloon()
 {
     balloonWindow.Visibility = Visibility.Hidden;
     balloonWindow.message = "";
     talkingDuration = 0;
     talkingAlert = false;
     imageFile = animationImage;
     messageToShow = null;
 }
 private void CheckModifierKeys()
 {
     if (messageToShow != null || (IsTalking() && talkingAlert) || talkingImage == null)
     {
         return;
     }
     ModifierKeys curModifier = watchModifiers?(Keyboard.Modifiers & (
         ModifierKeys.Control
         | ModifierKeys.Shift
         | ModifierKeys.Alt
     )):0;
     LockKeys curLocks = 0;
     if (Keyboard.GetKeyStates(Key.CapsLock) == KeyStates.Toggled)
     {
         curLocks |= LockKeys.CAPS;
     }
     if (Keyboard.GetKeyStates(Key.NumLock) == KeyStates.Toggled)
     {
         curLocks |= LockKeys.NUMLOCK;
     }
     if (curModifier == lastModifiers && (curModifier == 0 || curLocks == lastLockKeys))
     {
         lastLockKeys = curLocks;
         return;
     }
     lastModifiers = curModifier;
     lastLockKeys = curLocks;
     List<string> messages = new List<string>();
     if ((curModifier & ModifierKeys.Shift) != 0)
     {
         messages.Add("Shift");
     }
     if ((curModifier & ModifierKeys.Control) != 0)
     {
         messages.Add("Control");
     }
     if ((curModifier & ModifierKeys.Alt) != 0)
     {
         messages.Add("Alt");
     }
     if(messages.Count <= 0)
     {
         CloseBalloon();
         return;
     }
     if((curLocks & LockKeys.CAPS) != 0)
     {
         messages.Add("CAPS");
     }
     if ((curLocks & LockKeys.NUMLOCK) != 0)
     {
         messages.Add("NumLock");
     }
     messageToShow = () => OpenBalloon(string.Join("\n", messages));
 }
 public void Application_SessionEnding(object sender, SessionEndingCancelEventArgs e)
 {
     if(IsWorking)
     {
         e.Cancel = true;
         Title = "まだ働いてるよ! - 働くミニダム";
         messageToShow = () => OpenBalloon("まだ働いてるよ!", alerting: true);
     }
 }