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

Revision 2327, 19.3 KB checked in by lowjoel, 3 years ago (diff)

Merged revision(s) 2326 from trunk/eraser: The title of the balloon tip should be "Task Completed" and not "Task Executed" for clarity. This was suggested by Jackjack in #388.
Fixes #388: balloon tips wording post wiping

  • 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.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        {
44            InitializeComponent();
45            UXThemeApi.UpdateControlTheme(schedulerDefaultMenu);
46            if (!IsHandleCreated)
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.Executing)
95                task_TaskStarted(this, new TaskEventArgs(task));
96            else if (task.Schedule is RecurringSchedule)
97                item.SubItems[1].Text = ((task.Schedule as RecurringSchedule).NextRun.
98                    ToString("f", CultureInfo.CurrentCulture));
99            else if (task.Schedule == Schedule.RunNow || task.Schedule == Schedule.RunManually)
100                item.SubItems[1].Text = S._("Not queued");
101            else
102                item.SubItems[1].Text = task.Schedule.UIText;
103
104            //Set the group of the task.
105            CategorizeTask(task, item);
106        }
107
108        private void CategorizeTask(Task task)
109        {
110            CategorizeTask(task, GetTaskItem(task));
111        }
112
113        private void CategorizeTask(Task task, ListViewItem item)
114        {
115            if (task.Schedule == Schedule.RunNow || task.Schedule == Schedule.RunManually)
116                item.Group = scheduler.Groups["manual"];
117            else if (task.Schedule == Schedule.RunOnRestart)
118                item.Group = scheduler.Groups["restart"];
119            else
120                item.Group = scheduler.Groups["recurring"];
121        }
122
123        /// <summary>
124        /// Handles the Task Added event.
125        /// </summary>
126        private void TaskAdded(object sender, TaskEventArgs e)
127        {
128            if (InvokeRequired)
129            {
130                Invoke(new EventHandler<TaskEventArgs>(TaskAdded), sender, e);
131                return;
132            }
133
134            //Display a balloon notification if the parent frame has been minimised.
135            MainForm parent = (MainForm)FindForm();
136            if (parent != null && (parent.WindowState == FormWindowState.Minimized || !parent.Visible))
137            {
138                parent.ShowNotificationBalloon(S._("New task added"), S._("{0} " +
139                    "has just been added to the list of tasks.", e.Task.UIText),
140                    ToolTipIcon.Info);
141            }
142
143            DisplayTask(e.Task);
144        }
145
146        private void DeleteSelectedTasks()
147        {
148            if (MessageBox.Show(this, S._("Are you sure you want to delete the selected tasks?"),
149                   S._("Eraser"), MessageBoxButtons.YesNo, MessageBoxIcon.Question,
150                   MessageBoxDefaultButton.Button1,
151                   S.IsRightToLeft(this) ? MessageBoxOptions.RtlReading : 0) != DialogResult.Yes)
152            {
153                return;
154            }
155
156            foreach (ListViewItem item in scheduler.SelectedItems)
157            {
158                Task task = (Task)item.Tag;
159                if (!task.Executing)
160                    Program.eraserClient.Tasks.Remove(task);
161            }
162        }
163
164        /// <summary>
165        /// Handles the task deleted event.
166        /// </summary>
167        private void TaskDeleted(object sender, TaskEventArgs e)
168        {
169            if (InvokeRequired)
170            {
171                Invoke(new EventHandler<TaskEventArgs>(TaskDeleted), sender, e);
172                return;
173            }
174
175            foreach (ListViewItem item in scheduler.Items)
176                if (((Task)item.Tag) == e.Task)
177                {
178                    scheduler.Items.Remove(item);
179                    break;
180                }
181
182            PositionProgressBar();
183        }
184
185        /// <summary>
186        /// Handles the task start event.
187        /// </summary>
188        /// <param name="e">The task event object.</param>
189        void task_TaskStarted(object sender, TaskEventArgs e)
190        {
191            if (InvokeRequired)
192            {
193                Invoke(new EventHandler<TaskEventArgs>(task_TaskStarted), sender, e);
194                return;
195            }
196
197            //Get the list view item
198            ListViewItem item = GetTaskItem(e.Task);
199
200            //Update the status.
201            item.SubItems[1].Text = S._("Running...");
202
203            //Show the progress bar
204            schedulerProgress.Tag = item;
205            schedulerProgress.Visible = true;
206            schedulerProgress.Value = 0;
207            PositionProgressBar();
208        }
209
210        /// <summary>
211        /// Handles the progress event by the task.
212        /// </summary>
213        void task_ProgressChanged(object sender, TaskProgressEventArgs e)
214        {
215            //Make sure we handle the event in the main thread as this requires
216            //GUI calls.
217            if (InvokeRequired)
218            {
219                Invoke(new EventHandler<TaskProgressEventArgs>(task_ProgressChanged), sender, e);
220                return;
221            }
222
223            //Update the progress bar
224            schedulerProgress.Value = (int)(e.OverallProgress * 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(new 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 completed"), 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            if (e.Data.GetDataPresent("DragImageBits"))
407                DropTargetHelper.DragEnter(this, e.Data, new Point(e.X, e.Y), e.Effect,
408                    descriptionMessage, descriptionInsert);
409        }
410
411        private void scheduler_DragLeave(object sender, EventArgs e)
412        {
413            DropTargetHelper.DragLeave(this);
414        }
415
416        private void scheduler_DragOver(object sender, DragEventArgs e)
417        {
418            DropTargetHelper.DragOver(new Point(e.X, e.Y), e.Effect);
419        }
420
421        /// <summary>
422        /// Occurs when the user drops a file into the scheduler.
423        /// </summary>
424        private void scheduler_DragDrop(object sender, DragEventArgs e)
425        {
426            DropTargetHelper.Drop(e.Data, new Point(e.X, e.Y), e.Effect);
427
428            if (!e.Data.GetDataPresent(DataFormats.FileDrop))
429                e.Effect = DragDropEffects.None;
430            else
431            {
432                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
433
434                //Determine whether we are importing a task list or dragging files for
435                //erasure.
436                if (e.Effect == DragDropEffects.Copy)
437                {
438                    foreach (string file in files)
439                        using (FileStream stream = new FileStream(file, FileMode.Open,
440                            FileAccess.Read, FileShare.Read))
441                        {
442                            try
443                            {
444                                Program.eraserClient.Tasks.LoadFromStream(stream);
445                            }
446                            catch (SerializationException ex)
447                            {
448                                MessageBox.Show(S._("Could not import task list from {0}. The error " +
449                                    "returned was: {1}", file, ex.Message), S._("Eraser"),
450                                    MessageBoxButtons.OK, MessageBoxIcon.Error,
451                                    MessageBoxDefaultButton.Button1,
452                                    S.IsRightToLeft(null) ? MessageBoxOptions.RtlReading : 0);
453                            }
454                        }
455                }
456                else if (e.Effect == DragDropEffects.Move)
457                {
458                    //Create a task with the default settings
459                    Task task = new Task();
460                    foreach (string file in files)
461                    {
462                        FileSystemObjectTarget target;
463                        if (Directory.Exists(file))
464                            target = new FolderTarget();
465                        else
466                            target = new FileTarget();
467                        target.Path = file;
468
469                        task.Targets.Add(target);
470                    }
471
472                    //Confirm the erasure.
473                    if (MessageBox.Show(S._("Are you sure you wish to erase the selected files?"),
474                        S._("Eraser"), MessageBoxButtons.YesNo, MessageBoxIcon.Question,
475                        MessageBoxDefaultButton.Button2,
476                        S.IsRightToLeft(null) ? MessageBoxOptions.RtlReading : 0) == DialogResult.Yes)
477                    {
478                        //Add the task.
479                        Program.eraserClient.Tasks.Add(task);
480                    }
481                }
482            }
483        }
484
485        /// <summary>
486        /// Occurs when the user right-clicks the list view.
487        /// </summary>
488        /// <param name="sender">The list view which generated this event.</param>
489        /// <param name="e">Event argument.</param>
490        private void schedulerMenu_Opening(object sender, CancelEventArgs e)
491        {
492            //If nothing's selected, show the Scheduler menu which just allows users to
493            //create new tasks (like from the toolbar)
494            if (scheduler.SelectedItems.Count == 0)
495            {
496                schedulerDefaultMenu.Show(schedulerMenu.Left, schedulerMenu.Top);
497                e.Cancel = true;
498                return;
499            }
500
501            bool aTaskNotQueued = false;
502            bool aTaskExecuting = false;
503            foreach (ListViewItem item in scheduler.SelectedItems)
504            {
505                Task task = (Task)item.Tag;
506                aTaskNotQueued = aTaskNotQueued || (!task.Queued && !task.Executing);
507                aTaskExecuting = aTaskExecuting || task.Executing;
508            }
509
510            runNowToolStripMenuItem.Enabled = aTaskNotQueued;
511            cancelTaskToolStripMenuItem.Enabled = aTaskExecuting;
512
513            editTaskToolStripMenuItem.Enabled = scheduler.SelectedItems.Count == 1 &&
514                !((Task)scheduler.SelectedItems[0].Tag).Executing &&
515                !((Task)scheduler.SelectedItems[0].Tag).Queued;
516            deleteTaskToolStripMenuItem.Enabled = !aTaskExecuting;
517        }
518
519        /// <summary>
520        /// Occurs when the user selects the New Task context menu item.
521        /// </summary>
522        /// <param name="sender">The menu which generated this event.</param>
523        /// <param name="e">Event argument.</param>
524        private void newTaskToolStripMenuItem_Click(object sender, EventArgs e)
525        {
526            using (TaskPropertiesForm form = new TaskPropertiesForm())
527            {
528                if (form.ShowDialog() == DialogResult.OK)
529                {
530                    Task task = form.Task;
531                    Program.eraserClient.Tasks.Add(task);
532                }
533            }
534        }
535
536        /// <summary>
537        /// Occurs whent the user selects the Run Now context menu item.
538        /// </summary>
539        /// <param name="sender">The menu which generated this event.</param>
540        /// <param name="e">Event argument.</param>
541        private void runNowToolStripMenuItem_Click(object sender, EventArgs e)
542        {
543            foreach (ListViewItem item in scheduler.SelectedItems)
544            {
545                //Queue the task
546                Task task = (Task)item.Tag;
547                if (!task.Executing && !task.Queued)
548                {
549                    Program.eraserClient.QueueTask(task);
550
551                    //Update the UI
552                    item.SubItems[1].Text = S._("Queued for execution");
553                }
554            }
555        }
556
557        /// <summary>
558        /// Occurs whent the user selects the Cancel Task context menu item.
559        /// </summary>
560        /// <param name="sender">The menu which generated this event.</param>
561        /// <param name="e">Event argument.</param>
562        private void cancelTaskToolStripMenuItem_Click(object sender, EventArgs e)
563        {
564            foreach (ListViewItem item in scheduler.SelectedItems)
565            {
566                //Queue the task
567                Task task = (Task)item.Tag;
568                if (task.Executing || task.Queued)
569                {
570                    task.Cancel();
571
572                    //Update the UI
573                    item.SubItems[1].Text = string.Empty;
574                }
575            }
576        }
577
578        /// <summary>
579        /// Occurs when the user selects the View Task Log context menu item.
580        /// </summary>
581        /// <param name="sender">The menu item which generated this event.</param>
582        /// <param name="e">Event argument.</param>
583        private void viewTaskLogToolStripMenuItem_Click(object sender, EventArgs e)
584        {
585            if (scheduler.SelectedItems.Count != 1)
586                return;
587
588            ListViewItem item = scheduler.SelectedItems[0];
589            using (LogForm form = new LogForm((Task)item.Tag))
590                form.ShowDialog();
591        }
592
593        /// <summary>
594        /// Occurs when the user selects the Edit Task context menu item.
595        /// </summary>
596        /// <param name="sender">The menu item which generated this event.</param>
597        /// <param name="e">Event argument.</param>
598        private void editTaskToolStripMenuItem_Click(object sender, EventArgs e)
599        {
600            if (scheduler.SelectedItems.Count != 1 ||
601                ((Task)scheduler.SelectedItems[0].Tag).Executing ||
602                ((Task)scheduler.SelectedItems[0].Tag).Queued)
603            {
604                return;
605            }
606
607            //Make sure that the task is not being executed, or else. This can
608            //be done in the Client library, but there will be no effect on the
609            //currently running task.
610            ListViewItem item = scheduler.SelectedItems[0];
611            Task task = (Task)item.Tag;
612            if (task.Executing)
613                return;
614
615            //Edit the task.
616            using (TaskPropertiesForm form = new TaskPropertiesForm())
617            {
618                form.Task = task;
619                if (form.ShowDialog() == DialogResult.OK)
620                {
621                    task = form.Task;
622
623                    //Update the list view
624                    UpdateTask(item);
625                }
626            }
627        }
628
629        /// <summary>
630        /// Occurs when the user selects the Delete Task context menu item.
631        /// </summary>
632        /// <param name="sender">The menu item which generated this event.</param>
633        /// <param name="e">Event argument.</param>
634        private void deleteTaskToolStripMenuItem_Click(object sender, EventArgs e)
635        {
636            DeleteSelectedTasks();
637        }
638
639        #region Item management
640        /// <summary>
641        /// Retrieves the ListViewItem for the given task.
642        /// </summary>
643        /// <param name="task">The task object whose list view entry is being sought.</param>
644        /// <returns>A ListViewItem for the given task object.</returns>
645        private ListViewItem GetTaskItem(Task task)
646        {
647            foreach (ListViewItem item in scheduler.Items)
648                if (item.Tag == task)
649                    return item;
650
651            return null;
652        }
653
654        /// <summary>
655        /// Maintains the position of the progress bar.
656        /// </summary>
657        private void PositionProgressBar()
658        {
659            if (schedulerProgress.Tag == null)
660                return;
661
662            Rectangle rect = ((ListViewItem)schedulerProgress.Tag).SubItems[2].Bounds;
663            schedulerProgress.Location = rect.Location;
664            schedulerProgress.Size = rect.Size;
665        }
666
667        private void scheduler_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
668        {
669            e.DrawDefault = true;
670            if (schedulerProgress.Tag != null)
671                PositionProgressBar();
672        }
673
674        private void scheduler_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
675        {
676            e.DrawDefault = true;
677        }
678        #endregion
679    }
680}
Note: See TracBrowser for help on using the repository browser.