~and471/+junk/do-with-docky

« back to all changes in this revision

Viewing changes to Do.Interface.Linux.Docky/src/Docky.Interface/ItemPositionProvider.cs

  • Committer: rugby471 at gmail
  • Date: 2010-10-15 16:08:38 UTC
  • Revision ID: rugby471@gmail.com-20101015160838-z9m3utbf7bxzb5ty
reverted to before docky removal

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// ItemPositionProvider.cs
 
2
// 
 
3
// Copyright (C) 2009 GNOME Do
 
4
//
 
5
// This program is free software: you can redistribute it and/or modify
 
6
// it under the terms of the GNU General Public License as published by
 
7
// the Free Software Foundation, either version 3 of the License, or
 
8
// (at your option) any later version.
 
9
//
 
10
// This program is distributed in the hope that it will be useful,
 
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
// GNU General Public License for more details.
 
14
//
 
15
// You should have received a copy of the GNU General Public License
 
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
//
 
18
 
 
19
using System;
 
20
using System.Collections.Generic;
 
21
using System.Collections.ObjectModel;
 
22
using System.Linq;
 
23
 
 
24
using Gdk;
 
25
 
 
26
using Docky.Core;
 
27
using Docky.Utilities;
 
28
 
 
29
namespace Docky.Interface
 
30
{
 
31
        
 
32
        
 
33
        internal class ItemPositionProvider : IDisposable
 
34
        {
 
35
                DockArea parent;
 
36
                List<Gdk.Point> static_positions;
 
37
                
 
38
                ReadOnlyCollection<AbstractDockItem> DockItems {
 
39
                        get { return DockServices.ItemsService.DockItems; }
 
40
                }
 
41
                
 
42
                /// <value>
 
43
                /// The width of the visible dock
 
44
                /// </value>
 
45
                public int DockWidth {
 
46
                        get {
 
47
                                int val = 2 * HorizontalBuffer;
 
48
                                foreach (AbstractDockItem di in DockItems)
 
49
                                        val += 2 * DockPreferences.IconBorderWidth + di.Width;
 
50
                                return val;
 
51
                        }
 
52
                }
 
53
 
 
54
                public int DockHeight {
 
55
                        get {
 
56
                                return IconSize + 2 * VerticalBuffer;
 
57
                        }
 
58
                }
 
59
                
 
60
                public int VerticalBuffer {
 
61
                        get { return 5; }
 
62
                }
 
63
                
 
64
                public int HorizontalBuffer {
 
65
                        get { return 7; }
 
66
                }
 
67
                
 
68
                int IconSize {
 
69
                        get { return DockPreferences.IconSize; }
 
70
                }
 
71
                
 
72
                int Width {
 
73
                        get { return parent.Width; }
 
74
                }
 
75
                
 
76
                int Height {
 
77
                        get { return parent.Height; }
 
78
                }
 
79
                
 
80
                int ZoomSize {
 
81
                        get { return DockPreferences.ZoomSize; }
 
82
                }
 
83
                
 
84
                public Rectangle MinimumDockArea { get; private set; }
 
85
                
 
86
                internal ItemPositionProvider(DockArea parent)
 
87
                {
 
88
                        this.parent = parent;
 
89
                        MinimumDockArea = CalculateMinimumArea ();
 
90
                        static_positions = new List<Gdk.Point> ();
 
91
 
 
92
                        RegisterEvents ();
 
93
                }
 
94
 
 
95
                void RegisterEvents ()
 
96
                {
 
97
                        DockPreferences.IconSizeChanged += HandleIconSizeChanged;
 
98
                        DockServices.ItemsService.DockItemsChanged += HandleDockItemsChanged;
 
99
                }
 
100
 
 
101
                void UnregisterEvents ()
 
102
                {
 
103
                        DockPreferences.IconSizeChanged -= HandleIconSizeChanged;
 
104
                        DockServices.ItemsService.DockItemsChanged -= HandleDockItemsChanged;
 
105
                }
 
106
 
 
107
                void HandleDockItemsChanged(IEnumerable<AbstractDockItem> items)
 
108
                {
 
109
                        ForceUpdate ();
 
110
                }
 
111
                
 
112
                void HandleIconSizeChanged ()
 
113
                {
 
114
                        ForceUpdate ();
 
115
                }
 
116
 
 
117
                Rectangle CalculateMinimumArea ()
 
118
                {
 
119
                        int widthOffset;
 
120
                        widthOffset = (Width - DockWidth) / 2;
 
121
                        
 
122
                        Gdk.Rectangle rect;
 
123
                        switch (DockPreferences.Orientation) {
 
124
                        case DockOrientation.Bottom:
 
125
                                rect = new Gdk.Rectangle (widthOffset, Height - DockHeight, DockWidth, DockHeight);
 
126
                                break;
 
127
                        case DockOrientation.Top:
 
128
                                rect = new Gdk.Rectangle (widthOffset, 0, DockWidth, DockHeight);
 
129
                                break;
 
130
                        default:
 
131
                                rect = new Gdk.Rectangle (0, 0, 0, 0);
 
132
                                break;
 
133
                        }
 
134
 
 
135
                        return rect;
 
136
                }
 
137
                
 
138
                public void ForceUpdate ()
 
139
                {
 
140
                        static_positions.Clear ();
 
141
                        MinimumDockArea = CalculateMinimumArea ();
 
142
                }
 
143
                
 
144
                public Rectangle DockArea (double zoomByEntryTime, Gdk.Point cursor)
 
145
                {
 
146
                        Cairo.PointD startPosition, endPosition;
 
147
                        double start_zoom, end_zoom;
 
148
                        IconZoomedPosition (0, zoomByEntryTime, cursor, out startPosition, out start_zoom);
 
149
                        IconZoomedPosition (DockItems.Count - 1, zoomByEntryTime, cursor, out endPosition, out end_zoom);
 
150
                        
 
151
                        int leftEdge, rightEdge, topEdge, bottomEdge;
 
152
                        double startEdgeConstant = start_zoom * (IconSize / 2) + (start_zoom * HorizontalBuffer) + DockPreferences.IconBorderWidth;
 
153
                        double endEdgeConstant = end_zoom * (IconSize / 2) + (end_zoom * HorizontalBuffer) + DockPreferences.IconBorderWidth;
 
154
                        
 
155
                        switch (DockPreferences.Orientation) {
 
156
                        case DockOrientation.Bottom:
 
157
                                leftEdge = (int) (startPosition.X - startEdgeConstant);
 
158
                                rightEdge = (int) (endPosition.X + endEdgeConstant);
 
159
                                bottomEdge = Height;
 
160
                                topEdge = Height - DockHeight;
 
161
                                break;
 
162
                        case DockOrientation.Top:
 
163
                                leftEdge = (int) (startPosition.X - startEdgeConstant);
 
164
                                rightEdge = (int) (endPosition.X + endEdgeConstant);
 
165
                                bottomEdge = DockHeight;
 
166
                                topEdge = 0;
 
167
                                break;
 
168
                        default:
 
169
                                leftEdge = rightEdge = topEdge = bottomEdge = 0;
 
170
                                break;
 
171
                        }
 
172
                        
 
173
                        Gdk.Rectangle rect = new Gdk.Rectangle (leftEdge, 
 
174
                                                                topEdge, 
 
175
                                                                Math.Abs (leftEdge - rightEdge), 
 
176
                                                                Math.Abs (topEdge - bottomEdge));
 
177
 
 
178
                        return rect;
 
179
                }
 
180
                
 
181
                public Gdk.Point IconUnzoomedPosition (int icon)
 
182
                {
 
183
                        if (static_positions.Count != DockItems.Count) {
 
184
                                static_positions.Clear ();
 
185
                                
 
186
                                for (int i = 0; i < DockItems.Count; i++) {
 
187
                                        static_positions.Add (CalculateIconUnzoomedPosition (i));
 
188
                                }
 
189
                        }
 
190
                        
 
191
                        if (DockItems.Count <= icon)
 
192
                                return CalculateIconUnzoomedPosition (icon);
 
193
                        
 
194
                        return static_positions [icon];
 
195
                }
 
196
                
 
197
                private Gdk.Point CalculateIconUnzoomedPosition (int icon)
 
198
                {
 
199
                        // the first icons center is at dock X + border + IconBorder + half its width
 
200
                        // it is subtle, but it *is* a mistake to add the half width until the end.  adding
 
201
                        // premature will add the wrong width.  It hurts the brain.
 
202
                        if (DockItems.Count <= icon)
 
203
                                return new Gdk.Point (0, 0);
 
204
 
 
205
                        int startOffset = HorizontalBuffer + DockPreferences.IconBorderWidth;
 
206
 
 
207
 
 
208
                        // this awkward structure is faster than the simpler implemenation by about 30%
 
209
                        // while this would normally mean nothing, this method sees lots of use and can
 
210
                        // afford a bit of ugly in exchange for a bit of speed.
 
211
                        int i = 0;
 
212
                        foreach (AbstractDockItem di in DockItems) {
 
213
                                if (!(i < icon))
 
214
                                        break;
 
215
                                startOffset += di.Width;
 
216
                                i++;
 
217
                        }
 
218
 
 
219
                        startOffset += icon * 2 * DockPreferences.IconBorderWidth;
 
220
                        startOffset += DockItems [icon].Width >> 1;
 
221
 
 
222
                        switch (DockPreferences.Orientation) {
 
223
                        case DockOrientation.Bottom:
 
224
                                startOffset += MinimumDockArea.X;
 
225
                                return new Gdk.Point (startOffset, Height - (DockHeight >> 1));
 
226
                                
 
227
                        case DockOrientation.Top:
 
228
                                startOffset += MinimumDockArea.X;
 
229
                                return new Gdk.Point (startOffset, DockHeight >> 1);
 
230
                        default:
 
231
                                return new Gdk.Point (0, 0);
 
232
                        }
 
233
                }
 
234
                
 
235
                public void IconZoomedPosition (int icon, double zoomByEntryTime, Gdk.Point cursor, out Cairo.PointD position, out double zoom)
 
236
                {
 
237
                        // get our actual center
 
238
                        Gdk.Point center = IconUnzoomedPosition (icon);
 
239
                        cursor = RebaseCursor (cursor);
 
240
 
 
241
                        double cursorOrientedPosition, centerOrientedPosition;
 
242
                        cursorOrientedPosition = cursor.X;
 
243
                        centerOrientedPosition = center.X;
 
244
                        
 
245
                        // ZoomPercent is a number greater than 1.  It should never be less than one.
 
246
                        // ZoomIn is a range of 0 to 1. we need a number that is 1 when ZoomIn is 0, 
 
247
                        // and ZoomPercent when ZoomIn is 1.  Then we treat this as 
 
248
                        // if it were the ZoomPercent for the rest of the calculation
 
249
                        double zoomInPercent = 1 + (DockPreferences.ZoomPercent - 1) * zoomByEntryTime;
 
250
                        
 
251
                        // offset from the center of the true position, ranged between 0 and half of the zoom range
 
252
                        double offset = Math.Min (Math.Abs (cursorOrientedPosition - centerOrientedPosition), ZoomSize >> 1);
 
253
                        
 
254
                        if (ZoomSize == 0) {
 
255
                                zoom = 1;
 
256
                        } else {
 
257
                                double offsetPercent = offset / (ZoomSize / 2.0);
 
258
                                // zoom is calculated as 1 through target_zoom (default 2).  
 
259
                                // The larger your offset, the smaller your zoom
 
260
                                
 
261
                                // First we get the point on our curve that defines out current zoom
 
262
                                // offset is always going to fall on a point on the curve >= 0
 
263
                                zoom = 1 - offsetPercent * offsetPercent;
 
264
                                
 
265
                                // scale this to match out zoomInPercent
 
266
                                zoom = 1 + zoom * (zoomInPercent - 1);
 
267
                                
 
268
                                // pull in our offset to make things less spaced out
 
269
                                // explaination since this is a bit tricky...
 
270
                                // we have three terms, basically offset = f(x) * h(x) * g(x)
 
271
                                // f(x) == offset identify
 
272
                                // h(x) == a number from 0 to DockPreference.ZoomPercent - 1.  This is used to get the smooth "zoom in" effect.
 
273
                                //         additionally serves to "curve" the offset based on the max zoom
 
274
                                // g(x) == a term used to move the ends of the zoom inward.  Precalculated that the edges should be 66% of the current
 
275
                                //         value. The center is 100%. (1 - offsetPercent) == 0,1 distance from center
 
276
                                // The .66 value comes from the area under the curve.  Dont as me to explain it too much because it's too clever for me
 
277
                                offset = offset * (zoomInPercent - 1) * (1 - offsetPercent / 3);
 
278
                        }
 
279
                        
 
280
                        if (cursorOrientedPosition > centerOrientedPosition) {
 
281
                                centerOrientedPosition -= offset;
 
282
                        } else {
 
283
                                centerOrientedPosition += offset;
 
284
                        }
 
285
 
 
286
                        if (DockItems [icon].ScalingType == ScalingType.None) {
 
287
                                zoom = 1;
 
288
                                switch (DockPreferences.Orientation) {
 
289
                                case DockOrientation.Bottom:
 
290
                                        position = new Cairo.PointD (centerOrientedPosition, center.Y);
 
291
                                        break;
 
292
                                case DockOrientation.Top:
 
293
                                        position = new Cairo.PointD (centerOrientedPosition, center.Y);
 
294
                                        break;
 
295
                                default:
 
296
                                        position = new Cairo.PointD (0,0);
 
297
                                        break;
 
298
                                }
 
299
                                return;
 
300
                        }
 
301
                        
 
302
                        double zoomedCenterHeight = VerticalBuffer + DockItems [icon].Height * zoom / 2.0;
 
303
                        
 
304
                        if (zoom == 1)
 
305
                                centerOrientedPosition = Math.Round (centerOrientedPosition);
 
306
                        
 
307
                        switch (DockPreferences.Orientation) {
 
308
                        case DockOrientation.Bottom:
 
309
                                position = new Cairo.PointD (centerOrientedPosition, Height - zoomedCenterHeight);
 
310
                                break;
 
311
                        case DockOrientation.Top:
 
312
                                position = new Cairo.PointD (centerOrientedPosition, zoomedCenterHeight);
 
313
                                break;
 
314
                        default:
 
315
                                position = new Cairo.PointD (0, 0);
 
316
                                break;
 
317
                        }
 
318
                }
 
319
 
 
320
                public int IndexAtPosition (int x, int y)
 
321
                {
 
322
                        return IndexAtPosition (new Gdk.Point (x, y));
 
323
                }
 
324
                
 
325
                public int IndexAtPosition (Gdk.Point location)
 
326
                {
 
327
                        int position = location.X;
 
328
                        int startOffset = MinimumDockArea.X + HorizontalBuffer;
 
329
 
 
330
                        int i = 0;
 
331
                        int width;
 
332
                        foreach (AbstractDockItem di in DockItems) {
 
333
                                width = di.Width + 2 * DockPreferences.IconBorderWidth;
 
334
                                if (position >= startOffset && position <= startOffset + width)
 
335
                                        return i;
 
336
                                startOffset += width;
 
337
                                i++;
 
338
                        }
 
339
                        return -1;
 
340
                }
 
341
                
 
342
                Gdk.Point RebaseCursor (Gdk.Point point)
 
343
                {
 
344
                        int left = MinimumDockArea.X;
 
345
                        int right = left + MinimumDockArea.Width;
 
346
                        return new Gdk.Point (Math.Max (Math.Min (point.X, right), left), point.Y);
 
347
                }
 
348
 
 
349
                #region IDisposable implementation 
 
350
                
 
351
                public void Dispose ()
 
352
                {
 
353
                        UnregisterEvents ();
 
354
                        parent = null;
 
355
                }
 
356
                
 
357
                #endregion 
 
358
                
 
359
        }
 
360
}