~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusArea.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// 
 
2
// StatusArea.cs
 
3
//  
 
4
// Author:
 
5
//       Mike KrĆ¼ger <mkrueger@xamarin.com>
 
6
// 
 
7
// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com)
 
8
// 
 
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
10
// of this software and associated documentation files (the "Software"), to deal
 
11
// in the Software without restriction, including without limitation the rights
 
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
13
// copies of the Software, and to permit persons to whom the Software is
 
14
// furnished to do so, subject to the following conditions:
 
15
// 
 
16
// The above copyright notice and this permission notice shall be included in
 
17
// all copies or substantial portions of the Software.
 
18
// 
 
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
25
// THE SOFTWARE.
 
26
using System;
 
27
using System.Diagnostics;
 
28
using Gtk;
 
29
using MonoDevelop.Components;
 
30
using Cairo;
 
31
using MonoDevelop.Ide;
 
32
using MonoDevelop.Ide.Gui;
 
33
using MonoDevelop.Ide.Tasks;
 
34
using System.Collections.Generic;
 
35
using MonoDevelop.Ide.CodeCompletion;
 
36
using MonoDevelop.Core;
 
37
using MonoDevelop.Ide.Gui.Components;
 
38
 
 
39
using StockIcons = MonoDevelop.Ide.Gui.Stock;
 
40
 
 
41
namespace MonoDevelop.Components.MainToolbar
 
42
{
 
43
        class StatusArea : EventBox, StatusBar, Animatable
 
44
        {
 
45
                struct Message
 
46
                {
 
47
                        public string Text;
 
48
                        public IconId Icon;
 
49
                        public bool IsMarkup;
 
50
 
 
51
                        public Message (IconId icon, string text, bool markup)
 
52
                        {
 
53
                                Text = text;
 
54
                                Icon = icon;
 
55
                                IsMarkup = markup;
 
56
                        }
 
57
                }
 
58
 
 
59
                public struct RenderArg
 
60
                {
 
61
                        public Gdk.Rectangle Allocation { get; set; }
 
62
                        public float         BuildAnimationProgress { get; set; }
 
63
                        public float         BuildAnimationOpacity { get; set; }
 
64
                        public Gdk.Rectangle ChildAllocation { get; set; }
 
65
                        public Gdk.Pixbuf    CurrentPixbuf { get; set; }
 
66
                        public string        CurrentText { get; set; }
 
67
                        public bool          CurrentTextIsMarkup { get; set; }
 
68
                        public float         ErrorAnimationProgress { get; set; }
 
69
                        public float         HoverProgress { get; set; }
 
70
                        public string        LastText { get; set; }
 
71
                        public bool          LastTextIsMarkup { get; set; }
 
72
                        public Gdk.Pixbuf    LastPixbuf { get; set; }
 
73
                        public Gdk.Point     MousePosition { get; set; }
 
74
                        public Pango.Context Pango { get; set; }
 
75
                        public float         ProgressBarAlpha { get; set; }
 
76
                        public float         ProgressBarFraction { get; set; }
 
77
                        public bool          ShowProgressBar { get; set; }
 
78
                        public float         TextAnimationProgress { get; set; }
 
79
                }
 
80
 
 
81
                StatusAreaTheme theme;
 
82
                RenderArg renderArg;
 
83
 
 
84
                HBox contentBox = new HBox (false, 8);
 
85
 
 
86
                StatusAreaSeparator statusIconSeparator;
 
87
                Gtk.Widget buildResultWidget;
 
88
 
 
89
                readonly HBox messageBox = new HBox ();
 
90
                internal readonly HBox statusIconBox = new HBox ();
 
91
                Alignment mainAlign;
 
92
 
 
93
                uint animPauseHandle;
 
94
 
 
95
                MouseTracker tracker;
 
96
 
 
97
                AnimatedIcon iconAnimation;
 
98
                IconId currentIcon;
 
99
                static Pad sourcePad;
 
100
                IDisposable currentIconAnimation;
 
101
 
 
102
                bool errorAnimPending;
 
103
 
 
104
                MainStatusBarContextImpl mainContext;
 
105
                StatusBarContextImpl activeContext;
 
106
                bool progressBarVisible;
 
107
 
 
108
                Queue<Message> messageQueue;
 
109
                
 
110
                public StatusBar MainContext {
 
111
                        get { return mainContext; }
 
112
                }
 
113
 
 
114
                public int MaxWidth { get; set; }
 
115
 
 
116
                public StatusArea ()
 
117
                {
 
118
                        theme = new StatusAreaTheme ();
 
119
                        renderArg = new RenderArg ();
 
120
 
 
121
                        mainContext = new MainStatusBarContextImpl (this);
 
122
                        activeContext = mainContext;
 
123
                        contexts.Add (mainContext);
 
124
 
 
125
                        VisibleWindow = false;
 
126
                        NoShowAll = true;
 
127
                        WidgetFlags |= Gtk.WidgetFlags.AppPaintable;
 
128
 
 
129
                        statusIconBox.BorderWidth = 0;
 
130
                        statusIconBox.Spacing = 3;
 
131
 
 
132
                        Action<bool> animateProgressBar = 
 
133
                                showing => this.Animate ("ProgressBarFade",
 
134
                                                         easing: Easing.CubicInOut,
 
135
                                                         start: renderArg.ProgressBarAlpha,
 
136
                                                         end: showing ? 1.0f : 0.0f,
 
137
                                                         callback: val => renderArg.ProgressBarAlpha = val);
 
138
 
 
139
                        ProgressBegin += delegate {
 
140
                                renderArg.ShowProgressBar = true;
 
141
//                              StartBuildAnimation ();
 
142
                                renderArg.ProgressBarFraction = 0;
 
143
                                QueueDraw ();
 
144
                                animateProgressBar (true);
 
145
                        };
 
146
                        
 
147
                        ProgressEnd += delegate {
 
148
                                renderArg.ShowProgressBar = false;
 
149
//                              StopBuildAnimation ();
 
150
                                QueueDraw ();
 
151
                                animateProgressBar (false);
 
152
                        };
 
153
 
 
154
                        ProgressFraction += delegate(object sender, FractionEventArgs e) {
 
155
                                renderArg.ProgressBarFraction = (float)e.Work;
 
156
                                QueueDraw ();
 
157
                        };
 
158
 
 
159
                        contentBox.PackStart (messageBox, true, true, 0);
 
160
                        contentBox.PackEnd (statusIconBox, false, false, 0);
 
161
                        contentBox.PackEnd (statusIconSeparator = new StatusAreaSeparator (), false, false, 0);
 
162
                        contentBox.PackEnd (buildResultWidget = CreateBuildResultsWidget (Orientation.Horizontal), false, false, 0);
 
163
 
 
164
                        mainAlign = new Alignment (0, 0.5f, 1, 0);
 
165
                        mainAlign.LeftPadding = 12;
 
166
                        mainAlign.RightPadding = 8;
 
167
                        mainAlign.Add (contentBox);
 
168
                        Add (mainAlign);
 
169
 
 
170
                        mainAlign.ShowAll ();
 
171
                        statusIconBox.Hide ();
 
172
                        statusIconSeparator.Hide ();
 
173
                        buildResultWidget.Hide ();
 
174
                        Show ();
 
175
 
 
176
                        this.ButtonPressEvent += delegate {
 
177
                                if (sourcePad != null)
 
178
                                        sourcePad.BringToFront (true);
 
179
                        };
 
180
 
 
181
                        statusIconBox.Shown += delegate {
 
182
                                UpdateSeparators ();
 
183
                        };
 
184
 
 
185
                        statusIconBox.Hidden += delegate {
 
186
                                UpdateSeparators ();
 
187
                        };
 
188
 
 
189
                        messageQueue = new Queue<Message> ();
 
190
 
 
191
                        tracker = new MouseTracker(this);
 
192
                        tracker.MouseMoved += (sender, e) => QueueDraw ();
 
193
                        tracker.HoveredChanged += (sender, e) => {
 
194
                                this.Animate ("Hovered",
 
195
                                              easing: Easing.SinInOut,
 
196
                                              start: renderArg.HoverProgress,
 
197
                                              end: tracker.Hovered ? 1.0f : 0.0f,
 
198
                                              callback: x => renderArg.HoverProgress = x);
 
199
                        };
 
200
 
 
201
                        IdeApp.FocusIn += delegate {
 
202
                                // If there was an error while the application didn't have the focus,
 
203
                                // trigger the error animation again when it gains the focus
 
204
                                if (errorAnimPending) {
 
205
                                        errorAnimPending = false;
 
206
                                        TriggerErrorAnimation ();
 
207
                                }
 
208
                        };
 
209
                }
 
210
 
 
211
                protected override void OnDestroyed ()
 
212
                {
 
213
                        if (theme != null)
 
214
                                theme.Dispose ();
 
215
                        base.OnDestroyed ();
 
216
                }
 
217
 
 
218
                void StartBuildAnimation ()
 
219
                {
 
220
                        this.Animate ("Build",
 
221
                                      val => renderArg.BuildAnimationProgress = val,
 
222
                                      length: 5000,
 
223
                                      repeat: () => true);
 
224
 
 
225
                        this.Animate ("BuildOpacity",
 
226
                                      start: renderArg.BuildAnimationOpacity,
 
227
                                      end: 1.0f,
 
228
                                      callback: x => renderArg.BuildAnimationOpacity = x);
 
229
                }
 
230
 
 
231
                void StopBuildAnimation ()
 
232
                {
 
233
                        this.Animate ("BuildOpacity",
 
234
                                      start: renderArg.BuildAnimationOpacity,
 
235
                                      end: 0.0f,
 
236
                                      callback: x => renderArg.BuildAnimationOpacity = x,
 
237
                                      finished: (val, aborted) => { if (!aborted) this.AbortAnimation ("Build"); });
 
238
                }
 
239
 
 
240
                protected override void OnSizeAllocated (Gdk.Rectangle allocation)
 
241
                {
 
242
                        if (MaxWidth > 0 && allocation.Width > MaxWidth) {
 
243
                                allocation = new Gdk.Rectangle (allocation.X + (allocation.Width - MaxWidth) / 2, allocation.Y, MaxWidth, allocation.Height);
 
244
                        }
 
245
                        base.OnSizeAllocated (allocation);
 
246
                }
 
247
 
 
248
                void TriggerErrorAnimation ()
 
249
                {
 
250
/* Hack for a compiler error - csc crashes on this:
 
251
                        this.Animate (name: "statusAreaError",
 
252
                                      length: 700,
 
253
                                      callback: val => renderArg.ErrorAnimationProgress = val);
 
254
*/
 
255
                        this.Animate ("statusAreaError",
 
256
                                      val => renderArg.ErrorAnimationProgress = val,
 
257
                                      length: 900);
 
258
                }
 
259
 
 
260
                void UpdateSeparators ()
 
261
                {
 
262
                        statusIconSeparator.Visible = statusIconBox.Visible && buildResultWidget.Visible;
 
263
                }
 
264
 
 
265
                public Widget CreateBuildResultsWidget (Orientation orientation)
 
266
                {
 
267
                        EventBox ebox = new EventBox ();
 
268
 
 
269
                        Gtk.Box box;
 
270
                        if (orientation == Orientation.Horizontal)
 
271
                                box = new HBox ();
 
272
                        else
 
273
                                box = new VBox ();
 
274
                        box.Spacing = 3;
 
275
                        
 
276
                        Gdk.Pixbuf errorIcon = ImageService.GetPixbuf (StockIcons.Error, IconSize.Menu);
 
277
                        Gdk.Pixbuf noErrorIcon = ImageService.MakeGrayscale (errorIcon); // creates a new pixbuf instance
 
278
                        Gdk.Pixbuf warningIcon = ImageService.GetPixbuf (StockIcons.Warning, IconSize.Menu);
 
279
                        Gdk.Pixbuf noWarningIcon = ImageService.MakeGrayscale (warningIcon); // creates a new pixbuf instance
 
280
                        
 
281
                        Gtk.Image errorImage = new Gtk.Image (errorIcon);
 
282
                        Gtk.Image warningImage = new Gtk.Image (warningIcon);
 
283
                        
 
284
                        box.PackStart (errorImage, false, false, 0);
 
285
                        Label errors = new Gtk.Label ();
 
286
                        box.PackStart (errors, false, false, 0);
 
287
                        
 
288
                        box.PackStart (warningImage, false, false, 0);
 
289
                        Label warnings = new Gtk.Label ();
 
290
                        box.PackStart (warnings, false, false, 0);
 
291
                        box.NoShowAll = true;
 
292
                        box.Show ();
 
293
                        
 
294
                        TaskEventHandler updateHandler = delegate {
 
295
                                int ec=0, wc=0;
 
296
                                foreach (Task t in TaskService.Errors) {
 
297
                                        if (t.Severity == TaskSeverity.Error)
 
298
                                                ec++;
 
299
                                        else if (t.Severity == TaskSeverity.Warning)
 
300
                                                wc++;
 
301
                                }
 
302
                                errors.Visible = ec > 0;
 
303
                                errors.Text = ec.ToString ();
 
304
                                errorImage.Visible = ec > 0;
 
305
 
 
306
                                warnings.Visible = wc > 0;
 
307
                                warnings.Text = wc.ToString ();
 
308
                                warningImage.Visible = wc > 0;
 
309
                                ebox.Visible = ec > 0 || wc > 0;
 
310
                                UpdateSeparators ();
 
311
                        };
 
312
                        
 
313
                        updateHandler (null, null);
 
314
                        
 
315
                        TaskService.Errors.TasksAdded += updateHandler;
 
316
                        TaskService.Errors.TasksRemoved += updateHandler;
 
317
                        
 
318
                        box.Destroyed += delegate {
 
319
                                noErrorIcon.Dispose ();
 
320
                                noWarningIcon.Dispose ();
 
321
                                TaskService.Errors.TasksAdded -= updateHandler;
 
322
                                TaskService.Errors.TasksRemoved -= updateHandler;
 
323
                        };
 
324
 
 
325
                        ebox.VisibleWindow = false;
 
326
                        ebox.Add (box);
 
327
                        ebox.ShowAll ();
 
328
                        ebox.ButtonReleaseEvent += delegate {
 
329
                                var pad = IdeApp.Workbench.GetPad<MonoDevelop.Ide.Gui.Pads.ErrorListPad> ();
 
330
                                pad.BringToFront ();
 
331
                        };
 
332
 
 
333
                        errors.Visible = false;
 
334
                        errorImage.Visible = false;
 
335
                        warnings.Visible = false;
 
336
                        warningImage.Visible = false;
 
337
 
 
338
                        return ebox;
 
339
                }
 
340
 
 
341
                protected override void OnRealized ()
 
342
                {
 
343
                        base.OnRealized ();
 
344
                        ModifyText (StateType.Normal, Styles.StatusBarTextColor.ToGdkColor ());
 
345
                        ModifyFg (StateType.Normal, Styles.StatusBarTextColor.ToGdkColor ());
 
346
                }
 
347
 
 
348
                protected override void OnSizeRequested (ref Requisition requisition)
 
349
                {
 
350
                        requisition.Height = 32;
 
351
                        base.OnSizeRequested (ref requisition);
 
352
                }
 
353
 
 
354
                protected override bool OnExposeEvent (Gdk.EventExpose evnt)
 
355
                {
 
356
                        using (var context = Gdk.CairoHelper.Create (evnt.Window)) {
 
357
                                renderArg.Allocation            = Allocation;
 
358
                                renderArg.ChildAllocation       = messageBox.Allocation;
 
359
                                renderArg.MousePosition         = tracker.MousePosition;
 
360
                                renderArg.Pango                 = PangoContext;
 
361
 
 
362
                                theme.Render (context, renderArg);
 
363
                        }
 
364
                        return base.OnExposeEvent (evnt);
 
365
                }
 
366
 
 
367
 
 
368
                #region StatusBar implementation
 
369
 
 
370
                public void ShowCaretState (int line, int column, int selectedChars, bool isInInsertMode)
 
371
                {
 
372
                        throw new NotImplementedException ();
 
373
                }
 
374
 
 
375
                public void ClearCaretState ()
 
376
                {
 
377
                        throw new NotImplementedException ();
 
378
                }
 
379
 
 
380
                public StatusBarIcon ShowStatusIcon (Gdk.Pixbuf pixbuf)
 
381
                {
 
382
                        DispatchService.AssertGuiThread ();
 
383
                        StatusIcon icon = new StatusIcon (this, pixbuf);
 
384
                        statusIconBox.PackEnd (icon.box);
 
385
                        statusIconBox.ShowAll ();
 
386
                        return icon;
 
387
                }
 
388
                
 
389
                void HideStatusIcon (StatusIcon icon)
 
390
                {
 
391
                        statusIconBox.Remove (icon.EventBox);
 
392
                        if (statusIconBox.Children.Length == 0)
 
393
                                statusIconBox.Hide ();
 
394
                        icon.EventBox.Destroy ();
 
395
                }
 
396
 
 
397
                List<StatusBarContextImpl> contexts = new List<StatusBarContextImpl> ();
 
398
                public StatusBarContext CreateContext ()
 
399
                {
 
400
                        StatusBarContextImpl ctx = new StatusBarContextImpl (this);
 
401
                        contexts.Add (ctx);
 
402
                        return ctx;
 
403
                }
 
404
 
 
405
                public void ShowReady ()
 
406
                {
 
407
                        ShowMessage ("");
 
408
                }
 
409
 
 
410
                public void SetMessageSourcePad (Pad pad)
 
411
                {
 
412
                        sourcePad = pad;
 
413
                }
 
414
 
 
415
                public bool HasResizeGrip {
 
416
                        get;
 
417
                        set;
 
418
                }
 
419
 
 
420
                public class StatusIcon : StatusBarIcon
 
421
                {
 
422
                        StatusArea statusBar;
 
423
                        internal EventBox box;
 
424
                        string tip;
 
425
                        DateTime alertEnd;
 
426
                        Gdk.Pixbuf icon;
 
427
                        uint animation;
 
428
                        Gtk.Image image;
 
429
                        
 
430
                        int astep;
 
431
                        Gdk.Pixbuf[] images;
 
432
                        TooltipPopoverWindow tooltipWindow;
 
433
                        bool mouseOver;
 
434
                        
 
435
                        public StatusIcon (StatusArea statusBar, Gdk.Pixbuf icon)
 
436
                        {
 
437
                                this.statusBar = statusBar;
 
438
                                this.icon = icon;
 
439
                                box = new EventBox ();
 
440
                                box.VisibleWindow = false;
 
441
                                image = new Image (icon);
 
442
                                image.SetPadding (0, 0);
 
443
                                box.Child = image;
 
444
                                box.Events |= Gdk.EventMask.EnterNotifyMask | Gdk.EventMask.LeaveNotifyMask;
 
445
                                box.EnterNotifyEvent += HandleEnterNotifyEvent;
 
446
                                box.LeaveNotifyEvent += HandleLeaveNotifyEvent;
 
447
                        }
 
448
                        
 
449
                        [GLib.ConnectBefore]
 
450
                        void HandleLeaveNotifyEvent (object o, LeaveNotifyEventArgs args)
 
451
                        {
 
452
                                mouseOver = false;
 
453
                                HideTooltip ();
 
454
                        }
 
455
                        
 
456
                        [GLib.ConnectBefore]
 
457
                        void HandleEnterNotifyEvent (object o, EnterNotifyEventArgs args)
 
458
                        {
 
459
                                mouseOver = true;
 
460
                                ShowTooltip ();
 
461
                        }
 
462
                        
 
463
                        void ShowTooltip ()
 
464
                        {
 
465
                                if (!string.IsNullOrEmpty (tip)) {
 
466
                                        HideTooltip ();
 
467
                                        tooltipWindow = new TooltipPopoverWindow ();
 
468
                                        tooltipWindow.ShowArrow = true;
 
469
                                        tooltipWindow.Text = tip;
 
470
                                        tooltipWindow.ShowPopup (box, PopupPosition.Top);
 
471
                                }
 
472
                        }
 
473
                        
 
474
                        void HideTooltip ()
 
475
                        {
 
476
                                if (tooltipWindow != null) {
 
477
                                        tooltipWindow.Destroy ();
 
478
                                        tooltipWindow = null;
 
479
                                }
 
480
                        }
 
481
                        
 
482
                        public void Dispose ()
 
483
                        {
 
484
                                HideTooltip ();
 
485
                                statusBar.HideStatusIcon (this);
 
486
                                if (images != null) {
 
487
                                        foreach (Gdk.Pixbuf img in images) {
 
488
                                                img.Dispose ();
 
489
                                        }
 
490
                                }
 
491
                                if (animation != 0) {
 
492
                                        GLib.Source.Remove (animation);
 
493
                                        animation = 0;
 
494
                                }
 
495
                        }
 
496
                        
 
497
                        public string ToolTip {
 
498
                                get { return tip; }
 
499
                                set {
 
500
                                        tip = value;
 
501
                                        if (tooltipWindow != null) {
 
502
                                                if (!string.IsNullOrEmpty (tip))
 
503
                                                        tooltipWindow.Text = value;
 
504
                                                else
 
505
                                                        HideTooltip ();
 
506
                                        } else if (!string.IsNullOrEmpty (tip) && mouseOver)
 
507
                                                ShowTooltip ();
 
508
                                }
 
509
                        }
 
510
                        
 
511
                        public EventBox EventBox {
 
512
                                get { return box; }
 
513
                        }
 
514
                        
 
515
                        public Gdk.Pixbuf Image {
 
516
                                get { return icon; }
 
517
                                set {
 
518
                                        icon = value;
 
519
                                        image.Pixbuf = icon;
 
520
                                }
 
521
                        }
 
522
                        
 
523
                        public void SetAlertMode (int seconds)
 
524
                        {
 
525
                                astep = 0;
 
526
                                alertEnd = DateTime.Now.AddSeconds (seconds);
 
527
                                
 
528
                                if (animation != 0)
 
529
                                        GLib.Source.Remove (animation);
 
530
                                
 
531
                                animation = GLib.Timeout.Add (60, new GLib.TimeoutHandler (AnimateIcon));
 
532
                                
 
533
                                if (images == null) {
 
534
                                        images = new Gdk.Pixbuf [10];
 
535
                                        for (int n=0; n<10; n++)
 
536
                                                images [n] = ImageService.MakeTransparent (icon, ((double)(9-n))/10.0);
 
537
                                }
 
538
                        }
 
539
                        
 
540
                        bool AnimateIcon ()
 
541
                        {
 
542
                                if (DateTime.Now >= alertEnd && astep == 0) {
 
543
                                        image.Pixbuf = icon;
 
544
                                        animation = 0;
 
545
                                        return false;
 
546
                                }
 
547
                                if (astep < 10)
 
548
                                        image.Pixbuf = images [astep];
 
549
                                else
 
550
                                        image.Pixbuf = images [20 - astep - 1];
 
551
                                
 
552
                                astep = (astep + 1) % 20;
 
553
                                return true;
 
554
                        }
 
555
                }
 
556
                
 
557
                #endregion
 
558
 
 
559
                #region StatusBarContextBase implementation
 
560
 
 
561
                public void ShowError (string error)
 
562
                {
 
563
                        ShowMessage (StockIcons.StatusError, error);
 
564
                }
 
565
 
 
566
                public void ShowWarning (string warning)
 
567
                {
 
568
                        DispatchService.AssertGuiThread ();
 
569
                        ShowMessage (StockIcons.StatusWarning, warning);
 
570
                }
 
571
 
 
572
                public void ShowMessage (string message)
 
573
                {
 
574
                        ShowMessage (null, message, false);
 
575
                }
 
576
 
 
577
                public void ShowMessage (string message, bool isMarkup)
 
578
                {
 
579
                        ShowMessage (null, message, isMarkup);
 
580
                }
 
581
 
 
582
                public void ShowMessage (IconId image, string message)
 
583
                {
 
584
                        ShowMessage (image, message, false);
 
585
                }
 
586
 
 
587
                public void ShowMessage (IconId image, string message, bool isMarkup)
 
588
                {
 
589
                        if (this.AnimationIsRunning("Text") || animPauseHandle > 0) {
 
590
                                messageQueue.Clear ();
 
591
                                messageQueue.Enqueue (new Message (image, message, isMarkup));
 
592
                        } else {
 
593
                                ShowMessageInner (image, message, isMarkup);
 
594
                        }
 
595
                }
 
596
 
 
597
                void ShowMessageInner (IconId image, string message, bool isMarkup)
 
598
                {
 
599
                        DispatchService.AssertGuiThread ();
 
600
 
 
601
                        if (image == StockIcons.StatusError) {
 
602
                                // If the application doesn't have the focus, trigger the animation
 
603
                                // again when it gains the focus
 
604
                                if (!IdeApp.CommandService.ApplicationHasFocus)
 
605
                                        errorAnimPending = true;
 
606
                                TriggerErrorAnimation ();
 
607
                        }
 
608
 
 
609
                        LoadText (message, isMarkup);
 
610
                        LoadPixbuf (image);
 
611
                        /* Hack for a compiler error - csc crashes on this:
 
612
                        this.Animate ("Text", easing: Easing.SinInOut,
 
613
                                      callback: x => renderArg.TextAnimationProgress = x,
 
614
                                      finished: x => { animPauseHandle = GLib.Timeout.Add (1000, () => {
 
615
                                        if (messageQueue.Count > 0) {
 
616
                                                Message m = messageQueue.Dequeue();
 
617
                                                ShowMessageInner (m.Icon, m.Text, m.IsMarkup);
 
618
                                        }
 
619
                                        animPauseHandle = 0;
 
620
                                        return false;
 
621
                                });     
 
622
                        });
 
623
                        */
 
624
                        this.Animate ("Text", 
 
625
                                      x => renderArg.TextAnimationProgress = x,
 
626
                                      easing: Easing.SinInOut,
 
627
                                      finished: (x, b) => { animPauseHandle = GLib.Timeout.Add (1000, () => {
 
628
                                        if (messageQueue.Count > 0) {
 
629
                                                Message m = messageQueue.Dequeue();
 
630
                                                ShowMessageInner (m.Icon, m.Text, m.IsMarkup);
 
631
                                        }
 
632
                                        animPauseHandle = 0;
 
633
                                        return false;
 
634
                                });     
 
635
                        });
 
636
 
 
637
 
 
638
                        if (renderArg.CurrentText == renderArg.LastText)
 
639
                                this.AbortAnimation ("Text");
 
640
 
 
641
                        QueueDraw ();
 
642
                }
 
643
 
 
644
                void LoadText (string message, bool isMarkup)
 
645
                {
 
646
                        if (string.IsNullOrEmpty(message))
 
647
                                message = BrandingService.ApplicationName;
 
648
                        message = message ?? "";
 
649
 
 
650
                        renderArg.LastText = renderArg.CurrentText;
 
651
                        renderArg.CurrentText = message.Replace (System.Environment.NewLine, " ").Replace ("\n", " ").Trim ();
 
652
 
 
653
                        renderArg.LastTextIsMarkup = renderArg.CurrentTextIsMarkup;
 
654
                        renderArg.CurrentTextIsMarkup = isMarkup;
 
655
                }
 
656
 
 
657
                static bool iconLoaded = false;
 
658
                void LoadPixbuf (IconId image)
 
659
                {
 
660
                        // We dont need to load the same image twice
 
661
                        if (currentIcon == image && iconLoaded)
 
662
                                return;
 
663
 
 
664
                        currentIcon = image;
 
665
                        iconAnimation = null;
 
666
 
 
667
                        // clean up previous running animation
 
668
                        if (currentIconAnimation != null) {
 
669
                                currentIconAnimation.Dispose ();
 
670
                                currentIconAnimation = null;
 
671
                        }
 
672
 
 
673
                        // if we have nothing, use the default icon
 
674
                        if (image == IconId.Null)
 
675
                                image = "md-status-steady";
 
676
 
 
677
                        // load image now
 
678
                        if (ImageService.IsAnimation (image, Gtk.IconSize.Menu)) {
 
679
                                iconAnimation = ImageService.GetAnimatedIcon (image, Gtk.IconSize.Menu);
 
680
                                renderArg.CurrentPixbuf = iconAnimation.FirstFrame;
 
681
                                currentIconAnimation = iconAnimation.StartAnimation (delegate (Gdk.Pixbuf p) {
 
682
                                        renderArg.CurrentPixbuf = p;
 
683
                                        QueueDraw ();
 
684
                                });
 
685
                        } else {
 
686
                                renderArg.CurrentPixbuf = ImageService.GetPixbuf (image, Gtk.IconSize.Menu);
 
687
                        }
 
688
 
 
689
                        iconLoaded = true;
 
690
                }
 
691
                #endregion
 
692
 
 
693
 
 
694
                #region Progress Monitor implementation
 
695
                public static event EventHandler ProgressBegin, ProgressEnd, ProgressPulse;
 
696
                public static event EventHandler<FractionEventArgs> ProgressFraction;
 
697
                
 
698
                public sealed class FractionEventArgs : EventArgs
 
699
                {
 
700
                        public double Work { get; private set; }
 
701
                        
 
702
                        public FractionEventArgs (double work)
 
703
                        {
 
704
                                this.Work = work;
 
705
                        }
 
706
                }
 
707
                
 
708
                static void OnProgressBegin (EventArgs e)
 
709
                {
 
710
                        var handler = ProgressBegin;
 
711
                        if (handler != null)
 
712
                                handler (null, e);
 
713
                }
 
714
                
 
715
                static void OnProgressEnd (EventArgs e)
 
716
                {
 
717
                        var handler = ProgressEnd;
 
718
                        if (handler != null)
 
719
                                handler (null, e);
 
720
                }
 
721
                
 
722
                static void OnProgressPulse (EventArgs e)
 
723
                {
 
724
                        var handler = ProgressPulse;
 
725
                        if (handler != null)
 
726
                                handler (null, e);
 
727
                }
 
728
                
 
729
                static void OnProgressFraction (FractionEventArgs e)
 
730
                {
 
731
                        var handler = ProgressFraction;
 
732
                        if (handler != null)
 
733
                                handler (null, e);
 
734
                }
 
735
                
 
736
                public void BeginProgress (string name)
 
737
                {
 
738
                        ShowMessage (name);
 
739
                        if (!progressBarVisible) {
 
740
                                progressBarVisible = true;
 
741
                                OnProgressBegin (EventArgs.Empty);
 
742
                        }
 
743
                }
 
744
                
 
745
                public void BeginProgress (IconId image, string name)
 
746
                {
 
747
                        ShowMessage (image, name);
 
748
                        if (!progressBarVisible) {
 
749
                                progressBarVisible = true;
 
750
                                OnProgressBegin (EventArgs.Empty);
 
751
                        }
 
752
                }
 
753
                
 
754
                public void SetProgressFraction (double work)
 
755
                {
 
756
                        DispatchService.AssertGuiThread ();
 
757
                        OnProgressFraction (new FractionEventArgs (work));
 
758
                }
 
759
                
 
760
                public void EndProgress ()
 
761
                {
 
762
                        if (!progressBarVisible)
 
763
                                return;
 
764
 
 
765
                        progressBarVisible = false;
 
766
                        OnProgressEnd (EventArgs.Empty);
 
767
                        AutoPulse = false;
 
768
                }
 
769
                
 
770
                public void Pulse ()
 
771
                {
 
772
                        DispatchService.AssertGuiThread ();
 
773
                        OnProgressPulse (EventArgs.Empty);
 
774
                }
 
775
                
 
776
                uint autoPulseTimeoutId;
 
777
                public bool AutoPulse {
 
778
                        get { return autoPulseTimeoutId != 0; }
 
779
                        set {
 
780
                                DispatchService.AssertGuiThread ();
 
781
                                if (value) {
 
782
                                        if (autoPulseTimeoutId == 0) {
 
783
                                                autoPulseTimeoutId = GLib.Timeout.Add (100, delegate {
 
784
                                                        Pulse ();
 
785
                                                        return true;
 
786
                                                });
 
787
                                        }
 
788
                                } else {
 
789
                                        if (autoPulseTimeoutId != 0) {
 
790
                                                GLib.Source.Remove (autoPulseTimeoutId);
 
791
                                                autoPulseTimeoutId = 0;
 
792
                                        }
 
793
                                }
 
794
                        }
 
795
                }
 
796
                #endregion
 
797
        
 
798
                internal bool IsCurrentContext (StatusBarContextImpl ctx)
 
799
                {
 
800
                        return ctx == activeContext;
 
801
                }
 
802
 
 
803
                internal void Remove (StatusBarContextImpl ctx)
 
804
                {
 
805
                        if (ctx == mainContext)
 
806
                                return;
 
807
                        
 
808
                        StatusBarContextImpl oldActive = activeContext;
 
809
                        contexts.Remove (ctx);
 
810
                        UpdateActiveContext ();
 
811
                        if (oldActive != activeContext) {
 
812
                                // Removed the active context. Update the status bar.
 
813
                                activeContext.Update ();
 
814
                        }
 
815
                }
 
816
                
 
817
                internal void UpdateActiveContext ()
 
818
                {
 
819
                        for (int n = contexts.Count - 1; n >= 0; n--) {
 
820
                                StatusBarContextImpl ctx = contexts [n];
 
821
                                if (ctx.StatusChanged) {
 
822
                                        if (ctx != activeContext) {
 
823
                                                activeContext = ctx;
 
824
                                                activeContext.Update ();
 
825
                                        }
 
826
                                        return;
 
827
                                }
 
828
                        }
 
829
                        throw new InvalidOperationException (); // There must be at least the main context
 
830
                }
 
831
        }
 
832
 
 
833
        class StatusAreaSeparator: HBox
 
834
        {
 
835
                protected override bool OnExposeEvent (Gdk.EventExpose evnt)
 
836
                {
 
837
                        using (var ctx = Gdk.CairoHelper.Create (this.GdkWindow)) {
 
838
                                var alloc = Allocation;
 
839
                                //alloc.Inflate (0, -2);
 
840
                                ctx.Rectangle (alloc.X, alloc.Y, 1, alloc.Height);
 
841
                                using (Cairo.LinearGradient gr = new LinearGradient (alloc.X, alloc.Y, alloc.X, alloc.Y + alloc.Height)) {
 
842
                                        gr.AddColorStop (0, new Cairo.Color (0, 0, 0, 0));
 
843
                                        gr.AddColorStop (0.5, new Cairo.Color (0, 0, 0, 0.2));
 
844
                                        gr.AddColorStop (1, new Cairo.Color (0, 0, 0, 0));
 
845
                                        ctx.Pattern = gr;
 
846
                                        ctx.Fill ();
 
847
                                }
 
848
                        }
 
849
                        return true;
 
850
                }
 
851
 
 
852
                protected override void OnSizeRequested (ref Requisition requisition)
 
853
                {
 
854
                        base.OnSizeRequested (ref requisition);
 
855
                        requisition.Width = 1;
 
856
                }
 
857
        }
 
858
}
 
859