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

Revision 1634, 18.9 KB checked in by lowjoel, 5 years ago (diff)

Forward port from Eraser 6.0: Fixed regression introduced in r1490 where CreateControl? was called in MainForm?. CreateControl? however does not create handles when the control is not visible (delayed creation.) Since only SchedulerPanel? needs to have a handle created, we'll force creation of the handle there. In addition, remove an unnecessary call to UXThemeApi.UpdateControlTheme?.

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