~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/Main/ICSharpCode.SharpDevelop.Widgets/Project/ZoomScrollViewer.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
 
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
 
3
 
 
4
using System;
 
5
using System.Collections.Generic;
 
6
using System.Text;
 
7
using System.Windows;
 
8
using System.Windows.Controls;
 
9
using System.Windows.Controls.Primitives;
 
10
using System.Windows.Data;
 
11
using System.Windows.Documents;
 
12
using System.Windows.Input;
 
13
using System.Windows.Media;
 
14
 
 
15
namespace ICSharpCode.SharpDevelop.Widgets
 
16
{
 
17
        public class ZoomScrollViewer : ScrollViewer
 
18
        {
 
19
                static ZoomScrollViewer()
 
20
                {
 
21
                        DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomScrollViewer),
 
22
                                                                 new FrameworkPropertyMetadata(typeof(ZoomScrollViewer)));
 
23
                }
 
24
                
 
25
                public static readonly DependencyProperty CurrentZoomProperty =
 
26
                        DependencyProperty.Register("CurrentZoom", typeof(double), typeof(ZoomScrollViewer),
 
27
                                                    new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CalculateZoomButtonCollapsed, CoerceZoom));
 
28
                
 
29
                public double CurrentZoom {
 
30
                        get { return (double)GetValue(CurrentZoomProperty); }
 
31
                        set { SetValue(CurrentZoomProperty, value); }
 
32
                }
 
33
                
 
34
                static object CoerceZoom(DependencyObject d, object baseValue)
 
35
                {
 
36
                        var zoom = (double)baseValue;
 
37
                        ZoomScrollViewer sv = (ZoomScrollViewer)d;
 
38
                        return Math.Max(sv.MinimumZoom, Math.Min(sv.MaximumZoom, zoom));
 
39
                }
 
40
                
 
41
                public static readonly DependencyProperty MinimumZoomProperty =
 
42
                        DependencyProperty.Register("MinimumZoom", typeof(double), typeof(ZoomScrollViewer),
 
43
                                                    new FrameworkPropertyMetadata(0.2));
 
44
                
 
45
                public double MinimumZoom {
 
46
                        get { return (double)GetValue(MinimumZoomProperty); }
 
47
                        set { SetValue(MinimumZoomProperty, value); }
 
48
                }
 
49
                
 
50
                public static readonly DependencyProperty MaximumZoomProperty =
 
51
                        DependencyProperty.Register("MaximumZoom", typeof(double), typeof(ZoomScrollViewer),
 
52
                                                    new FrameworkPropertyMetadata(5.0));
 
53
                
 
54
                public double MaximumZoom {
 
55
                        get { return (double)GetValue(MaximumZoomProperty); }
 
56
                        set { SetValue(MaximumZoomProperty, value); }
 
57
                }
 
58
                
 
59
                public static readonly DependencyProperty MouseWheelZoomProperty =
 
60
                        DependencyProperty.Register("MouseWheelZoom", typeof(bool), typeof(ZoomScrollViewer),
 
61
                                                    new FrameworkPropertyMetadata(true));
 
62
                
 
63
                public bool MouseWheelZoom {
 
64
                        get { return (bool)GetValue(MouseWheelZoomProperty); }
 
65
                        set { SetValue(MouseWheelZoomProperty, value); }
 
66
                }
 
67
                
 
68
                public static readonly DependencyProperty AlwaysShowZoomButtonsProperty =
 
69
                        DependencyProperty.Register("AlwaysShowZoomButtons", typeof(bool), typeof(ZoomScrollViewer),
 
70
                                                    new FrameworkPropertyMetadata(false, CalculateZoomButtonCollapsed));
 
71
                
 
72
                public bool AlwaysShowZoomButtons {
 
73
                        get { return (bool)GetValue(AlwaysShowZoomButtonsProperty); }
 
74
                        set { SetValue(AlwaysShowZoomButtonsProperty, value); }
 
75
                }
 
76
                
 
77
                static readonly DependencyPropertyKey ComputedZoomButtonCollapsedPropertyKey =
 
78
                        DependencyProperty.RegisterReadOnly("ComputedZoomButtonCollapsed", typeof(bool), typeof(ZoomScrollViewer),
 
79
                                                            new FrameworkPropertyMetadata(true));
 
80
                
 
81
                public static readonly DependencyProperty ComputedZoomButtonCollapsedProperty = ComputedZoomButtonCollapsedPropertyKey.DependencyProperty;
 
82
                
 
83
                public bool ComputedZoomButtonCollapsed {
 
84
                        get { return (bool)GetValue(ComputedZoomButtonCollapsedProperty); }
 
85
                        private set { SetValue(ComputedZoomButtonCollapsedPropertyKey, value); }
 
86
                }
 
87
                
 
88
                static void CalculateZoomButtonCollapsed(DependencyObject d, DependencyPropertyChangedEventArgs e)
 
89
                {
 
90
                        ZoomScrollViewer z = d as ZoomScrollViewer;
 
91
                        if (z != null)
 
92
                                z.ComputedZoomButtonCollapsed = (z.AlwaysShowZoomButtons == false) && (z.CurrentZoom == 1.0);
 
93
                }
 
94
                
 
95
                protected override void OnMouseWheel(MouseWheelEventArgs e)
 
96
                {
 
97
                        if (!e.Handled && Keyboard.Modifiers == ModifierKeys.Control && MouseWheelZoom) {
 
98
                                double oldZoom = CurrentZoom;
 
99
                                double newZoom = RoundToOneIfClose(CurrentZoom * Math.Pow(1.001, e.Delta));
 
100
                                newZoom = Math.Max(this.MinimumZoom, Math.Min(this.MaximumZoom, newZoom));
 
101
                                
 
102
                                // adjust scroll position so that mouse stays over the same virtual coordinate
 
103
                                ContentPresenter presenter = Template.FindName("PART_Presenter", this) as ContentPresenter;
 
104
                                Vector relMousePos;
 
105
                                if (presenter != null) {
 
106
                                        Point mousePos = e.GetPosition(presenter);
 
107
                                        relMousePos = new Vector(mousePos.X / presenter.ActualWidth, mousePos.Y / presenter.ActualHeight);
 
108
                                } else {
 
109
                                        relMousePos = new Vector(0.5, 0.5);
 
110
                                }
 
111
                                
 
112
                                Point scrollOffset = new Point(this.HorizontalOffset, this.VerticalOffset);
 
113
                                Vector oldHalfViewport = new Vector(this.ViewportWidth / 2, this.ViewportHeight / 2);
 
114
                                Vector newHalfViewport = oldHalfViewport / newZoom * oldZoom;
 
115
                                Point oldCenter = scrollOffset + oldHalfViewport;
 
116
                                Point virtualMousePos = scrollOffset + new Vector(relMousePos.X * this.ViewportWidth, relMousePos.Y * this.ViewportHeight);
 
117
                                
 
118
                                // As newCenter, we want to choose a point between oldCenter and virtualMousePos. The more we zoom in, the closer
 
119
                                // to virtualMousePos. We'll create the line x = oldCenter + lambda * (virtualMousePos-oldCenter).
 
120
                                // On this line, we need to choose lambda between -1 and 1:
 
121
                                // -1 = zoomed out completely
 
122
                                //  0 = zoom unchanged
 
123
                                // +1 = zoomed in completely
 
124
                                // But the zoom factor (newZoom/oldZoom) we have is in the range [0,+Infinity].
 
125
                                
 
126
                                // Basically, I just played around until I found a function that maps this to [-1,1] and works well.
 
127
                                // "f" is squared because otherwise the mouse simply stays over virtualMousePos, but I wanted virtualMousePos
 
128
                                // to move towards the middle -> squaring f causes lambda to be closer to 1, giving virtualMousePos more weight
 
129
                                // then oldCenter.
 
130
                                
 
131
                                double f = Math.Min(newZoom, oldZoom) / Math.Max(newZoom, oldZoom);
 
132
                                double lambda = 1 - f*f;
 
133
                                if (oldZoom > newZoom)
 
134
                                        lambda = -lambda;
 
135
                                
 
136
                                Point newCenter = oldCenter + lambda * (virtualMousePos - oldCenter);
 
137
                                scrollOffset = newCenter - newHalfViewport;
 
138
                                
 
139
                                SetCurrentValue(CurrentZoomProperty, newZoom);
 
140
                                
 
141
                                this.ScrollToHorizontalOffset(scrollOffset.X);
 
142
                                this.ScrollToVerticalOffset(scrollOffset.Y);
 
143
                                
 
144
                                e.Handled = true;
 
145
                        }
 
146
                        base.OnMouseWheel(e);
 
147
                }
 
148
                
 
149
                internal static double RoundToOneIfClose(double val)
 
150
                {
 
151
                        if (Math.Abs(val - 1.0) < 0.0001)
 
152
                                return 1.0;
 
153
                        else
 
154
                                return val;
 
155
                }
 
156
        }
 
157
        
 
158
        sealed class IsNormalZoomConverter : IValueConverter
 
159
        {
 
160
                public static readonly IsNormalZoomConverter Instance = new IsNormalZoomConverter();
 
161
                
 
162
                public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
 
163
                {
 
164
                        if (parameter is bool && (bool)parameter)
 
165
                                return true;
 
166
                        return ((double)value) == 1.0;
 
167
                }
 
168
                
 
169
                public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
 
170
                {
 
171
                        throw new NotImplementedException();
 
172
                }
 
173
        }
 
174
}