source: branches/eraser6/6.0/Eraser/SchedulerPanel.cs @ 1734

Revision 1734, 19.1 KB checked in by lowjoel, 4 years ago (diff)

Fixed potential regression in r1721 and r1633 where we added in calls to CreateHandle? so that window handles are created immediately for calls to InvokeRequired?. Move those calls to the start of the constructor so that the handle will not be already created and throw an InvalidOperationException?

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