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

Revision 1745, 19.0 KB checked in by lowjoel, 5 years ago (diff)

Forward ported changes from trunk to r1743

  • 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.Data;
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,
150                   S.IsRightToLeft(this) ? MessageBoxOptions.RtlReading : 0) != DialogResult.Yes)
151            {
152                return;
153            }
154
155            foreach (ListViewItem item in scheduler.SelectedItems)
156            {
157                Task task = (Task)item.Tag;
158                if (!task.Executing)
159                    Program.eraserClient.Tasks.Remove(task);
160            }
161        }
162
163        /// <summary>
164        /// Handles the task deleted event.
165        /// </summary>
166        private void TaskDeleted(object sender, TaskEventArgs e)
167        {
168            if (InvokeRequired)
169            {
170                Invoke((EventHandler<TaskEventArgs>)TaskDeleted, sender, e);
171                return;
172            }
173
174            foreach (ListViewItem item in scheduler.Items)
175                if (((Task)item.Tag) == e.Task)
176                {
177                    scheduler.Items.Remove(item);
178                    break;
179                }
180
181            PositionProgressBar();
182        }
183
184        /// <summary>
185        /// Handles the task start event.
186        /// </summary>
187        /// <param name="e">The task event object.</param>
188        void task_TaskStarted(object sender, TaskEventArgs e)
189        {
190            if (InvokeRequired)
191            {
192                Invoke((EventHandler<TaskEventArgs>)task_TaskStarted, sender, e);
193                return;
194            }
195
196            //Get the list view item
197            ListViewItem item = GetTaskItem(e.Task);
198
199            //Update the status.
200            item.SubItems[1].Text = S._("Running...");
201
202            //Show the progress bar
203            schedulerProgress.Tag = item;
204            schedulerProgress.Visible = true;
205            schedulerProgress.Value = 0;
206            PositionProgressBar();
207        }
208
209        /// <summary>
210        /// Handles the progress event by the task.
211        /// </summary>
212        void task_ProgressChanged(object sender, ProgressChangedEventArgs e)
213        {
214            //Make sure we handle the event in the main thread as this requires
215            //GUI calls.
216            if (InvokeRequired)
217            {
218                Invoke((EventHandler<ProgressChangedEventArgs>)task_ProgressChanged, sender, e);
219                return;
220            }
221
222            //Update the progress bar
223            ErasureTarget target = (ErasureTarget)sender;
224            schedulerProgress.Value = (int)(target.Task.Progress.Progress * 1000.0);
225        }
226
227        /// <summary>
228        /// Handles the task completion event.
229        /// </summary>
230        void task_TaskFinished(object sender, TaskEventArgs e)
231        {
232            if (InvokeRequired)
233            {
234                Invoke((EventHandler<TaskEventArgs>)task_TaskFinished, sender, e);
235                return;
236            }
237
238            //Get the list view item
239            ListViewItem item = GetTaskItem(e.Task);
240            if (item == null)
241                return;
242
243            //Hide the progress bar
244            if (schedulerProgress.Tag != null && schedulerProgress.Tag == item)
245            {
246                schedulerProgress.Tag = null;
247                schedulerProgress.Visible = false;
248            }
249
250            //Get the exit status of the task.
251            LogLevel highestLevel = LogLevel.Information;
252            LogEntryCollection logs = e.Task.Log.LastSessionEntries;
253            foreach (LogEntry log in logs)
254                if (log.Level > highestLevel)
255                    highestLevel = log.Level;
256
257            //Show a balloon to inform the user
258            MainForm parent = (MainForm)FindForm();
259
260            //TODO: Is this still needed?
261            if (parent == null)
262                throw new InvalidOperationException();
263            if (parent.WindowState == FormWindowState.Minimized || !parent.Visible)
264            {
265                string message = null;
266                ToolTipIcon icon = ToolTipIcon.None;
267
268                switch (highestLevel)
269                {
270                    case LogLevel.Warning:
271                        message = S._("The task {0} has completed with warnings.", e.Task.UIText);
272                        icon = ToolTipIcon.Warning;
273                        break;
274                    case LogLevel.Error:
275                        message = S._("The task {0} has completed with errors.", e.Task.UIText);
276                        icon = ToolTipIcon.Error;
277                        break;
278                    case LogLevel.Fatal:
279                        message = S._("The task {0} did not complete.", e.Task.UIText);
280                        icon = ToolTipIcon.Error;
281                        break;
282                    default:
283                        message = S._("The task {0} has completed.", e.Task.UIText);
284                        icon = ToolTipIcon.Info;
285                        break;
286                }
287
288                parent.ShowNotificationBalloon(S._("Task executed"), message,
289                    icon);
290            }
291
292            //If the user requested us to remove completed one-time tasks, do so.
293            if (EraserSettings.Get().ClearCompletedTasks &&
294                (e.Task.Schedule == Schedule.RunNow) && highestLevel < LogLevel.Warning)
295            {
296                Program.eraserClient.Tasks.Remove(e.Task);
297            }
298
299            //Otherwise update the UI
300            else
301            {
302                switch (highestLevel)
303                {
304                    case LogLevel.Warning:
305                        item.SubItems[2].Text = S._("Completed with warnings");
306                        break;
307                    case LogLevel.Error:
308                        item.SubItems[2].Text = S._("Completed with errors");
309                        break;
310                    case LogLevel.Fatal:
311                        item.SubItems[2].Text = S._("Not completed");
312                        break;
313                    default:
314                        item.SubItems[2].Text = S._("Completed");
315                        break;
316                }
317
318                //Recategorize the task. Do not assume the task has maintained the
319                //category since run-on-restart tasks will be changed to immediately
320                //run tasks.
321                CategorizeTask(e.Task, item);
322
323                //Update the status of the task.
324                UpdateTask(item);
325            }
326        }
327
328        /// <summary>
329        /// Occurs when the user presses a key on the list view.
330        /// </summary>
331        /// <param name="sender">The list view which triggered the event.</param>
332        /// <param name="e">Event argument.</param>
333        private void scheduler_KeyDown(object sender, KeyEventArgs e)
334        {
335            if (e.KeyCode == Keys.Delete)
336                DeleteSelectedTasks();
337        }
338
339        /// <summary>
340        /// Occurs when the user double-clicks a scheduler item. This will result
341        /// in the log viewer being called, or the progress dialog to be displayed.
342        /// </summary>
343        /// <param name="sender">The list view which triggered the event.</param>
344        /// <param name="e">Event argument.</param>
345        private void scheduler_ItemActivate(object sender, EventArgs e)
346        {
347            if (scheduler.SelectedItems.Count == 0)
348                return;
349
350            ListViewItem item = scheduler.SelectedItems[0];
351            if (((Task)item.Tag).Executing)
352                using (ProgressForm form = new ProgressForm((Task)item.Tag))
353                    form.ShowDialog();
354            else
355                editTaskToolStripMenuItem_Click(sender, e);
356        }
357
358        /// <summary>
359        /// Occurs when the user drags a file over the scheduler
360        /// </summary>
361        private void scheduler_DragEnter(object sender, DragEventArgs e)
362        {
363            string descriptionMessage = string.Empty;
364            string descriptionInsert = string.Empty;
365            const string descrptionPlaceholder = "%1";
366            if (!e.Data.GetDataPresent(DataFormats.FileDrop))
367                e.Effect = DragDropEffects.None;
368            else
369            {
370                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
371                bool isTaskList = true;
372                foreach (string file in files)
373                {
374                    if (descriptionInsert.Length < 259 &&
375                        (descriptionInsert.Length < 3 || descriptionInsert.Substring(descriptionInsert.Length - 3) != "..."))
376                    {
377                        string append = string.Format(CultureInfo.InvariantCulture, "{0}, ",
378                            Path.GetFileNameWithoutExtension(file));
379                        if (descriptionInsert.Length + append.Length > 259)
380                        {
381                            descriptionInsert += ".....";
382                        }
383                        else
384                        {
385                            descriptionInsert += append;
386                        }
387                    }
388
389                    if (Path.GetExtension(file) != ".ersx")
390                        isTaskList = false;
391                }
392                descriptionInsert = descriptionInsert.Remove(descriptionInsert.Length - 2);
393
394                if (isTaskList)
395                {
396                    e.Effect = DragDropEffects.Copy;
397                    descriptionMessage = S._("Import tasks from {0}", descrptionPlaceholder);
398                }
399                else
400                {
401                    e.Effect = DragDropEffects.Move;
402                    descriptionMessage = S._("Erase {0}", descrptionPlaceholder);
403                }
404            }
405
406            DropTargetHelper.DragEnter(this, e.Data, new Point(e.X, e.Y), e.Effect,
407                descriptionMessage, descriptionInsert);
408        }
409
410        private void scheduler_DragLeave(object sender, EventArgs e)
411        {
412            DropTargetHelper.DragLeave(this);
413        }
414
415        private void scheduler_DragOver(object sender, DragEventArgs e)
416        {
417            DropTargetHelper.DragOver(new Point(e.X, e.Y), e.Effect);
418        }
419
420        /// <summary>
421        /// Occurs when the user drops a file into the scheduler.
422        /// </summary>
423        private void scheduler_DragDrop(object sender, DragEventArgs e)
424        {
425            if (!e.Data.GetDataPresent(DataFormats.FileDrop))
426                e.Effect = DragDropEffects.None;
427            else
428            {
429                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
430
431                //Determine whether we are importing a task list or dragging files for
432                //erasure.
433                if (e.Effect == DragDropEffects.Copy)
434                {
435                    foreach (string file in files)
436                        using (FileStream stream = new FileStream(file, FileMode.Open,
437                            FileAccess.Read, FileShare.Read))
438                        {
439                            try
440                            {
441                                Program.eraserClient.Tasks.LoadFromStream(stream);
442                            }
443                            catch (SerializationException ex)
444                            {
445                                MessageBox.Show(S._("Could not import task list from {0}. The error " +
446                                    "returned was: {1}", file, ex.Message), S._("Eraser"),
447                                    MessageBoxButtons.OK, MessageBoxIcon.Error,
448                                    MessageBoxDefaultButton.Button1,
449                                    S.IsRightToLeft(null) ? MessageBoxOptions.RtlReading : 0);
450                            }
451                        }
452                }
453                else if (e.Effect == DragDropEffects.Move)
454                {
455                    //Create a task with the default settings
456                    Task task = new Task();
457                    foreach (string file in files)
458                    {
459                        FileSystemObjectTarget target;
460                        if (Directory.Exists(file))
461                            target = new FolderTarget();
462                        else
463                            target = new FileTarget();
464                        target.Path = file;
465
466                        task.Targets.Add(target);
467                    }
468
469                    //Add the task.
470                    Program.eraserClient.Tasks.Add(task);
471                }
472            }
473
474            DropTargetHelper.Drop(e.Data, new Point(e.X, e.Y), e.Effect);
475        }
476
477        /// <summary>
478        /// Occurs when the user right-clicks the list view.
479        /// </summary>
480        /// <param name="sender">The list view which generated this event.</param>
481        /// <param name="e">Event argument.</param>
482        private void schedulerMenu_Opening(object sender, CancelEventArgs e)
483        {
484            //If nothing's selected, show the Scheduler menu which just allows users to
485            //create new tasks (like from the toolbar)
486            if (scheduler.SelectedItems.Count == 0)
487            {
488                schedulerDefaultMenu.Show(schedulerMenu.Left, schedulerMenu.Top);
489                e.Cancel = true;
490                return;
491            }
492
493            bool aTaskNotQueued = false;
494            bool aTaskExecuting = false;
495            foreach (ListViewItem item in scheduler.SelectedItems)
496            {
497                Task task = (Task)item.Tag;
498                aTaskNotQueued = aTaskNotQueued || (!task.Queued && !task.Executing);
499                aTaskExecuting = aTaskExecuting || task.Executing;
500            }
501
502            runNowToolStripMenuItem.Enabled = aTaskNotQueued;
503            cancelTaskToolStripMenuItem.Enabled = aTaskExecuting;
504
505            editTaskToolStripMenuItem.Enabled = scheduler.SelectedItems.Count == 1 &&
506                !((Task)scheduler.SelectedItems[0].Tag).Executing &&
507                !((Task)scheduler.SelectedItems[0].Tag).Queued;
508            deleteTaskToolStripMenuItem.Enabled = !aTaskExecuting;
509        }
510
511        /// <summary>
512        /// Occurs when the user selects the New Task context menu item.
513        /// </summary>
514        /// <param name="sender">The menu which generated this event.</param>
515        /// <param name="e">Event argument.</param>
516        private void newTaskToolStripMenuItem_Click(object sender, EventArgs e)
517        {
518            using (TaskPropertiesForm form = new TaskPropertiesForm())
519            {
520                if (form.ShowDialog() == DialogResult.OK)
521                {
522                    Task task = form.Task;
523                    Program.eraserClient.Tasks.Add(task);
524                }
525            }
526        }
527
528        /// <summary>
529        /// Occurs whent the user selects the Run Now context menu item.
530        /// </summary>
531        /// <param name="sender">The menu which generated this event.</param>
532        /// <param name="e">Event argument.</param>
533        private void runNowToolStripMenuItem_Click(object sender, EventArgs e)
534        {
535            foreach (ListViewItem item in scheduler.SelectedItems)
536            {
537                //Queue the task
538                Task task = (Task)item.Tag;
539                if (!task.Executing && !task.Queued)
540                {
541                    Program.eraserClient.QueueTask(task);
542
543                    //Update the UI
544                    item.SubItems[1].Text = S._("Queued for execution");
545                }
546            }
547        }
548
549        /// <summary>
550        /// Occurs whent the user selects the Cancel Task context menu item.
551        /// </summary>
552        /// <param name="sender">The menu which generated this event.</param>
553        /// <param name="e">Event argument.</param>
554        private void cancelTaskToolStripMenuItem_Click(object sender, EventArgs e)
555        {
556            foreach (ListViewItem item in scheduler.SelectedItems)
557            {
558                //Queue the task
559                Task task = (Task)item.Tag;
560                if (task.Executing || task.Queued)
561                {
562                    task.Cancel();
563
564                    //Update the UI
565                    item.SubItems[1].Text = string.Empty;
566                }
567            }
568        }
569
570        /// <summary>
571        /// Occurs when the user selects the View Task Log context menu item.
572        /// </summary>
573        /// <param name="sender">The menu item which generated this event.</param>
574        /// <param name="e">Event argument.</param>
575        private void viewTaskLogToolStripMenuItem_Click(object sender, EventArgs e)
576        {
577            if (scheduler.SelectedItems.Count != 1)
578                return;
579
580            ListViewItem item = scheduler.SelectedItems[0];
581            using (LogForm form = new LogForm((Task)item.Tag))
582                form.ShowDialog();
583        }
584
585        /// <summary>
586        /// Occurs when the user selects the Edit Task context menu item.
587        /// </summary>
588        /// <param name="sender">The menu item which generated this event.</param>
589        /// <param name="e">Event argument.</param>
590        private void editTaskToolStripMenuItem_Click(object sender, EventArgs e)
591        {
592            if (scheduler.SelectedItems.Count != 1 ||
593                ((Task)scheduler.SelectedItems[0].Tag).Executing ||
594                ((Task)scheduler.SelectedItems[0].Tag).Queued)
595            {
596                return;
597            }
598
599            //Make sure that the task is not being executed, or else. This can
600            //be done in the Client library, but there will be no effect on the
601            //currently running task.
602            ListViewItem item = scheduler.SelectedItems[0];
603            Task task = (Task)item.Tag;
604            if (task.Executing)
605                return;
606
607            //Edit the task.
608            using (TaskPropertiesForm form = new TaskPropertiesForm())
609            {
610                form.Task = task;
611                if (form.ShowDialog() == DialogResult.OK)
612                {
613                    task = form.Task;
614
615                    //Update the list view
616                    UpdateTask(item);
617                }
618            }
619        }
620
621        /// <summary>
622        /// Occurs when the user selects the Delete Task context menu item.
623        /// </summary>
624        /// <param name="sender">The menu item which generated this event.</param>
625        /// <param name="e">Event argument.</param>
626        private void deleteTaskToolStripMenuItem_Click(object sender, EventArgs e)
627        {
628            DeleteSelectedTasks();
629        }
630
631        #region Item management
632        /// <summary>
633        /// Retrieves the ListViewItem for the given task.
634        /// </summary>
635        /// <param name="task">The task object whose list view entry is being sought.</param>
636        /// <returns>A ListViewItem for the given task object.</returns>
637        private ListViewItem GetTaskItem(Task task)
638        {
639            foreach (ListViewItem item in scheduler.Items)
640                if (item.Tag == task)
641                    return item;
642
643            return null;
644        }
645
646        /// <summary>
647        /// Maintains the position of the progress bar.
648        /// </summary>
649        private void PositionProgressBar()
650        {
651            if (schedulerProgress.Tag == null)
652                return;
653
654            Rectangle rect = ((ListViewItem)schedulerProgress.Tag).SubItems[2].Bounds;
655            rect.Offset(2, 2);
656            schedulerProgress.Location = rect.Location;
657            schedulerProgress.Size = rect.Size;
658        }
659
660        private void scheduler_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
661        {
662            e.DrawDefault = true;
663            if (schedulerProgress.Tag != null)
664                PositionProgressBar();
665        }
666
667        private void scheduler_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
668        {
669            e.DrawDefault = true;
670        }
671        #endregion
672    }
673}
Note: See TracBrowser for help on using the repository browser.