Several updates - added support for second pattern engine and adding commands
authorPhilipp Zabel <philipp.zabel@gmail.com>
Sun, 28 Feb 2010 13:50:10 +0000 (14:50 +0100)
committerPhilipp Zabel <philipp.zabel@gmail.com>
Sun, 28 Feb 2010 15:48:07 +0000 (16:48 +0100)
* support for second pattern engine
  - ledPatternRX51 stores two LedColors instead of a led_map string
  - added LedColor.OFF state
* updated LED command widgets
  - all LED command widgets contain Hildon.PickerButtons now
  - custom dialogs are used to edit details, delete commands
  - adding new commands supported
* basic trigger support
  - updated timing calculations

src/led-command-widget.vala
src/led-pattern-rx51.vala
src/led-pattern-view.vala
src/led-pattern.vala
src/led-program-dialog.vala

index 36f18d7..3e33bea 100644 (file)
 class LedCommandWidget : Gtk.HBox {
        private const double CYCLE_TIME_MS = 1000.0 / 32768.0;
 
-       LedCommand command;
+       LedPatternRX51 pattern;
+       unowned List<LedCommandRX51> engine;
+       LedCommandRX51 command;
 
-       public LedCommandWidget (LedCommand _command) {
+       public LedCommandWidget (LedPatternRX51 _pattern, List<LedCommandRX51> _engine,
+                                LedCommandRX51 _command) {
                homogeneous = true;
+               pattern = _pattern;
+               engine = _engine;
                command = _command;
 
-               string text = "";
+               var button = new Hildon.Button (Hildon.SizeType.FINGER_HEIGHT,
+                                               Hildon.ButtonArrangement.VERTICAL);
+               button.set_style (Hildon.ButtonStyle.PICKER);
+               button.set_alignment (0, 0.5f, 0, 0.5f);
+
                switch (command.type) {
                case CommandType.UNKNOWN:
-                       LedCommandRX51 cmd = command as LedCommandRX51;
-                       if (cmd != null)
-                               text = "??? (0x%04x)".printf (cmd.code);
-                       else
-                               text = "???";
+                       button.set_title ("???");
+                       button.set_value ("0x%04x".printf (command.code));
+                       button.set_sensitive (false);
                        break;
                case CommandType.RAMP_WAIT:
-                       text = "Ramp / Wait";
+                       button.set_title ((command.steps != 0) ? "Ramp" : "Wait");
+                       button.set_value ((command.steps != 0) ?
+                                         "%+d steps, %.2f ms each".printf (command.steps,
+                                                                           command.step_time) :
+                                         "%.2f ms".printf (command.duration));
+                       button.clicked.connect (on_ramp_wait_clicked);
                        break;
                case CommandType.SET_PWM:
-                       text = "Set PWM";
+                       button.set_title ("Set PWM");
+                       button.set_value ("Level = %d".printf (command.level));
+                       button.clicked.connect (on_set_pwm_clicked);
                        break;
                case CommandType.RESET_MUX:
-                       text = "Reset Mux";
+                       button.set_title ("Reset Mux");
                        break;
                case CommandType.GO_TO_START:
-                       text = "Go To Start";
+                       button.set_title ("Go To Start");
+                       button.set_value ("");
+                       button.clicked.connect (on_end_or_go_to_start_clicked);
                        break;
                case CommandType.BRANCH:
-                       text = "Branch";
+                       button.set_title ("Branch");
+                       button.set_value ("0x%04x".printf (command.code));
+                       button.set_sensitive (false);
                        break;
                case CommandType.END:
-                       text = "End";
+                       button.set_title ("End");
+                       button.set_value ((command.steps == -255) ? "Reset" : "Hold");
+                       button.clicked.connect (on_end_or_go_to_start_clicked);
                        break;
                case CommandType.TRIGGER:
-                       text = "Trigger";
+                       button.set_title ("Trigger");
+                       string text = "";
+                       if (0x0100 in command.code)
+                               text += "wait 2 ";
+                       if (0x0004 in command.code)
+                               text += "set 2 ";
+                       if (0x0080 in command.code)
+                               text += "wait 1 ";
+                       if (0x0002 in command.code)
+                               text += "set 1 ";
+                       if ((command.code & ~0xe186) != 0) {
+                               text = "Unsupported: 0x%04x".printf (command.code);
+                               button.set_sensitive (false);
+                       }
+                       button.set_value (text);
+                       button.clicked.connect (on_trigger_clicked);
                        break;
                }
+               pack_start (button, true, true, 0);
+       }
 
-               var label = new Gtk.Label (text);
-               label.set_alignment (0.0f, 0.5f);
-               pack_start (label, true, true, 0);
+       private void on_ramp_wait_clicked (Gtk.Button source) {
+               double old_step_time = command.step_time;
+               int old_steps = command.steps;
+               var dialog = new Gtk.Dialog ();
+               dialog.set_title ("Ramp / Wait");
 
-               switch (command.type) {
-               case CommandType.RAMP_WAIT:
-                       var selector = new Hildon.TouchSelector.text ();
-                       for (int i = 1; i <= 31; i++)
-                               selector.append_text ("%.2f ms".printf (i * (16 * CYCLE_TIME_MS)));
-                       for (int i = 1; i <= 31; i++)
-                               selector.append_text ("%.1f ms".printf (i * (512 * CYCLE_TIME_MS)));
-                       var picker = new Hildon.PickerButton (Hildon.SizeType.FINGER_HEIGHT,
-                                                             Hildon.ButtonArrangement.VERTICAL);
-                       picker.set_title ("Step time");
-                       picker.set_selector (selector);
-                       int j;
-                       if (command.step_time <= 31*(16 * CYCLE_TIME_MS))
-                               j = (int) ((command.step_time + 0.001) / (16 * CYCLE_TIME_MS)) - 1;
+               var content = (Gtk.VBox) dialog.get_content_area ();
+
+               var lpv = new LedPatternView (pattern);
+               lpv.set_size_request (-1, 70);
+               content.pack_start (lpv, true, true, 0);
+
+               var scale1 = new Gtk.HScale.with_range (1, 62, 1);
+               int v = (int) (command.step_time / 16 / CYCLE_TIME_MS);
+               if (v > 62)
+                       v /= 32;
+               scale1.set_value (v);
+               scale1.format_value.connect ((v) => {
+                       if (v < 32)
+                               return "%.2f ms".printf (v * 16 * CYCLE_TIME_MS);
                        else
-                               j = (int) ((command.step_time + 0.01) / (512 * CYCLE_TIME_MS)) + 30;
-                       picker.set_active (j);
-                       picker.value_changed.connect ((s) => {
-                               double step_time;
-                               int i = s.get_active ();
-                               if (i < 31)
-                                       step_time = (i + 1) * (16 * CYCLE_TIME_MS);
-                               else
-                                       step_time = (i - 30) * (512 * CYCLE_TIME_MS);
-                               command.ramp_wait (step_time, command.steps);
-                       });
-                       pack_start (picker, true, true, 0);
-
-                       selector = new Hildon.TouchSelector.text ();
-                       for (int i = -255; i < 256; i++)
-                               selector.append_text ("%+d".printf (i));
-                       picker = new Hildon.PickerButton (Hildon.SizeType.FINGER_HEIGHT,
-                                                         Hildon.ButtonArrangement.VERTICAL);
-                       picker.set_title ("Steps");
-                       picker.set_selector (selector);
-                       picker.set_active (command.steps + 255);
-                       picker.value_changed.connect ((s) => {
-                               int steps = s.get_active () - 255;
-                               command.ramp_wait (command.step_time, steps);
-                       });
-                       pack_start (picker, true, true, 0);
-                       break;
-               case CommandType.SET_PWM:
-                       label = new Gtk.Label ("");
-                       pack_start (label, true, true, 0);
-                       var selector = new Hildon.TouchSelector.text ();
-                       for (int i = 0; i < 256; i++)
-                               selector.append_text ("%d".printf (i));
-                       var picker = new Hildon.PickerButton (Hildon.SizeType.FINGER_HEIGHT,
-                                                             Hildon.ButtonArrangement.VERTICAL);
-                       picker.set_title ("Level");
-                       picker.set_selector (selector);
-                       picker.set_active (command.level);
-                       picker.value_changed.connect ((s) => {
-                               int level = s.get_active ();
-                               command.set_pwm (level);
-                       });
-                       pack_start (picker, true, true, 0);
-                       break;
-               case CommandType.END:
-                       var check = new Hildon.CheckButton (Hildon.SizeType.FINGER_HEIGHT);
-                       check.set_label ("Reset");
-                       check.set_active (command.steps == -255);
-                       check.toggled.connect ((s) => {
-                               command.steps = s.get_active () ? -255 : 0;
-                               command.changed ();
-                       });
-                       pack_start (check, true, true, 0);
-                       label = new Gtk.Label ("");
-                       pack_start (label, true, true, 0);
-                       break;
-               case CommandType.UNKNOWN:
-               case CommandType.RESET_MUX:
-               case CommandType.GO_TO_START:
-               case CommandType.BRANCH:
-               case CommandType.TRIGGER:
-                       label = new Gtk.Label ("");
-                       pack_start (label, true, true, 0);
-                       label = new Gtk.Label ("");
-                       pack_start (label, true, true, 0);
-                       break;
+                               return "%.1f ms".printf ((v - 31) * 512 * CYCLE_TIME_MS);
+               });
+               scale1.value_changed.connect ((s) => {
+                       int val = (int) s.get_value ();
+                       if (val < 32)
+                               command.ramp_wait (val * 16 * CYCLE_TIME_MS, command.steps);
+                       else
+                               command.ramp_wait ((val - 31) * 512 * CYCLE_TIME_MS, command.steps);
+               });
+               content.pack_start (scale1, true, true, 0);
+
+               var hbox = new Gtk.HBox (false, 0);
+               var hbox2 = new Gtk.HBox (true, 0);
+               var radio_inc = (Gtk.RadioButton) Hildon.gtk_radio_button_new (Hildon.SizeType.FINGER_HEIGHT, null);
+               radio_inc.set_mode (false);
+               radio_inc.set_label ("+");
+               hbox2.pack_start (radio_inc, false, false, 0);
+               var radio_dec = (Gtk.RadioButton) Hildon.gtk_radio_button_new_from_widget (Hildon.SizeType.FINGER_HEIGHT, radio_inc);
+               radio_dec.set_mode (false);
+               radio_dec.set_label ("-");
+               bool sign = command.steps < 0;
+               radio_dec.set_active (sign);
+               radio_dec.toggled.connect ((s) => {
+                       sign = s.get_active ();
+                       if (sign != (command.steps < 0))
+                               command.ramp_wait (command.step_time, -command.steps);
+               });
+               hbox2.pack_start (radio_dec, false, false, 0);
+               hbox.pack_start (hbox2, false, false, 0);
+               var scale2 = new Gtk.HScale.with_range (0, 255, 1);
+               scale2.set_value ((command.steps < 0) ? -command.steps : command.steps);
+               scale2.set_value_pos (Gtk.PositionType.RIGHT);
+               scale2.format_value.connect ((v) => {
+                       return "%+d".printf (sign ? -(int) v : (int) v);
+               });
+               scale2.value_changed.connect ((s) => {
+                       if (sign)
+                               command.ramp_wait (command.step_time, -(int) scale2.get_value ());
+                       else
+                               command.ramp_wait (command.step_time, (int) scale2.get_value ());
+               });
+               hbox.pack_start (scale2, true, true, 0);
+               content.pack_start (hbox, false, false, 0);
+
+               content.show_all ();
+               dialog.add_button ("Delete", 1);
+               dialog.add_button ("Done", Gtk.ResponseType.OK);
+
+               int response = dialog.run ();
+               if (response == Gtk.ResponseType.OK) {
+                       var button = source as Hildon.Button;
+                       button.set_title ((command.steps != 0) ? "Ramp" : "Wait");
+                       button.set_value ((command.steps != 0) ?
+                                         "%+d steps, %.2f ms each".printf (command.steps,
+                                                                           command.step_time) :
+                                         "%.2f ms".printf (command.duration));
+               } else if (response == 1) {
+                       engine.remove (command);
+                       engine.first ().data.changed ();
+                       this.destroy ();
+               } else {
+                       command.ramp_wait (old_step_time, old_steps);
+               }
+               dialog.destroy ();
+       }
+
+       private void on_set_pwm_clicked (Gtk.Button source) {
+               int old_level = command.level;
+               var dialog = new Gtk.Dialog ();
+               dialog.set_title ("Set PWM");
+
+               var content = (Gtk.VBox) dialog.get_content_area ();
+
+               var lpv = new LedPatternView (pattern);
+               lpv.set_size_request (-1, 70);
+               content.pack_start (lpv, true, true, 0);
+
+               var scale = new Gtk.HScale.with_range (0, 255, 1);
+               scale.set_value (command.level);
+               scale.value_changed.connect ((s) => {
+                       command.set_pwm ((int) s.get_value ());
+               });
+               content.pack_start (scale, true, true, 0);
+
+               content.show_all ();
+               dialog.add_button ("Delete", 1);
+               dialog.add_button ("Done", Gtk.ResponseType.OK);
+
+               int response = dialog.run ();
+               if (response == Gtk.ResponseType.OK) {
+                       command.set_pwm ((int) scale.get_value ());
+
+                       var button = source as Hildon.Button;
+                       button.set_value ("Level = %d".printf (command.level));
+               } else if (response == 1) {
+                       engine.remove (command);
+                       engine.first ().data.changed ();
+                       this.destroy ();
+               } else {
+                       command.set_pwm (old_level);
+               }
+               dialog.destroy ();
+       }
+
+       private void on_end_or_go_to_start_clicked (Gtk.Button source) {
+               CommandType old_type = command.type;
+               int old_steps = command.steps;
+               uint16 old_code = command.code;
+               var dialog = new Gtk.Dialog ();
+               if (command.type == CommandType.END)
+                       dialog.set_title ("End");
+               else
+                       dialog.set_title ("Go To Start");
+
+               var content = (Gtk.VBox) dialog.get_content_area ();
+
+               var lpv = new LedPatternView (pattern);
+               lpv.set_size_request (-1, 70);
+               content.pack_start (lpv, true, true, 0);
+
+               var hbox = new Gtk.HBox (true, 0);
+               var radio_end = (Gtk.RadioButton) Hildon.gtk_radio_button_new (Hildon.SizeType.FINGER_HEIGHT, null);
+               radio_end.set_mode (false);
+               radio_end.set_label ("End");
+               hbox.pack_start (radio_end, true, true, 0);
+               var radio_go_to_start = (Gtk.RadioButton) Hildon.gtk_radio_button_new_from_widget (Hildon.SizeType.FINGER_HEIGHT, radio_end);
+               radio_go_to_start.set_mode (false);
+               radio_go_to_start.set_label ("Go To Start");
+               hbox.pack_start (radio_go_to_start, true, true, 0);
+               content.pack_start (hbox, true, true, 0);
+
+               hbox = new Gtk.HBox (true, 0);
+               var radio_hold = (Gtk.RadioButton) Hildon.gtk_radio_button_new (Hildon.SizeType.FINGER_HEIGHT, null);
+               radio_hold.set_mode (false);
+               radio_hold.set_label ("Hold");
+               hbox.pack_start (radio_hold, true, true, 0);
+               var radio_reset = (Gtk.RadioButton) Hildon.gtk_radio_button_new_from_widget (Hildon.SizeType.FINGER_HEIGHT, radio_hold);
+               radio_reset.set_mode (false);
+               radio_reset.set_label ("Reset");
+               hbox.pack_start (radio_reset, true, true, 0);
+               content.pack_start (hbox, true, true, 0);
+
+               if (command.type == CommandType.END)
+                       radio_end.set_active (true);
+               else
+                       radio_go_to_start.set_active (true);
+               radio_hold.set_sensitive (command.type == CommandType.END);
+               radio_reset.set_sensitive (command.type == CommandType.END);
+               radio_reset.set_active (command.steps == -255);
+
+               radio_end.toggled.connect ((s) => {
+                       if (s.get_active ()) {
+                               command.end (radio_reset.get_active ());
+                               radio_hold.set_sensitive (true);
+                               radio_reset.set_sensitive (true);
+                               dialog.set_title ("End");
+                       } else {
+                               command.go_to_start ();
+                               radio_hold.set_sensitive (false);
+                               radio_reset.set_sensitive (false);
+                               dialog.set_title ("Go To Start");
+                       }
+                       command.changed ();
+               });
+               radio_reset.toggled.connect ((s) => {
+                       if (command.type == CommandType.END) {
+                               command.end (s.get_active ());
+                       }
+               });
+
+               content.show_all ();
+               dialog.add_button ("Delete", 1);
+               dialog.add_button ("Done", Gtk.ResponseType.OK);
+
+               int response = dialog.run ();
+               if (response == Gtk.ResponseType.OK) {
+                       var button = source as Hildon.Button;
+                       button.set_value ((command.type == CommandType.END) ?
+                                         "End" : "Go To Start");
+                       button.set_value ((command.type == CommandType.END) ?
+                                         ((command.steps == -255) ? "Reset" : "Hold") : "");
+               } else if (response == 1) {
+                       engine.remove (command);
+                       engine.first ().data.changed ();
+                       this.destroy ();
+               } else {
+                       command.type = old_type;
+                       command.steps = old_steps;
+                       command.code = old_code;
+                       command.changed ();
+               }
+               dialog.destroy ();
+       }
+
+       private void on_trigger_clicked (Gtk.Button source) {
+               uint16 old_code = command.code;
+               var dialog = new Gtk.Dialog ();
+               dialog.set_title ("Trigger");
+
+               var content = (Gtk.VBox) dialog.get_content_area ();
+
+               var lpv = new LedPatternView (pattern);
+               lpv.set_size_request (-1, 70);
+               content.pack_start (lpv, true, true, 0);
+
+               var check = new Hildon.CheckButton (Hildon.SizeType.FINGER_HEIGHT);
+               check.set_label ("Set trigger");
+               if ((command.code & 0x0006) != 0)
+                       check.set_active (true);
+               check.toggled.connect ((s) => {
+                       int bit = (engine == pattern.engine1) ? 0x0004 : 0x0002;
+                       if (s.get_active ())
+                               command.code |= bit;
+                       else
+                               command.code &= ~bit;
+                       command.changed ();
+               });
+               content.pack_start (check, true, true, 0);
+
+               check = new Hildon.CheckButton (Hildon.SizeType.FINGER_HEIGHT);
+               check.set_label ("Wait for trigger");
+               if ((command.code & 0x0180) != 0)
+                       check.set_active (true);
+               check.toggled.connect ((s) => {
+                       int bit = (engine == pattern.engine1) ? 0x0100 : 0x0080;
+                       if (s.get_active ())
+                               command.code |= bit;
+                       else
+                               command.code &= ~bit;
+                       command.changed ();
+               });
+               content.pack_start (check, true, true, 0);
+
+               content.show_all ();
+               dialog.add_button ("Delete", 1);
+               dialog.add_button ("Done", Gtk.ResponseType.OK);
+
+               int response = dialog.run ();
+               if (response == Gtk.ResponseType.OK) {
+                       var button = source as Hildon.Button;
+                       string text = "";
+                       if (0x0100 in command.code)
+                               text += "wait 2 ";
+                       if (0x0004 in command.code)
+                               text += "set 2 ";
+                       if (0x0080 in command.code)
+                               text += "wait 1 ";
+                       if (0x0002 in command.code)
+                               text += "set 1 ";
+                       if ((command.code & ~0xe186) != 0) {
+                               text = "Unsupported: 0x%04x".printf (command.code);
+                               button.set_sensitive (false);
+                       }
+                       button.set_value (text);
+               } else if (response == 1) {
+                       engine.remove (command);
+                       engine.first ().data.changed ();
+                       this.destroy ();
+               } else {
+                       command.code = old_code;
+                       command.changed ();
                }
+               dialog.destroy ();
        }
 }
index fe2dc79..72f3638 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 class LedPatternRX51 : LedPattern {
-       public string led_map;
+       public LedColor color1;
+       public LedColor color2;
        public List<LedCommandRX51> engine1;
        public List<LedCommandRX51> engine2;
 
@@ -44,7 +45,7 @@ class LedPatternRX51 : LedPattern {
                priority = p[0].to_int ();
                screen_on = p[1].to_int ();
                timeout = p[2].to_int ();
-               led_map = p[3];
+               parse_led_map (p[3], out color1, out color2);
                engine1 = parse_pattern (p[4]);
                engine2 = parse_pattern (p[5]);
 
@@ -58,6 +59,23 @@ class LedPatternRX51 : LedPattern {
                on_changed ();
        }
 
+       private void parse_led_map (string led_map, out LedColor color1, out LedColor color2) {
+               color1 = LedColor.OFF;
+               color2 = LedColor.OFF;
+               if ("r" in led_map)
+                       color1 |= LedColor.R;
+               if ("g" in led_map)
+                       color1 |= LedColor.G;
+               if ("b" in led_map)
+                       color1 |= LedColor.B;
+               if ("R" in led_map)
+                       color2 |= LedColor.R;
+               if ("G" in led_map)
+                       color2 |= LedColor.G;
+               if ("B" in led_map)
+                       color2 |= LedColor.B;
+       }
+
        private List<LedCommandRX51> parse_pattern (string pattern) {
                var list = new List<LedCommandRX51> ();
                var length = pattern.length;
@@ -78,10 +96,27 @@ class LedPatternRX51 : LedPattern {
        }
 
        public string dump () {
-               return "%s=%d;%d;%d;%s;%s;%s".printf (name, priority, screen_on, timeout, led_map,
+               return "%s=%d;%d;%d;%s;%s;%s".printf (name, priority, screen_on, timeout, dump_led_map (),
                                                      dump_pattern (engine1), dump_pattern (engine2));
        }
 
+       private string dump_led_map () {
+               string led_map = "";
+               if (LedColor.R in color1)
+                       led_map += "r";
+               if (LedColor.R in color2)
+                       led_map += "R";
+               if (LedColor.G in color1)
+                       led_map += "g";
+               if (LedColor.G in color2)
+                       led_map += "G";
+               if (LedColor.B in color1)
+                       led_map += "b";
+               if (LedColor.B in color2)
+                       led_map += "B";
+               return led_map;
+       }
+
        private string dump_pattern (List<LedCommandRX51> list) {
                string result = "";
                foreach (LedCommandRX51 command in list) {
@@ -100,7 +135,8 @@ class LedPatternRX51 : LedPattern {
 
                pattern.duration = duration;
 
-               pattern.led_map = led_map;
+               pattern.color1 = color1;
+               pattern.color2 = color2;
                pattern.engine1 = deep_copy (pattern, engine1);
                pattern.engine2 = deep_copy (pattern, engine2);
 
@@ -127,38 +163,105 @@ class LedPatternRX51 : LedPattern {
 
                duration = pattern.duration;
 
-               led_map = pattern.led_map;
+               color1 = pattern.color1;
+               color2 = pattern.color2;
                engine1 = deep_copy (this, pattern.engine1);
                engine2 = deep_copy (this, pattern.engine2);
 
                changed ();
        }
 
-       void on_changed () {
-               // calculate timing and level info
+       public void on_changed () {
+               bool unresolved = calculate_timing ();
+               if (unresolved)
+                       unresolved = calculate_timing ();
+               if (unresolved)
+                       Hildon.Banner.show_information (null, null, "Timing unresolved");
+               changed ();
+       }
+
+       private bool calculate_timing () {
+               bool unresolved = false;
+               // Calculate timing and level info for engine 1
                double time = 0;
                int level = 0;
                foreach (LedCommandRX51 command in engine1) {
                        command.time = time;
-                       time += command.duration;
                        if (command.type == CommandType.SET_PWM) {
                                level = command.level;
                        } else {
                                command.level = level;
                                level += command.steps;
                        }
+                       if (command.type == CommandType.TRIGGER &&
+                           (command.code & 0x0180) != 0) {
+                               command.duration = wait_for_trigger (time, engine2);
+                               if (command.duration == 0)
+                                       unresolved = true;
+                               command.duration += 16 * LedCommandRX51.CYCLE_TIME_MS;
+                       }
+                       time += command.duration;
                        if (level < 0)
                                level = 0;
                        if (level > 255)
                                level = 255;
                }
                duration = time;
-               changed ();
+               // Calculate timing and level info for engine 2
+               time = 0;
+               level = 0;
+               foreach (LedCommandRX51 command in engine2) {
+                       command.time = time;
+                       if (command.type == CommandType.SET_PWM) {
+                               level = command.level;
+                       } else {
+                               command.level = level;
+                               level += command.steps;
+                       }
+                       if (command.type == CommandType.TRIGGER &&
+                           (command.code & 0x0180) != 0) {
+                               command.duration = wait_for_trigger (time, engine1);
+                               if (command.duration == 0)
+                                       unresolved = true;
+                               command.duration += 16 * LedCommandRX51.CYCLE_TIME_MS;
+                       }
+                       time += command.duration;
+                       if (level < 0)
+                               level = 0;
+                       if (level > 255)
+                               level = 255;
+               }
+               if (time > duration)
+                       duration = time;
+               return unresolved;
+       }
+
+       double wait_for_trigger (double time, List<LedCommandRX51> engine) {
+               double duration = 0;
+               bool repeat = false;
+               foreach (LedCommandRX51 command in engine) {
+                       duration = command.time + command.duration;
+                       if (command.type == CommandType.TRIGGER &&
+                           (command.code & 0x0006) != 0 && command.time > time) {
+                               return command.time - time;
+                       }
+                       if (command.type == CommandType.GO_TO_START) {
+                               repeat = true;
+                               break;
+                       }
+               }
+               if (repeat) foreach (LedCommandRX51 command in engine) {
+                       if (command.type == CommandType.TRIGGER &&
+                           (command.code & 0x0006) != 0 && (duration + command.time) > time) {
+                               return duration + command.time - time;
+                       }
+               }
+               return 0;
        }
 }
 
 class LedCommandRX51 : LedCommand {
-       private const double CYCLE_TIME_MS = 1000.0 / 32768.0;
+       internal const double CYCLE_TIME_MS = 1000.0 / 32768.0;
 
        public uint16 code;
 
@@ -199,12 +302,12 @@ class LedCommandRX51 : LedCommand {
                                // 0x1000: interrupt
                                if ((code & 0x0800) != 0) // Reset
                                        steps = -255;
-                       } else if ((code & ~ 0x13f0) == 0xe000) {
+                       } else if ((code & ~ 0x13fe) == 0xe000) {
                                type = CommandType.TRIGGER;
                                // 0x1000: wait ext
                                // 0x0380: wait B G R
                                // 0x0040: set ext
-                               // ??: set B G R
+                               // 0x000e: set B G R
                        }
                }
        }
@@ -216,12 +319,12 @@ class LedCommandRX51 : LedCommand {
 
        public override void ramp_wait (double _step_time, int _steps) requires (_step_time >= (16 * CYCLE_TIME_MS)) {
                int step_time;
-               if (_step_time <= 31 * (16 * CYCLE_TIME_MS)) {
+               if (_step_time < 32 * (16 * CYCLE_TIME_MS)) {
                        step_time = (int) ((_step_time + 0.001) / (16 * CYCLE_TIME_MS));
                        code = (uint16) step_time << 9;
                        _step_time = step_time * (16 * CYCLE_TIME_MS);
-               } else if (_step_time <= 31*(512 * CYCLE_TIME_MS)) {
-                       step_time = (int) ((_step_time + 0.01) / (512 * CYCLE_TIME_MS));
+               } else if (_step_time < 32*(512 * CYCLE_TIME_MS)) {
+                       step_time = (int) ((_step_time + 0.001) / (512 * CYCLE_TIME_MS));
                        code = 0x4000 | (step_time << 9);
                        _step_time = step_time * (512 * CYCLE_TIME_MS);
                } else {
@@ -235,6 +338,18 @@ class LedCommandRX51 : LedCommand {
                base.ramp_wait (_step_time, _steps);
        }
 
+       public override void go_to_start () {
+               code = 0x0000;
+               base.go_to_start ();
+       }
+
+       public override void end (bool reset) {
+               code = 0xc000;
+               if (reset)
+                       code |= 0x0800;
+               base.end (reset);
+       }
+
        public LedCommandRX51 copy () {
                var command = new LedCommandRX51 ();
 
index 89b6163..0e908d5 100644 (file)
@@ -82,131 +82,120 @@ class LedPatternView : Gtk.DrawingArea {
                ctx.stroke ();
 
                if (pattern != null) {
-                       ctx.new_path ();
+                       if (pattern.color1 != LedColor.OFF)
+                               draw_pattern (ctx, width, height,
+                                             pattern.color1, pattern.engine1);
+                       if (pattern.color2 != LedColor.OFF)
+                               draw_pattern (ctx, width, height,
+                                             pattern.color2, pattern.engine2);
+               }
+               return true;
+       }
 
-                       if (pattern.led_map == "r") {
-                               ctx.set_source_rgb (1, 0, 0);
-                       } else if (pattern.led_map == "g") {
-                               ctx.set_source_rgb (0, 1, 0);
-                       } else if (pattern.led_map == "b") {
-                               ctx.set_source_rgb (0, 0, 1);
-                       } else if (pattern.led_map == "rg") {
-                               ctx.set_source_rgb (1, 1, 0);
-                       } else if (pattern.led_map == "rb") {
-                               ctx.set_source_rgb (1, 0, 1);
-                       } else if (pattern.led_map == "gb") {
-                               ctx.set_source_rgb (0, 1, 1);
-                       } else if (pattern.led_map == "rgb") {
-                               ctx.set_source_rgb (1, 1, 1);
-                       } else {
-                               ctx.set_source_rgb (0.75, 0.75, 0.75);
+       private void draw_pattern (Cairo.Context ctx, int width, int height, LedColor color,
+                                  List<LedCommandRX51> engine) {
+               double pps = width / duration; // pixel per second
+
+               ctx.new_path ();
+
+               ctx.set_operator (Cairo.Operator.ADD);
+
+               ctx.set_source_rgb ((LedColor.R in color) ? 1.0 : 0.0,
+                                   (LedColor.G in color) ? 1.0 : 0.0,
+                                   (LedColor.B in color) ? 1.0 : 0.0);
+               ctx.set_line_width (3.0);
+
+               double x = 0, y = 0;
+               foreach (LedCommand command in engine) {
+                       x = command.time * pps/1000.0;
+                       y = (255 - command.level) * (height - 1)/255.0;
+                       switch (command.type) {
+                       case CommandType.RAMP_WAIT:
+                       case CommandType.TRIGGER:
+                               x += command.duration * pps/1000.0;
+                               y -= command.steps * (height - 1)/255.0;
+                               if (y < 0)
+                                       y = 0;
+                               if (y > (height - 1))
+                                       y = height - 1;
+                               ctx.line_to (x, y);
+                               break;
+                       default:
+                               ctx.line_to (x, y);
+                               break;
                        }
-                       ctx.set_line_width (3.0);
-
-                       double x = 0, y = 0;
-                       foreach (LedCommand command in pattern.engine1) {
-                               x = command.time * pps/1000.0;
-                               y = (255 - command.level) * (height - 1)/255.0;
-                               switch (command.type) {
-                               case CommandType.RAMP_WAIT:
-                                       x += command.duration * pps/1000.0;
-                                       y -= command.steps * (height - 1)/255.0;
-                                       if (y < 0)
-                                               y = 0;
-                                       if (y > (height - 1))
-                                               y = height - 1;
-                                       ctx.line_to (x, y);
-                                       break;
-                               default:
-                                       ctx.line_to (x, y);
-                                       break;
-                               }
+               }
+               ctx.stroke ();
+
+               ctx.set_source_rgb ((LedColor.R in color) ? 0.75 : 0.0,
+                                   (LedColor.G in color) ? 0.75 : 0.0,
+                                   (LedColor.B in color) ? 0.75 : 0.0);
+               ctx.set_line_width (1.0);
+
+               LedCommandRX51 last_command = null;
+               foreach (LedCommandRX51 command in engine) {
+                       if (command.type == CommandType.END ||
+                           command.type == CommandType.GO_TO_START) {
+                               last_command = command;
+                               break;
                        }
-                       ctx.stroke ();
+               }
+               if (last_command == null)
+                       return;
+
+               if (last_command.type == CommandType.END) {
+                       ctx.new_path ();
 
-                       if (pattern.led_map == "r") {
-                               ctx.set_source_rgb (0.75, 0, 0);
-                       } else if (pattern.led_map == "g") {
-                               ctx.set_source_rgb (0, 0.75, 0);
-                       } else if (pattern.led_map == "b") {
-                               ctx.set_source_rgb (0, 0, 0.75);
-                       } else if (pattern.led_map == "rg") {
-                               ctx.set_source_rgb (0.75, 0.75, 0);
-                       } else if (pattern.led_map == "rb") {
-                               ctx.set_source_rgb (0.75, 0, 0.75);
-                       } else if (pattern.led_map == "gb") {
-                               ctx.set_source_rgb (0, 0.75, 0.75);
-                       } else if (pattern.led_map == "rgb") {
-                               ctx.set_source_rgb (0.75, 0.75, 0.75);
+                       ctx.move_to (x, y);
+                       if (last_command.steps == -255) {
+                               ctx.line_to (x, height - 0.5);
+                               ctx.line_to (width - 0.5, height - 0.5);
                        } else {
-                               ctx.set_source_rgb (0.66, 0.66, 0.66);
-                       }
-                       ctx.set_line_width (1.0);
-
-                       CommandType type = CommandType.UNKNOWN;
-                       int steps = 0;
-                       foreach (LedCommandRX51 command in pattern.engine1) {
-                               if (command.type == CommandType.END ||
-                                   command.type == CommandType.GO_TO_START) {
-                                       type = command.type;
-                                       steps = command.steps;
-                                       break;
-                               }
+                               ctx.line_to (width - 0.5, y);
                        }
 
-                       if (type == CommandType.END) {
-                               ctx.new_path ();
+                       ctx.stroke ();
+               }
+               var engine_duration = last_command.time + last_command.duration;
+               if (last_command.type == CommandType.GO_TO_START && engine_duration >= 3 * 0.49) {
+                       ctx.new_path ();
 
+                       for (double offset = engine_duration; (offset * pps/1000.0) <= width; offset += engine_duration) {
                                ctx.move_to (x, y);
-                               if (steps == -255) {
-                                       ctx.line_to (x, height - 0.5);
-                                       ctx.line_to (width - 0.5, height - 0.5);
-                               } else {
-                                       ctx.line_to (width - 0.5, y);
-                               }
-
-                               ctx.stroke ();
-                       }
-                       if (type == CommandType.GO_TO_START && pattern.duration >= 3 * 0.49) {
-                               ctx.new_path ();
-
-                               for (double offset = pattern.duration; (offset * pps/1000.0) <= width; offset += pattern.duration) {
-                                       ctx.move_to (x, y);
-                                       foreach (LedCommand command in pattern.engine1) {
-                                               x = (command.time + offset) * pps/1000.0;
-                                               y = (255 - command.level) * (height - 1)/255.0;
-                                               if (x >= width)
-                                                       break;
-                                               switch (command.type) {
-                                               case CommandType.RAMP_WAIT:
-                                                       x += command.duration * pps/1000.0;
-                                                       y -= command.steps * (height - 1)/255.0;
-                                                       if (y < 0)
-                                                               y = 0;
-                                                       if (y > (height - 1))
-                                                               y = height - 1;
-                                                       ctx.line_to (x, y);
-                                                       break;
-                                               default:
-                                                       ctx.line_to (x, y);
-                                                       break;
-                                               }
+                               foreach (LedCommand command in engine) {
+                                       x = (command.time + offset) * pps/1000.0;
+                                       y = (255 - command.level) * (height - 1)/255.0;
+                                       if (x >= width)
+                                               break;
+                                       switch (command.type) {
+                                       case CommandType.RAMP_WAIT:
+                                       case CommandType.TRIGGER:
+                                               x += command.duration * pps/1000.0;
+                                               y -= command.steps * (height - 1)/255.0;
+                                               if (y < 0)
+                                                       y = 0;
+                                               if (y > (height - 1))
+                                                       y = height - 1;
+                                               ctx.line_to (x, y);
+                                               break;
+                                       default:
+                                               ctx.line_to (x, y);
+                                               break;
                                        }
                                }
-
-                               ctx.stroke ();
                        }
-                       if (type == CommandType.GO_TO_START && pattern.duration < 3 * 0.49) {
-                               ctx.new_path ();
 
+                       ctx.stroke ();
+               }
+               if (last_command.type == CommandType.GO_TO_START && engine_duration < 3 * 0.49) {
+                       ctx.new_path ();
 
-                               ctx.move_to (x, y);
-                               ctx.line_to (width - 0.5, y);
 
-                               ctx.stroke ();
-                       }
+                       ctx.move_to (x, y);
+                       ctx.line_to (width - 0.5, y);
+
+                       ctx.stroke ();
                }
-               return true;
        }
 
        public void update () {
index 002048c..de4e393 100644 (file)
@@ -72,6 +72,17 @@ class LedCommand : Object {
                changed ();
        }
 
+       public virtual void go_to_start () {
+               type = CommandType.GO_TO_START;
+               changed ();
+       }
+
+       public virtual void end (bool reset) {
+               type = CommandType.END;
+               steps = reset ? -255 : 0;
+               changed ();
+       }
+
        public signal void changed ();
 }
 
index b3e9614..ff47d6a 100644 (file)
@@ -20,6 +20,11 @@ class LedProgramDialog : Gtk.Dialog {
        LedPatternView lpv;
        LedPatternRX51 pattern;
 
+       LedColorButton led_color1;
+       LedColorButton led_color2;
+       Gtk.Widget list1;
+       Gtk.Widget list2;
+
        public LedProgramDialog (LedPatternRX51 _pattern) {
                pattern = _pattern;
                set_title ("LED pattern editor - " +
@@ -33,24 +38,40 @@ class LedProgramDialog : Gtk.Dialog {
                lpv.set_size_request (-1, 70);
                content.pack_start (lpv, false, false, 0);
 
-               var pannable = new Hildon.PannableArea ();
-               var vbox = new Gtk.VBox (false, 0);
-
-               foreach (LedCommand command in lpv.pattern.engine1) {
-                       var command_widget = new LedCommandWidget (command);
-
-                       vbox.pack_start (command_widget, false, false, 0);
-               }
-
-               pannable.add_with_viewport (vbox);
-               content.pack_start (pannable, true, true, 0);
+               var hbox = new Gtk.HBox (true, 0);
+               list1 = led_command_list (1);
+               hbox.pack_start (list1, true, true, 0);
+               list2 = led_command_list (2);
+               hbox.pack_start (list2, true, true, 0);
+               content.pack_start (hbox, true, true, 0);
 
                content.show_all ();
-
-               var led_color = new LedColorButton.with_map (lpv.pattern.led_map);
-               led_color.clicked.connect (on_color_clicked);
-               add_action_widget (led_color, 2);
-               action_area.set_child_secondary (led_color, true);
+               list2.hide ();
+
+               hbox = new Gtk.HBox (true, 0);
+               led_color1 = new LedColorButton.with_color (lpv.pattern.color1);
+               led_color1.clicked.connect (on_color1_clicked);
+               hbox.pack_start (led_color1, true, true, 0);
+               led_color2 = new LedColorButton.with_color (lpv.pattern.color2);
+               led_color2.clicked.connect (on_color2_clicked);
+               hbox.pack_start (led_color2, true, true, 0);
+               add_action_widget (hbox, 2);
+               action_area.set_child_secondary (hbox, true);
+
+               hbox = new Gtk.HBox (true, 0);
+               var radio = (Gtk.RadioButton) Hildon.gtk_radio_button_new (Hildon.SizeType.FINGER_HEIGHT, null);
+               radio.set_mode (false);
+               radio.set_label ("1");
+               radio.set_active (true);
+               radio.toggled.connect (on_engine1_toggled);
+               hbox.pack_start (radio, true, true, 0);
+               radio = (Gtk.RadioButton) Hildon.gtk_radio_button_new_from_widget (Hildon.SizeType.FINGER_HEIGHT, radio);
+               radio.set_mode (false);
+               radio.set_label ("2");
+               radio.toggled.connect (on_engine2_toggled);
+               hbox.pack_start (radio, true, true, 0);
+               add_action_widget (hbox, 2);
+               action_area.set_child_secondary (hbox, true);
 
                action_area.show_all ();
 
@@ -60,6 +81,34 @@ class LedProgramDialog : Gtk.Dialog {
                response.connect (on_response);
        }
 
+       private Gtk.Widget led_command_list (int engine) {
+               var pannable = new Hildon.PannableArea ();
+               var vbox = new Gtk.VBox (false, 0);
+
+               var label = new Gtk.Label ("Engine %d:".printf (engine));
+               label.set_alignment (0, 0.5f);
+               vbox.pack_start (label, false, false, 0);
+
+               unowned List<LedCommandRX51> list = (engine == 1) ?
+                                                   lpv.pattern.engine1 : lpv.pattern.engine2;
+               foreach (LedCommandRX51 command in list) {
+                       if (command.type == CommandType.RESET_MUX)
+                               continue;
+                       var command_widget = new LedCommandWidget (lpv.pattern, list,
+                                                                  command);
+
+                       vbox.pack_start (command_widget, false, false, 0);
+               }
+
+               var button = new Gtk.Button.with_label ("New command");
+               Hildon.gtk_widget_set_theme_size (button, Hildon.SizeType.FINGER_HEIGHT);
+               button.clicked.connect (on_new_command_clicked);
+               vbox.pack_end (button, false, false, 0);
+
+               pannable.add_with_viewport (vbox);
+               return pannable;
+       }
+
        void on_response (int response) {
                if (response == 1) {
                        Timeout.add (200, delayed_spawn);
@@ -90,35 +139,116 @@ class LedProgramDialog : Gtk.Dialog {
                return false;
        }
 
-       void on_color_clicked (Gtk.Button button) {
+       void on_color1_clicked (Gtk.Button button) {
                var dialog = new LedColorDialog ();
                int response = dialog.run ();
                if (response > 0) {
-                       ((LedColorButton) button).set_color ((LedColor) response);
-                       switch ((LedColor) response) {
-                       case LedColor.R:
-                               lpv.pattern.led_map = "r";
-                               break;
-                       case LedColor.G:
-                               lpv.pattern.led_map = "g";
+                       LedColor color = (LedColor) response;
+                       led_color1.set_color (color);
+                       lpv.pattern.color1 = color;
+                       color = led_color2.get_color () & ~response;
+                       led_color2.set_color (color);
+                       lpv.pattern.color2 = color;
+                       lpv.pattern.changed ();
+               }
+               dialog.destroy ();
+       }
+
+       void on_color2_clicked (Gtk.Button button) {
+               var dialog = new LedColorDialog ();
+               int response = dialog.run ();
+               if (response > 0) {
+                       LedColor color = (LedColor) response;
+                       led_color2.set_color (color);
+                       lpv.pattern.color2 = color;
+                       color = led_color1.get_color () & ~response;
+                       led_color1.set_color (color);
+                       lpv.pattern.color1 = color;
+                       lpv.pattern.changed ();
+               }
+               dialog.destroy ();
+       }
+
+       void on_engine1_toggled (Gtk.ToggleButton source) {
+               if (source.get_active ())
+                       list1.show ();
+               else
+                       list1.hide ();
+       }
+
+       void on_engine2_toggled (Gtk.ToggleButton source) {
+               if (source.get_active ())
+                       list2.show ();
+               else
+                       list2.hide ();
+       }
+
+       void on_new_command_clicked (Gtk.Button button) {
+               var widget = button.parent.parent.parent;
+               unowned List<LedCommandRX51> engine;
+               if (widget == list1) {
+                       engine = lpv.pattern.engine1;
+               } else if (widget == list2) {
+                       engine = lpv.pattern.engine2;
+               } else {
+                       return;
+               }
+
+               var dialog = new Hildon.PickerDialog (this);
+               dialog.set_title ("New command");
+
+               var touch_selector = new Hildon.TouchSelector.text ();
+               touch_selector.append_text ("Set PWM");
+               touch_selector.append_text ("Ramp");
+               touch_selector.append_text ("Wait");
+               touch_selector.append_text ("Trigger");
+               touch_selector.append_text ("Go To Start");
+               touch_selector.append_text ("End");
+               dialog.set_selector (touch_selector);
+
+               int response = dialog.run ();
+               if (response == Gtk.ResponseType.OK) {
+                       var command = new LedCommandRX51 ();
+                       LedCommandRX51 last_command = engine.last ().data;
+                       engine.append (command);
+                       command.changed.connect (lpv.pattern.on_changed);
+
+                       switch (touch_selector.get_active (0)) {
+                       case 0:
+                               command.set_pwm (127);
                                break;
-                       case LedColor.B:
-                               lpv.pattern.led_map = "b";
+                       case 1:
+                               if (last_command.type == CommandType.RAMP_WAIT) {
+                                       command.ramp_wait (last_command.step_time,
+                                                          -last_command.steps);
+                               } else if (last_command.level > 0) {
+                                       command.ramp_wait (125.0 / last_command.level,
+                                                          -last_command.level);
+                               } else {
+                                       command.ramp_wait (0.49, 255);
+                               }
                                break;
-                       case LedColor.RG:
-                               lpv.pattern.led_map = "rg";
+                       case 2:
+                               command.ramp_wait (100.0, 0);
                                break;
-                       case LedColor.RB:
-                               lpv.pattern.led_map = "rb";
+                       case 3:
+                               command.type = CommandType.TRIGGER;
+                               command.code = 0xe000;
+                               command.changed ();
                                break;
-                       case LedColor.GB:
-                               lpv.pattern.led_map = "gb";
+                       case 4:
+                               command.go_to_start ();
                                break;
-                       case LedColor.RGB:
-                               lpv.pattern.led_map = "rgb";
+                       case 5:
+                               command.end (false);
                                break;
                        }
-                       lpv.pattern.changed ();
+                       var vbox = (Gtk.VBox) button.parent;
+                       var command_widget = new LedCommandWidget (lpv.pattern, engine,
+                                                                  command);
+                       vbox.pack_start (command_widget, false, false, 0);
+                       command_widget.show_all ();
+
                }
                dialog.destroy ();
        }