- renamed aa to offset
[FaRetSys.git] / Schematic.cs
blob77c7aae0b0bca1d00422d02c9db2139e1112eada
1 using System;
2 using System.Collections;
3 using Cairo;
4 using Gtk;
5 using Mono.Unix;
7 namespace Eithne
9 class Schematic : DrawingArea
11 private static bool Antialias = Config.Get("schematic/antialias", true);
12 private static bool ChangeBackground = Config.Get("schematic/changebackground", false);
13 private static int BackgroundRed = Config.Get("schematic/red", 128);
14 private static int BackgroundGreen = Config.Get("schematic/green", 128);
15 private static int BackgroundBlue = Config.Get("schematic/blue", 128);
16 private static int BackgroundAlpha = Config.Get("schematic/alpha", 255);
17 private static bool ConnectionAnimation = Config.Get("schematic/connectionanimation", true);
19 internal new class Action
21 public enum Mode
23 Normal,
24 Move,
25 Connect
28 public static Mode m = Mode.Normal;
29 public static object data;
32 internal enum Connection
34 None,
35 Good,
36 Bad
39 private Statusbar status;
40 private ArrayList blocks = new ArrayList();
41 private static int offset = 10;
42 private object selected = null;
43 private int tmpx, tmpy;
45 public ArrayList Blocks
47 get { return blocks; }
50 public static void CheckGConf()
52 bool NewAntialias = Config.Get("schematic/antialias", true);
53 bool NewChangeBackground = Config.Get("schematic/changebackground", false);
54 int NewRed = Config.Get("schematic/red", 128);
55 int NewGreen = Config.Get("schematic/green", 128);
56 int NewBlue = Config.Get("schematic/blue", 128);
57 int NewAlpha = Config.Get("schematic/alpha", 255);
58 bool NewConnectionAnimation = Config.Get("schematic/connectionanimation", true);
60 bool redraw =
61 Antialias != NewAntialias ||
62 ChangeBackground != NewChangeBackground ||
63 BackgroundRed != NewRed ||
64 BackgroundGreen != NewGreen ||
65 BackgroundBlue != NewBlue ||
66 BackgroundAlpha != NewAlpha ||
67 ConnectionAnimation != NewConnectionAnimation;
69 Antialias = NewAntialias;
70 ChangeBackground = NewChangeBackground;
71 BackgroundRed = NewRed;
72 BackgroundGreen = NewGreen;
73 BackgroundBlue = NewBlue;
74 BackgroundAlpha = NewAlpha;
75 ConnectionAnimation = NewConnectionAnimation;
77 if(redraw)
78 MainWindow.RedrawSchematic();
81 public Schematic(Statusbar status) : base()
83 this.status = status;
85 // FIXME hardcoded working space size
86 SetSizeRequest(2048, 2048);
88 Events |= Gdk.EventMask.PointerMotionMask | Gdk.EventMask.ButtonPressMask | Gdk.EventMask.ButtonReleaseMask
89 | Gdk.EventMask.LeaveNotifyMask;
92 protected override bool OnExposeEvent(Gdk.EventExpose args)
94 Context c = Gdk.Context.CreateDrawable(this.GdkWindow);
96 if(Antialias)
97 c.Antialias = Cairo.Antialias.Gray;
98 else
99 c.Antialias = Cairo.Antialias.None;
101 Draw(c);
103 ((IDisposable) c.Target).Dispose();
104 ((IDisposable) c).Dispose();
106 return true;
109 private object CheckSelection(int x, int y)
111 foreach(Block b in new ReverseIterator(blocks))
113 object o = b.Overlap(x, y);
115 if(o != null)
116 return o;
119 return null;
122 private Connection CheckConnection(Socket from, Socket to)
124 if(to.Type == Socket.T.Out)
125 return Connection.None;
126 else
127 if(IsGoodConnection(from, to))
128 return Connection.Good;
129 else
130 return Connection.Bad;
133 private bool IsGoodConnection(Socket from, Socket to)
135 // the same block
136 if(from.Parent == to.Parent)
137 return false;
138 // something is already connected to the socket
139 if(to.Other != null)
140 return false;
142 Plugin.Base fp = from.Parent.Plugin;
143 Plugin.Base tp = to.Parent.Plugin;
145 foreach(string mOut in fp.MatchOut)
147 string tmp = mOut;
148 ArrayList matches = new ArrayList();
149 matches.Add(tmp);
151 while(tmp.LastIndexOf('/') != -1)
153 int lastIndex = tmp.LastIndexOf('/');
154 tmp = tmp.Substring(0, lastIndex);
155 matches.Add(tmp);
157 matches.Add("");
159 foreach(string mIn in tp.MatchIn)
161 if(matches.Contains(mIn))
162 return true;
166 return false;
169 protected override bool OnMotionNotifyEvent(Gdk.EventMotion args)
171 if(Action.m == Action.Mode.Normal)
173 object oldselected = selected;
175 selected = CheckSelection((int)args.X, (int)args.Y);
177 if(oldselected != selected)
179 status.Pop(1);
181 if(selected is Block)
182 status.Push(1, String.Format(Catalog.GetString("{0} block selected"), (selected as Block).Plugin.Info.Name));
183 else if(selected is Socket)
185 Socket s = selected as Socket;
186 Plugin.Base p = s.Parent.Plugin;
188 if(s.Type == Socket.T.In)
189 status.Push(1, String.Format(Catalog.GetString("{0} block, input socket {1}. {2}"),
190 p.Info.Name, s.Num, p.DescIn(s.Num)));
191 else
192 status.Push(1, String.Format(Catalog.GetString("{0} block, output socket {1}. {2}"),
193 p.Info.Name, s.Num, p.DescOut(s.Num)));
196 QueueDraw();
199 else if(Action.m == Action.Mode.Move)
201 int dx = (int)args.X - tmpx;
202 int dy = (int)args.Y - tmpy;
204 (selected as Block).Move(dx, dy);
205 QueueDraw();
207 tmpx = (int)args.X;
208 tmpy = (int)args.Y;
210 else if(Action.m == Action.Mode.Connect)
212 tmpx = (int)args.X;
213 tmpy = (int)args.Y;
215 object tmp = CheckSelection((int)args.X, (int)args.Y);
217 if(tmp is Socket)
218 Action.data = CheckConnection(selected as Socket, tmp as Socket);
219 else
220 Action.data = Connection.None;
222 QueueDraw();
225 return true;
228 protected override bool OnButtonPressEvent(Gdk.EventButton args)
230 status.Pop(1);
232 Schematic _t = this;
233 object selected = _t.selected;
235 object tmp = CheckSelection((int)args.X, (int)args.Y);
237 if(selected != null && selected == tmp)
239 if(selected is Block)
241 Block b = selected as Block;
243 if(args.Button == 1)
245 if(args.Type == Gdk.EventType.ButtonPress)
247 status.Push(1, Catalog.GetString("Move block to desired location"));
249 // move clicked block to the top
250 if(blocks[blocks.Count-1] != selected)
252 blocks.Remove(selected);
253 blocks.Add(selected);
254 QueueDraw();
257 Action.m = Action.Mode.Move;
258 tmpx = (int)args.X;
259 tmpy = (int)args.Y;
261 else if(args.Type == Gdk.EventType.TwoButtonPress)
263 Action.m = Action.Mode.Normal;
265 if(b.Plugin is Plugin.Out && b.Plugin.WorkDone)
269 (b.Plugin as Plugin.Out).DisplayResults();
271 catch(Exception e)
273 b.ShowError = true;
274 QueueDraw();
275 new PluginError(e, b, true);
278 else if(b.Plugin.HasSetup)
282 b.Plugin.Setup();
284 catch(Exception e)
286 b.ShowError = true;
287 QueueDraw();
288 new PluginError(e, b, true);
293 else if(args.Button == 3)
295 ImageMenuItem mi;
297 status.Push(1, String.Format(Catalog.GetString("{0} menu"), b.Plugin.Info.Name));
299 Action.m = Action.Mode.Normal;
301 Menu m = new Menu();
303 if(b.Plugin is Plugin.Out)
305 mi = new ImageMenuItem(Catalog.GetString("Display _results"));
306 mi.Image = new Image(null, "system-search.png");
307 mi.Activated += PluginResults;
308 if(!b.Plugin.WorkDone)
309 mi.Sensitive = false;
310 m.Append(mi);
313 if(b.Plugin.HasSetup)
315 mi = new ImageMenuItem(Catalog.GetString("_Setup"));
316 mi.Image = new Image(null, "preferences-desktop.png");
317 mi.Activated += PluginSetup;
318 m.Append(mi);
321 if(b.Plugin.HasSetup || b.Plugin is Plugin.Out)
322 m.Append(new SeparatorMenuItem());
324 mi = new ImageMenuItem(Catalog.GetString("D_isconnect all"));
325 mi.Image = new Image(null, "edit-cut.png");
326 mi.Activated += delegate(object sender, EventArgs eargs)
328 b.Disconnect();
329 QueueDraw();
330 status.Pop(1);
331 status.Push(1, Catalog.GetString("Removed all block's connections"));
333 m.Append(mi);
335 mi = new ImageMenuItem(Catalog.GetString("In_validate"));
336 mi.Image = new Image(null, "user-trash-full.png");
337 if(b.CheckState() != Block.State.Good)
338 mi.Sensitive = false;
339 mi.Activated += delegate(object sender, EventArgs eargs)
341 b.Invalidate();
343 status.Pop(1);
344 status.Push(1, Catalog.GetString("Invalidated results"));
346 m.Append(mi);
348 mi = new ImageMenuItem(Catalog.GetString("_Delete"));
349 mi.Image = new Image(null, "edit-delete.png");
350 mi.Activated += delegate(object sender, EventArgs eargs)
352 b.Disconnect();
353 blocks.Remove(selected);
354 QueueDraw();
356 status.Pop(1);
357 status.Push(1, String.Format(Catalog.GetString("Deleted {0} block"), b.Plugin.Info.Name));
359 m.Append(mi);
361 m.Append(new SeparatorMenuItem());
363 mi = new ImageMenuItem(Catalog.GetString("_About"));
364 mi.Image = new Image(null, "help-browser.png");
365 mi.Activated += delegate(object sender, EventArgs eargs) { new PluginAbout(b.Plugin); };
366 m.Append(mi);
368 m.ShowAll();
369 m.Popup();
372 else if(selected is Socket)
374 Socket s = selected as Socket;
376 if(s.Other == null)
378 if(s.Type == Socket.T.Out)
379 if(args.Button == 1)
381 status.Push(1, Catalog.GetString("Connect block with another"));
382 Action.m = Action.Mode.Connect;
383 Action.data = Connection.None;
384 tmpx = (int)args.X;
385 tmpy = (int)args.Y;
386 QueueDraw();
389 else
391 if(args.Button == 1 && args.Type == Gdk.EventType.TwoButtonPress)
393 status.Push(1, Catalog.GetString("Removed connection"));
394 s.Disconnect();
395 QueueDraw();
397 else if(args.Button == 3)
399 Menu m = new Menu();
400 ImageMenuItem mi = new ImageMenuItem(Catalog.GetString("_Disconnect"));
401 mi.Image = new Image(null, "edit-cut.png");
402 mi.Activated += delegate(object sender, EventArgs eargs)
404 s.Disconnect();
405 _t.QueueDraw();
407 _t.status.Pop(1);
408 _t.status.Push(1, Catalog.GetString("Removed connection"));
410 m.Append(mi);
412 m.ShowAll();
413 m.Popup();
418 else
420 Action.m = Action.Mode.Normal;
421 selected = tmp;
422 QueueDraw();
425 return true;
428 private void PluginSetup(object sender, EventArgs args)
432 (selected as Block).Plugin.Setup();
434 catch(Exception e)
436 (selected as Block).ShowError = true;
437 QueueDraw();
438 new PluginError(e, selected as Block, true);
442 private void PluginResults(object sender, EventArgs args)
446 ((selected as Block).Plugin as Plugin.Out).DisplayResults();
448 catch(Exception e)
450 (selected as Block).ShowError = true;
451 QueueDraw();
452 new PluginError(e, selected as Block, true);
456 protected override bool OnButtonReleaseEvent(Gdk.EventButton args)
458 if(args.Button != 1)
459 return true;
461 if(Action.m != Action.Mode.Normal)
462 status.Pop(1);
464 Action.m = Action.Mode.Normal;
466 if(selected is Socket)
468 Socket from = selected as Socket;
470 selected = CheckSelection((int)args.X, (int)args.Y);
472 if(selected is Socket)
474 Socket to = selected as Socket;
476 if(CheckConnection(from, to) == Connection.Good)
478 status.Push(1, Catalog.GetString("Connected blocks"));
479 from.Connect(to);
483 QueueDraw();
486 return true;
489 public void Add(Plugin.Base plugin)
491 Context c = Gdk.Context.CreateDrawable(this.GdkWindow);
493 blocks.Add(new Block(this, c, plugin, 10, offset));
495 ((IDisposable) c.Target).Dispose();
496 ((IDisposable) c).Dispose();
498 offset += 50;
499 if(offset > 400)
500 offset = 10;
501 QueueDraw();
504 public void Load(ArrayList blocks)
506 this.blocks = blocks;
507 offset = 10;
508 selected = null;
509 QueueDraw();
512 public void Clear()
514 blocks = new ArrayList();
515 offset = 10;
516 selected = null;
517 QueueDraw();
520 public void Draw(Context c)
522 if(ChangeBackground)
524 if(MainWindow.HaveAlpha)
525 c.Color = new Color(BackgroundRed/255.0, BackgroundGreen/255.0, BackgroundBlue/255.0, BackgroundAlpha/255.0);
526 else
527 c.Color = new Color(BackgroundRed/255.0, BackgroundGreen/255.0, BackgroundBlue/255.0, 1.0);
529 c.Operator = Operator.Source;
530 c.Paint();
531 c.Operator = Operator.Over;
534 foreach(Block b in blocks)
535 b.Draw(c, selected);
537 foreach(Block b in blocks)
538 b.DrawConnections(c);
540 if(Action.m == Action.Mode.Connect)
542 c.Color = new Color(0, 0, 0, 0.75);
543 c.SetDash(new double[2] {4,2}, 0);
544 c.LineWidth = 1.0;
545 c.MoveTo(tmpx, tmpy);
546 c.LineTo((selected as Socket).PX + 6.5, (selected as Socket).PY + 5);
547 c.Stroke();
549 c.SetDash(new double[0] {}, 0);
550 switch((Connection)Action.data)
552 case Connection.None:
553 c.Color = new Color(1, 1, 1, 0.8);
554 c.Arc(tmpx, tmpy, 4, 0, 2*Math.PI);
555 break;
557 case Connection.Good:
558 c.Color = new Color(0.3, 1, 0.3, 0.8);
559 if(ConnectionAnimation)
560 c.Arc(tmpx, tmpy, 7 + Math.Sin(DateTime.Now.Ticks/1000000.0d), 0, 2*Math.PI);
561 else
562 c.Arc(tmpx, tmpy, 7, 0, 2*Math.PI);
563 break;
565 case Connection.Bad:
566 c.Color = new Color(1, 0.3, 0.3, 0.8);
567 if(ConnectionAnimation)
568 c.Arc(tmpx, tmpy, 7 + Math.Sin(DateTime.Now.Ticks/500000.0d) * 1.5f, 0, 2*Math.PI);
569 else
570 c.Arc(tmpx, tmpy, 7, 0, 2*Math.PI);
571 break;
573 c.FillPreserve();
574 switch((Connection)Action.data)
576 case Connection.None:
577 c.LineWidth = 1.5;
578 c.Color = new Color(0, 0, 0);
579 break;
581 case Connection.Good:
582 c.LineWidth = 3.0;
583 c.Color = new Color(0, 0.5, 0);
584 break;
586 case Connection.Bad:
587 c.LineWidth = 3.0;
588 c.Color = new Color(0.5, 0, 0);
589 break;
591 c.Stroke();
593 if(ConnectionAnimation)
594 GLib.Timeout.Add(200, new GLib.TimeoutHandler(AnimTick));
598 public void Redraw()
600 QueueDraw();
603 private bool AnimTick()
605 QueueDraw();
606 return false;