source: branches/eraser6/CodeReview/Eraser/SchedulerPanel.cs @ 1777

Revision 1777, 18.8 KB checked in by lowjoel, 5 years ago (diff)

Redesigned the Logger paradigm -- instead of a dealing with a logger object to do any form of logging, we have a static Logger class which code can log to, and define log targets to receive those logs. Also included is a LogSession? class which sets the log target for the current thread or for a few threads. Addresses #320: Logging improvements

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Drawing;
26using System.Text;
27using System.Windows.Forms;
28
29using Eraser.Manager;
30using Eraser.Util;
31using System.Globalization;
32using System.Runtime.InteropServices;
33using System.Diagnostics;
34using System.IO;
35using System.Runtime.Serialization;
36using System.ComponentModel;
37using ProgressChangedEventArgs = Eraser.Util.ProgressChangedEventArgs;
38
39namespace Eraser
40{
41    internal partial class SchedulerPanel : Eraser.BasePanel
42    {
43        public SchedulerPanel()
44        {
45            InitializeComponent();
46            Theming.ApplyTheme(schedulerDefaultMenu);
47            if (!IsHandleCreated)
48                CreateHandle();
49
50            //Populate the scheduler list-view with the current task list
51            ExecutorTasksCollection tasks = Program.eraserClient.Tasks;
52            foreach (Task task in tasks)
53                DisplayTask(task);
54
55            //Hook the event machinery to our class. Handle the task Added and Removed
56            //events.
57            Program.eraserClient.TaskAdded += TaskAdded;
58            Program.eraserClient.TaskDeleted += TaskDeleted;
59        }
60
61        private void DisplayTask(Task task)
62        {
63            //Add the item to the list view
64            ListViewItem item = scheduler.Items.Add(task.UIText);
65            item.SubItems.Add(string.Empty);
66            item.SubItems.Add(string.Empty);
67
68            //Set the tag of the item so we know which task on the LV corresponds
69            //to the physical task object.
70            item.Tag = task;
71
72            //Add our event handlers to the task
73            task.TaskStarted += task_TaskStarted;
74            task.ProgressChanged += task_ProgressChanged;
75            task.TaskFinished += task_TaskFinished;
76
77            //Show the fields on the list view
78            UpdateTask(item);
79        }
80
81        private void UpdateTask(ListViewItem item)
82        {
83            //Get the task object
84            Task task = (Task)item.Tag;
85
86            //Set the task name
87            item.Text = task.UIText;
88
89            //Set the next run time of the task
90            if (task.Queued)
91            {
92                item.SubItems[1].Text = S._("Queued for execution");
93                item.SubItems[2].Text = string.Empty;
94            }
95            else if (task.Schedule is RecurringSchedule)
96                item.SubItems[1].Text = ((task.Schedule as RecurringSchedule).NextRun.
97                    ToString("F", CultureInfo.CurrentCulture));
98            else if (task.Schedule == Schedule.RunNow || task.Schedule == Schedule.RunManually)
99                item.SubItems[1].Text = S._("Not queued");
100            else
101                item.SubItems[1].Text = task.Schedule.UIText;
102
103            //Set the group of the task.
104            CategorizeTask(task, item);
105        }
106
107        private void CategorizeTask(Task task)
108        {
109            CategorizeTask(task, GetTaskItem(task));
110        }
111
112        private void CategorizeTask(Task task, ListViewItem item)
113        {
114            if (task.Schedule == Schedule.RunNow || task.Schedule == Schedule.RunManually)
115                item.Group = scheduler.Groups["manual"];
116            else if (task.Schedule == Schedule.RunOnRestart)
117                item.Group = scheduler.Groups["restart"];
118            else
119                item.Group = scheduler.Groups["recurring"];
120        }
121
122        /// <summary>
123        /// Handles the Task Added event.
124        /// </summary>
125        private void TaskAdded(object sender, TaskEventArgs e)
126        {
127            if (InvokeRequired)
128            {
129                Invoke((EventHandler<TaskEventArgs>)TaskAdded, sender, e);
130                return;
131            }
132
133            //Display a balloon notification if the parent frame has been minimised.
134            MainForm parent = (MainForm)FindForm();
135            if (parent != null && (parent.WindowState == FormWindowState.Minimized || !parent.Visible))
136            {
137                parent.ShowNotificationBalloon(S._("New task added"), S._("{0} " +
138                    "has just been added to the list of tasks.", e.Task.UIText),
139                    ToolTipIcon.Info);
140            }
141
142            DisplayTask(e.Task);
143        }
144
145        private void DeleteSelectedTasks()
146        {
147            if (MessageBox.Show(this, S._("Are you sure you want to delete the selected tasks?"),
148                    S._("Eraser"), MessageBoxButtons.YesNo, MessageBoxIcon.Question,
149                    MessageBoxDefaultButton.Button1, S.IsRightToLeft(this) ?
150                        MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0
151                ) != DialogResult.Yes)
152            {
153                return;
154            }
155
156            foreach (ListViewItem item in scheduler.SelectedItems)
157            {
158                Task task = (Task)item.Tag;
159                if (!task.Executing)
160                    Program.eraserClient.Tasks.Remove(task);
161            }
162        }
163
164        /// <summary>
165        /// Handles the task deleted event.
166        /// </summary>
167        private void TaskDeleted(object sender, TaskEventArgs e)
168        {
169            if (InvokeRequired)
170            {
171                Invoke((EventHandler<TaskEventArgs>)TaskDeleted, sender, e);
172                return;
173            }
174
175            foreach (ListViewItem item in scheduler.Items)
176                if (((Task)item.Tag) == e.Task)
177                {
178                    scheduler.Items.Remove(item);
179                    break;
180                }
181
182            PositionProgressBar();
183        }
184
185        /// <summary>
186        /// Handles the task start event.
187        /// </summary>
188        /// <param name="e">The task event object.</param>
189        void task_TaskStarted(object sender, EventArgs e)
190        {
191            if (InvokeRequired)
192            {
193                Invoke((EventHandler)task_TaskStarted, sender, e);
194                return;
195            }
196
197            //Get the list view item
198            Task task = (Task)sender;
199            ListViewItem item = GetTaskItem(task);
200
201            //Update the status.
202            item.SubItems[1].Text = S._("Running...");
203
204            //Show the progress bar
205            schedulerProgress.Tag = item;
206            schedulerProgress.Visible = true;
207            schedulerProgress.Value = 0;
208            PositionProgressBar();
209        }
210
211        /// <summary>
212        /// Handles the progress event by the task.
213        /// </summary>
214        void task_ProgressChanged(object sender, ProgressChangedEventArgs e)
215        {
216            //Make sure we handle the event in the main thread as this requires
217            //GUI calls.
218            if (InvokeRequired)
219            {
220                Invoke((EventHandler<ProgressChangedEventArgs>)task_ProgressChanged, sender, e);
221                return;
222            }
223
224            //Update the progress bar
225            ErasureTarget target = (ErasureTarget)sender;
226            schedulerProgress.Value = (int)(target.Task.Progress.Progress * 1000.0);
227        }
228
229        /// <summary>
230        /// Handles the task completion event.
231        /// </summary>
232        void task_TaskFinished(object sender, EventArgs e)
233        {
234            if (InvokeRequired)
235            {
236                Invoke((EventHandler)task_TaskFinished, sender, e);
237                return;
238            }
239
240            //Get the list view item
241            Task task = (Task)sender;
242            ListViewItem item = GetTaskItem(task);
243            if (item == null)
244                return;
245
246            //Hide the progress bar
247            if (schedulerProgress.Tag != null && schedulerProgress.Tag == item)
248            {
249                schedulerProgress.Tag = null;
250                schedulerProgress.Visible = false;
251            }
252
253            //Get the exit status of the task.
254            LogLevel highestLevel = task.Log.Last().Highest;
255
256            //Show a balloon to inform the user
257            MainForm parent = (MainForm)FindForm();
258            if (parent.WindowState == FormWindowState.Minimized || !parent.Visible)
259            {
260                string message = null;
261                ToolTipIcon icon = ToolTipIcon.None;
262
263                switch (highestLevel)
264                {
265                    case LogLevel.Warning:
266                        message = S._("The task {0} has completed with warnings.", task.UIText);
267                        icon = ToolTipIcon.Warning;
268                        break;
269                    case LogLevel.Error:
270                        message = S._("The task {0} has completed with errors.", task.UIText);
271                        icon = ToolTipIcon.Error;
272                        break;
273                    case LogLevel.Fatal:
274                        message = S._("The task {0} did not complete.", task.UIText);
275                        icon = ToolTipIcon.Error;
276                        break;
277                    default:
278                        message = S._("The task {0} has completed.", task.UIText);
279                        icon = ToolTipIcon.Info;
280                        break;
281                }
282
283                parent.ShowNotificationBalloon(S._("Task executed"), message,
284                    icon);
285            }
286
287            //If the user requested us to remove completed one-time tasks, do so.
288            if (EraserSettings.Get().ClearCompletedTasks &&
289                (task.Schedule == Schedule.RunNow) && highestLevel < LogLevel.Warning)
290            {
291                Program.eraserClient.Tasks.Remove(task);
292            }
293
294            //Otherwise update the UI
295            else
296            {
297                switch (highestLevel)
298                {
299                    case LogLevel.Warning:
300                        item.SubItems[2].Text = S._("Completed with warnings");
301                        break;
302                    case LogLevel.Error:
303                        item.SubItems[2].Text = S._("Completed with errors");
304                        break;
305                    case LogLevel.Fatal:
306                        item.SubItems[2].Text = S._("Not completed");
307                        break;
308                    default:
309                        item.SubItems[2].Text = S._("Completed");
310                        break;
311                }
312
313                //Recategorize the task. Do not assume the task has maintained the
314                //category since run-on-restart tasks will be changed to immediately
315                //run tasks.
316                CategorizeTask(task, item);
317
318                //Update the status of the task.
319                UpdateTask(item);
320            }
321        }
322
323        /// <summary>
324        /// Occurs when the user presses a key on the list view.
325        /// </summary>
326        /// <param name="sender">The list view which triggered the event.</param>
327        /// <param name="e">Event argument.</param>
328        private void scheduler_KeyDown(object sender, KeyEventArgs e)
329        {
330            if (e.KeyCode == Keys.Delete)
331                DeleteSelectedTasks();
332        }
333
334        /// <summary>
335        /// Occurs when the user double-clicks a scheduler item. This will result
336        /// in the log viewer being called, or the progress dialog to be displayed.
337        /// </summary>
338        /// <param name="sender">The list view which triggered the event.</param>
339        /// <param name="e">Event argument.</param>
340        private void scheduler_ItemActivate(object sender, EventArgs e)
341        {
342            if (scheduler.SelectedItems.Count == 0)
343                return;
344
345            ListViewItem item = scheduler.SelectedItems[0];
346            if (((Task)item.Tag).Executing)
347                using (ProgressForm form = new ProgressForm((Task)item.Tag))
348                    form.ShowDialog();
349            else
350                editTaskToolStripMenuItem_Click(sender, e);
351        }
352
353        /// <summary>
354        /// Occurs when the user drags a file over the scheduler
355        /// </summary>
356        private void scheduler_DragEnter(object sender, DragEventArgs e)
357        {
358            string descriptionMessage = string.Empty;
359            string descriptionInsert = string.Empty;
360            const string descrptionPlaceholder = "%1";
361            if (!e.Data.GetDataPresent(DataFormats.FileDrop))
362                e.Effect = DragDropEffects.None;
363            else
364            {
365                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
366                bool isTaskList = true;
367                foreach (string file in files)
368                {
369                    if (descriptionInsert.Length < 259 &&
370                        (descriptionInsert.Length < 3 || descriptionInsert.Substring(descriptionInsert.Length - 3) != "..."))
371                    {
372                        string append = string.Format(CultureInfo.InvariantCulture, "{0}, ",
373                            Path.GetFileNameWithoutExtension(file));
374                        if (descriptionInsert.Length + append.Length > 259)
375                        {
376                            descriptionInsert += ".....";
377                        }
378                        else
379                        {
380                            descriptionInsert += append;
381                        }
382                    }
383
384                    if (Path.GetExtension(file) != ".ersx")
385                        isTaskList = false;
386                }
387                descriptionInsert = descriptionInsert.Remove(descriptionInsert.Length - 2);
388
389                if (isTaskList)
390                {
391                    e.Effect = DragDropEffects.Copy;
392                    descriptionMessage = S._("Import tasks from {0}", descrptionPlaceholder);
393                }
394                else
395                {
396                    e.Effect = DragDropEffects.Move;
397                    descriptionMessage = S._("Erase {0}", descrptionPlaceholder);
398                }
399            }
400
401            DropTargetHelper.DragEnter(this, e.Data, new Point(e.X, e.Y), e.Effect,
402                descriptionMessage, descriptionInsert);
403        }
404
405        private void scheduler_DragLeave(object sender, EventArgs e)
406        {
407            DropTargetHelper.DragLeave(this);
408        }
409
410        private void scheduler_DragOver(object sender, DragEventArgs e)
411        {
412            DropTargetHelper.DragOver(new Point(e.X, e.Y), e.Effect);
413        }
414
415        /// <summary>
416        /// Occurs when the user drops a file into the scheduler.
417        /// </summary>
418        private void scheduler_DragDrop(object sender, DragEventArgs e)
419        {
420            if (!e.Data.GetDataPresent(DataFormats.FileDrop))
421                e.Effect = DragDropEffects.None;
422            else
423            {
424                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
425
426                //Determine whether we are importing a task list or dragging files for
427                //erasure.
428                if (e.Effect == DragDropEffects.Copy)
429                {
430                    foreach (string file in files)
431                        using (FileStream stream = new FileStream(file, FileMode.Open,
432                            FileAccess.Read, FileShare.Read))
433                        {
434                            try
435                            {
436                                Program.eraserClient.Tasks.LoadFromStream(stream);
437                            }
438                            catch (SerializationException ex)
439                            {
440                                MessageBox.Show(S._("Could not import task list from {0}. The " +
441                                    "error returned was: {1}", file, ex.Message), S._("Eraser"),
442                                    MessageBoxButtons.OK, MessageBoxIcon.Error,
443                                    MessageBoxDefaultButton.Button1,
444                                    S.IsRightToLeft(null) ?
445                                        MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0);
446                            }
447                        }
448                }
449                else if (e.Effect == DragDropEffects.Move)
450                {
451                    //Create a task with the default settings
452                    Task task = new Task();
453                    foreach (string file in files)
454                    {
455                        FileSystemObjectTarget target;
456                        if (Directory.Exists(file))
457                            target = new FolderTarget();
458                        else
459                            target = new FileTarget();
460                        target.Path = file;
461
462                        task.Targets.Add(target);
463                    }
464
465                    //Add the task.
466                    Program.eraserClient.Tasks.Add(task);
467                }
468            }
469
470            DropTargetHelper.Drop(e.Data, new Point(e.X, e.Y), e.Effect);
471        }
472
473        /// <summary>
474        /// Occurs when the user right-clicks the list view.
475        /// </summary>
476        /// <param name="sender">The list view which generated this event.</param>
477        /// <param name="e">Event argument.</param>
478        private void schedulerMenu_Opening(object sender, CancelEventArgs e)
479        {
480            //If nothing's selected, show the Scheduler menu which just allows users to
481            //create new tasks (like from the toolbar)
482            if (scheduler.SelectedItems.Count == 0)
483            {
484                schedulerDefaultMenu.Show(schedulerMenu.Left, schedulerMenu.Top);
485                e.Cancel = true;
486                return;
487            }
488
489            bool aTaskNotQueued = false;
490            bool aTaskExecuting = false;
491            foreach (ListViewItem item in scheduler.SelectedItems)
492            {
493                Task task = (Task)item.Tag;
494                aTaskNotQueued = aTaskNotQueued || (!task.Queued && !task.Executing);
495                aTaskExecuting = aTaskExecuting || task.Executing;
496            }
497
498            runNowToolStripMenuItem.Enabled = aTaskNotQueued;
499            cancelTaskToolStripMenuItem.Enabled = aTaskExecuting;
500
501            editTaskToolStripMenuItem.Enabled = scheduler.SelectedItems.Count == 1 &&
502                !((Task)scheduler.SelectedItems[0].Tag).Executing &&
503                !((Task)scheduler.SelectedItems[0].Tag).Queued;
504            deleteTaskToolStripMenuItem.Enabled = !aTaskExecuting;
505        }
506
507        /// <summary>
508        /// Occurs when the user selects the New Task context menu item.
509        /// </summary>
510        /// <param name="sender">The menu which generated this event.</param>
511        /// <param name="e">Event argument.</param>
512        private void newTaskToolStripMenuItem_Click(object sender, EventArgs e)
513        {
514            using (TaskPropertiesForm form = new TaskPropertiesForm())
515            {
516                if (form.ShowDialog() == DialogResult.OK)
517                {
518                    Task task = form.Task;
519                    Program.eraserClient.Tasks.Add(task);
520                }
521            }
522        }
523
524        /// <summary>
525        /// Occurs whent the user selects the Run Now context menu item.
526        /// </summary>
527        /// <param name="sender">The menu which generated this event.</param>
528        /// <param name="e">Event argument.</param>
529        private void runNowToolStripMenuItem_Click(object sender, EventArgs e)
530        {
531            foreach (ListViewItem item in scheduler.SelectedItems)
532            {
533                //Queue the task
534                Task task = (Task)item.Tag;
535                if (!task.Executing && !task.Queued)
536                {
537                    Program.eraserClient.QueueTask(task);
538
539                    //Update the UI
540                    item.SubItems[1].Text = S._("Queued for execution");
541                }
542            }
543        }
544
545        /// <summary>
546        /// Occurs whent the user selects the Cancel Task context menu item.
547        /// </summary>
548        /// <param name="sender">The menu which generated this event.</param>
549        /// <param name="e">Event argument.</param>
550        private void cancelTaskToolStripMenuItem_Click(object sender, EventArgs e)
551        {
552            foreach (ListViewItem item in scheduler.SelectedItems)
553            {
554                //Queue the task
555                Task task = (Task)item.Tag;
556                if (task.Executing || task.Queued)
557                {
558                    task.Cancel();
559
560                    //Update the UI
561                    item.SubItems[1].Text = string.Empty;
562                }
563            }
564        }
565
566        /// <summary>
567        /// Occurs when the user selects the View Task Log context menu item.
568        /// </summary>
569        /// <param name="sender">The menu item which generated this event.</param>
570        /// <param name="e">Event argument.</param>
571        private void viewTaskLogToolStripMenuItem_Click(object sender, EventArgs e)
572        {
573            if (scheduler.SelectedItems.Count != 1)
574                return;
575
576            ListViewItem item = scheduler.SelectedItems[0];
577            using (LogForm form = new LogForm((Task)item.Tag))
578                form.ShowDialog();
579        }
580
581        /// <summary>
582        /// Occurs when the user selects the Edit Task context menu item.
583        /// </summary>
584        /// <param name="sender">The menu item which generated this event.</param>
585        /// <param name="e">Event argument.</param>
586        private void editTaskToolStripMenuItem_Click(object sender, EventArgs e)
587        {
588            if (scheduler.SelectedItems.Count != 1 ||
589                ((Task)scheduler.SelectedItems[0].Tag).Executing ||
590                ((Task)scheduler.SelectedItems[0].Tag).Queued)
591            {
592                return;
593            }
594
595            //Make sure that the task is not being executed, or else. This can
596            //be done in the Client library, but there will be no effect on the
597            //currently running task.
598            ListViewItem item = scheduler.SelectedItems[0];
599            Task task = (Task)item.Tag;
600            if (task.Executing)
601                return;
602
603            //Edit the task.
604            using (TaskPropertiesForm form = new TaskPropertiesForm())
605            {
606                form.Task = task;
607                if (form.ShowDialog() == DialogResult.OK)
608                {
609                    task = form.Task;
610
611                    //Update the list view
612                    UpdateTask(item);
613                }
614            }
615        }
616
617        /// <summary>
618        /// Occurs when the user selects the Delete Task context menu item.
619        /// </summary>
620        /// <param name="sender">The menu item which generated this event.</param>
621        /// <param name="e">Event argument.</param>
622        private void deleteTaskToolStripMenuItem_Click(object sender, EventArgs e)
623        {
624            DeleteSelectedTasks();
625        }
626
627        #region Item management
628        /// <summary>
629        /// Retrieves the ListViewItem for the given task.
630        /// </summary>
631        /// <param name="task">The task object whose list view entry is being sought.</param>
632        /// <returns>A ListViewItem for the given task object.</returns>
633        private ListViewItem GetTaskItem(Task task)
634        {
635            foreach (ListViewItem item in scheduler.Items)
636                if (item.Tag == task)
637                    return item;
638
639            return null;
640        }
641
642        /// <summary>
643        /// Maintains the position of the progress bar.
644        /// </summary>
645        private void PositionProgressBar()
646        {
647            if (schedulerProgress.Tag == null)
648                return;
649
650            Rectangle rect = ((ListViewItem)schedulerProgress.Tag).SubItems[2].Bounds;
651            rect.Offset(2, 2);
652            schedulerProgress.Location = rect.Location;
653            schedulerProgress.Size = rect.Size;
654        }
655
656        private void scheduler_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
657        {
658            e.DrawDefault = true;
659            if (schedulerProgress.Tag != null)
660                PositionProgressBar();
661        }
662
663        private void scheduler_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
664        {
665            e.DrawDefault = true;
666        }
667        #endregion
668    }
669}
Note: See TracBrowser for help on using the repository browser.