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

Revision 1697, 19.0 KB checked in by lowjoel, 4 years ago (diff)

The progress bar on the scheduler panel is off by (2, 2); offset it by that amount so that the progress bar is precisely over the task.

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