source: trunk/eraser6/Eraser/SchedulerPanel.cs @ 1829

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

When we load a task list from the stream the stream may not be valid. If so, throw an InvalidDataException? instead. This prevents exceptions which are specific to the implementation of the Executor class from letting its exceptions bubble up the stack.

  • 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, Localisation.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 (InvalidDataException 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                                    Localisation.IsRightToLeft(this) ?
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.