comparison FBGUI/FBGUI.cs @ 30:24e918d2ac18

FBGUI: Added per-control Cursor handling, added more cursors for Form operations, fixed ImageBox image sizing
author Ivo Smits <Ivo@UCIS.nl>
date Thu, 18 Apr 2013 18:48:38 +0200
parents FBGUI.cs@5bfc6c68591e
children af27992b5972
comparison
equal deleted inserted replaced
29:2ecb82eea559 30:24e918d2ac18
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Drawing;
5 using System.Drawing.Drawing2D;
6 using System.Drawing.Imaging;
7 using System.IO;
8 using System.Reflection;
9 using System.Threading;
10 using System.Windows.Forms;
11 using UCIS.VNCServer;
12 using ThreadingTimer = System.Threading.Timer;
13
14 namespace UCIS.FBGUI {
15 public interface IFBGControl {
16 Rectangle Bounds { get; set; }
17 Boolean Visible { get; set; }
18 FBGCursor Cursor { get; }
19 void Paint(Graphics g);
20 void MouseMove(Point position, MouseButtons buttons);
21 void MouseDown(Point position, MouseButtons buttons);
22 void MouseUp(Point position, MouseButtons buttons);
23 void KeyDown(Keys key);
24 void KeyPress(Char keyChar);
25 void KeyUp(Keys key);
26 void LostKeyboardCapture();
27 void Orphaned();
28 }
29 public interface IFBGContainerControl {
30 void Invalidate(IFBGControl control, Rectangle rect);
31 void AddControl(IFBGControl control);
32 void RemoveControl(IFBGControl control);
33 Boolean CaptureMouse(IFBGControl control, Boolean capture);
34 Boolean CaptureKeyboard(IFBGControl control, Boolean capture);
35 }
36 public class FBGControl : IFBGControl {
37 private Rectangle bounds = new Rectangle(0, 0, 100, 100);
38 private Color backColor = Color.Transparent;
39 private Boolean visible = true;
40 protected FBGCursor CurrentCursor = null;
41 public virtual IFBGContainerControl Parent { get; private set; }
42 public event MouseEventHandler OnMouseDown;
43 public event MouseEventHandler OnMouseMove;
44 public event MouseEventHandler OnMouseUp;
45 public event PaintEventHandler OnPaint;
46 public event EventHandler OnResize;
47 public event EventHandler OnMove;
48 public FBGControl(IFBGContainerControl parent) {
49 this.Parent = parent;
50 if (Parent != null) Parent.AddControl(this);
51 }
52 public virtual Rectangle Bounds {
53 get { return bounds; }
54 set {
55 if (bounds == value) return;
56 Rectangle old = bounds;
57 bounds = value;
58 Parent.Invalidate(this, Rectangle.Union(new Rectangle(Point.Empty, value.Size), new Rectangle(old.X - value.X, old.Y - value.Y, old.Width, old.Height)));
59 if (value.Location != old.Location) RaiseEvent(OnMove);
60 if (value.Size != old.Size) RaiseEvent(OnResize);
61 }
62 }
63 public virtual Boolean Visible {
64 get { return visible; }
65 set {
66 visible = value;
67 Invalidate();
68 }
69 }
70 FBGCursor IFBGControl.Cursor { get { return CurrentCursor; } }
71 public virtual FBGCursor Cursor { get { return CurrentCursor; } set { CurrentCursor = value; } }
72 public Size Size { get { return Bounds.Size; } set { Rectangle r = Bounds; r.Size = value; Bounds = r; } }
73 public Point Location { get { return Bounds.Location; } set { Rectangle r = Bounds; r.Location = value; Bounds = r; } }
74 public int Left { get { return Bounds.Left; } set { Rectangle r = Bounds; r.X = value; Bounds = r; } }
75 public int Top { get { return Bounds.Top; } set { Rectangle r = Bounds; r.Y = value; Bounds = r; } }
76 public int Width { get { return Bounds.Width; } set { Rectangle r = Bounds; r.Width = value; Bounds = r; } }
77 public int Height { get { return Bounds.Height; } set { Rectangle r = Bounds; r.Height = value; Bounds = r; } }
78 public virtual Color BackColor { get { return backColor; } set { if (backColor == value) return; backColor = value; Invalidate(); } }
79 public virtual void Invalidate() {
80 Invalidate(new Rectangle(Point.Empty, Bounds.Size));
81 }
82 public virtual void Invalidate(Rectangle rect) {
83 Parent.Invalidate(this, rect);
84 }
85 void IFBGControl.Paint(Graphics g) { Paint(g); }
86 void IFBGControl.MouseMove(Point position, MouseButtons buttons) { MouseMove(position, buttons); }
87 void IFBGControl.MouseDown(Point position, MouseButtons buttons) { MouseDown(position, buttons); }
88 void IFBGControl.MouseUp(Point position, MouseButtons buttons) { MouseUp(position, buttons); }
89 void IFBGControl.KeyDown(Keys g) { KeyDown(g); }
90 void IFBGControl.KeyPress(Char g) { KeyPress(g); }
91 void IFBGControl.KeyUp(Keys g) { KeyUp(g); }
92 void IFBGControl.LostKeyboardCapture() { LostKeyboardCapture(); }
93 void IFBGControl.Orphaned() { Orphaned(); }
94 protected virtual void Paint(Graphics g) {
95 if (!visible) return;
96 if (backColor.A == 0xff) g.Clear(backColor);
97 else if (backColor.A != 0) using (Brush brush = new SolidBrush(backColor)) g.FillRectangle(brush, g.ClipBounds);
98 RaiseEvent(OnPaint, new PaintEventArgs(g, Rectangle.Round(g.ClipBounds)));
99 }
100 protected virtual void MouseMove(Point position, MouseButtons buttons) { RaiseEvent(OnMouseMove, new MouseEventArgs(buttons, 0, position.X, position.Y, 0)); }
101 protected virtual void MouseDown(Point position, MouseButtons buttons) { RaiseEvent(OnMouseDown, new MouseEventArgs(buttons, 1, position.X, position.Y, 0)); }
102 protected virtual void MouseUp(Point position, MouseButtons buttons) { RaiseEvent(OnMouseUp, new MouseEventArgs(buttons, 1, position.X, position.Y, 0)); }
103 protected virtual Boolean CaptureMouse(Boolean capture) {
104 return Parent.CaptureMouse(this, capture);
105 }
106 protected virtual void KeyDown(Keys key) { }
107 protected virtual void KeyPress(Char keyChar) { }
108 protected virtual void KeyUp(Keys key) { }
109 protected virtual Boolean CaptureKeyboard(Boolean capture) {
110 return Parent.CaptureKeyboard(this, capture);
111 }
112 protected virtual void LostKeyboardCapture() { }
113 protected virtual void Orphaned() {
114 //IDisposable disp = this as IDisposable;
115 //if (!ReferenceEquals(disp, null)) disp.Dispose();
116 }
117 protected void RaiseEvent(KeyEventHandler eh, KeyEventArgs ea) { if (eh != null) eh(this, ea); }
118 protected void RaiseEvent(KeyPressEventHandler eh, KeyPressEventArgs ea) { if (eh != null) eh(this, ea); }
119 protected void RaiseEvent(MouseEventHandler eh, MouseEventArgs ea) { if (eh != null) eh(this, ea); }
120 protected void RaiseEvent(PaintEventHandler eh, PaintEventArgs ea) { if (eh != null) eh(this, ea); }
121 protected void RaiseEvent<T>(EventHandler<T> eh, T ea) where T : EventArgs { if (eh != null) eh(this, ea); }
122 protected void RaiseEvent(EventHandler eh, EventArgs ea) { if (eh != null) eh(this, ea); }
123 protected void RaiseEvent(EventHandler eh) { if (eh != null) eh(this, new EventArgs()); }
124 }
125 public class FBGContainerControl : FBGControl, IFBGContainerControl {
126 protected List<IFBGControl> controls = new List<IFBGControl>();
127 protected IFBGControl mouseCaptureControl = null;
128 protected IFBGControl keyboardCaptureControl = null;
129 private Rectangle childarea = Rectangle.Empty;
130 protected FBGCursor DefaultCursor = null;
131 public Rectangle ClientRectangle { get { return childarea; } protected set { childarea = value; Invalidate(); } }
132 public Size ClientSize { get { return childarea.Size; } set { Bounds = new Rectangle(Bounds.Location, Bounds.Size - childarea.Size + value); } }
133 public override FBGCursor Cursor { get { return DefaultCursor; } set { DefaultCursor = value; } }
134 public FBGContainerControl(IFBGContainerControl parent) : base(parent) { }
135 void IFBGContainerControl.AddControl(IFBGControl control) { AddControl(control); }
136 protected virtual void AddControl(IFBGControl control) {
137 controls.Add(control);
138 if (control.Visible) Invalidate(control);
139 }
140 public virtual void RemoveControl(IFBGControl control) {
141 if (controls.Remove(control)) {
142 if (control.Visible) Invalidate(control);
143 CaptureMouse(control, false);
144 CaptureKeyboard(control, false);
145 control.Orphaned();
146 }
147 }
148 public virtual Point PointToChild(IFBGControl child, Point point) {
149 return point - (Size)child.Bounds.Location - (Size)ClientRectangle.Location;
150 }
151 public virtual Point PointFromChild(IFBGControl child, Point point) {
152 return point + (Size)child.Bounds.Location + (Size)ClientRectangle.Location;
153 }
154 public virtual void BringControlToFront(IFBGControl control) {
155 if (controls.Count == 0) return;
156 if (ReferenceEquals(controls[controls.Count - 1], control)) return;
157 if (!controls.Remove(control)) return;
158 controls.Add(control);
159 if (control.Visible) Invalidate(control);
160 }
161 public virtual void Invalidate(IFBGControl control) {
162 Invalidate(new Rectangle(PointFromChild(control, Point.Empty), control.Bounds.Size));
163 }
164 public virtual void Invalidate(IFBGControl control, Rectangle rect) {
165 Invalidate(new Rectangle(PointFromChild(control, rect.Location), rect.Size));
166 }
167 protected override void Paint(Graphics g) {
168 base.Paint(g);
169 if (controls == null) return;
170 GraphicsState state2 = null;
171 if (!childarea.IsEmpty) {
172 state2 = g.Save();
173 g.TranslateTransform(childarea.X, childarea.Y, MatrixOrder.Append);
174 g.IntersectClip(new Rectangle(Point.Empty, childarea.Size));
175 }
176 foreach (IFBGControl control in controls) {
177 if (!control.Visible) continue;
178 if (control.Bounds.Width <= 0 || control.Bounds.Height <= 0) continue;
179 if (!g.ClipBounds.IntersectsWith((RectangleF)control.Bounds)) continue;
180 GraphicsState state = g.Save();
181 g.TranslateTransform(control.Bounds.X, control.Bounds.Y, MatrixOrder.Append);
182 g.IntersectClip(new Rectangle(Point.Empty, control.Bounds.Size));
183 control.Paint(g);
184 g.Restore(state);
185 }
186 if (state2 != null) g.Restore(state2);
187 }
188 public IFBGControl FindControlAtPosition(Point p) {
189 if (!childarea.IsEmpty && !childarea.Contains(p)) return null;
190 p.Offset(-childarea.X, -childarea.Y);
191 return ((List<IFBGControl>)controls).FindLast(delegate(IFBGControl control) { return control.Visible && control.Bounds.Contains(p); });
192 }
193 protected override void MouseMove(Point position, MouseButtons buttons) {
194 IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : FindControlAtPosition(position);
195 if (control == null) {
196 base.MouseMove(position, buttons);
197 } else {
198 control.MouseMove(PointToChild(control, position), buttons);
199 }
200 CurrentCursor = control == null || control.Cursor == null ? DefaultCursor : control.Cursor;
201 }
202 protected override void MouseDown(Point position, MouseButtons buttons) {
203 IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : FindControlAtPosition(position);
204 if (control == null) {
205 base.MouseDown(position, buttons);
206 } else {
207 control.MouseDown(PointToChild(control, position), buttons);
208 }
209 CurrentCursor = control == null || control.Cursor == null ? DefaultCursor : control.Cursor;
210 }
211 protected override void MouseUp(Point position, MouseButtons buttons) {
212 IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : FindControlAtPosition(position);
213 if (control == null) {
214 base.MouseUp(position, buttons);
215 } else {
216 control.MouseUp(PointToChild(control, position), buttons);
217 }
218 CurrentCursor = control == null || control.Cursor == null ? DefaultCursor : control.Cursor;
219 }
220 Boolean IFBGContainerControl.CaptureMouse(IFBGControl control, Boolean capture) { return CaptureMouse(control, capture); }
221 protected Boolean CaptureMouse(IFBGControl control, Boolean capture) {
222 if (capture && !ReferenceEquals(mouseCaptureControl, null)) return false;
223 if (!capture && !ReferenceEquals(mouseCaptureControl, control)) return false;
224 if (!CaptureMouse(capture)) return false;
225 mouseCaptureControl = capture ? control : null;
226 return true;
227 }
228 protected override void KeyDown(Keys key) {
229 if (ReferenceEquals(keyboardCaptureControl, null)) base.KeyDown(key);
230 else keyboardCaptureControl.KeyDown(key);
231 }
232 protected override void KeyPress(Char keyChar) {
233 if (ReferenceEquals(keyboardCaptureControl, null)) base.KeyPress(keyChar);
234 else keyboardCaptureControl.KeyPress(keyChar);
235 }
236 protected override void KeyUp(Keys key) {
237 if (ReferenceEquals(keyboardCaptureControl, null)) base.KeyUp(key);
238 else keyboardCaptureControl.KeyUp(key);
239 }
240 Boolean IFBGContainerControl.CaptureKeyboard(IFBGControl control, Boolean capture) { return CaptureKeyboard(control, capture); }
241 protected Boolean CaptureKeyboard(IFBGControl control, Boolean capture) {
242 if (!capture && !ReferenceEquals(keyboardCaptureControl, control)) return false;
243 if (!CaptureKeyboard(capture)) return false;
244 IFBGControl prev = keyboardCaptureControl;
245 keyboardCaptureControl = capture ? control : null;
246 if (prev != null) LostKeyboardCapture();
247 return true;
248 }
249 protected override void LostKeyboardCapture() {
250 base.LostKeyboardCapture();
251 if (keyboardCaptureControl != null) keyboardCaptureControl.LostKeyboardCapture();
252 }
253 protected override void Orphaned() {
254 base.Orphaned();
255 IFBGControl[] c = controls.ToArray();
256 controls.Clear();
257 foreach (IFBGControl control in c) control.Orphaned();
258 mouseCaptureControl = null;
259 keyboardCaptureControl = null;
260 }
261 }
262 public class FBGDockContainer : FBGContainerControl {
263 private Dictionary<IFBGControl, DockStyle> dockStyles = new Dictionary<IFBGControl, DockStyle>();
264 private Dictionary<IFBGControl, AnchorStyles> anchorStyles = new Dictionary<IFBGControl, AnchorStyles>();
265 private Rectangle oldBounds;
266 public FBGDockContainer(IFBGContainerControl parent)
267 : base(parent) {
268 oldBounds = ClientRectangle.IsEmpty ? Bounds : new Rectangle(Bounds.Location + (Size)ClientRectangle.Location, ClientRectangle.Size);
269 }
270 protected override void AddControl(IFBGControl control) {
271 base.AddControl(control);
272 }
273 public override void BringControlToFront(IFBGControl control) {
274 base.BringControlToFront(control);
275 if (dockStyles.ContainsKey(control)) DoLayout();
276 }
277 public override void RemoveControl(IFBGControl control) {
278 base.RemoveControl(control);
279 if (dockStyles.Remove(control)) DoLayout();
280 }
281 public override Rectangle Bounds {
282 get { return base.Bounds; }
283 set {
284 base.Bounds = value;
285 DoLayout();
286 Rectangle newBounds = ClientRectangle.IsEmpty ? Bounds : new Rectangle(Bounds.Location + (Size)ClientRectangle.Location, ClientRectangle.Size);
287 foreach (KeyValuePair<IFBGControl, AnchorStyles> c in anchorStyles) {
288 Rectangle b = c.Key.Bounds;
289 if ((c.Value & AnchorStyles.Right) != 0) {
290 if ((c.Value & AnchorStyles.Left) == 0) b.X += newBounds.Width - oldBounds.Width;
291 else b.Width += newBounds.Width - oldBounds.Width;
292 } else if ((c.Value & AnchorStyles.Left) == 0) b.X += newBounds.X - oldBounds.X;
293 if ((c.Value & AnchorStyles.Bottom) != 0) {
294 if ((c.Value & AnchorStyles.Top) == 0) b.Y += newBounds.Height - oldBounds.Height;
295 else b.Height += newBounds.Height - oldBounds.Height;
296 } else if ((c.Value & AnchorStyles.Top) == 0) b.Y += newBounds.Y - oldBounds.Y;
297 c.Key.Bounds = b;
298 }
299 oldBounds = newBounds;
300 }
301 }
302 public DockStyle GetDockStyle(IFBGControl control) {
303 DockStyle ds;
304 if (!dockStyles.TryGetValue(control, out ds)) ds = DockStyle.None;
305 return ds;
306 }
307 public void SetDockStyle(IFBGControl control, DockStyle style) {
308 if (style == DockStyle.None) {
309 if (dockStyles.Remove(control)) DoLayout();
310 } else if (controls.Contains(control)) {
311 anchorStyles.Remove(control);
312 dockStyles[control] = style;
313 DoLayout();
314 }
315 }
316 public AnchorStyles GetAnchorStyle(IFBGControl control) {
317 AnchorStyles ds;
318 if (!anchorStyles.TryGetValue(control, out ds)) ds = AnchorStyles.Left | AnchorStyles.Top;
319 return ds;
320 }
321 public void SetAnchorStyle(IFBGControl control, AnchorStyles style) {
322 if (style == (AnchorStyles.Left | AnchorStyles.Top)) {
323 anchorStyles.Remove(control);
324 } else if (controls.Contains(control)) {
325 dockStyles.Remove(control);
326 anchorStyles[control] = style;
327 }
328 }
329 public void SetAnchor(IFBGControl control, AnchorStyles style, int value) {
330 if (controls.Contains(control)) {
331 AnchorStyles oldstyle;
332 if (!anchorStyles.TryGetValue(control, out oldstyle)) oldstyle = AnchorStyles.Left | AnchorStyles.Top;
333 Rectangle b = control.Bounds;
334 switch (style) {
335 case AnchorStyles.None: throw new ArgumentException("style", "Anchor style can not be None");
336 case AnchorStyles.Left: b.X = value; break;
337 case AnchorStyles.Top: b.Y = value; break;
338 case AnchorStyles.Right:
339 if ((oldstyle & AnchorStyles.Left) == 0) b.X = ClientRectangle.Width - b.Width - value;
340 else b.Width = ClientRectangle.Width - b.X - value;
341 break;
342 case AnchorStyles.Bottom:
343 if ((oldstyle & AnchorStyles.Top) == 0) b.Y = ClientRectangle.Height - b.Height - value;
344 else b.Height = ClientRectangle.Height - b.Y - value;
345 break;
346 default: throw new ArgumentOutOfRangeException("style", "The value vor the style argument is invalid");
347 }
348 control.Bounds = b;
349 dockStyles.Remove(control);
350 anchorStyles[control] = oldstyle | style;
351 }
352 }
353 private void DoLayout() {
354 Rectangle a = new Rectangle(Point.Empty, ClientRectangle.IsEmpty ? Bounds.Size : ClientRectangle.Size);
355 foreach (KeyValuePair<IFBGControl, DockStyle> c in dockStyles) {
356 Rectangle b = c.Key.Bounds;
357 if (c.Value == DockStyle.Left) {
358 b.Location = a.Location;
359 b.Height = a.Height;
360 a.X += b.Width;
361 a.Width -= b.Width;
362 } else if (c.Value == DockStyle.Top) {
363 b.Location = a.Location;
364 b.Width = a.Width;
365 a.Y += b.Height;
366 a.Height -= b.Height;
367 } else if (c.Value == DockStyle.Right) {
368 b.X = a.X + a.Width - b.Width;
369 b.Y = a.Y;
370 b.Height = a.Height;
371 a.Width -= b.Width;
372 } else if (c.Value == DockStyle.Bottom) {
373 b.X = a.X;
374 b.Y = a.Y + a.Height - b.Height;
375 b.Width = a.Width;
376 a.Height -= b.Height;
377 } else if (c.Value == DockStyle.Fill) {
378 b = a;
379 }
380 c.Key.Bounds = b;
381 if (a.Width < 0) a.Width = 0;
382 if (a.Height < 0) a.Height = 0;
383 }
384 }
385 }
386 public class FBGGroupBox : FBGDockContainer {
387 private String text = String.Empty;
388 public FBGGroupBox(IFBGContainerControl parent)
389 : base(parent) {
390 ClientRectangle = new Rectangle(1, 15, Bounds.Width - 2, Bounds.Height - 17);
391 }
392 public override Rectangle Bounds {
393 get { return base.Bounds; }
394 set {
395 ClientRectangle = new Rectangle(1, 15, value.Width - 2, value.Height - 17);
396 base.Bounds = value;
397 }
398 }
399 public String Text { get { return text; } set { if (text == value) return; text = value; Invalidate(new Rectangle(0, 0, Bounds.Width, 15)); } }
400 protected override void Paint(Graphics g) {
401 base.Paint(g);
402 g.DrawRectangle(Pens.Gray, 0, 6, Bounds.Width - 1, Bounds.Height - 7);
403 SizeF ss = g.MeasureString(Text, SystemFonts.DefaultFont, new Size(Bounds.Width - 10 - 9, 15));
404 g.FillRectangle(SystemBrushes.Control, 9, 0, ss.Width, ss.Height);
405 g.DrawString(Text, SystemFonts.DefaultFont, Brushes.DarkBlue, new Rectangle(9, 0, Bounds.Width - 10 - 9, 15));
406 }
407 }
408 public class WinFormsFBGHost : Control, IFBGContainerControl {
409 private IFBGControl childControl = null;
410 private IFBGControl mouseCaptureControl = null;
411 private IFBGControl keyboardCaptureControl = null;
412 public IFBGControl ChildControl { get { return childControl; } }
413
414 public WinFormsFBGHost() {
415 DoubleBuffered = true;
416 }
417
418 public virtual Point PointToChild(IFBGControl child, Point point) {
419 return point - (Size)child.Bounds.Location;
420 }
421 public virtual Point PointFromChild(IFBGControl child, Point point) {
422 return point + (Size)child.Bounds.Location;
423 }
424
425 void IFBGContainerControl.Invalidate(IFBGControl control, Rectangle rect) {
426 Invalidate(new Rectangle(PointFromChild(control, rect.Location), rect.Size));
427 }
428 void IFBGContainerControl.AddControl(IFBGControl control) {
429 if (!ReferenceEquals(childControl, null)) throw new InvalidOperationException("This container can have only one child control");
430 childControl = control;
431 control.Bounds = new Rectangle(Point.Empty, ClientSize);
432 Invalidate(control.Bounds);
433 }
434 void IFBGContainerControl.RemoveControl(IFBGControl control) {
435 if (!ReferenceEquals(childControl, control)) return;
436 childControl = null;
437 Invalidate(control.Bounds);
438 if (mouseCaptureControl == control) mouseCaptureControl = null;
439 if (keyboardCaptureControl == control) control = null;
440 control.Orphaned();
441 }
442 Boolean IFBGContainerControl.CaptureMouse(IFBGControl control, Boolean capture) {
443 if (capture && !ReferenceEquals(mouseCaptureControl, null)) return false;
444 if (!capture && !ReferenceEquals(mouseCaptureControl, control)) return false;
445 mouseCaptureControl = capture ? control : null;
446 return true;
447 }
448 Boolean IFBGContainerControl.CaptureKeyboard(IFBGControl control, Boolean capture) {
449 if (!capture && !ReferenceEquals(keyboardCaptureControl, control)) return false;
450 keyboardCaptureControl = capture ? control : null;
451 return true;
452 }
453
454 protected override void OnPaint(PaintEventArgs e) {
455 base.OnPaint(e);
456 Graphics g = e.Graphics;
457 GraphicsState state = g.Save();
458 g.SetClip(e.ClipRectangle);
459 if (ReferenceEquals(childControl, null)) return;
460 if (childControl.Bounds.Width <= 0 || childControl.Bounds.Height <= 0) return;
461 if (!g.ClipBounds.IntersectsWith((RectangleF)childControl.Bounds)) return;
462 g.TranslateTransform(childControl.Bounds.X, childControl.Bounds.Y, MatrixOrder.Append);
463 g.IntersectClip(new Rectangle(Point.Empty, childControl.Bounds.Size));
464 childControl.Paint(g);
465 g.Restore(state);
466 }
467 protected override void OnResize(EventArgs e) {
468 if (!ReferenceEquals(childControl, null)) childControl.Bounds = new Rectangle(Point.Empty, ClientSize);
469 base.OnResize(e);
470 }
471 protected override void OnMouseDown(MouseEventArgs e) {
472 base.OnMouseDown(e);
473 IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : childControl;
474 if (control != null) control.MouseDown(PointToChild(control, e.Location), e.Button);
475 }
476 protected override void OnMouseUp(MouseEventArgs e) {
477 base.OnMouseUp(e);
478 IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : childControl;
479 if (control != null) control.MouseUp(PointToChild(control, e.Location), e.Button);
480 }
481 protected override void OnMouseMove(MouseEventArgs e) {
482 IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : childControl;
483 if (control != null) control.MouseMove(PointToChild(control, e.Location), e.Button);
484 }
485 protected override bool IsInputChar(char charCode) {
486 return true;
487 }
488 protected override bool IsInputKey(Keys keyData) {
489 return true;
490 }
491 protected override void OnKeyDown(KeyEventArgs e) {
492 //base.OnKeyDown(e);
493 if (!ReferenceEquals(keyboardCaptureControl, null)) keyboardCaptureControl.KeyDown(e.KeyData);
494 }
495 protected override void OnKeyPress(KeyPressEventArgs e) {
496 //base.OnKeyPress(e);
497 if (!ReferenceEquals(keyboardCaptureControl, null)) keyboardCaptureControl.KeyPress(e.KeyChar);
498 }
499 protected override void OnKeyUp(KeyEventArgs e) {
500 //base.OnKeyUp(e);
501 if (!ReferenceEquals(keyboardCaptureControl, null)) keyboardCaptureControl.KeyUp(e.KeyData);
502 }
503 protected override void OnHandleDestroyed(EventArgs e) {
504 if (!ReferenceEquals(childControl, null)) childControl.Orphaned();
505 base.OnHandleDestroyed(e);
506 }
507 }
508 public class FBGCursor {
509 public Image Image { get; private set; }
510 public Point Hotspot { get; private set; }
511 public Size Size { get; private set; }
512 public FBGCursor(Image image, Point hotspot) {
513 this.Image = image;
514 this.Hotspot = hotspot;
515 this.Size = image.Size;
516 }
517 public Rectangle Area { get { return new Rectangle(-Hotspot.X, -Hotspot.Y, Size.Width, Size.Height); } }
518 public FBGCursor RotateFlip(RotateFlipType type) {
519 Image img = new Bitmap(Image);
520 img.RotateFlip(type);
521 Point hs = Hotspot;
522 switch (type) {
523 case RotateFlipType.RotateNoneFlipNone: break;
524 case RotateFlipType.Rotate90FlipNone: hs = new Point(img.Width - hs.Y, hs.X); break;
525 case RotateFlipType.Rotate180FlipNone: hs = new Point(img.Width - hs.X, img.Height - hs.Y); break;
526 case RotateFlipType.Rotate270FlipNone: hs = new Point(hs.Y, img.Height - hs.X); break;
527 case RotateFlipType.RotateNoneFlipX: hs.X = img.Width - hs.X; break;
528 case RotateFlipType.Rotate90FlipX: hs = new Point(hs.Y, hs.X); break;
529 case RotateFlipType.RotateNoneFlipY: hs.Y = img.Height - hs.Y; break;
530 case RotateFlipType.Rotate90FlipY: hs = new Point(img.Width - hs.Y, img.Height - hs.X); break;
531 }
532 return new FBGCursor(img, hs);
533 }
534 public static FBGCursor FromBase64Image(String data, Point hotspot) {
535 return new FBGCursor(Image.FromStream(new MemoryStream(Convert.FromBase64String(data))), hotspot);
536 }
537 private static FBGCursor LoadFromResource(String name, int hotX, int hotY) {
538 using (Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("UCIS.FBGUI." + name + ".png")) {
539 return new FBGCursor(Image.FromStream(s), new Point(hotX, hotY));
540 }
541 }
542
543 public static readonly FBGCursor Arrow = LoadFromResource("cursor_arrow", 0, 0);
544 public static readonly FBGCursor Move = LoadFromResource("cursor_move", 8, 8);
545 public static readonly FBGCursor SizeLeft = LoadFromResource("cursor_left", 1, 10);
546 public static readonly FBGCursor SizeRight = SizeLeft.RotateFlip(RotateFlipType.RotateNoneFlipX);
547 public static readonly FBGCursor SizeTop = SizeLeft.RotateFlip(RotateFlipType.Rotate90FlipNone);
548 public static readonly FBGCursor SizeBottom = SizeLeft.RotateFlip(RotateFlipType.Rotate90FlipY);
549 public static readonly FBGCursor SizeTopLeft = LoadFromResource("cursor_topleft", 1, 1);
550 public static readonly FBGCursor SizeTopRight = SizeTopLeft.RotateFlip(RotateFlipType.RotateNoneFlipX);
551 public static readonly FBGCursor SizeBottomLeft = SizeTopLeft.RotateFlip(RotateFlipType.RotateNoneFlipY);
552 public static readonly FBGCursor SizeBottomRight = SizeTopLeft.RotateFlip(RotateFlipType.RotateNoneFlipXY);
553 public static FBGCursor ArrowCursor { get { return Arrow; } }
554 }
555 public class FBGRenderer : FBGContainerControl, IDisposable {
556 private FBGCursor cursor = null;
557 private Point cursorposition = Point.Empty;
558 public IFramebuffer Framebuffer { get; private set; }
559 private Bitmap Frontbuffer = null;
560 protected Object RenderLock = new object();
561 public event EventHandler<InvalidateEventArgs> Painted;
562 protected Size size = Size.Empty;
563 private ThreadingTimer PaintTimer = null;
564 private Boolean PaintScheduled = false;
565 private int PaintDelay = 0;
566 public Boolean SuspendDrawing {
567 get { return suspenddrawing; }
568 set {
569 lock (RenderLock) {
570 suspenddrawing = value;
571 if (!value) {
572 Refresh(DirtyRectangle);
573 DirtyRectangle = Rectangle.Empty;
574 }
575 }
576 }
577 }
578 public int RedrawDelay {
579 get { return PaintDelay; }
580 set {
581 lock (RenderLock) {
582 if (value < 0) throw new ArgumentOutOfRangeException("value");
583 PaintDelay = value;
584 if (PaintDelay == 0) {
585 if (PaintTimer != null) PaintTimer.Dispose();
586 PaintTimer = null;
587 if (PaintScheduled) PaintTimerCallback(null);
588 } else {
589 PaintTimer = new ThreadingTimer(PaintTimerCallback);
590 }
591 }
592 }
593 }
594 private Boolean suspenddrawing = false;
595 private Rectangle DirtyRectangle;
596
597 public Point CursorPosition {
598 get { return cursorposition; }
599 set { UpdateCursor(value, cursor); }
600 }
601 protected void UpdateCursor(Point position, FBGCursor cursor) {
602 if (cursorposition == position && cursor == this.cursor) return;
603 Rectangle r1 = Rectangle.Empty;
604 if (this.cursor != null) {
605 r1 = this.cursor.Area;
606 r1.Offset(cursorposition);
607 }
608 if (cursor != null) {
609 Rectangle r2 = cursor.Area;
610 r2.Offset(position);
611 r1 = r1.IsEmpty ? r2 : Rectangle.Union(r1, r2);
612 }
613 this.cursor = cursor;
614 cursorposition = position;
615 Invalidate(r1);
616 }
617 public override Rectangle Bounds {
618 get { return new Rectangle(Point.Empty, size); }
619 set { throw new NotSupportedException("Can not change the top control bounds"); }
620 }
621 public FBGRenderer(IFramebuffer fb) : this(fb.Width, fb.Height) {
622 Framebuffer = fb;
623 }
624 public FBGRenderer(Size fbsize) : this(fbsize.Width, fbsize.Height) { }
625 public FBGRenderer(int width, int height) : this(new Bitmap(width, height, PixelFormat.Format32bppRgb)) { }
626 public FBGRenderer(Bitmap bmp) : base(null) {
627 Frontbuffer = bmp;
628 BackColor = SystemColors.Control;
629 size = Frontbuffer.Size;
630 }
631 protected FBGRenderer() : base(null) {
632 BackColor = SystemColors.Control;
633 }
634 public override void Invalidate(Rectangle rect) {
635 if (rect.Width == 0 || rect.Height == 0) return;
636 lock (RenderLock) {
637 if (SuspendDrawing || PaintTimer != null) {
638 DirtyRectangle = DirtyRectangle.IsEmpty ? rect : Rectangle.Union(DirtyRectangle, rect);
639 if (!SuspendDrawing && !PaintScheduled) {
640 PaintScheduled = true;
641 PaintTimer.Change(PaintDelay, Timeout.Infinite);
642 }
643 } else {
644 Refresh(rect);
645 }
646 }
647 }
648 private void PaintTimerCallback(Object state) {
649 try {
650 lock (RenderLock) {
651 PaintScheduled = false;
652 Refresh(DirtyRectangle);
653 DirtyRectangle = Rectangle.Empty;
654 }
655 } catch (Exception ex) {
656 Debug.WriteLine(ex);
657 Console.Error.WriteLine(ex);
658 }
659 }
660 protected virtual void Refresh(Rectangle rect) {
661 lock (RenderLock) {
662 rect.Intersect(Bounds);
663 if (rect.Width == 0 || rect.Height == 0) return;
664 if (Frontbuffer != null) {
665 using (Graphics g = Graphics.FromImage(Frontbuffer)) {
666 g.SetClip(rect);
667 Paint(g);
668 }
669 if (Framebuffer != null) Framebuffer.DrawImage(Frontbuffer, rect, rect.Location);
670 }
671 RaiseEvent(Painted, new InvalidateEventArgs(rect));
672 }
673 }
674 protected override void Paint(Graphics g) {
675 base.Paint(g);
676 if (cursor != null) {
677 Point r = CursorPosition;
678 r.Offset(-cursor.Hotspot.X, -cursor.Hotspot.Y);
679 g.DrawImage(cursor.Image, new Rectangle(r, cursor.Size));
680 }
681 }
682 protected override Boolean CaptureMouse(Boolean capture) {
683 return true;
684 }
685 protected override Boolean CaptureKeyboard(bool capture) {
686 return true;
687 }
688 public virtual void Dispose() {
689 lock (RenderLock) {
690 if (PaintTimer != null) PaintTimer.Dispose();
691 PaintTimer = null;
692 }
693 Orphaned();
694 if (Frontbuffer != null) Frontbuffer.Dispose();
695 }
696 public Bitmap LockBitmapBuffer() {
697 Monitor.Enter(RenderLock);
698 return Frontbuffer;
699 }
700 public void UnlockBitmapBuffer() {
701 Monitor.Exit(RenderLock);
702 }
703 public new void MouseMove(Point position, MouseButtons buttons) { base.MouseMove(position, buttons); UpdateCursor(cursorposition, CurrentCursor); }
704 public new void MouseDown(Point position, MouseButtons buttons) { base.MouseDown(position, buttons); UpdateCursor(cursorposition, CurrentCursor); }
705 public new void MouseUp(Point position, MouseButtons buttons) { base.MouseUp(position, buttons); UpdateCursor(cursorposition, CurrentCursor); }
706 public new void KeyDown(Keys key) { base.KeyDown(key); }
707 public new void KeyPress(Char key) { base.KeyPress(key); }
708 public new void KeyUp(Keys key) { base.KeyUp(key); }
709 }
710 public class FBGForm : FBGDockContainer {
711 private Point prevPosition = Point.Empty;
712 private NonClientOps moveresize = 0;
713 private String text = String.Empty;
714 public event EventHandler TextChanged;
715 public Boolean Sizable { get; set; }
716 public Boolean Movable { get; set; }
717 public Boolean Closable { get; set; }
718 [Flags]
719 private enum NonClientOps : int {
720 None = 0,
721 Move = 1,
722 ResizeLeft = 2,
723 ResizeRight = 4,
724 ResizeTop = 8,
725 ResizeBottom = 16,
726 ButtonClose = 32,
727 MoveResize = ResizeLeft | ResizeRight | ResizeBottom | ResizeTop | Move,
728 }
729 public FBGForm(IFBGContainerControl parent) : base(parent) {
730 BackColor = SystemColors.Control;
731 ClientRectangle = new Rectangle(4, 22, Bounds.Width - 8, Bounds.Height - 26);
732 Sizable = true;
733 Movable = true;
734 Closable = false;
735 }
736 public override Rectangle Bounds {
737 get { return base.Bounds; }
738 set {
739 ClientRectangle = new Rectangle(4, 22, value.Width - 8, value.Height - 26);
740 base.Bounds = value;
741 }
742 }
743 public String Text { get { return text; } set { if (text == value) return; text = value; Invalidate(new Rectangle(0, 0, Bounds.Width, 20)); RaiseEvent(TextChanged); } }
744 private NonClientOps GetNonClientOperation(Point p) {
745 if ((new Rectangle(Bounds.Width - 5 - 14, 4, 14, 14)).Contains(p)) return NonClientOps.ButtonClose;
746 NonClientOps mr = 0;
747 if (Sizable) {
748 if (Movable) {
749 if (p.X < 4) mr |= NonClientOps.ResizeLeft;
750 if (p.Y < 4) mr |= NonClientOps.ResizeTop;
751 }
752 if (p.X >= Bounds.Width - 4) mr |= NonClientOps.ResizeRight;
753 if (p.Y >= Bounds.Height - 4) mr |= NonClientOps.ResizeBottom;
754 }
755 if (mr == 0 && Movable && p.Y < 20) mr = NonClientOps.Move;
756 return mr;
757 }
758 private void SetCursorForNonClientOperation(NonClientOps op) {
759 switch (op & NonClientOps.MoveResize) {
760 case NonClientOps.Move: CurrentCursor = FBGCursor.Move; break;
761 case NonClientOps.ResizeLeft: CurrentCursor = FBGCursor.SizeLeft; break;
762 case NonClientOps.ResizeRight: CurrentCursor = FBGCursor.SizeRight; break;
763 case NonClientOps.ResizeBottom: CurrentCursor = FBGCursor.SizeBottom; break;
764 case NonClientOps.ResizeTop: CurrentCursor = FBGCursor.SizeTop; break;
765 case NonClientOps.ResizeTop | NonClientOps.ResizeLeft: CurrentCursor = FBGCursor.SizeTopLeft; break;
766 case NonClientOps.ResizeTop | NonClientOps.ResizeRight: CurrentCursor = FBGCursor.SizeTopRight; break;
767 case NonClientOps.ResizeBottom | NonClientOps.ResizeLeft: CurrentCursor = FBGCursor.SizeBottomLeft; break;
768 case NonClientOps.ResizeBottom | NonClientOps.ResizeRight: CurrentCursor = FBGCursor.SizeBottomRight; break;
769 default: CurrentCursor = DefaultCursor; break;
770 }
771 }
772 protected override void MouseDown(Point p, MouseButtons buttons) {
773 NonClientOps mr = 0;
774 if ((buttons & MouseButtons.Left) != 0) mr = GetNonClientOperation(p);
775 if (mr != 0) {
776 moveresize = mr;
777 prevPosition = p;
778 CaptureMouse(true);
779 } else {
780 base.MouseDown(p, buttons);
781 }
782 }
783 protected override void MouseMove(Point position, MouseButtons buttons) {
784 if (moveresize == 0) {
785 base.MouseMove(position, buttons);
786 } else if ((moveresize & NonClientOps.MoveResize) != 0) {
787 Rectangle b = Bounds;
788 int dx = position.X - prevPosition.X;
789 int dy = position.Y - prevPosition.Y;
790 if (moveresize == NonClientOps.Move) {
791 b.Offset(dx, dy);
792 }
793 if ((moveresize & NonClientOps.ResizeLeft) != 0) {
794 b.X += dx;
795 b.Width -= dx;
796 } else if ((moveresize & NonClientOps.ResizeRight) != 0) {
797 b.Width += dx;
798 prevPosition.X = position.X;
799 }
800 if ((moveresize & NonClientOps.ResizeTop) != 0) {
801 b.Y += dy;
802 b.Height -= dy;
803 } else if ((moveresize & NonClientOps.ResizeBottom) != 0) {
804 b.Height += dy;
805 prevPosition.Y = position.Y;
806 }
807 if (b.Width < 55) b.Width = 55;
808 if (b.Height < 25) b.Height = 25;
809 Bounds = b;
810 }
811 SetCursorForNonClientOperation(moveresize == 0 ? GetNonClientOperation(position) : moveresize);
812 }
813 protected override void MouseUp(Point position, MouseButtons buttons) {
814 if (moveresize == 0) {
815 base.MouseUp(position, buttons);
816 } else if ((buttons & MouseButtons.Left) != 0) {
817 MouseMove(position, buttons);
818 CaptureMouse(false);
819 if (moveresize == NonClientOps.ButtonClose && (new Rectangle(Bounds.Width - 5 - 14, 4, 14, 14)).Contains(position) && Closable) Close();
820 moveresize = 0;
821 }
822 SetCursorForNonClientOperation(moveresize == 0 ? GetNonClientOperation(position) : moveresize);
823 }
824 protected override void Paint(Graphics g) {
825 base.Paint(g);
826 g.DrawRectangle(Pens.Gray, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
827 g.DrawRectangle(Pens.LightGray, 1, 1, Bounds.Width - 3, Bounds.Height - 3);
828 g.DrawRectangle(Pens.DarkGray, 2, 20, Bounds.Width - 5, Bounds.Height - 23);
829 g.DrawRectangle(Pens.Gray, 3, 21, Bounds.Width - 7, Bounds.Height - 25);
830 using (Brush b = new LinearGradientBrush(new Rectangle(0, 1, 1, 18), Color.Gray, Color.LightGray, LinearGradientMode.Vertical))
831 g.FillRectangle(b, 2, 2, Bounds.Width - 4, 18);
832
833 g.DrawString(Text, SystemFonts.CaptionFont, Brushes.Black, 4, 1);
834
835 g.DrawRectangle(Pens.Gray, Bounds.Width - 5 - 14, 4, 14, 14);
836 g.DrawRectangle(Pens.Gray, Bounds.Width - 5 - 14 - 16, 4, 14, 14);
837 g.DrawRectangle(Pens.Gray, Bounds.Width - 5 - 14 - 32, 4, 14, 14);
838 using (Brush b = new LinearGradientBrush(new Rectangle(0, 5, 1, 13), Color.LightGray, Color.DarkGray, LinearGradientMode.Vertical)) {
839 g.FillRectangle(b, Bounds.Width - 5 - 14 + 1, 5, 13, 13);
840 g.FillRectangle(b, Bounds.Width - 5 - 14 + 1 - 16, 5, 13, 13);
841 g.FillRectangle(b, Bounds.Width - 5 - 14 + 1 - 32, 5, 13, 13);
842 }
843 if (Closable) {
844 g.DrawLine(Pens.Black, Bounds.Width - 5 - 14 + 3, 4 + 3, Bounds.Width - 5 - 14 + 14 - 3, 4 + 14 - 3);
845 g.DrawLine(Pens.Black, Bounds.Width - 5 - 14 + 3, 4 + 14 - 3, Bounds.Width - 5 - 14 + 14 - 3, 4 + 3);
846 }
847 }
848 public void Close() {
849 Parent.RemoveControl(this);
850 }
851 }
852 public class FBGDesktop : FBGContainerControl {
853 FBGContainerControl windowcontainer;
854 FBGContainerControl menucontainer;
855 Boolean startup = true;
856 class FBGWindowState {
857 public IFBGControl Control;
858 public FBGButton MenuButton;
859 public Boolean Visible;
860 public Rectangle OldBounds;
861 public FBGWindowState(IFBGControl cntrl, FBGButton btn) {
862 Control = cntrl;
863 MenuButton = btn;
864 Visible = true;
865 }
866 }
867 Dictionary<IFBGControl, FBGWindowState> windowstates = new Dictionary<IFBGControl, FBGWindowState>();
868 public FBGDesktop(IFBGContainerControl parent) : base(parent) {
869 menucontainer = new FBGContainerControl(this);
870 menucontainer.Bounds = new Rectangle(0, Bounds.Height - 25, Bounds.Width, 25);
871 windowcontainer = new FBGContainerControl(this);
872 windowcontainer.Bounds = new Rectangle(0, 0, Bounds.Width, Bounds.Height - 25);
873 startup = false;
874 }
875 public override Rectangle Bounds {
876 get { return base.Bounds; }
877 set {
878 if (Bounds == value) return;
879 base.Bounds = value;
880 if (startup) return;
881 menucontainer.Bounds = new Rectangle(0, value.Height - 25, value.Width, 25);
882 windowcontainer.Bounds = new Rectangle(0, 0, value.Width, value.Height - 25);
883 ScaleMenuButtons();
884 }
885 }
886 protected override void AddControl(IFBGControl control) {
887 if (startup) {
888 base.AddControl(control);
889 return;
890 }
891 ((IFBGContainerControl)windowcontainer).AddControl(control);
892 FBGButton btn = new FBGButton(menucontainer);
893 FBGForm formcontrol = control as FBGForm;
894 if (formcontrol == null) {
895 btn.Text = "Untitled";
896 } else {
897 formcontrol.TextChanged += delegate(Object sender, EventArgs e) {
898 btn.Text = formcontrol.Text;
899 };
900 btn.Text = formcontrol.Text;
901 }
902 FBGWindowState ws = new FBGWindowState(control, btn);
903 windowstates.Add(control, ws);
904 ScaleMenuButtons();
905 btn.Click += delegate(Object sender, EventArgs e) {
906 if (ws.Visible) {
907 if (ws.MenuButton.BackColor == Color.DarkGray) {
908 ws.OldBounds = ws.Control.Bounds;
909 ws.Visible = false;
910 ws.Control.Bounds = Rectangle.Empty;
911 } else {
912 windowcontainer.BringControlToFront(ws.Control);
913 foreach (FBGWindowState wsa in windowstates.Values) if (!ReferenceEquals(ws, wsa)) wsa.MenuButton.BackColor = SystemColors.ButtonFace;
914 ws.MenuButton.BackColor = Color.DarkGray;
915 }
916 } else {
917 ws.Control.Bounds = ws.OldBounds;
918 ws.Visible = true;
919 windowcontainer.BringControlToFront(ws.Control);
920 foreach (FBGWindowState wsa in windowstates.Values) if (!ReferenceEquals(ws, wsa)) wsa.MenuButton.BackColor = SystemColors.ButtonFace;
921 ws.MenuButton.BackColor = Color.DarkGray;
922 }
923 };
924 }
925 public override void RemoveControl(IFBGControl control) {
926 windowcontainer.RemoveControl(control);
927 windowstates.Remove(control);
928 ScaleMenuButtons();
929 }
930 private void ScaleMenuButtons() {
931 int bcount = windowstates.Count;
932 int bwidth = 200;
933 int twidth = bwidth * bcount;
934 if (twidth > Bounds.Width) bwidth = menucontainer.Bounds.Width / bcount;
935 int i = 0;
936 foreach (FBGWindowState ws in windowstates.Values) {
937 ws.MenuButton.Bounds = new Rectangle(i * bwidth, 0, bwidth, 25);
938 i++;
939 }
940 }
941 protected override void Paint(Graphics g) {
942 base.Paint(g);
943 g.DrawLine(Pens.Black, 0, Bounds.Height - 25, Bounds.Width, Bounds.Height - 25);
944 }
945 protected override void MouseDown(Point position, MouseButtons buttons) {
946 IFBGControl control = FindControlAtPosition(position);
947 if (ReferenceEquals(control, windowcontainer)) {
948 control = windowcontainer.FindControlAtPosition(PointToChild(windowcontainer, position));
949 if (!ReferenceEquals(control, null)) {
950 windowcontainer.BringControlToFront(control);
951 foreach (FBGWindowState ws in windowstates.Values)
952 ws.MenuButton.BackColor = ReferenceEquals(ws.Control, control) ? Color.DarkGray : SystemColors.ButtonFace;
953 }
954 }
955 base.MouseDown(position, buttons);
956 }
957 }
958
959 public class FBGLabel : FBGControl, IDisposable {
960 public FBGLabel(IFBGContainerControl parent) : base(parent) {
961 Size = new Size(200, 16);
962 }
963 private String text = String.Empty;
964 private Font font = SystemFonts.DefaultFont;
965 private Brush brush = SystemBrushes.ControlText;
966 private StringFormat stringformat = new StringFormat();
967 public String Text { get { return text; } set { text = value; Invalidate(); } }
968 public Font Font { get { return font; } set { font = value; Invalidate(); } }
969 public Brush Brush { get { return brush; } set { brush = value; Invalidate(); } }
970 public Color Color { set { Brush = new SolidBrush(value); } }
971 public StringAlignment Alignment { get { return stringformat.Alignment; } set { stringformat.Alignment = value; Invalidate(); } }
972 public StringFormatFlags FormatFlags { get { return stringformat.FormatFlags; } set { stringformat.FormatFlags = value; Invalidate(); } }
973 public StringAlignment LineAlignment { get { return stringformat.LineAlignment; } set { stringformat.LineAlignment = value; Invalidate(); } }
974 public StringTrimming Trimming { get { return stringformat.Trimming; } set { stringformat.Trimming = value; Invalidate(); } }
975 protected override void Paint(Graphics g) {
976 base.Paint(g);
977 g.DrawString(text, font, brush, new Rectangle(Point.Empty, Bounds.Size), stringformat);
978 }
979 public void Dispose() {
980 stringformat.Dispose();
981 }
982 protected override void Orphaned() {
983 base.Orphaned();
984 Dispose();
985 }
986 }
987 public class FBGTextBox : FBGControl {
988 public FBGTextBox(IFBGContainerControl parent) : base(parent) {
989 BackColor = Color.White;
990 Size = new Size(200, 20);
991 }
992 private String text = String.Empty;
993 private Char passwordChar = (Char)0;
994 private Font font = new Font(FontFamily.GenericMonospace, 10);
995 private Brush brush = SystemBrushes.ControlText;
996 private Boolean hasKeyboardFocus = false;
997 public String Text { get { return text; } set { text = value; if (CaretPosition > text.Length) CaretPosition = text.Length; Invalidate(); RaiseEvent(TextChanged); } }
998 public Font Font { get { return font; } set { font = value; Invalidate(); } }
999 public Brush Brush { get { return brush; } set { brush = value; Invalidate(); } }
1000 public Color Color { set { Brush = new SolidBrush(value); } }
1001 public Int32 CaretPosition { get; private set; }
1002 public Char PasswordChar { get { return passwordChar; } set { passwordChar = value; Invalidate(); } }
1003 public event EventHandler TextChanged;
1004 public event KeyPressEventHandler OnKeyPress;
1005 protected override void Paint(Graphics g) {
1006 base.Paint(g);
1007 g.DrawRectangle(Pens.Gray, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
1008 using (StringFormat sf_nonprinting = new StringFormat(StringFormat.GenericTypographic)) {
1009 sf_nonprinting.Trimming = StringTrimming.None;
1010 sf_nonprinting.FormatFlags = StringFormatFlags.DisplayFormatControl | StringFormatFlags.MeasureTrailingSpaces;
1011 sf_nonprinting.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
1012
1013 float x = 1;
1014 float y = 1;
1015 float w = Width - 2;
1016 if (hasKeyboardFocus && CaretPosition == 0) {
1017 g.DrawLine(Pens.Black, x + 2, 2, x + 2, Height - 4);
1018 }
1019 String c = passwordChar == 0 ? null : new String(passwordChar, 1);
1020 for (int i = 0; i < text.Length; i++) {
1021 if (passwordChar == 0) c = text.Substring(i, 1);
1022 SizeF s = g.MeasureString(c, font, (int)Math.Ceiling(w), sf_nonprinting);
1023 g.DrawString(c, font, brush, x, y);
1024 x += (float)Math.Ceiling(s.Width);
1025 w -= (float)Math.Ceiling(s.Width);
1026
1027 if (hasKeyboardFocus && i == CaretPosition - 1) {
1028 g.DrawLine(Pens.Black, x + 2, 2, x + 2, Height - 4);
1029 }
1030 }
1031 }
1032 }
1033 protected override void MouseDown(Point position, MouseButtons buttons) {
1034 hasKeyboardFocus = CaptureKeyboard(true);
1035 CaretPosition = text.Length;
1036 float x = 1;
1037 String c = passwordChar == 0 ? null : new String(passwordChar, 1);
1038 for (int i = 0; i < text.Length; i++) {
1039 if (passwordChar == 0) c = text.Substring(i, 1);
1040 Size s;
1041 try {
1042 s = TextRenderer.MeasureText(c, font, Size.Empty, TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
1043 } catch (Exception) {
1044 break;
1045 }
1046 x += s.Width;
1047 if (position.X < x) {
1048 CaretPosition = i;
1049 break;
1050 }
1051 }
1052 Invalidate();
1053 }
1054 protected override void KeyDown(Keys key) {
1055 if ((key & Keys.KeyCode) == Keys.Left) {
1056 CaretPosition--;
1057 if (CaretPosition < 0) CaretPosition = 0;
1058 Invalidate();
1059 } else if ((key & Keys.KeyCode) == Keys.Right) {
1060 CaretPosition++;
1061 if (CaretPosition > text.Length) CaretPosition = text.Length;
1062 Invalidate();
1063 } else if ((key & Keys.KeyCode) == Keys.Home) {
1064 CaretPosition = 0;
1065 Invalidate();
1066 } else if ((key & Keys.KeyCode) == Keys.End) {
1067 CaretPosition = text.Length;
1068 Invalidate();
1069 } else if ((key & Keys.Control) != 0 && (key & Keys.KeyCode) == Keys.V) {
1070 String cbtext = Clipboard.GetText(TextDataFormat.UnicodeText);
1071 CaretPosition += cbtext.Length;
1072 Text = Text.Insert(CaretPosition - cbtext.Length, cbtext);
1073 }
1074 }
1075 protected override void KeyPress(char keyChar) {
1076 KeyPressEventArgs e = new KeyPressEventArgs(keyChar);
1077 RaiseEvent(OnKeyPress, e);
1078 if (e.Handled) return;
1079 hasKeyboardFocus = true;
1080 if (keyChar == 8) {
1081 if (CaretPosition > 0) {
1082 CaretPosition--;
1083 Text = Text.Remove(CaretPosition, 1);
1084 }
1085 } else if (keyChar == 127) {
1086 if (CaretPosition < Text.Length) {
1087 Text = Text.Remove(CaretPosition, 1);
1088 }
1089 } else if (keyChar < 32) {
1090 } else {
1091 CaretPosition++;
1092 Text = Text.Insert(CaretPosition - 1, new String(keyChar, 1));
1093 }
1094 }
1095 public void Focus() {
1096 hasKeyboardFocus = CaptureKeyboard(true);
1097 }
1098 protected override void LostKeyboardCapture() {
1099 base.LostKeyboardCapture();
1100 hasKeyboardFocus = false;
1101 Invalidate();
1102 }
1103 }
1104 public class FBGButton : FBGControl {
1105 public FBGButton(IFBGContainerControl parent) : base(parent) {
1106 BackColor = SystemColors.ButtonFace;
1107 }
1108 private String text = String.Empty;
1109 private Font font = SystemFonts.DefaultFont;
1110 private Color color = SystemColors.ControlText;
1111 private Boolean pressed = false;
1112 private Boolean enabled = true;
1113 public String Text { get { return text; } set { text = value; Invalidate(); } }
1114 public Font Font { get { return font; } set { font = value; Invalidate(); } }
1115 public Color Color { get { return color; } set { color = value; Invalidate(); } }
1116 public Boolean Enabled { get { return enabled; } set { enabled = value; Invalidate(); } }
1117 public event EventHandler Click;
1118 protected override void Paint(Graphics g) {
1119 base.Paint(g);
1120 if (Bounds.Width == 0 || Bounds.Height == 0) return;
1121 if (BackColor == SystemColors.ButtonFace) {
1122 ControlPaint.DrawButton(g, new Rectangle(0, 0, Bounds.Width, Bounds.Height), enabled ? (pressed ? ButtonState.Pushed : ButtonState.Normal) : ButtonState.Inactive);
1123 } else {
1124 //Hackish and not completely right...
1125 //Todo: borrowed from mono... possible licencing issues!?
1126 g.DrawLine(new Pen(ControlPaint.LightLight(BackColor)), 0, 0, Bounds.Width, 0);
1127 g.DrawLine(new Pen(ControlPaint.LightLight(BackColor)), 0, 0, 0, Bounds.Height);
1128 g.DrawLine(new Pen(ControlPaint.Dark(BackColor)), 1, Bounds.Height - 2, Bounds.Width - 1, Bounds.Height - 2);
1129 g.DrawLine(new Pen(ControlPaint.Dark(BackColor)), Bounds.Width - 2, 1, Bounds.Width - 2, Bounds.Height - 2);
1130 g.DrawLine(new Pen(ControlPaint.DarkDark(BackColor)), 0, Bounds.Height - 1, Bounds.Width, Bounds.Height - 1);
1131 g.DrawLine(new Pen(ControlPaint.DarkDark(BackColor)), Bounds.Width - 1, 1, Bounds.Width - 1, Bounds.Height - 1);
1132 Graphics dc = g;
1133 Rectangle rectangle = new Rectangle(0, 0, Bounds.Width - 1, Bounds.Height - 1);
1134 Color ColorControl = BackColor;
1135 Color ColorControlLight = ControlPaint.Light(ColorControl);
1136 ButtonState state = pressed ? ButtonState.Pushed : ButtonState.Normal;
1137 using (Pen NormalPen = new Pen(BackColor), LightPen = new Pen(ControlPaint.Light(BackColor)), DarkPen = new Pen(ControlPaint.Dark(BackColor))) {
1138 // sadly enough, the rectangle gets always filled with a hatchbrush
1139 using (HatchBrush hb = new HatchBrush(HatchStyle.Percent50, Color.FromArgb(Math.Min(255, ColorControl.R + 3), ColorControl.G, ColorControl.B), ColorControl)) {
1140 dc.FillRectangle(hb, rectangle.X + 1, rectangle.Y + 1, rectangle.Width - 2, rectangle.Height - 2);
1141 }
1142 if ((state & ButtonState.All) == ButtonState.All || ((state & ButtonState.Checked) == ButtonState.Checked && (state & ButtonState.Flat) == ButtonState.Flat)) {
1143 using (HatchBrush hb = new HatchBrush(HatchStyle.Percent50, ColorControlLight, ColorControl)) {
1144 dc.FillRectangle(hb, rectangle.X + 2, rectangle.Y + 2, rectangle.Width - 4, rectangle.Height - 4);
1145 }
1146 dc.DrawRectangle(SystemPens.ControlDark, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1);
1147 } else if ((state & ButtonState.Flat) == ButtonState.Flat) {
1148 dc.DrawRectangle(SystemPens.ControlDark, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1);
1149 } else if ((state & ButtonState.Checked) == ButtonState.Checked) {
1150 using (HatchBrush hb = new HatchBrush(HatchStyle.Percent50, ColorControlLight, ColorControl)) {
1151 dc.FillRectangle(hb, rectangle.X + 2, rectangle.Y + 2, rectangle.Width - 4, rectangle.Height - 4);
1152 }
1153 Pen pen = DarkPen;
1154 dc.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.X, rectangle.Bottom - 2);
1155 dc.DrawLine(pen, rectangle.X + 1, rectangle.Y, rectangle.Right - 2, rectangle.Y);
1156
1157 pen = NormalPen;
1158 dc.DrawLine(pen, rectangle.X + 1, rectangle.Y + 1, rectangle.X + 1, rectangle.Bottom - 3);
1159 dc.DrawLine(pen, rectangle.X + 2, rectangle.Y + 1, rectangle.Right - 3, rectangle.Y + 1);
1160
1161 pen = LightPen;
1162 dc.DrawLine(pen, rectangle.X, rectangle.Bottom - 1, rectangle.Right - 2, rectangle.Bottom - 1);
1163 dc.DrawLine(pen, rectangle.Right - 1, rectangle.Y, rectangle.Right - 1, rectangle.Bottom - 1);
1164 } else if (((state & ButtonState.Pushed) == ButtonState.Pushed) && ((state & ButtonState.Normal) == ButtonState.Normal)) {
1165 Pen pen = DarkPen;
1166 dc.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.X, rectangle.Bottom - 2);
1167 dc.DrawLine(pen, rectangle.X + 1, rectangle.Y, rectangle.Right - 2, rectangle.Y);
1168
1169 pen = NormalPen;
1170 dc.DrawLine(pen, rectangle.X + 1, rectangle.Y + 1, rectangle.X + 1, rectangle.Bottom - 3);
1171 dc.DrawLine(pen, rectangle.X + 2, rectangle.Y + 1, rectangle.Right - 3, rectangle.Y + 1);
1172
1173 pen = LightPen;
1174 dc.DrawLine(pen, rectangle.X, rectangle.Bottom - 1, rectangle.Right - 2, rectangle.Bottom - 1);
1175 dc.DrawLine(pen, rectangle.Right - 1, rectangle.Y, rectangle.Right - 1, rectangle.Bottom - 1);
1176 } else if (((state & ButtonState.Inactive) == ButtonState.Inactive) || ((state & ButtonState.Normal) == ButtonState.Normal)) {
1177 Pen pen = LightPen;
1178 dc.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.Right - 2, rectangle.Y);
1179 dc.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.X, rectangle.Bottom - 2);
1180
1181 pen = NormalPen;
1182 dc.DrawLine(pen, rectangle.X + 1, rectangle.Bottom - 2, rectangle.Right - 2, rectangle.Bottom - 2);
1183 dc.DrawLine(pen, rectangle.Right - 2, rectangle.Y + 1, rectangle.Right - 2, rectangle.Bottom - 3);
1184
1185 pen = DarkPen;
1186 dc.DrawLine(pen, rectangle.X, rectangle.Bottom - 1, rectangle.Right - 1, rectangle.Bottom - 1);
1187 dc.DrawLine(pen, rectangle.Right - 1, rectangle.Y, rectangle.Right - 1, rectangle.Bottom - 2);
1188 }
1189 }
1190 }
1191 Rectangle frect = new Rectangle(Point.Empty, Bounds.Size);
1192 SizeF textsize = g.MeasureString(Text, Font);
1193 using (Brush b = new SolidBrush(enabled ? Color : Color.DarkGray)) {
1194 g.DrawString(Text, Font, b, new PointF(Bounds.Width / 2.0f - textsize.Width / 2.0f, Bounds.Height / 2.0f - textsize.Height / 2.0f));
1195 }
1196 }
1197 protected override void MouseDown(Point position, MouseButtons buttons) {
1198 pressed = true;
1199 Invalidate();
1200 CaptureMouse(true);
1201 base.MouseDown(position, buttons);
1202 }
1203 protected override void MouseUp(Point position, MouseButtons buttons) {
1204 pressed = false;
1205 Invalidate();
1206 CaptureMouse(false);
1207 if (position.X >= 0 && position.X <= Bounds.Width && position.Y >= 0 && position.Y <= Bounds.Height && enabled) RaiseEvent(Click);
1208 base.MouseUp(position, buttons);
1209 }
1210 protected override void KeyDown(Keys key) {
1211 if (key == Keys.Return || key == Keys.Space) {
1212 pressed = true;
1213 Invalidate();
1214 }
1215 base.KeyDown(key);
1216 }
1217 protected override void KeyUp(Keys key) {
1218 if (key == Keys.Return || key == Keys.Space) {
1219 if (pressed) RaiseEvent(Click);
1220 pressed = false;
1221 Invalidate();
1222 }
1223 base.KeyUp(key);
1224 }
1225 public void Focus() {
1226 CaptureKeyboard(true);
1227 }
1228 }
1229 public class FBGCheckBox : FBGControl {
1230 public FBGCheckBox(IFBGContainerControl parent) : base(parent) { }
1231 private String text = String.Empty;
1232 private Font font = SystemFonts.DefaultFont;
1233 private Color color = SystemColors.ControlText;
1234 private Boolean _checked = false;
1235 public String Text { get { return text; } set { text = value; Invalidate(); } }
1236 public Font Font { get { return font; } set { font = value; Invalidate(); } }
1237 public Color Color { get { return color; } set { color = value; Invalidate(); } }
1238 public Boolean Checked { get { return _checked; } set { _checked = value; Invalidate(); } }
1239 public event EventHandler CheckedChanged;
1240 protected override void Paint(Graphics g) {
1241 base.Paint(g);
1242 ControlPaint.DrawCheckBox(g, 0, 0, 13, 13, _checked ? ButtonState.Checked : ButtonState.Normal);
1243 g.DrawString(Text, Font, new SolidBrush(Color), 15, 0);
1244 }
1245 protected override void MouseDown(Point position, MouseButtons buttons) {
1246 Checked = !Checked;
1247 RaiseEvent(CheckedChanged);
1248 base.MouseDown(position, buttons);
1249 }
1250 }
1251 public class FBGImageBox : FBGControl {
1252 Image image = null;
1253 PictureBoxSizeMode sizeMode = PictureBoxSizeMode.Normal;
1254 Rectangle imageRect;
1255 public Image Image { get { return image; } set { image = value; UpdateImageRect(Size.Empty); } }
1256 public FBGImageBox(IFBGContainerControl parent) : base(parent) { }
1257 public PictureBoxSizeMode SizeMode { get { return sizeMode; } set { sizeMode = value; UpdateImageRect(Size.Empty); } }
1258 public override Rectangle Bounds {
1259 get {
1260 return base.Bounds;
1261 }
1262 set {
1263 UpdateImageRect(value.Size);
1264 base.Bounds = value;
1265 }
1266 }
1267 private void UpdateImageRect(Size csize) {
1268 if (image == null) return;
1269 Boolean boundsset = !csize.IsEmpty;
1270 if (!boundsset && sizeMode == PictureBoxSizeMode.AutoSize) {
1271 Size = Image.Size;
1272 return;
1273 }
1274 if (!boundsset) csize = Bounds.Size;
1275 switch (sizeMode) {
1276 case PictureBoxSizeMode.AutoSize:
1277 case PictureBoxSizeMode.Normal:
1278 imageRect = new Rectangle(Point.Empty, image.Size);
1279 break;
1280 case PictureBoxSizeMode.CenterImage:
1281 imageRect = new Rectangle(csize.Width / 2 - image.Width / 2, csize.Height / 2 - Image.Height / 2, image.Width, image.Height);
1282 break;
1283 case PictureBoxSizeMode.StretchImage:
1284 imageRect = new Rectangle(Point.Empty, csize);
1285 break;
1286 case PictureBoxSizeMode.Zoom:
1287 float xrat = (float)csize.Width / (float)image.Width;
1288 float yrat = (float)csize.Height / (float)image.Height;
1289 float rat = Math.Min(xrat, yrat);
1290 SizeF dispsize = new SizeF(image.Width * rat, image.Height * rat);
1291 imageRect = Rectangle.Round(new RectangleF(csize.Width / 2f - dispsize.Width / 2f, csize.Height / 2f - dispsize.Height / 2f, dispsize.Width, dispsize.Height));
1292 break;
1293 }
1294 if (!boundsset) Invalidate();
1295 }
1296 protected override void Paint(Graphics g) {
1297 if (!Visible) return;
1298 base.Paint(g);
1299 if (image != null) g.DrawImage(image, imageRect);
1300 }
1301 public Point PointToImage(Point point) {
1302 switch (sizeMode) {
1303 case PictureBoxSizeMode.AutoSize:
1304 case PictureBoxSizeMode.Normal:
1305 break;
1306 case PictureBoxSizeMode.CenterImage:
1307 point.X -= imageRect.X;
1308 point.Y -= imageRect.Y;
1309 break;
1310 case PictureBoxSizeMode.StretchImage:
1311 case PictureBoxSizeMode.Zoom:
1312 default:
1313 point.X = (point.X - imageRect.X) * image.Width / imageRect.Width;
1314 point.Y = (point.Y - imageRect.Y) * image.Height / imageRect.Height;
1315 break;
1316 }
1317 return point;
1318 }
1319 }
1320 public class FBGListBox : FBGControl {
1321 private List<Object> items = new List<object>();
1322 private Object selected = null;
1323 private Object highlighted = null;
1324 private Boolean hasScrollBar = false;
1325 private Boolean hitScrollBar = false;
1326 private int offset = 0;
1327 private ButtonState buttonUpState = ButtonState.Normal;
1328 private ButtonState buttonDownState = ButtonState.Normal;
1329 private Converter<Object, String> itemFormatter = null;
1330 public Converter<Object, String> ItemFormatter { get { return itemFormatter; } set { itemFormatter = value; Invalidate(); } }
1331 public FBGListBox(IFBGContainerControl parent)
1332 : base(parent) {
1333 BackColor = Color.White;
1334 }
1335 public void AddItem(Object item) {
1336 items.Add(item);
1337 Invalidate();
1338 }
1339 protected override void Paint(Graphics g) {
1340 base.Paint(g);
1341 g.DrawRectangle(Pens.DarkBlue, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
1342 int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight());
1343 int th = lh * items.Count;
1344 int y = 2;
1345 using (Pen dottedpen = new Pen(Brushes.Black)) {
1346 dottedpen.DashStyle = DashStyle.Dot;
1347 using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap)) {
1348 for (int i = offset; i < items.Count; i++) {
1349 Object item = items[i];
1350 String text = itemFormatter == null ? (item == null ? String.Empty : item.ToString()) : itemFormatter(item);
1351 if (item == selected) g.FillRectangle(Brushes.DarkGray, 2, y, Bounds.Width - 4, lh);
1352 if (item == highlighted) g.DrawRectangle(dottedpen, 2, y, Bounds.Width - 5, lh - 1);
1353 g.DrawString(text, SystemFonts.DefaultFont, SystemBrushes.WindowText, new Rectangle(3, y, Bounds.Width - 6, lh), sf);
1354 y += lh;
1355 if (y + lh + 2 >= Bounds.Height) break;
1356 }
1357 }
1358 }
1359 if (y < th) hasScrollBar = true;
1360 if (hasScrollBar) {
1361 int xoff = Bounds.Width - 17;
1362 using (Brush b = new LinearGradientBrush(new Rectangle(xoff, 0, 17, 1), Color.LightGray, Color.White, LinearGradientMode.Horizontal))
1363 g.FillRectangle(b, xoff, 17, 16, Bounds.Height - 17 - 17);
1364 ControlPaint.DrawScrollButton(g, xoff, 1, 16, 16, ScrollButton.Up, buttonUpState);
1365 ControlPaint.DrawScrollButton(g, xoff, Bounds.Height - 17, 16, 16, ScrollButton.Down, buttonDownState);
1366 g.DrawRectangle(Pens.Black, new Rectangle(xoff, 17 + offset * (Bounds.Height - 17 - 17 - 20) / (items.Count - 1), 15, 20));
1367 }
1368 }
1369 protected override void MouseDown(Point position, MouseButtons buttons) {
1370 if ((buttons & MouseButtons.Left) != 0) {
1371 CaptureMouse(true);
1372 if (hasScrollBar && position.X > Bounds.Width - 17) {
1373 hitScrollBar = true;
1374 if (position.Y < 17) {
1375 offset--;
1376 buttonUpState = ButtonState.Pushed;
1377 } else if (position.Y > Bounds.Height - 17) {
1378 offset++;
1379 buttonDownState = ButtonState.Pushed;
1380 } else {
1381 offset = (int)Math.Round((position.Y - 17) * (items.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
1382 }
1383 if (offset < 0) offset = 0;
1384 if (offset >= items.Count) offset = items.Count - 1;
1385 Invalidate();
1386 } else {
1387 MouseHandler(position, buttons);
1388 }
1389 }
1390 }
1391 protected override void MouseMove(Point position, MouseButtons buttons) {
1392 if (hitScrollBar) {
1393 if (position.Y < 17) {
1394 } else if (position.Y > Bounds.Height - 17) {
1395 } else {
1396 offset = (int)Math.Round((position.Y - 17) * (items.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
1397 if (offset < 0) offset = 0;
1398 if (offset >= items.Count) offset = items.Count - 1;
1399 Invalidate();
1400 }
1401 return;
1402 }
1403 MouseHandler(position, buttons);
1404 }
1405 protected override void MouseUp(Point position, MouseButtons buttons) {
1406 if ((buttons & MouseButtons.Left) != 0) {
1407 CaptureMouse(false);
1408 buttonUpState = buttonDownState = ButtonState.Normal;
1409 Invalidate();
1410 if (hitScrollBar) {
1411 hitScrollBar = false;
1412 return;
1413 }
1414 }
1415 if (hitScrollBar) return;
1416 MouseHandler(position, buttons);
1417 }
1418 private void MouseHandler(Point position, MouseButtons buttons) {
1419 if ((buttons & MouseButtons.Left) != 0) {
1420 int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight());
1421 int i = (position.Y - 2) / lh + offset;
1422 if (i < 0) i = 0;
1423 if (i >= items.Count) i = items.Count - 1;
1424 Boolean changed = false;
1425 Object current = items[i];
1426 if (!ReferenceEquals(highlighted, current)) changed = true;
1427 highlighted = current;
1428 if ((new Rectangle(Point.Empty, Bounds.Size)).Contains(position)) {
1429 if (!ReferenceEquals(selected, current)) changed = true;
1430 selected = current;
1431 }
1432 if (changed) Invalidate();
1433 }
1434 }
1435 }
1436 public abstract class FBGUpDownControlBase : FBGControl {
1437 private ButtonState buttonUpState = ButtonState.Normal;
1438 private ButtonState buttonDownState = ButtonState.Normal;
1439 public FBGUpDownControlBase(IFBGContainerControl parent) : base(parent) {
1440 BackColor = Color.White;
1441 Height = 25;
1442 }
1443 protected override void Paint(Graphics g) {
1444 base.Paint(g);
1445 g.DrawRectangle(Pens.DarkBlue, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
1446 int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight());
1447 String text = SelectedText;
1448 if (text == null) {
1449 g.FillRectangle(Brushes.DarkGray, 2, 2, Bounds.Width - 4 - 16, Bounds.Height - 4);
1450 } else {
1451 using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap)) {
1452 sf.LineAlignment = StringAlignment.Center;
1453 g.FillRectangle(Brushes.LightGray, 2, 2, Bounds.Width - 4 - 16, Bounds.Height - 4);
1454 g.DrawString(text, SystemFonts.DefaultFont, SystemBrushes.WindowText, new Rectangle(3, 2, Bounds.Width - 6 - 16, Bounds.Height - 4), sf);
1455 }
1456 }
1457 int xoff = Bounds.Width - 17;
1458 int he = (Bounds.Height - 2) / 2;
1459 ControlPaint.DrawScrollButton(g, xoff, 1, 16, he, ScrollButton.Up, buttonUpState);
1460 ControlPaint.DrawScrollButton(g, xoff, Bounds.Height - he - 1, 16, he, ScrollButton.Down, buttonDownState);
1461 }
1462 protected abstract String SelectedText { get; }
1463 protected override void MouseDown(Point position, MouseButtons buttons) {
1464 CaptureKeyboard(true);
1465 if ((buttons & MouseButtons.Left) != 0) {
1466 CaptureMouse(true);
1467 if (position.X > Bounds.Width - 17) {
1468 if (position.Y < Bounds.Height / 2) {
1469 buttonUpState = ButtonState.Pushed;
1470 ButtonPressUp();
1471 } else {
1472 buttonDownState = ButtonState.Pushed;
1473 ButtonPressDown();
1474 }
1475 Invalidate(new Rectangle(Bounds.Width - 16, 0, 16, Bounds.Height));
1476 }
1477 }
1478 }
1479 protected override void MouseUp(Point position, MouseButtons buttons) {
1480 if ((buttons & MouseButtons.Left) != 0) {
1481 CaptureMouse(false);
1482 buttonUpState = buttonDownState = ButtonState.Normal;
1483 Invalidate(new Rectangle(Bounds.Width - 16, 0, 16, Bounds.Height));
1484 }
1485 }
1486 protected override void KeyDown(Keys key) {
1487 base.KeyDown(key);
1488 if (key == Keys.Down) ButtonPressDown();
1489 else if (key == Keys.Up) ButtonPressUp();
1490 }
1491 protected abstract void ButtonPressUp();
1492 protected abstract void ButtonPressDown();
1493 }
1494 public class FBGDomainUpDown : FBGUpDownControlBase {
1495 private List<Object> items = new List<object>();
1496 private int selectedIndex = -1;
1497 private Converter<Object, String> itemFormatter = null;
1498 public Boolean AllowSelectEmpty { get; set; }
1499 public Converter<Object, String> ItemFormatter { get { return itemFormatter; } set { itemFormatter = value; Invalidate(); } }
1500 public event EventHandler SelectedIndexChanged;
1501 public FBGDomainUpDown(IFBGContainerControl parent) : base(parent) { }
1502 public void AddItem(Object item) {
1503 items.Add(item);
1504 Invalidate();
1505 }
1506 public void RemoveItem(Object item) {
1507 items.Remove(item);
1508 FixSelectedIndex(0);
1509 }
1510 public void RemoveItem(int index) {
1511 items.RemoveAt(index);
1512 FixSelectedIndex(0);
1513 }
1514 public int SelectedIndex {
1515 get { return selectedIndex; }
1516 set {
1517 if (value < -2 || value >= items.Count) throw new ArgumentOutOfRangeException("value", "Value must be between -1 and the number of items minus one");
1518 if (selectedIndex == value) return;
1519 selectedIndex = value;
1520 Invalidate();
1521 RaiseEvent(SelectedIndexChanged);
1522 }
1523 }
1524 public Object SelectedItem {
1525 get { return selectedIndex == -1 ? null : items[selectedIndex]; }
1526 set {
1527 if (value == null) {
1528 SelectedIndex = -1;
1529 } else {
1530 for (int i = 0; i < items.Count; i++) {
1531 if (items[i] == value) {
1532 SelectedIndex = i;
1533 break;
1534 }
1535 }
1536 }
1537 }
1538 }
1539 protected override string SelectedText {
1540 get {
1541 if (selectedIndex == -1) return null;
1542 Object item = items[selectedIndex];
1543 if (itemFormatter != null) return itemFormatter(item);
1544 if (item == null) return null;
1545 return item.ToString();
1546 }
1547 }
1548 private void FixSelectedIndex(int change) {
1549 int value = selectedIndex;
1550 if (value == 0 && change == -1 && !AllowSelectEmpty) change = 0;
1551 value += change;
1552 if (value < -1) value = -1;
1553 if (value >= items.Count) value = items.Count - 1;
1554 SelectedIndex = value;
1555 }
1556 protected override void ButtonPressDown() {
1557 FixSelectedIndex(1);
1558 }
1559 protected override void ButtonPressUp() {
1560 FixSelectedIndex(-1);
1561 }
1562 }
1563 public class FBGNumericUpDown : FBGUpDownControlBase {
1564 private int minimum = 0;
1565 private int maximum = 0;
1566 private int value = 0;
1567 public event EventHandler SelectedValueChanged;
1568 public FBGNumericUpDown(IFBGContainerControl parent) : base(parent) { }
1569 public int Value {
1570 get { return value; }
1571 set { if (this.value == value) return; this.value = value; Invalidate(); RaiseEvent(SelectedValueChanged); }
1572 }
1573 public int Minimum {
1574 get { return minimum; }
1575 set { minimum = value; if (this.value < minimum) this.Value = minimum; }
1576 }
1577 public int Maximum {
1578 get { return maximum; }
1579 set { maximum = value; if (this.value > maximum) this.Value = maximum; }
1580 }
1581 public int Step { get; set; }
1582 protected override string SelectedText {
1583 get { return value.ToString(); }
1584 }
1585 protected override void ButtonPressDown() {
1586 Value = Math.Max(minimum, value - Step);
1587 }
1588 protected override void ButtonPressUp() {
1589 Value = Math.Min(maximum, value + Step);
1590 }
1591 }
1592 public interface IFBGTreeParent {
1593 FBGTreeView TreeView { get; }
1594 int Depth { get; }
1595 void AddChild(FBGTreeNode node);
1596 void RemoveChild(FBGTreeNode node);
1597 }
1598 public class FBGTreeNode : IFBGTreeParent {
1599 private List<FBGTreeNode> children = new List<FBGTreeNode>();
1600 private Boolean expanded = true;
1601 private Boolean hasCheckBox = false;
1602 private Boolean isChecked = false;
1603 private Object item;
1604
1605 public FBGTreeView TreeView { get; private set; }
1606 public int Depth { get; private set; }
1607 public Object Tag { get; set; }
1608 public IFBGTreeParent Parent { get; private set; }
1609
1610 public IList<FBGTreeNode> Children { get { return children.AsReadOnly(); } }
1611
1612 public Object Item {
1613 get { return item; }
1614 set {
1615 item = value;
1616 Invalidate();
1617 }
1618 }
1619 public Boolean Expanded {
1620 get { return expanded; }
1621 set {
1622 if (expanded == value) return;
1623 expanded = value;
1624 UpdateTree();
1625 }
1626 }
1627 public Boolean HasCheckBox {
1628 get { return hasCheckBox; }
1629 set {
1630 if (hasCheckBox == value) return;
1631 hasCheckBox = value;
1632 Invalidate();
1633 }
1634 }
1635 public Boolean Checked {
1636 get { return isChecked; }
1637 set {
1638 if (isChecked == value) return;
1639 isChecked = value;
1640 Invalidate();
1641 if (TreeView != null) TreeView.RaiseNodeCheckedChanged(this);
1642 }
1643 }
1644
1645 public FBGTreeNode(IFBGTreeParent parent, Object item) {
1646 this.TreeView = parent.TreeView;
1647 this.Depth = parent.Depth + 1;
1648 this.Parent = parent;
1649 this.item = item;
1650 parent.AddChild(this);
1651 }
1652
1653 public void Remove() {
1654 Parent.RemoveChild(this);
1655 }
1656 void IFBGTreeParent.AddChild(FBGTreeNode node) {
1657 children.Add(node);
1658 if (Expanded) UpdateTree();
1659 else Invalidate();
1660 }
1661 void IFBGTreeParent.RemoveChild(FBGTreeNode node) {
1662 children.Remove(node);
1663 TreeView.ReleaseNodeFromTree(node);
1664 if (Expanded) UpdateTree();
1665 else Invalidate();
1666 }
1667 public void Invalidate() {
1668 if (TreeView != null) TreeView.Invalidate(this);
1669 }
1670 private void UpdateTree() {
1671 if (TreeView != null) TreeView.UpdateView();
1672 }
1673 public FBGTreeNode AddNode(Object item) {
1674 return new FBGTreeNode(this, item);
1675 }
1676 }
1677 public class FBGTreeView : FBGControl, IFBGTreeParent {
1678 private List<FBGTreeNode> items = new List<FBGTreeNode>();
1679 private List<FBGTreeNode> itemsView = new List<FBGTreeNode>();
1680 private FBGTreeNode selected = null;
1681 private FBGTreeNode highlighted = null;
1682 private Boolean hasScrollBar = false;
1683 private Boolean hitScrollBar = false;
1684 private int offset = 0;
1685 private ButtonState buttonUpState = ButtonState.Normal;
1686 private ButtonState buttonDownState = ButtonState.Normal;
1687 private Converter<Object, String> itemFormatter = null;
1688
1689 public Converter<Object, String> ItemFormatter { get { return itemFormatter; } set { itemFormatter = value; Invalidate(); } }
1690 public FBGTreeNode SelectedNode { get { return selected; } set { if (selected != value) { selected = value; Invalidate(); RaiseEvent(SelectedNodeChanged); } } }
1691 public IList<FBGTreeNode> Nodes { get { return items.AsReadOnly(); } }
1692
1693 public event EventHandler SelectedNodeChanged;
1694 public event EventHandler NodeCheckedChanged;
1695
1696 public FBGTreeView(IFBGContainerControl parent) : base(parent) {
1697 BackColor = Color.White;
1698 }
1699 FBGTreeView IFBGTreeParent.TreeView { get { return this; } }
1700 int IFBGTreeParent.Depth { get { return -1; } }
1701 void IFBGTreeParent.AddChild(FBGTreeNode node) {
1702 items.Add(node);
1703 UpdateView();
1704 }
1705 void IFBGTreeParent.RemoveChild(FBGTreeNode node) {
1706 items.Remove(node);
1707 ReleaseNodeFromTree(node);
1708 UpdateView();
1709 }
1710 public FBGTreeNode AddNode(Object item) { return new FBGTreeNode(this, item); }
1711 internal void ReleaseNodeFromTree(FBGTreeNode node) {
1712 if (highlighted == node) highlighted = null;
1713 if (selected == node) SelectedNode = null;
1714 }
1715 internal void RaiseNodeCheckedChanged(FBGTreeNode node) {
1716 RaiseEvent(NodeCheckedChanged);
1717 }
1718 internal void UpdateView() {
1719 List<FBGTreeNode> newView = new List<FBGTreeNode>();
1720 Stack<Queue<FBGTreeNode>> stack = new Stack<Queue<FBGTreeNode>>();
1721 stack.Push(new Queue<FBGTreeNode>(items));
1722 while (stack.Count > 0) {
1723 Queue<FBGTreeNode> list = stack.Peek();
1724 if (list.Count == 0) {
1725 stack.Pop();
1726 continue;
1727 }
1728 FBGTreeNode item = list.Dequeue();
1729 newView.Add(item);
1730 if (item.Expanded) stack.Push(new Queue<FBGTreeNode>(item.Children));
1731 }
1732 itemsView = newView;
1733 Invalidate();
1734 }
1735 internal void Invalidate(FBGTreeNode node) {
1736 int i = itemsView.IndexOf(node);
1737 if (i == -1) return;
1738 int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight() / 2.0) * 2;
1739 Invalidate(new Rectangle(1, i * lh, Bounds.Width - 1, lh));
1740 }
1741 protected override void Paint(Graphics g) {
1742 base.Paint(g);
1743
1744 int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight() / 2.0) * 2;
1745 int th = lh * itemsView.Count;
1746 hasScrollBar = offset > 0 || th + 2 > Bounds.Height;
1747 int y = 2;
1748 using (Pen dottedpen = new Pen(Brushes.Black)) {
1749 dottedpen.DashStyle = DashStyle.Dot;
1750 using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap)) {
1751 int lw = Bounds.Width - 2;
1752 if (hasScrollBar) lw -= 17;
1753 for (int i = offset; i < itemsView.Count; i++) {
1754 FBGTreeNode item = itemsView[i];
1755 if (y + 2 < Bounds.Height) {
1756 Object obj = item.Item;
1757 String text = itemFormatter == null ? (obj == null ? String.Empty : obj.ToString()) : itemFormatter(obj);
1758 if (item == selected) g.FillRectangle(Brushes.DarkGray, 2, y, lw - 2, lh);
1759 if (item == highlighted) g.DrawRectangle(dottedpen, 2, y, lw - 3, lh - 1);
1760 int x = 3 + 19 * item.Depth + 14;
1761 if (item.HasCheckBox) {
1762 x += 2;
1763 ControlPaint.DrawCheckBox(g, x, y, lh, lh, item.Checked ? ButtonState.Checked : ButtonState.Normal);
1764 x += lh + 1;
1765 }
1766 g.DrawString(text, SystemFonts.DefaultFont, SystemBrushes.WindowText, new Rectangle(x, y, lw - x, lh), sf);
1767 }
1768 int upto = y + 2 + 4 - 8;
1769 for (int j = i - 1; j >= 0; j--) {
1770 if (itemsView[j].Depth < item.Depth) {
1771 break;
1772 }
1773 if (itemsView[j].Depth == item.Depth) {
1774 if (itemsView[j].Children.Count > 0) {
1775 upto = 2 + lh * (j - offset) + 10;
1776 } else {
1777 upto = 2 + lh * (j - offset) + 6;
1778 }
1779 break;
1780 }
1781 if (j <= offset) {
1782 upto = 2 + 2 + 4 - 8;
1783 break;
1784 }
1785 }
1786 if (item.Children.Count > 0) {
1787 g.DrawRectangle(Pens.Black, 3 + 19 * item.Depth, y + 2, 8, 8);
1788 g.DrawLine(Pens.Black, 3 + 19 * item.Depth + 2, y + 2 + 4, 3 + 19 * item.Depth + 6, y + 2 + 4);
1789 if (!item.Expanded) g.DrawLine(Pens.Black, 3 + 19 * item.Depth + 4, y + 4, 3 + 19 * item.Depth + 4, y + 2 + 6);
1790
1791 g.DrawLine(dottedpen, 3 + 19 * item.Depth + 8, y + 2 + 4, 3 + 19 * item.Depth + 14, y + 2 + 4);
1792 g.DrawLine(dottedpen, 3 + 19 * item.Depth + 4, y + 2 + 4 - 6, 3 + 19 * item.Depth + 4, upto);
1793 } else {
1794 g.DrawLine(dottedpen, 3 + 19 * item.Depth + 4, y + 2 + 4, 3 + 19 * item.Depth + 14, y + 2 + 4);
1795 g.DrawLine(dottedpen, 3 + 19 * item.Depth + 4, y + 2 + 4 - 2, 3 + 19 * item.Depth + 4, upto);
1796 }
1797 y += lh;
1798 //if (y + lh + 2 >= Bounds.Height && item.Depth == 0) break;
1799 if (y + 2 >= Bounds.Height && item.Depth == 0) break;
1800 }
1801 }
1802 }
1803 //if (y < th) hasScrollBar = true;
1804 //hasScrollBar = true;
1805 if (hasScrollBar) {
1806 int xoff = Bounds.Width - 17;
1807 using (Brush b = new LinearGradientBrush(new Rectangle(xoff, 0, 17, 1), Color.LightGray, Color.White, LinearGradientMode.Horizontal))
1808 g.FillRectangle(b, xoff, 17, 16, Bounds.Height - 17 - 17);
1809 ControlPaint.DrawScrollButton(g, xoff, 1, 16, 16, ScrollButton.Up, buttonUpState);
1810 ControlPaint.DrawScrollButton(g, xoff, Bounds.Height - 17, 16, 16, ScrollButton.Down, buttonDownState);
1811 g.DrawRectangle(Pens.Black, new Rectangle(xoff, 17 + offset * (Bounds.Height - 17 - 17 - 20) / (itemsView.Count - 1), 15, 20));
1812 }
1813
1814 g.DrawRectangle(Pens.DarkBlue, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
1815 }
1816 protected override void MouseDown(Point position, MouseButtons buttons) {
1817 CaptureKeyboard(true);
1818 if ((buttons & MouseButtons.Left) != 0) {
1819 CaptureMouse(true);
1820 if (hasScrollBar && position.X > Bounds.Width - 17) {
1821 hitScrollBar = true;
1822 if (position.Y < 17) {
1823 offset--;
1824 buttonUpState = ButtonState.Pushed;
1825 } else if (position.Y > Bounds.Height - 17) {
1826 offset++;
1827 buttonDownState = ButtonState.Pushed;
1828 } else {
1829 offset = (int)Math.Round((position.Y - 17) * (itemsView.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
1830 }
1831 if (offset < 0) offset = 0;
1832 if (offset >= itemsView.Count) offset = itemsView.Count - 1;
1833 Invalidate();
1834 } else {
1835 MouseHandler(position, buttons, true);
1836 }
1837 }
1838 }
1839 protected override void MouseMove(Point position, MouseButtons buttons) {
1840 if (hitScrollBar) {
1841 if (position.Y < 17) {
1842 } else if (position.Y > Bounds.Height - 17) {
1843 } else {
1844 offset = (int)Math.Round((position.Y - 17) * (itemsView.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
1845 if (offset < 0) offset = 0;
1846 if (offset >= itemsView.Count) offset = itemsView.Count - 1;
1847 Invalidate();
1848 }
1849 return;
1850 }
1851 MouseHandler(position, buttons, false);
1852 }
1853 protected override void MouseUp(Point position, MouseButtons buttons) {
1854 if ((buttons & MouseButtons.Left) != 0) {
1855 CaptureMouse(false);
1856 buttonUpState = buttonDownState = ButtonState.Normal;
1857 Invalidate();
1858 if (hitScrollBar) {
1859 hitScrollBar = false;
1860 return;
1861 }
1862 }
1863 if (hitScrollBar) return;
1864 MouseHandler(position, buttons, false);
1865 }
1866 private void MouseHandler(Point position, MouseButtons buttons, Boolean down) {
1867 if ((buttons & MouseButtons.Left) != 0 && itemsView.Count > 0) {
1868 int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight() / 2.0) * 2;
1869 int i = (position.Y - 2) / lh + offset;
1870 if (i < 0) i = 0;
1871 if (i >= itemsView.Count) i = itemsView.Count - 1;
1872 Boolean changed = false;
1873 FBGTreeNode current = itemsView[i];
1874 if (!ReferenceEquals(highlighted, current)) changed = true;
1875 highlighted = current;
1876 if (current.Children.Count > 0 && (new Rectangle(3 + 19 * current.Depth, 2 + lh * (i - offset) + 2, 8, 8)).Contains(position)) {
1877 if (down) current.Expanded = !current.Expanded;
1878 } else if (current.HasCheckBox && (new Rectangle(3 + 19 * current.Depth + 14 + 2, 2 + lh * (i - offset), lh, lh)).Contains(position)) {
1879 if (down) current.Checked = !current.Checked;
1880 } else if ((new Rectangle(Point.Empty, Bounds.Size)).Contains(position)) {
1881 SelectedNode = current;
1882 changed = false;
1883 }
1884 if (changed) Invalidate();
1885 }
1886 }
1887 protected override void KeyDown(Keys key) {
1888 base.KeyDown(key);
1889 if (key == Keys.Up) {
1890 int i = itemsView.IndexOf(selected);
1891 i--;
1892 if (i >= 0) SelectAndScrollIntoView(itemsView[i]);
1893 } else if (key == Keys.Down) {
1894 int i = itemsView.IndexOf(selected);
1895 i++;
1896 if (i < itemsView.Count) SelectAndScrollIntoView(itemsView[i]);
1897 } else if (key == Keys.Left && selected != null) {
1898 if (selected.Expanded && selected.Children.Count > 0) {
1899 selected.Expanded = false;
1900 } else {
1901 FBGTreeNode tn = selected.Parent as FBGTreeNode;
1902 if (tn != null) SelectAndScrollIntoView(tn);
1903 }
1904 } else if (key == Keys.Right && selected != null) {
1905 if (!selected.Expanded && selected.Children.Count > 0) {
1906 selected.Expanded = true;
1907 } else if (selected.Children.Count > 0) {
1908 SelectAndScrollIntoView(selected.Children[0]);
1909 }
1910 } else if (key == Keys.Space && selected != null) {
1911 if (selected.HasCheckBox) selected.Checked = !selected.Checked;
1912 }
1913 }
1914 private void SelectAndScrollIntoView(FBGTreeNode tn) {
1915 int i = itemsView.IndexOf(tn);
1916 if (i == -1) {
1917 for (FBGTreeNode tp = tn.Parent as FBGTreeNode; tp != null; tp = tp.Parent as FBGTreeNode) if (!tp.Expanded) tp.Expanded = true;
1918 i = itemsView.IndexOf(tn);
1919 }
1920 if (i != -1) {
1921 int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight() / 2.0) * 2;
1922 if (i < offset) offset = i;
1923 offset = Math.Max(offset, i - Bounds.Height / lh + 1);
1924 }
1925 highlighted = tn;
1926 SelectedNode = tn;
1927 }
1928 }
1929 }