source: trunk/eraser/Eraser/MainForm.cs @ 2339

Revision 2339, 12.8 KB checked in by lowjoel, 3 years ago (diff)

Fixed initialisation order. The settings panel should be initialised first because it loads settings and may raise a dialog for invalid settings. The scheduler may then be affected and be in a deadlock when it receives events from the Executor instance. Potential fix for http://bbs.heidi.ie/viewtopic.php?f=2&t=8207&p=24805#p24535.

  • 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.Drawing.Drawing2D;
28using System.Text;
29using System.Windows.Forms;
30using Eraser.Util;
31using Eraser.Manager;
32using Eraser.Properties;
33using System.IO;
34using System.Diagnostics;
35using System.Reflection;
36using System.Runtime.Serialization;
37
38namespace Eraser
39{
40    public partial class MainForm : Form
41    {
42        private BasePanel CurrPage;
43        private SchedulerPanel SchedulerPage;
44        private SettingsPanel SettingsPage;
45
46        public MainForm()
47        {
48            InitializeComponent();
49            SettingsPage = new SettingsPanel();
50            SchedulerPage = new SchedulerPanel();
51            contentPanel.Controls.Add(SchedulerPage);
52            contentPanel.Controls.Add(SettingsPage);
53            if (!IsHandleCreated)
54                CreateHandle();
55
56            Theming.ApplyTheme(this);
57            Theming.ApplyTheme(notificationMenu);
58
59            //For every task we need to register the Task Started and Task Finished
60            //event handlers for progress notifications
61            foreach (Task task in Program.eraserClient.Tasks)
62                OnTaskAdded(this, new TaskEventArgs(task));
63            Program.eraserClient.TaskAdded += OnTaskAdded;
64            Program.eraserClient.TaskDeleted += OnTaskDeleted;
65
66            //Check if we have tasks running already.
67            foreach (Task task in Program.eraserClient.Tasks)
68                if (task.Executing)
69                {
70                    OnTaskProcessing(task, EventArgs.Empty);
71                    break;
72                }
73
74            //Check the notification area context menu's minimise to tray item.
75            hideWhenMinimisedToolStripMenuItem.Checked = EraserSettings.Get().HideWhenMinimised;
76
77            //Set the docking style for each of the pages
78            SchedulerPage.Dock = DockStyle.Fill;
79            SettingsPage.Visible = false;
80
81            //Show the default page.
82            ChangePage(MainFormPage.Scheduler);
83        }
84
85        /// <summary>
86        /// Diplays the given title, message and icon as a system notification area balloon.
87        /// </summary>
88        /// <param name="title">The title of the balloon.</param>
89        /// <param name="message">The message to display.</param>
90        /// <param name="icon">The icon to show.</param>
91        public void ShowNotificationBalloon(string title, string message, ToolTipIcon icon)
92        {
93            notificationIcon.BalloonTipTitle = title;
94            notificationIcon.BalloonTipText = message;
95            notificationIcon.BalloonTipIcon = icon;
96            notificationIcon.ShowBalloonTip(0);
97        }
98
99        /// <summary>
100        /// Changes the active page displayed in the form.
101        /// </summary>
102        /// <param name="page">The new page to change to. No action is done when the
103        /// current page is the same as the new page requested</param>
104        public void ChangePage(MainFormPage page)
105        {
106            BasePanel oldPage = CurrPage;
107            switch (page)
108            {
109                case MainFormPage.Scheduler:
110                    CurrPage = SchedulerPage;
111                    break;
112                case MainFormPage.Settings:
113                    CurrPage = SettingsPage;
114                    break;
115            }
116
117            if (oldPage != CurrPage)
118            {
119                contentPanel.SuspendLayout();
120
121                //Hide the old page before showing the new one
122                if (oldPage != null)
123                    oldPage.Visible = false;
124
125                //If the page is not set to dock, we need to specify the dimensions of the page
126                //so it fits properly.
127                if (CurrPage.Dock == DockStyle.None)
128                {
129                    CurrPage.Anchor = AnchorStyles.Left | AnchorStyles.Right |
130                        AnchorStyles.Top;
131                    CurrPage.Left = 0;
132                    CurrPage.Top = 0;
133                    CurrPage.Width = contentPanel.Width;
134                }
135
136                //Show the new page then bring it to the top of the z-order.
137                CurrPage.Visible = true;
138                CurrPage.BringToFront();
139                contentPanel.ResumeLayout();
140            }
141        }
142
143        private static GraphicsPath CreateRoundRect(float X, float Y, float width,
144            float height, float radius)
145        {
146            GraphicsPath result = new GraphicsPath();
147
148            //Top line.
149            result.AddLine(X + radius, Y, X + width - 2 * radius, Y);
150
151            //Top-right corner
152            result.AddArc(X + width - 2 * radius, Y, 2 * radius, 2 * radius, 270, 90);
153
154            //Right line.
155            result.AddLine(X + width, Y + radius, X + width, Y + height - 2 * radius);
156
157            //Bottom-right corner
158            result.AddArc(X + width - 2 * radius, Y + height - 2 * radius, 2 * radius, 2 * radius, 0, 90);
159
160            //Bottom line.
161            result.AddLine(X + width - 2 * radius, Y + height, X + radius, Y + height);
162
163            //Bottom-left corner
164            result.AddArc(X, Y + height - 2 *radius, 2 * radius, 2 * radius, 90, 90);
165
166            //Left line
167            result.AddLine(X, Y + height - 2 * radius, X, Y + radius);
168
169            //Top-left corner
170            result.AddArc(X, Y, 2 * radius, 2 * radius, 180, 90);
171            result.CloseFigure();
172
173            return result;
174        }
175
176        private void DrawBackground(Graphics dc)
177        {
178            //Draw the base background
179            dc.FillRectangle(new SolidBrush(Color.FromArgb(unchecked((int)0xFF292929))),
180                new Rectangle(new Point(0, 0), Size));
181
182            //Then the side gradient
183            dc.FillRectangle(new LinearGradientBrush(new Rectangle(0, 0, 338, Math.Max(1, ClientSize.Height)),
184                    Color.FromArgb(unchecked((int)0xFF363636)),
185                    Color.FromArgb(unchecked((int)0xFF292929)), 0.0),
186                0, 0, 338, ClientSize.Height);
187
188            //Draw the top background
189            dc.FillRectangle(new SolidBrush(Color.FromArgb(unchecked((int)0xFF414141))),
190                new Rectangle(0, 0, ClientSize.Width, 32));
191
192            //The top gradient
193            dc.DrawImage(Properties.Resources.BackgroundGradient, new Point(0, 0));
194
195            dc.SmoothingMode = SmoothingMode.AntiAlias;
196            dc.FillPath(Brushes.White, CreateRoundRect(11, 74, contentPanel.Width + 8, ClientSize.Height - 85, 3));
197        }
198
199        private void MainForm_Paint(object sender, PaintEventArgs e)
200        {
201            e.Graphics.SetClip(new Rectangle(0, 0, Width, Height), CombineMode.Intersect);
202            DrawBackground(e.Graphics);
203        }
204
205        private void MainForm_Resize(object sender, EventArgs e)
206        {
207            if (WindowState != FormWindowState.Minimized)
208            {
209                Bitmap bmp = new Bitmap(Width, Height);
210                Graphics dc = Graphics.FromImage(bmp);
211                DrawBackground(dc);
212
213                CreateGraphics().DrawImage(bmp, new Point(0, 0));
214            }
215            else if (EraserSettings.Get().HideWhenMinimised)
216            {
217                Visible = false;
218            }
219        }
220
221        private void tbSchedule_Click(object sender, EventArgs e)
222        {
223            ChangePage(MainFormPage.Scheduler);
224        }
225
226        private void tbSettings_Click(object sender, EventArgs e)
227        {
228            ChangePage(MainFormPage.Settings);
229        }
230
231        private void newTaskToolStripMenuItem_Click(object sender, EventArgs e)
232        {
233            using (TaskPropertiesForm form = new TaskPropertiesForm())
234            {
235                if (form.ShowDialog() == DialogResult.OK)
236                {
237                    Task task = form.Task;
238                    Program.eraserClient.Tasks.Add(task);
239                }
240            }
241        }
242
243        private void exportTaskListToolStripMenuItem_Click(object sender, EventArgs e)
244        {
245            using (SaveFileDialog dialog = new SaveFileDialog())
246            {
247                dialog.Filter = "Eraser 6 task lists (*.ersx)|*.ersx";
248                dialog.DefaultExt = "ersx";
249                dialog.OverwritePrompt = true;
250
251                if (dialog.ShowDialog() == DialogResult.OK)
252                {
253                    using (FileStream stream = new FileStream(dialog.FileName,
254                        FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
255                    {
256                        Program.eraserClient.Tasks.SaveToStream(stream);
257                    }
258                }
259            }
260        }
261
262        private void importTaskListToolStripMenuItem_Click(object sender, EventArgs e)
263        {
264            using (OpenFileDialog dialog = new OpenFileDialog())
265            {
266                dialog.Filter = "Eraser 6 task lists (*.ersx)|*.ersx";
267                dialog.DefaultExt = "ersx";
268
269                if (dialog.ShowDialog() == DialogResult.OK)
270                {
271                    using (FileStream stream = new FileStream(dialog.FileName,
272                        FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
273                    {
274                        try
275                        {
276                            Program.eraserClient.Tasks.LoadFromStream(stream);
277                        }
278                        catch (InvalidDataException ex)
279                        {
280                            MessageBox.Show(S._("The task list could not be imported. The error " +
281                                "returned was: {0}", ex.Message), S._("Eraser"),
282                                MessageBoxButtons.OK, MessageBoxIcon.Error,
283                                MessageBoxDefaultButton.Button1,
284                                Localisation.IsRightToLeft(this) ?
285                                    MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0);
286                        }
287                    }
288                }
289            }
290        }
291
292        private void tbHelp_Click(object sender, EventArgs e)
293        {
294            try
295            {
296                Process.Start(Path.Combine(Path.GetDirectoryName(
297                        Assembly.GetEntryAssembly().Location),
298                    "Eraser Documentation.pdf"));
299            }
300            catch (Win32Exception ex)
301            {
302                MessageBox.Show(this, S._("The Eraser documentation file could not be " +
303                    "opened. Check that Adobe Reader installed and that your Eraser " +
304                    "install is not corrupt.\n\nThe error returned was: {0}", ex.Message),
305                    S._("Eraser"), MessageBoxButtons.OK, MessageBoxIcon.Error,
306                    MessageBoxDefaultButton.Button1, Localisation.IsRightToLeft(this) ?
307                        MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0);
308            }
309        }
310
311        private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
312        {
313            using (UpdateForm form = new UpdateForm())
314            {
315                form.ShowDialog();
316            }
317        }
318
319        private void aboutEraserToolStripMenuItem_Click(object sender, EventArgs e)
320        {
321            using (AboutForm form = new AboutForm(this))
322            {
323                form.ShowDialog();
324            }
325        }
326
327        private void eraserLogo_Click(object sender, EventArgs e)
328        {
329            try
330            {
331                Process.Start("http://eraser.heidi.ie/");
332            }
333            catch (Win32Exception ex)
334            {
335                //We've got an error executing the the browser to pass the links: show an error
336                //to the user.
337                MessageBox.Show(S._("Could not open the required web page. The error returned " +
338                    "was: {0}", ex.Message), S._("Eraser"), MessageBoxButtons.OK,
339                    MessageBoxIcon.Error, MessageBoxDefaultButton.Button1,
340                    Localisation.IsRightToLeft(this) ?
341                        MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0);
342            }
343        }
344
345        #region Task processing code (for notification area animation)
346        void OnTaskAdded(object sender, TaskEventArgs e)
347        {
348            e.Task.TaskStarted += OnTaskProcessing;
349            e.Task.TaskFinished += OnTaskProcessed;
350        }
351
352        void OnTaskDeleted(object sender, TaskEventArgs e)
353        {
354            e.Task.TaskStarted -= OnTaskProcessing;
355            e.Task.TaskFinished -= OnTaskProcessed;
356        }
357
358        void OnTaskProcessing(object sender, EventArgs e)
359        {
360            if (InvokeRequired)
361            {
362                Invoke((EventHandler)OnTaskProcessing, sender, e);
363                return;
364            }
365
366            Task task = (Task)sender;
367            string iconText = S._("Eraser") + " - " + S._("Processing:") + ' ' + task.UIText;
368            if (iconText.Length >= 64)
369                iconText = iconText.Remove(60) + "...";
370
371            ProcessingAnimationFrame = 0;
372            notificationIcon.Text = iconText;
373            notificationIconTimer.Enabled = true;
374        }
375
376        void OnTaskProcessed(object sender, EventArgs e)
377        {
378            if (InvokeRequired)
379            {
380                Invoke((EventHandler)OnTaskProcessed, sender, e);
381                return;
382            }
383
384            //Reset the notification area icon.
385            notificationIconTimer.Enabled = false;
386            if (notificationIcon.Icon != null)
387            {
388                ComponentResourceManager resources = new ComponentResourceManager(typeof(MainForm));
389                resources.ApplyResources(notificationIcon, "notificationIcon");
390            }
391        }
392
393        private void notificationIconTimer_Tick(object sender, EventArgs e)
394        {
395            notificationIcon.Icon = ProcessingAnimationFrames[ProcessingAnimationFrame++];
396            if (ProcessingAnimationFrame == ProcessingAnimationFrames.Length)
397                ProcessingAnimationFrame = 0;
398        }
399
400        private int ProcessingAnimationFrame;
401        private Icon[] ProcessingAnimationFrames = new Icon[] {
402            Resources.NotifyBusy1,
403            Resources.NotifyBusy2,
404            Resources.NotifyBusy3,
405            Resources.NotifyBusy4,
406            Resources.NotifyBusy5,
407            Resources.NotifyBusy4,
408            Resources.NotifyBusy3,
409            Resources.NotifyBusy2,
410            Resources.NotifyBusy1
411        };
412        #endregion
413
414        #region Minimise to tray code
415        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
416        {
417            if (EraserSettings.Get().HideWhenMinimised && e.CloseReason == CloseReason.UserClosing)
418            {
419                e.Cancel = true;
420                Visible = false;
421            }
422        }
423
424        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
425        {
426            Application.Exit();
427        }
428
429        private void MainForm_VisibleChanged(object sender, EventArgs e)
430        {
431            if (Visible)
432            {
433                WindowState = FormWindowState.Normal;
434                Activate();
435            }
436        }
437
438        private void openToolStripMenuItem_Click(object sender, EventArgs e)
439        {
440            Visible = true;
441        }
442
443        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
444        {
445            Application.Exit();
446        }
447
448        private void hideWhenMinimiseToolStripMenuItem_Click(object sender, EventArgs e)
449        {
450            EraserSettings.Get().HideWhenMinimised =
451                hideWhenMinimisedToolStripMenuItem.Checked;
452        }
453        #endregion
454    }
455
456    public enum MainFormPage
457    {
458        Scheduler = 0,
459        Settings
460    }
461}
Note: See TracBrowser for help on using the repository browser.