Subversion Repositories SoapBoxCore

Rev

Rev 12 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 scott 1
//Copyright (c) 2007-2010, Adolfo Marinucci
7 scott 2
//All rights reserved.
3
 
4
//Redistribution and use in source and binary forms, with or without modification, 
5
//are permitted provided that the following conditions are met:
6
//
7
//* Redistributions of source code must retain the above copyright notice, 
8
//  this list of conditions and the following disclaimer.
9
//* Redistributions in binary form must reproduce the above copyright notice, 
10
//  this list of conditions and the following disclaimer in the documentation 
11
//  and/or other materials provided with the distribution.
12
//* Neither the name of Adolfo Marinucci nor the names of its contributors may 
13
//  be used to endorse or promote products derived from this software without 
14
//  specific prior written permission.
15
//
16
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
17
//AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
18
//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
19
//IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
20
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
21
//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
22
//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
23
//OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
24
//EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25
 
26
using System;
27
using System.Collections.Generic;
28
using System.Text;
29
using System.Windows;
30
using System.Windows.Controls;
31
using System.Windows.Controls.Primitives;
32
using System.Windows.Data;
33
using System.Windows.Documents;
34
using System.Windows.Input;
35
using System.Windows.Media;
36
using System.Windows.Media.Imaging;
37
using System.Windows.Shapes;
38
using System.ComponentModel;
39
using System.Windows.Markup;
40
using System.Diagnostics;
41
using System.Windows.Threading;
42
using System.Windows.Media.Animation;
43
using System.Windows.Interop;
44
using System.Runtime.InteropServices;
17 scott 45
using System.Linq;
7 scott 46
 
47
namespace AvalonDock
48
{
17 scott 49
    /// <summary>
50
    /// Represents the base class for <see cref="DockableFloatingWindow"/> and <see cref="DocumentFloatingWindow"/> classes
51
    /// </summary>
52
    /// <remarks>Provides base services for floating windows</remarks>
53
    public abstract class FloatingWindow : AvalonDockWindow
7 scott 54
    {
55
        static FloatingWindow()
56
        {
57
            Window.ShowInTaskbarProperty.OverrideMetadata(typeof(FloatingWindow), new FrameworkPropertyMetadata(false));
58
            Window.WindowStyleProperty.OverrideMetadata(typeof(FloatingWindow), new FrameworkPropertyMetadata(WindowStyle.ToolWindow));
17 scott 59
 
60
            //DefaultStyleKeyProperty.OverrideMetadata(typeof(FloatingWindow), new FrameworkPropertyMetadata(typeof(FloatingWindow)));
7 scott 61
        }
62
 
12 scott 63
 
17 scott 64
        internal FloatingWindow()
7 scott 65
        {
66
            this.Loaded += new RoutedEventHandler(OnLoaded);
67
            this.Unloaded += new RoutedEventHandler(OnUnloaded);
68
 
69
            this.SizeChanged += new SizeChangedEventHandler(FloatingWindow_SizeChanged);
17 scott 70
        }
7 scott 71
 
17 scott 72
        internal FloatingWindow(DockingManager manager)
73
            : this()
74
        {
75
            //save manager ref
76
            _manager = manager;
7 scott 77
        }
78
 
79
        void FloatingWindow_SizeChanged(object sender, SizeChangedEventArgs e)
80
        {
81
            if (HostedPane != null)
82
            {
83
                foreach (ManagedContent c in HostedPane.Items)
84
                    c.FloatingWindowSize = new Size(Width, Height);
85
 
86
                ResizingPanel.SetEffectiveSize(HostedPane, new Size(Width, Height));
87
            }
88
        }
89
 
90
        DockingManager _manager = null;
91
 
92
        internal DockingManager Manager
93
        {
94
            get { return _manager; }
95
        }
96
 
17 scott 97
        public Pane HostedPane
98
        {
99
            get { return Content as Pane; }
100
        }
7 scott 101
 
17 scott 102
        #region ContentTitle
103
 
104
        /// <summary>
105
        /// ContentTitle Read-Only Dependency Property
106
        /// </summary>
107
        private static readonly DependencyPropertyKey ContentTitlePropertyKey
108
            = DependencyProperty.RegisterReadOnly("ContentTitle", typeof(object), typeof(FloatingWindow),
109
                new FrameworkPropertyMetadata((object)null));
110
 
111
        public static readonly DependencyProperty ContentTitleProperty
112
            = ContentTitlePropertyKey.DependencyProperty;
113
 
114
        /// <summary>
115
        /// Gets the ContentTitle property.  This dependency property 
116
        /// indicates title of the content currectly hosted in the floating window.
117
        /// </summary>
118
        public object ContentTitle
7 scott 119
        {
17 scott 120
            get { return (object)GetValue(ContentTitleProperty); }
7 scott 121
        }
122
 
17 scott 123
        /// <summary>
124
        /// Provides a secure method for setting the ContentTitle property.  
125
        /// This dependency property indicates title of the content currectly hosted in the floating window.
126
        /// </summary>
127
        /// <param name="value">The new value for the property.</param>
128
        protected void SetContentTitle(object value)
129
        {
130
            SetValue(ContentTitlePropertyKey, value);
131
        }
7 scott 132
 
133
 
17 scott 134
        private void UpdateContentTitle()
135
        {
136
            if (HostedPane == null)
137
                return;
138
 
139
            var cnt = HostedPane.SelectedItem as ManagedContent;
140
            if (cnt != null)
141
                SetContentTitle(cnt.Title);
142
        }
143
        #endregion
144
 
145
 
146
 
147
        //#region HostedPane
148
 
149
        ///// <summary>
150
        ///// HostedPane Read-Only Dependency Property
151
        ///// </summary>
152
        //private static readonly DependencyPropertyKey HostedPanePropertyKey
153
        //    = DependencyProperty.RegisterReadOnly("HostedPane", typeof(Pane), typeof(FloatingWindow),
154
        //        new FrameworkPropertyMetadata((Pane)null,
155
        //            new PropertyChangedCallback(OnHostedPaneChanged)));
156
 
157
        //public static readonly DependencyProperty HostedPaneProperty
158
        //    = HostedPanePropertyKey.DependencyProperty;
159
 
160
        ///// <summary>
161
        ///// Gets the HostedPane property.  This dependency property 
162
        ///// indicates the hosted pane contained into the floating window (an hosted pane can be of type <see cref="FloatingDocumentPane"/> or type <see cref="FloatingDockablePane"/>.
163
        ///// </summary>
164
        //public Pane HostedPane
165
        //{
166
        //    get { return (Pane)GetValue(HostedPaneProperty); }
167
        //}
168
 
169
        ///// <summary>
170
        ///// Provides a secure method for setting the HostedPane property.  
171
        ///// This dependency property indicates the hosted pane contained into the floating window (an hosted pane can be of type <see cref="FloatingDocumentPane"/> or type <see cref="FloatingDockablePane"/>.
172
        ///// </summary>
173
        ///// <param name="value">The new value for the property.</param>
174
        //protected void SetHostedPane(Pane value)
175
        //{
176
        //    SetValue(HostedPanePropertyKey, value);
177
        //}
178
 
179
        ///// <summary>
180
        ///// Handles changes to the HostedPane property.
181
        ///// </summary>
182
        //private static void OnHostedPaneChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
183
        //{
184
        //    ((FloatingWindow)d).OnHostedPaneChanged(e);
185
        //}
186
 
187
        ///// <summary>
188
        ///// Provides derived classes an opportunity to handle changes to the HostedPane property.
189
        ///// </summary>
190
        //protected virtual void OnHostedPaneChanged(DependencyPropertyChangedEventArgs e)
191
        //{
192
        //}
193
 
194
        //#endregion
195
 
196
        protected override void OnInitialized(EventArgs e)
197
        {
198
 
199
            base.OnInitialized(e);
200
        }
201
 
202
        protected override void OnContentChanged(object oldContent, object newContent)
203
        {
204
            base.OnContentChanged(oldContent, newContent);
205
 
206
            if (_manager != null)
207
            {
208
                _manager.RegisterFloatingWindow(this);
209
                _manager.RefreshContents();
210
            }
211
 
212
            UpdateContentTitle();
213
        }
214
 
215
 
216
 
7 scott 217
        internal virtual void OnEndDrag()
218
        {
219
        }
220
 
221
        internal virtual void OnShowSelectionBox()
222
        {
223
 
224
        }
225
 
226
        internal virtual void OnHideSelectionBox()
227
        {
228
 
229
        }
230
 
17 scott 231
        #region Move/Resize
232
 
233
        public override void OnApplyTemplate()
234
        {
235
            base.OnApplyTemplate();
236
 
237
            Resizer resLeftAnchor = GetTemplateChild("PART_LeftAnchor") as Resizer;
238
            Resizer resTopAnchor = GetTemplateChild("PART_TopAnchor") as Resizer;
239
            Resizer resBottomAnchor = GetTemplateChild("PART_BottomAnchor") as Resizer;
240
            Resizer resRightAnchor = GetTemplateChild("PART_RightAnchor") as Resizer;
241
 
242
            Resizer resLeftTopAnchor = GetTemplateChild("PART_LeftTopAnchor") as Resizer;
243
            Resizer resLeftBottomAnchor = GetTemplateChild("PART_LeftBottomAnchor") as Resizer;
244
 
245
            Resizer resRightTopAnchor = GetTemplateChild("PART_RightTopAnchor") as Resizer;
246
            Resizer resRightBottomAnchor = GetTemplateChild("PART_RightBottomAnchor") as Resizer;
247
 
248
            //Resizer resMoveAnchor = GetTemplateChild("PART_MoveAnchor") as Resizer;
249
            Border resMoveAnchor = GetTemplateChild("PART_MoveAnchor") as Border;
250
 
251
            if (resLeftAnchor != null) resLeftAnchor.DragDelta += (s, e) =>
252
            {
253
                double delta = Math.Max(MinWidth, Width - e.HorizontalChange) - Width;
254
                this.Left -= delta;
255
                this.Width += delta;
256
            };
257
            if (resRightAnchor != null) resRightAnchor.DragDelta += (s, e) =>
258
            {
259
                double delta = Math.Max(MinWidth, Width + e.HorizontalChange) - Width;
260
                this.Width += delta;
261
            };
262
            if (resTopAnchor != null) resTopAnchor.DragDelta += (s, e) =>
263
            {
264
                double delta = Math.Max(MinHeight, Height - e.VerticalChange) - Height;
265
                this.Top -= delta;
266
                this.Height += delta;
267
            };
268
            if (resBottomAnchor != null) resBottomAnchor.DragDelta += (s, e) =>
269
            {
270
                double delta = Math.Max(MinHeight, Height + e.VerticalChange) - Height;
271
                this.Height += delta;
272
            };
273
 
274
            if (resLeftTopAnchor != null) resLeftTopAnchor.DragDelta += (s, e) =>
275
            {
276
                double delta = Math.Max(MinWidth, Width - e.HorizontalChange) - Width;
277
                this.Left -= delta;
278
                this.Width += delta;
279
 
280
                delta = Math.Max(MinHeight, Height - e.VerticalChange) - Height;
281
                this.Top -= delta;
282
                this.Height += delta;
283
            };
284
            if (resLeftBottomAnchor != null) resLeftBottomAnchor.DragDelta += (s, e) =>
285
            {
286
                double delta = Math.Max(MinWidth, Width - e.HorizontalChange) - Width;
287
                this.Left -= delta;
288
                this.Width += delta;
289
 
290
                delta = Math.Max(MinHeight, Height + e.VerticalChange) - Height;
291
                this.Height += delta;
292
            };
293
            if (resRightTopAnchor != null) resRightTopAnchor.DragDelta += (s, e) =>
294
            {
295
                double delta = Math.Max(MinWidth, Width + e.HorizontalChange) - Width;
296
                this.Width += delta;
297
 
298
                delta = Math.Max(MinHeight, Height - e.VerticalChange) - Height;
299
                this.Top -= delta;
300
                this.Height += delta;
301
            };
302
            if (resRightBottomAnchor != null) resRightBottomAnchor.DragDelta += (s, e) =>
303
            {
304
                double delta = Math.Max(MinWidth, Width + e.HorizontalChange) - Width;
305
                this.Width += delta;
306
 
307
                delta = Math.Max(MinHeight, Height + e.VerticalChange) - Height;
308
                this.Height += delta;
309
            };
310
 
311
            if (resMoveAnchor != null)
312
            {
313
                bool isMouseDown = false;
314
                Point ptStartDrag = new Point();
315
                resMoveAnchor.MouseLeftButtonDown += (s, e) =>
316
                {
317
                    isMouseDown = true;
318
                    ptStartDrag = e.GetPosition(s as IInputElement);
319
                    resMoveAnchor.CaptureMouse();
320
                };                
321
 
322
                resMoveAnchor.MouseMove += (s, e) =>
323
                {
324
                    if (isMouseDown && resMoveAnchor.IsMouseCaptured)
325
                    {
326
                        Point ptMouseMove = e.GetPosition(s as IInputElement);
327
                        if (Math.Abs(ptMouseMove.X - ptStartDrag.X) > SystemParameters.MinimumHorizontalDragDistance ||
328
                            Math.Abs(ptMouseMove.Y - ptStartDrag.Y) > SystemParameters.MinimumVerticalDragDistance)
329
                        {
330
                            isMouseDown = false;
331
                            resMoveAnchor.ReleaseMouseCapture();
332
                            HandleMove();
333
                        }
334
                    }
335
                };
336
 
337
                resMoveAnchor.MouseLeftButtonUp += (s, e)=>
338
                {
339
                    isMouseDown = false;
340
                    resMoveAnchor.ReleaseMouseCapture();
341
                };
342
 
343
            }
344
 
345
            var pupupButton = GetTemplateChild("PART_ShowContextMenuButton") as FrameworkElement;
346
 
347
            if (pupupButton != null)
348
                pupupButton.MouseLeftButtonDown += (s, e) =>
349
                {
350
                    e.Handled = OpenContextMenu(s as Border, e.GetPosition(s as IInputElement));
351
                };
352
 
353
            var titleAnchor = GetTemplateChild("PART_MoveAnchor") as FrameworkElement;
354
            if (titleAnchor != null)
355
                titleAnchor.MouseRightButtonDown += (s, e) =>
356
                {
357
                    e.Handled = OpenContextMenu(s as Border, e.GetPosition(s as IInputElement));
358
                };
359
 
360
 
361
            base.OnApplyTemplate();
362
        }
363
 
364
        protected virtual bool OpenContextMenu(UIElement popupButton, Point ptMouse)
365
        {
366
            return false;
367
        }
368
 
369
        protected virtual void HandleMove()
370
        {
371
            Point mousePosition = PointToScreen(Mouse.GetPosition(null));
372
            Point clickPoint = this.TransformToDeviceDPI(mousePosition);
373
            if (!Manager.DragPaneServices.IsDragging)
374
                Manager.Drag(this, clickPoint, new Point(clickPoint.X - Left, clickPoint.Y - Top));
375
        }
376
 
377
        #endregion
378
 
7 scott 379
        #region Active Content Management
380
        ManagedContent lastActiveContent = null;
381
 
382
        protected override void OnActivated(EventArgs e)
383
        {
384
            if (Manager != null)
385
            {
386
                lastActiveContent = Manager.ActiveContent;
387
                Manager.ActiveContent = HostedPane.SelectedItem as ManagedContent;
388
            }
389
 
390
            base.OnActivated(e);
391
        }
392
 
393
        protected override void OnDeactivated(EventArgs e)
394
        {
395
            if (Manager != null && lastActiveContent != null)
396
            {
397
                Manager.ActiveContent = lastActiveContent;
398
            }
399
            base.OnDeactivated(e);
400
        }
401
        #endregion
402
 
403
        #region IsClosing Flag Management
17 scott 404
 
405
        /// <summary>
406
        /// Closes the window regardless of result of contents CanClose method call
407
        /// </summary>
408
        /// <param name="force"></param>
409
        internal void Close(bool force)
7 scott 410
        {
411
            ForcedClosing = force;
412
            base.Close();
413
        }
414
 
415
        protected bool ForcedClosing { get; private set; }
416
 
417
        internal bool IsClosing { get; private set; }
418
 
419
        protected override void OnClosing(CancelEventArgs e)
420
        {
17 scott 421
            IsClosing = true;
422
 
423
            if (HostedPane.Items.Count > 0)
7 scott 424
            {
17 scott 425
                var contentsToClose = HostedPane.Items.Cast<ManagedContent>().ToArray();
426
                foreach (var cntToClose in contentsToClose)
7 scott 427
                {
17 scott 428
                    //if even a content can't close than cancel the close process, but continue try closing other contents
429
                    if (!cntToClose.Close())
430
                    {
431
                        //forced closing continues the window close process
432
                        if (!ForcedClosing)
433
                            e.Cancel = true;
434
                    }
7 scott 435
                }
436
            }
437
 
17 scott 438
            if (e.Cancel)
439
                IsClosing = false;
440
            else if (_manager != null)
441
            {
442
                _manager.UnregisterFloatingWindow(this);
443
            }
444
 
7 scott 445
            base.OnClosing(e);
446
        }
447
 
448
        protected override void OnClosed(EventArgs e)
449
        {
450
            IsClosing = false;
451
            base.OnClosed(e);
452
        }
453
        #endregion
454
 
455
        public abstract Pane ClonePane();
456
 
457
 
458
        #region Enable/Disable window Close Button
459
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
460
        private static extern IntPtr GetSystemMenu(
461
            IntPtr hWnd,
462
            Int32 bRevert
463
        );
464
 
465
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
466
        private static extern int GetMenuItemCount(
467
            IntPtr hMenu
468
        );
469
 
470
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
471
        private static extern int DrawMenuBar(
472
            IntPtr hWnd
473
        );
474
 
475
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
476
        private static extern bool EnableMenuItem(
477
            IntPtr hMenu,
478
            Int32 uIDEnableItem,
479
            Int32 uEnable
480
        );
481
 
482
        private const Int32 MF_BYPOSITION = 0x400;
483
        private const Int32 MF_ENABLED = 0x0000;
484
        private const Int32 MF_GRAYED = 0x0001;
485
        private const Int32 MF_DISABLED = 0x0002;
486
 
487
        void EnableXButton()
488
        {
489
            WindowInteropHelper helper = new WindowInteropHelper(this);
490
            IntPtr hMenu = GetSystemMenu(helper.Handle, 0);
491
 
492
            int menuItemCount = GetMenuItemCount(hMenu);
493
 
494
            EnableMenuItem(hMenu, menuItemCount - 1, MF_BYPOSITION | MF_ENABLED);
495
            DrawMenuBar(helper.Handle);
496
        }
497
 
498
        void DisableXButton()
499
        {
500
            WindowInteropHelper helper = new WindowInteropHelper(this);
501
            IntPtr hMenu = GetSystemMenu(helper.Handle, 0);
502
 
503
            int menuItemCount = GetMenuItemCount(hMenu);
504
 
505
            EnableMenuItem(hMenu, menuItemCount - 1, MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
506
            DrawMenuBar(helper.Handle);
507
        }
508
 
509
        #endregion
510
 
511
        #region Non-Client area management
512
 
513
        protected const int WM_MOVE = 0x0003;
514
        protected const int WM_SIZE = 0x0005;
515
        protected const int WM_NCMOUSEMOVE = 0xa0;
516
        protected const int WM_NCLBUTTONDOWN = 0xA1;
517
        protected const int WM_NCLBUTTONUP = 0xA2;
518
        protected const int WM_NCLBUTTONDBLCLK = 0xA3;
519
        protected const int WM_NCRBUTTONDOWN = 0xA4;
520
        protected const int WM_NCRBUTTONUP = 0xA5;
521
        protected const int HTCAPTION = 2;
522
        protected const int SC_MOVE = 0xF010;
523
        protected const int WM_SYSCOMMAND = 0x0112;
524
 
525
 
526
 
527
        #region Load/Unload window events
528
 
17 scott 529
 
7 scott 530
        protected void OnLoaded(object sender, EventArgs e)
531
        {
17 scott 532
            WindowInteropWrapper wih = new WindowInteropWrapper(this);
7 scott 533
 
17 scott 534
            //wih.WindowActivating += (s, ce) => ce.Cancel = true;//prevent window activating
535
            wih.FilterMessage += new EventHandler<FilterMessageEventArgs>(FilterMessage);
536
 
7 scott 537
            if (HostedPane.Items.Count > 0)
538
            {
539
                ManagedContent cntHosted = HostedPane.Items[0] as ManagedContent;
540
                if (!cntHosted.IsCloseable)
541
                {
542
                    DisableXButton();
543
                }
544
            }
545
        }
17 scott 546
 
7 scott 547
        protected void OnUnloaded(object sender, EventArgs e)
17 scott 548
        { }
7 scott 549
        #endregion
550
 
551
 
17 scott 552
        protected virtual void FilterMessage(object sender, FilterMessageEventArgs e)
7 scott 553
        {
17 scott 554
            if (e.Handled)
555
                return;
7 scott 556
 
557
            if (Manager == null)
17 scott 558
                return;
7 scott 559
 
17 scott 560
            switch (e.Msg)
7 scott 561
            {
562
                case WM_SIZE:
563
                case WM_MOVE:
564
                    break;
17 scott 565
                case WM_NCRBUTTONDOWN: //Right button click on title area -> show context menu
566
                    if (e.WParam.ToInt32() == HTCAPTION)
7 scott 567
                    {
17 scott 568
                        short x = (short)((e.LParam.ToInt32() & 0xFFFF));
569
                        short y = (short)((e.LParam.ToInt32() >> 16));
570
                        OpenContextMenu(null, new Point(x, y));
571
                        e.Handled = true;
7 scott 572
                    }
573
                    break;
17 scott 574
                case WM_NCRBUTTONUP: //set as handled right button click on title area (after showing context menu)
575
                    if (e.WParam.ToInt32() == HTCAPTION)
7 scott 576
                    {
17 scott 577
                        e.Handled = true;
7 scott 578
                    }
579
                    break;
580
 
581
            }
582
 
583
        }
584
        #endregion
585
 
586
        #region Floating/dockable window state
587
 
588
 
17 scott 589
        /// <summary>
590
        /// Redock contained <see cref="ManagedContent"/> object to the <see cref="DcokingManager"/>
591
        /// </summary>
592
        public virtual void Dock()
7 scott 593
        {
594
 
595
        }
596
        #endregion
597
 
598
 
599
 
17 scott 600
        internal void CheckContents()
7 scott 601
        {
17 scott 602
            if (HostedPane == null)
603
                return;
7 scott 604
 
17 scott 605
            ManagedContent[] cntsToCheck = HostedPane.Items.Cast<ManagedContent>().ToArray();
7 scott 606
 
17 scott 607
            cntsToCheck.ForEach(cnt =>
7 scott 608
                {
17 scott 609
                    if (cnt.Manager == null ||
610
                        cnt.Manager != Manager ||
611
                        (!cnt.Manager.DockableContents.Contains(cnt) &&
612
                        !cnt.Manager.Documents.Contains(cnt)))
613
                        cnt.ContainerPane.RemoveContent(cnt);
614
                });
7 scott 615
        }
616
    }
617
}