source: trunk/eraser/Eraser/SchedulerPanel.cs @ 1964

Revision 1952, 18.9 KB checked in by lowjoel, 4 years ago (diff)

Forward port from Eraser 6.0: Allow non-Explorer drag/drop file operations to complete, allowing files to be dropped from older file explorers (e.g. Total Commander). Fixes #341

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