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

Revision 2958, 21.0 KB checked in by gtrant, 2 months ago (diff)

Updated Copyright to 2014
Updated versions to 6.2
Fix for date stamp on erased artefacts

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