rewrite callscreen.
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Mon, 27 Aug 2012 06:58:06 +0000 (03:58 -0300)
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Mon, 27 Aug 2012 06:58:06 +0000 (03:58 -0300)
The initial version was not good and were written before we had full
understanding of lifetime and state changes of calls, multi-party and
others.

The new version is simpler and achives the following design goals:
 - calls should not change order in the GUI;
 - held calls should show elapsed time as well.

data/themes/default-hd.edc
data/themes/default-sd.edc
data/themes/includes/call.edc
dialer/callscreen.c

index e997519..0df8b40 100644 (file)
@@ -4,6 +4,8 @@
 #define ACTION_WIDTH 240
 #define ACTION_HEIGHT 170
 
+#define CALL_HEIGHT 200
+
 #define BORDER_PADDING 46
 #define ITEM_PADDING 16
 
index 1f4cb9b..5045d8d 100644 (file)
@@ -4,6 +4,8 @@
 #define ACTION_WIDTH 120
 #define ACTION_HEIGHT 85
 
+#define CALL_HEIGHT 100
+
 #define BORDER_PADDING 23
 #define ITEM_PADDING 8
 
index f50f321..48a4f55 100644 (file)
@@ -5,11 +5,11 @@ group {
     * Represents the dialer ongoing voice call(s) screen.
     *
     * Parts:
-    *   TEXT: elm.text.name
-    *   TEXT: elm.text.status
-    *   TEXT: elm.text.elapsed
+    *   TEXT: elm.text.<CALL>.name
+    *   TEXT: elm.text.<CALL>.status
+    *   TEXT: elm.text.<CALL>.elapsed
+    *   TEXT: elm.text.<CALL>.phone.type
     *   TEXT: elm.text.waiting
-    *   TEXT: elm.text.held
     *   SWALLOW: elm.swallow.multiparty-details
     *
     * Signals:
@@ -18,38 +18,39 @@ group {
     *     released,<ID>: key <ID> was released
     *     clicked,<ID>:  key <ID> was clicked (press and release in the key)
     *   Listen (source is "call"):
-    *     show,answer:     show "answer" action
-    *     hide,answer:     hide "answer" action
-    *     show,waiting:    show call waiting popup (decline, hold+answer...)
-    *     hide,waiting:    hide call waiting popup
-    *     show,held:       show call held entry
-    *     hide,held:       hide call held entry
-    *     show,elapsed:    have elapsed (elm.text.elapsed) and should be visible
-    *     hide,elapsed:    don't have elapsed and it should be hidden
-    *     show,multiparty: active call is multiparty
-    *     hide,multiparty: active call is not multiparty
-    *     show,held,multiparty: held call is multiparty
-    *     hide,held,multiparty: held call is not multiparty
-    *     state,<STATE>:   state changed to <STATE>
-    *     disable,<ID>:    disable action <ID>
-    *     enable,<ID>:     enable action <ID>
-    *     toggle,on,<ID>:  mark toggle action <ID> as selected
-    *     toggle,off,<ID>: mark toggle action <ID> as unselected
+    *     show,answer:            show "answer" action
+    *     hide,answer:            hide "answer" action
+    *     show,waiting:           show call waiting popup (hold+answer...)
+    *     hide,waiting:           hide call waiting popup
+    *     show,<CALL>,elapsed:    have elapsed (elm.text.<CALL>.elapsed)
+    *     hide,<CALL>,elapsed:    don't have elapsed and it should be hidden
+    *     show,<CALL>,multiparty: call is multiparty
+    *     hide,<CALL>,multiparty: call is not multiparty
+    *     state,<CALL>,<STATE>:   state changed to <STATE>
+    *     disable,<ID>:           disable action <ID>
+    *     enable,<ID>:            enable action <ID>
+    *     toggle,on,<ID>:         mark toggle action <ID> as selected
+    *     toggle,off,<ID>:        mark toggle action <ID> as unselected
     *     show,multiparty-details: ask the multiparty details to be visible
     *     hide,multiparty-details: ask the multiparty details to be hidden
+    *     calls,1                  single call mode
+    *     calls,2                  two calls mode
     *
     * Messages:
     *     1 - FLOAT: speaker volume 0.0-1.0
     *     2 - FLOAT: microphone volume 0.0-1.0
-    *     3 - FLOAT: elapsed time in seconds (> 0.0)
+    *     11 - FLOAT: elapsed time in seconds (> 0.0) for call #1
+    *     12 - FLOAT: elapsed time in seconds (> 0.0) for call #2
     *
     * Where <ID> is: numbers 0 to 9, star, hash, mute, keypad,
     * speaker, add-call, merge, swap, contacts, answer, hangup,
-    * waiting-hangup, hold-answer, hangup-answer, multiparty,
-    * held-multiparty, multiparty-details
+    * waiting-hangup, hold-answer, hangup-answer, <CALL>,multiparty,
+    * multiparty-details, actions.
     *
     * Where <STATE> is: disconnected, active, held, dialing, alerting,
     * incoming, waiting.
+    *
+    * Where <CALL> is: 1 or 2.
     */
 
    min: WIDTH HEIGHT;
@@ -84,201 +85,6 @@ group {
          }
       }
 
-      part {
-         name: "clipper.multiparty";
-         type: RECT;
-         clip_to: "clipper.multiparty-details-hidden";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 0;
-            visible: 0;
-         }
-         description {
-            state: "multiparty" 0.0;
-            inherit: "default" 0.0;
-            color: 255 255 255 255;
-            visible: 1;
-         }
-      }
-      part {
-         name: "button.multiparty";
-         type: IMAGE;
-         mouse_events: 1;
-         clip_to: "clipper.multiparty";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 255;
-            color_class: "action";
-            min: 80 80;
-            max: 80 80;
-            rel1 {
-               relative: 1.0 0.0;
-               offset: -80 (BORDER_PADDING - 1);
-            }
-            rel2 {
-               relative: 1.0 0.0;
-               offset: -1 141;
-            }
-            image.normal: "ico_multiparty.png";
-         }
-         description {
-            state: "pressed" 0.0;
-            inherit: "default" 0.0;
-            color_class: "light";
-         }
-      }
-      programs {
-         program {
-            signal: "mouse,up,1";
-            source: "button.multiparty";
-            action: SIGNAL_EMIT "released,multiparty" "call";
-            after: "show_up_multiparty";
-            api: "multiparty_released" "multiparty was released";
-         }
-         program {
-            name: "show_up_multiparty";
-            action: STATE_SET "default" 0.0;
-            transition: DECELERATE 0.1;
-            target: "button.multiparty";
-         }
-         program {
-            signal: "mouse,down,1";
-            source: "button.multiparty";
-            after: "show_down_multiparty";
-            action: SIGNAL_EMIT "pressed,multiparty" "call";
-            api: "multiparty_pressed" "multiparty was pressed";
-         }
-         program {
-            name: "show_down_multiparty";
-            action: STATE_SET "pressed" 0.0;
-            transition: ACCELERATE 0.1;
-            target: "button.multiparty";
-         }
-         program {
-            signal: "mouse,clicked,1";
-            source: "button.multiparty";
-            action: SIGNAL_EMIT "clicked,multiparty" "call";
-            api: "multiparty_clicked" "multiparty was clicked";
-         }
-
-         program {
-            signal: "show,multiparty";
-            source: "call";
-            action: STATE_SET "multiparty" 0.0;
-            target: "clipper.multiparty";
-            target: "elm.text.name";
-            api: "multiparty_show" "make multiparty visible";
-         }
-         program {
-            signal: "hide,multiparty";
-            source: "call";
-            action: STATE_SET "default" 0.0;
-            target: "clipper.multiparty";
-            target: "elm.text.name";
-            api: "multiparty_hide" "make multiparty hidden";
-         }
-      }
-
-      part {
-         name: "elm.text.name";
-         type: TEXT;
-         mouse_events: 0;
-         scale: 1;
-         clip_to: "clipper.multiparty-details-hidden";
-         api: "name" "remote party call name";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 255;
-            color_class: "action";
-            align: 0.0 0.5;
-            rel1.offset: BORDER_PADDING 0;
-            rel2 {
-               relative: 1.0 0.0;
-               offset: (-BORDER_PADDING - 1) (ACTION_HEIGHT - 1);
-            }
-            text {
-               text: "Gustavo Barbieri";
-               font: FONT_NORMAL;
-               size: SIZE_HUGE;
-               size_range: SIZE_TINY SIZE_HUGE;
-               fit: 1 1;
-               align: 0.0 0.5;
-               ellipsis: 0.0;
-            }
-         }
-         description {
-            state: "multiparty" 0.0;
-            inherit: "default" 0.0;
-            rel2.offset: (-80 - ITEM_PADDING) (ACTION_HEIGHT - 1);
-         }
-      }
-
-      part {
-         name: "elm.text.status";
-         type: TEXT;
-         mouse_events: 0;
-         scale: 1;
-         clip_to: "clipper.multiparty-details-hidden";
-         api: "status" "call status";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 255;
-            color_class: "light";
-            fixed: 1 1;
-            align: 0.0 0.0;
-            rel1 {
-               to: "elm.text.name";
-               relative: 0.0 1.0;
-               offset: 0 -20;
-            }
-            rel2 {
-               to: "elm.text.name";
-               relative: 0.0 1.0;
-               offset: 0 -1;
-            }
-            text {
-               text: "calling...";
-               font: FONT_NORMAL;
-               size: SIZE_MEDIUM;
-               min: 1 1;
-               align: 0.0 0.0;
-            }
-         }
-      }
-
-      part {
-         name: "elm.text.elapsed";
-         type: TEXT;
-         mouse_events: 0;
-         scale: 1;
-         clip_to: "clipper.multiparty-details-hidden";
-         api: "elapsed" "call elapsed time (formatted)";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 255;
-            color_class: "light";
-            fixed: 1 1;
-            align: 0.0 0.0;
-            rel1 {
-               to: "elm.text.status";
-               relative: 1.0 0.0;
-               offset: ITEM_PADDING 0;
-            }
-            rel2 {
-               to: "elm.text.status";
-               relative: 1.0 1.0;
-               offset: ITEM_PADDING -1;
-            }
-            text {
-               text: "01:23";
-               font: FONT_NORMAL;
-               size: SIZE_MEDIUM;
-               min: 1 1;
-               align: 0.0 0.0;
-            }
-         }
-      }
-
 #define SEPARATOR(id, clip, rely, offy, relto)                          \
       part {                                                            \
          name: "separator.dark."##id;                                   \
@@ -323,237 +129,297 @@ group {
          }                                                              \
       }
 
-      part {
-         name: "clipper.held";
-         type: RECT;
-         clip_to: "clipper.keypad-hidden";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 0;
-            visible: 0;
-         }
-         description {
-            state: "visible" 0.0;
-            inherit: "default" 0.0;
-            color: 255 255 255 255;
-            visible: 1;
-         }
-      }
-
-      part {
-         name: "clipper.held.multiparty";
-         type: RECT;
-         clip_to: "clipper.held";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 0;
-            visible: 0;
-         }
-         description {
-            state: "multiparty" 0.0;
-            inherit: "default" 0.0;
-            color: 255 255 255 255;
-            visible: 1;
-         }
-      }
-      part {
-         name: "button.held.multiparty";
-         type: IMAGE;
-         mouse_events: 1;
-         clip_to: "clipper.held.multiparty";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 255;
-            color_class: "action";
-            min: 80 80;
-            max: 80 80;
-            rel1 {
-               to_y: "elm.text.status";
-               relative: 1.0 1.0;
-               offset: -80 65;
-            }
-            rel2 {
-               to_y: "elm.text.status";
-               relative: 1.0 1.0;
-               offset: -1 161;
-            }
-            image.normal: "ico_multiparty.png";
-         }
-         description {
-            state: "pressed" 0.0;
-            inherit: "default" 0.0;
-            color_class: "light";
-         }
-      }
-      programs {
-         program {
-            signal: "mouse,up,1";
-            source: "button.held.multiparty";
-            action: SIGNAL_EMIT "released,held-multiparty" "call";
-            after: "show_up_held_multiparty";
-            api: "held_multiparty_released" "held multiparty was released";
-         }
-         program {
-            name: "show_up_held_multiparty";
-            action: STATE_SET "default" 0.0;
-            transition: DECELERATE 0.1;
-            target: "button.held.multiparty";
-         }
-         program {
-            signal: "mouse,down,1";
-            source: "button.held.multiparty";
-            after: "show_down_held_multiparty";
-            action: SIGNAL_EMIT "pressed,held-multiparty" "call";
-            api: "held_multiparty_pressed" "held multiparty was pressed";
-         }
-         program {
-            name: "show_down_held_multiparty";
-            action: STATE_SET "pressed" 0.0;
-            transition: ACCELERATE 0.1;
-            target: "button.held.multiparty";
-         }
-         program {
-            signal: "mouse,clicked,1";
-            source: "button.held.multiparty";
-            action: SIGNAL_EMIT "clicked,held-multiparty" "call";
-            api: "held_multiparty_clicked" "held multiparty was clicked";
-         }
-
-         program {
-            signal: "show,held,multiparty";
-            source: "call";
-            action: STATE_SET "multiparty" 0.0;
-            target: "clipper.held.multiparty";
-            target: "elm.text.held";
-            api: "held_multiparty_show" "make held multiparty visible";
-         }
-         program {
-            signal: "hide,held,multiparty";
-            source: "call";
-            action: STATE_SET "default" 0.0;
-            target: "clipper.held.multiparty";
-            target: "elm.text.held";
-            api: "held_multiparty_hide" "make held multiparty hidden";
-         }
-      }
-
-
-      part {
-         name: "area.held";
-         type: RECT;
-         mouse_events: 1;
-         clip_to: "clipper.held";
-         description {
-            state: "default" 0.0;
-            color: 0 0 0 0;
-            rel1.to_y: "elm.text.held";
-            rel2 {
-               to_x: "elm.text.held";
-               to_y: "label.held";
-            }
-         }
-      }
-      programs {
-         program {
-            signal: "mouse,clicked,1";
-            source: "area.held";
-            action: SIGNAL_EMIT "clicked,swap" "call";
-         }
-      }
-
-      part {
-         name: "elm.text.held";
-         type: TEXT;
-         mouse_events: 0;
-         scale: 1;
-         api: "held" "held party call name";
-         clip_to: "clipper.held";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 255;
-            color_class: "action";
-            align: 0.0 0.5;
-            rel1 {
-               to_y: "elm.text.status";
-               to_x: "elm.text.name";
-               relative: 0.0 1.0;
-               offset: 0 30;
-            }
-            rel2 {
-               to_y: "elm.text.status";
-               relative: 1.0 1.0;
-               offset: (-BORDER_PADDING -1) (ACTION_HEIGHT + 30 - 1);
-            }
-            text {
-               text: "Someone on hold";
-               font: FONT_NORMAL;
-               size: SIZE_HUGE;
-               size_range: SIZE_TINY SIZE_HUGE;
-               fit: 1 1;
-               align: 0.0 0.5;
-               ellipsis: 0.0;
-            }
-         }
-         description {
-            state: "multiparty" 0.0;
-            inherit: "default" 0.0;
-            rel2.offset: (-80 - ITEM_PADDING) (ACTION_HEIGHT - 1);
-         }
-      }
-      part {
-         name: "label.held";
-         type: TEXT;
-         mouse_events: 0;
-         scale: 1;
-         clip_to: "clipper.held";
-         description {
-            state: "default" 0.0;
-            color: 255 255 255 255;
-            color_class: "light";
-            fixed: 1 1;
-            align: 0.0 0.0;
-            rel1 {
-               to: "elm.text.held";
-               relative: 0.0 1.0;
-               offset: -1.0 -20;
-            }
-            rel2 {
-               to: "elm.text.held";
-               relative: 0.0 1.0;
-               offset: -1.0 -1;
-            }
-            text {
-               text: "On Hold";
-               font: FONT_NORMAL;
-               size: SIZE_MEDIUM;
-               min: 1 1;
-               align: 0.0 0.0;
-            }
-         }
+#define CALL(id, offy)                                                  \
+      part {                                                            \
+         name: "call."##id;                                             \
+         type: RECT;                                                    \
+         clip_to: "clipper.multiparty-details-hidden";                  \
+         description {                                                  \
+            state: "default" 0.0;                                       \
+            rel1 {                                                      \
+               relative: 0.0 0.0;                                       \
+               offset: 0 (-SEPARATOR_HEIGHT + offy);                    \
+            }                                                           \
+            rel2 {                                                      \
+               relative: 1.0 0.0;                                       \
+               offset: -1 (CALL_HEIGHT + offy);                         \
+            }                                                           \
+         }                                                              \
+         description {                                                  \
+            state: "hidden" 0.0;                                        \
+            inherit: "default" 0.0;                                     \
+            color: 255 255 255 0;                                       \
+            visible: 0;                                                 \
+         }                                                              \
+      }                                                                 \
+                                                                        \
+      SEPARATOR("call."##id, "call."##id, 0.0, 0, "call."##id);         \
+                                                                        \
+      part {                                                            \
+         name: "clipper."##id".multiparty";                             \
+         type: RECT;                                                    \
+         clip_to: "call."##id;                                          \
+         description {                                                  \
+            state: "default" 0.0;                                       \
+            color: 255 255 255 0;                                       \
+            visible: 0;                                                 \
+         }                                                              \
+         description {                                                  \
+            state: "multiparty" 0.0;                                    \
+            inherit: "default" 0.0;                                     \
+            color: 255 255 255 255;                                     \
+            visible: 1;                                                 \
+         }                                                              \
+      }                                                                 \
+      part {                                                            \
+         name: "button."##id".multiparty";                              \
+         type: IMAGE;                                                   \
+         mouse_events: 1;                                               \
+         clip_to: "clipper."##id".multiparty";                          \
+         description {                                                  \
+            state: "default" 0.0;                                       \
+            color: 255 255 255 255;                                     \
+            color_class: "action";                                      \
+            min: 80 80;                                                 \
+            max: 80 80;                                                 \
+            rel1 {                                                      \
+               to: "call."##id;                                         \
+               relative: 1.0 0.0;                                       \
+               offset: -80 (BORDER_PADDING - 1);                        \
+            }                                                           \
+            rel2 {                                                      \
+               to: "call."##id;                                         \
+               relative: 1.0 0.0;                                       \
+               offset: -1 141;                                          \
+            }                                                           \
+            image.normal: "ico_multiparty.png";                         \
+         }                                                              \
+         description {                                                  \
+            state: "pressed" 0.0;                                       \
+            inherit: "default" 0.0;                                     \
+            color_class: "light";                                       \
+         }                                                              \
+      }                                                                 \
+      programs {                                                        \
+         program {                                                      \
+            signal: "mouse,up,1";                                       \
+            source: "button."##id".multiparty";                         \
+            action: SIGNAL_EMIT "released,"##id",multiparty" "call";    \
+            after: "show_up_"##id"_multiparty";                         \
+            api: ""##id"_multiparty_released"                           \
+               "call #"##id" multiparty was released";                  \
+         }                                                              \
+         program {                                                      \
+            name: "show_up_"##id"_multiparty";                          \
+            action: STATE_SET "default" 0.0;                            \
+            transition: DECELERATE 0.1;                                 \
+            target: "button."##id".multiparty";                         \
+         }                                                              \
+         program {                                                      \
+            signal: "mouse,down,1";                                     \
+            source: "button."##id".multiparty";                         \
+            after: "show_down_"##id"_multiparty";                       \
+            action: SIGNAL_EMIT "pressed,"##id",multiparty" "call";     \
+            api: ""##id"_multiparty_pressed"                            \
+               "call #"##id" multiparty was pressed";                   \
+         }                                                              \
+         program {                                                      \
+            name: "show_down_"##id"_multiparty";                        \
+            action: STATE_SET "pressed" 0.0;                            \
+            transition: ACCELERATE 0.1;                                 \
+            target: "button."##id".multiparty";                         \
+         }                                                              \
+         program {                                                      \
+            signal: "mouse,clicked,1";                                  \
+            source: "button."##id".multiparty";                         \
+            action: SIGNAL_EMIT "clicked,"##id",multiparty" "call";     \
+            api: ""##id"_multiparty_clicked"                            \
+               "call #"##id" multiparty was clicked";                   \
+         }                                                              \
+                                                                        \
+         program {                                                      \
+            signal: "show,"##id",multiparty";                           \
+            source: "call";                                             \
+            action: STATE_SET "multiparty" 0.0;                         \
+            target: "clipper."##id".multiparty";                        \
+            target: "area."##id".name";                                 \
+            api: ""##id"_multiparty_show"                               \
+               "call #"##id" make multiparty visible";                  \
+         }                                                              \
+         program {                                                      \
+            signal: "hide,"##id",multiparty";                           \
+            source: "call";                                             \
+            action: STATE_SET "default" 0.0;                            \
+            target: "clipper."##id".multiparty";                        \
+            target: "area."##id".name";                                 \
+            api: ""##id"_multiparty_hide"                               \
+               "call #"##id" make multiparty hidden";                   \
+         }                                                              \
+      }                                                                 \
+                                                                        \
+      part {                                                           \
+         name: "area."##id".name";                                      \
+         type: RECT;                                                    \
+         mouse_events: 1;                                               \
+         scale: 1;                                                      \
+         clip_to: "call."##id;                                          \
+         description {                                                  \
+            state: "default" 0.0;                                       \
+            color: 0 0 0 0;                                             \
+            rel1 {                                                      \
+               to: "call."##id;                                         \
+               offset: BORDER_PADDING SEPARATOR_HEIGHT;                 \
+            }                                                           \
+            rel2 {                                                      \
+               to: "call."##id;                                         \
+               relative: 1.0 0.0;                                       \
+               offset: (-BORDER_PADDING - 1) (ACTION_HEIGHT - 1);       \
+            }                                                           \
+         }                                                              \
+         description {                                                  \
+            state: "multiparty" 0.0;                                    \
+            inherit: "default" 0.0;                                     \
+            rel2.offset: (-80 - ITEM_PADDING) (ACTION_HEIGHT - 1);      \
+         }                                                              \
+      }                                                                 \
+      part {                                                            \
+         name: "elm.text."##id".name";                                  \
+         type: TEXT;                                                    \
+         mouse_events: 0;                                               \
+         scale: 1;                                                      \
+         clip_to: "call."##id;                                          \
+         api: ""##id"_name" "remote party call name (call #"##id")";    \
+         description {                                                  \
+            state: "default" 0.0;                                       \
+            color: 255 255 255 255;                                     \
+            color_class: "action";                                      \
+            rel1.to: "area."##id".name";                                \
+            rel2.to: "area."##id".name";                                \
+            text {                                                      \
+               text: "Gustavo Barbieri";                                \
+               font: FONT_NORMAL;                                       \
+               size: SIZE_HUGE;                                         \
+               size_range: SIZE_TINY SIZE_HUGE;                         \
+               fit: 1 1;                                                \
+               align: 0.0 0.5;                                          \
+               ellipsis: 0.0;                                           \
+            }                                                           \
+         }                                                              \
+         description {                                                  \
+            state: "held" 0.0;                                          \
+            inherit: "default" 0.0;                                     \
+            color_class: "light";                                       \
+         }                                                              \
+      }                                                                 \
+                                                                        \
+      part {                                                            \
+         name: "elm.text."##id".status";                                \
+         type: TEXT;                                                    \
+         mouse_events: 0;                                               \
+         scale: 1;                                                      \
+         clip_to: "call."##id;                                          \
+         api: ""##id"_status" "call status (call #"##id")";             \
+         description {                                                  \
+            state: "default" 0.0;                                       \
+            color: 255 255 255 255;                                     \
+            color_class: "light";                                       \
+            fixed: 1 1;                                                 \
+            align: 0.0 0.0;                                             \
+            rel1 {                                                      \
+               to: "area."##id".name";                                  \
+               relative: 0.0 1.0;                                       \
+               offset: 0 -20;                                           \
+            }                                                           \
+            rel2 {                                                      \
+               to: "area."##id".name";                                  \
+               relative: 0.0 1.0;                                       \
+               offset: 0 -1;                                            \
+            }                                                           \
+            text {                                                      \
+               text: "calling...";                                      \
+               font: FONT_NORMAL;                                       \
+               size: SIZE_MEDIUM;                                       \
+               min: 1 1;                                                \
+               align: 0.0 0.0;                                          \
+            }                                                           \
+         }                                                              \
+      }                                                                 \
+                                                                        \
+      part {                                                            \
+         name: "elm.text."##id".elapsed";                               \
+         type: TEXT;                                                    \
+         mouse_events: 0;                                               \
+         scale: 1;                                                      \
+         clip_to: "call."##id;                                          \
+         api: "1_elapsed" "call elapsed formatted time (call #"##id")"; \
+         description {                                                  \
+            state: "default" 0.0;                                       \
+            color: 255 255 255 255;                                     \
+            color_class: "light";                                       \
+            fixed: 1 1;                                                 \
+            align: 0.0 0.0;                                             \
+            rel1 {                                                      \
+               to: "elm.text."##id".status";                            \
+               relative: 1.0 0.0;                                       \
+               offset: ITEM_PADDING 0;                                  \
+            }                                                           \
+            rel2 {                                                      \
+               to: "elm.text."##id".status";                            \
+               relative: 1.0 1.0;                                       \
+               offset: ITEM_PADDING -1;                                 \
+            }                                                           \
+            text {                                                      \
+               text: "01:23";                                           \
+               font: FONT_NORMAL;                                       \
+               size: SIZE_MEDIUM;                                       \
+               min: 1 1;                                                \
+               align: 0.0 0.0;                                          \
+            }                                                           \
+         }                                                              \
+      }                                                                 \
+                                                                        \
+      programs {                                                        \
+         program {                                                      \
+            signal: "mouse,clicked,1";                                  \
+            source: "area."##id".name";                                 \
+            action: SIGNAL_EMIT "clicked,swap" "call";                  \
+         }                                                              \
+         program {                                                      \
+            signal: "state,"##id",held";                                \
+            source: "call";                                             \
+            action: STATE_SET "held" 0.0;                               \
+            transition: ACCELERATE 0.1;                                 \
+            target: "elm.text."##id".name";                             \
+         }                                                              \
+         program {                                                      \
+            signal: "state,"##id",active";                              \
+            source: "call";                                             \
+            action: STATE_SET "default" 0.0;                            \
+            transition: ACCELERATE 0.1;                                 \
+            target: "elm.text."##id".name";                             \
+         }                                                              \
       }
 
-      SEPARATOR("held1", "clipper.held", 0.0, -SEPARATOR_HEIGHT, "elm.text.held");
+      CALL("1", 0);
+      CALL("2", CALL_HEIGHT + SEPARATOR_HEIGHT);
 
       programs {
          program {
-            signal: "show,held";
+            signal: "calls,1";
             source: "call";
-            action: STATE_SET "visible" 0.0;
-            transition: DECELERATE 0.3;
-            target: "clipper.held";
-            api: "held_show" "make held entry visible";
+            action: STATE_SET "hidden" 0.0;
+            transition: ACCELERATE 0.1;
+            target: "call.2";
          }
          program {
-            signal: "hide,held";
+            signal: "calls,2";
             source: "call";
             action: STATE_SET "default" 0.0;
-            transition: ACCELERATE 0.3;
-            target: "clipper.held";
-            api: "held_hide" "make held entry hidden";
+            transition: ACCELERATE 0.1;
+            target: "call.2";
          }
       }
 
-
       SEPARATOR("actions", "clipper.actions", 0.0, -SEPARATOR_HEIGHT, "bg.actions");
 
       part {
@@ -598,47 +464,35 @@ group {
       }
       programs {
          program {
-            name: "show,actions";
-            action: STATE_SET "visible" 0.0;
-            transition: DECELERATE 0.3;
-            target: "clipper.actions";
+            name: "enable,actions1";
+            signal: "enable,actions";
+            source: "call";
+            action: ACTION_STOP;
+            target: "disable,actions1";
+            target: "disable,actions2";
+            after: "enable,actions2";
          }
          program {
-            name: "hide,actions";
-            action: STATE_SET "default" 0.0;
-            transition: ACCELERATE 0.3;
+            name: "enable,actions2";
+            action: STATE_SET "visible" 0.0;
+            transition: DECELERATE 0.3;
             target: "clipper.actions";
          }
 
          program {
-            signal: "state,disconnected";
-            source: "call";
-            after: "hide,actions";
-         }
-         program {
-            signal: "state,active";
-            source: "call";
-            after: "show,actions";
-         }
-         program {
-            signal: "state,held";
-            source: "call";
-            after: "show,actions";
-         }
-         program {
-            signal: "state,dialing";
+            name: "disable,actions1";
+            signal: "disable,actions";
             source: "call";
-            after: "hide,actions";
+            action: ACTION_STOP;
+            target: "enable,actions1";
+            target: "enable,actions2";
+            after: "disable,actions2";
          }
          program {
-            signal: "state,alerting";
-            source: "call";
-            after: "hide,actions";
-         }
-         program {
-            signal: "state,incoming";
-            source: "call";
-            after: "hide,actions";
+            name: "disable,actions2";
+            action: STATE_SET "default" 0.0;
+            transition: ACCELERATE 0.3;
+            target: "clipper.actions";
          }
       }
 
@@ -1949,14 +1803,14 @@ group {
          }
 
         program {
-            signal: "clicked,held-multiparty"; /* just show on user request */
+            signal: "clicked,2,multiparty";
             source: "call";
             after: "show-multiparty-details-phase1";
          }
 
          program {
             name: "show-multiparty-details-phase1";
-            signal: "clicked,multiparty"; /* just show on user request */
+            signal: "clicked,1,multiparty"; /* just show on user request */
             source: "call";
             action: STATE_SET "alternate" 0.0;
             transition: ACCELERATE 0.3;
@@ -2294,14 +2148,13 @@ group {
     *
     * Parts:
     *   TEXT: elm.text.name
+    *   TEXT: elm.text.phone.type
     *   TEXT: elm.text.status
     *   TEXT: elm.text.elapsed
     * Signals:
     *   Emit (source is "call"):
     *     clicked:
     * Listen (source is "call"):
-    *     show,elapsed:    have elapsed (elm.text.elapsed) and should be visible
-    *     hide,elapsed:    don't have elapsed and it should be hidden
     *     show,multiparty: active call is multiparty
     *     hide,multiparty: active call is not multiparty
     *     state,<STATE>:   state changed to <STATE>
index 87842f3..3cb65e5 100644 (file)
@@ -19,12 +19,13 @@ typedef struct _Callscreen
                double start;
        } multiparty;
        struct {
-               OFono_Call *active;
+               OFono_Call *first;
+               OFono_Call *second;
+               OFono_Call *current; /* first or second */
                OFono_Call *waiting;
-               OFono_Call *held;
+               OFono_Call *incoming; /* should be first && current */
                Eina_List *list;
        } calls;
-       OFono_Call_State last_state;
        Ecore_Timer *elapsed_updater;
        struct {
                Eina_Strbuf *todo;
@@ -54,8 +55,45 @@ static OFono_Callback_List_Call_Node *callback_node_call_changed = NULL;
 static OFono_Callback_List_Call_Disconnected_Node
 *callback_node_call_disconnected = NULL;
 
-static char *_call_name_or_id(const OFono_Call *call);
-static char *_call_type_get(const OFono_Call *call);
+static char *_call_name_or_id(const OFono_Call *call)
+{
+       const char *s = ofono_call_line_id_get(call);
+       Contact_Info *info;
+
+       if (!s)
+               return NULL;;
+
+       info = gui_contact_search(s, NULL);
+       if (info)
+               return strdup(contact_info_full_name_get(info));
+
+       return phone_format(s);
+}
+
+static char *_call_name_get(const Callscreen *ctx __UNUSED__,
+                               const OFono_Call *call)
+{
+       if (!ofono_call_multiparty_get(call))
+               return _call_name_or_id(call);
+
+       return strdup("Conference");
+}
+
+static const char *_call_type_get(const OFono_Call *call)
+{
+       const char *type;
+       const char *s = ofono_call_line_id_get(call);
+       Contact_Info *info;
+
+       if (!s)
+               return NULL;
+
+       info = gui_contact_search(s, &type);
+       if (info)
+               return type;
+
+       return NULL;
+}
 
 static void _on_mp_hangup(void *data, Evas_Object *o __UNUSED__,
                                const char *emission __UNUSED__,
@@ -137,7 +175,8 @@ repopulate:
        }
 
        EINA_LIST_FOREACH(new, n1, c) {
-               char *name, *number, *type;
+               char *name, *number;
+               const char *type;
 
                name = _call_name_or_id(c);
                type = _call_type_get(c);
@@ -169,7 +208,6 @@ repopulate:
 
                free(name);
                free(number);
-               free(type);
        }
        elm_object_signal_emit(ctx->self, "show,multiparty-details", "call");
 }
@@ -177,101 +215,469 @@ repopulate:
 static void _multiparty_private_available_update(Callscreen *ctx)
 {
        Eina_List *lst = elm_box_children_get(ctx->multiparty.bx);
-       const char *sig = ctx->calls.held ? "hide,private" : "show,private";
+       const char *sig = ctx->calls.second ? "hide,private" : "show,private";
        Evas_Object *it;
 
        EINA_LIST_FREE(lst, it)
                elm_object_signal_emit(it, sig, "call");
 }
 
-static void _calls_update(Callscreen *ctx)
+static void _call_text_set(Callscreen *ctx, unsigned int id,
+                               const char *part, const char *str)
 {
-       const Eina_List *n;
-       OFono_Call *c, *found = NULL, *waiting = NULL, *held = NULL;
-       OFono_Call_State found_state = OFONO_CALL_STATE_DISCONNECTED;
-       static const int state_priority[] = {
-               [OFONO_CALL_STATE_DISCONNECTED] = 0,
-               [OFONO_CALL_STATE_ACTIVE] = 6,
-               [OFONO_CALL_STATE_HELD] = 3,
-               [OFONO_CALL_STATE_DIALING] = 5,
-               [OFONO_CALL_STATE_ALERTING] = 4,
-               [OFONO_CALL_STATE_INCOMING] = 2,
-               [OFONO_CALL_STATE_WAITING] = 1
-       };
+       char buf[128];
+       snprintf(buf, sizeof(buf), "elm.text.%u.%s", id, part);
+       elm_object_part_text_set(ctx->self, buf, str ? str : "");
+}
 
-       if (ctx->calls.active) {
-               OFono_Call_State s = ofono_call_state_get(ctx->calls.active);
-               if (s != OFONO_CALL_STATE_ACTIVE) {
-                       ctx->calls.active = NULL;
-                       ctx->last_state = 0;
-               }
+static const char *_call_state_str(OFono_Call_State state)
+{
+       switch (state) {
+       case OFONO_CALL_STATE_DISCONNECTED:
+               return "Disconnected";
+       case OFONO_CALL_STATE_ACTIVE:
+               return "Active";
+       case OFONO_CALL_STATE_HELD:
+               return "On Hold";
+       case OFONO_CALL_STATE_DIALING:
+               return "Dialing...";
+       case OFONO_CALL_STATE_ALERTING:
+               return "Alerting...";
+       case OFONO_CALL_STATE_INCOMING:
+               return "Incoming...";
+       case OFONO_CALL_STATE_WAITING:
+               return "Waiting...";
+       default:
+               ERR("unknown state: %d", state);
+               return NULL;
        }
-       if (ctx->calls.waiting) {
-               OFono_Call_State s = ofono_call_state_get(ctx->calls.waiting);
-               if (s != OFONO_CALL_STATE_WAITING)
-                       ctx->calls.waiting = NULL;
+}
+
+static const char *_call_state_id(OFono_Call_State state)
+{
+       switch (state) {
+       case OFONO_CALL_STATE_DISCONNECTED:
+               return "disconnected";
+       case OFONO_CALL_STATE_ACTIVE:
+               return "active";
+       case OFONO_CALL_STATE_HELD:
+               return "held";
+       case OFONO_CALL_STATE_DIALING:
+               return "dialing";
+       case OFONO_CALL_STATE_ALERTING:
+               return "alerting";
+       case OFONO_CALL_STATE_INCOMING:
+               return "incoming";
+       case OFONO_CALL_STATE_WAITING:
+               return "waiting";
+       default:
+               ERR("unknown state: %d", state);
+               return NULL;
        }
-       if (ctx->calls.held) {
-               OFono_Call_State s = ofono_call_state_get(ctx->calls.held);
-               if (s != OFONO_CALL_STATE_HELD)
-                       ctx->calls.held = NULL;
+}
+
+static void _call_show(Callscreen *ctx, unsigned int id, const OFono_Call *c)
+{
+       char *contact = _call_name_get(ctx, c);
+       const char *type = _call_type_get(c);
+       const char *status = _call_state_str(ofono_call_state_get(c));
+
+       _call_text_set(ctx, id, "name", contact);
+       _call_text_set(ctx, id, "phone.type", type);
+       _call_text_set(ctx, id, "status", status);
+       _call_text_set(ctx, id, "elapsed", "");
+
+       free(contact);
+}
+
+static void _call_elapsed_update(Callscreen *ctx, unsigned int id,
+                                       const OFono_Call *c)
+{
+       Edje_Message_Float msgf = {0};
+       Evas_Object *ed;
+       double start, now, elapsed;
+       char part[128], buf[128] = "";
+
+       if (ofono_call_multiparty_get(c))
+               start = ctx->multiparty.start;
+       else
+               start = ofono_call_start_time_get(c);
+
+       if (start < 0) {
+               ERR("Unknown start time for call");
+               goto end;
        }
 
-       _multiparty_update(ctx);
+       now = ecore_loop_time_get();
+       elapsed =  now - start;
+       if (elapsed < 0) {
+               ERR("Time rewinded? %f - %f = %f", now, start, elapsed);
+               goto end;
+       }
 
-       if (ctx->calls.active && ctx->calls.waiting && ctx->calls.held)
-               return;
+       msgf.val = elapsed;
+       if (elapsed > 3600)
+               snprintf(buf, sizeof(buf), "%02d:%02d:%02d",
+                               (int)elapsed / 3600,
+                               (int)elapsed % 3600 / 60,
+                               (int)elapsed % 60);
+       else
+               snprintf(buf, sizeof(buf), "%02d:%02d",
+                               (int)elapsed / 60,
+                               (int)elapsed % 60);
+
+end:
+       ed = elm_layout_edje_get(ctx->self);
+       edje_object_message_send(ed, EDJE_MESSAGE_FLOAT, 10 + id, &msgf);
 
-       EINA_LIST_FOREACH(ctx->calls.list, n, c) {
-               OFono_Call_State state = ofono_call_state_get(c);
+       snprintf(part, sizeof(part), "elm.text.%u.elapsed", id);
+       elm_object_part_text_set(ctx->self, part, buf);
+}
 
-               DBG("compare %p (%d) to %p (%d)", found, found_state,
-                       c, state);
-               if (state_priority[state] > state_priority[found_state]) {
-                       found_state = state;
-                       found = c;
-               }
-               if ((!waiting) && (state == OFONO_CALL_STATE_WAITING))
-                       waiting = c;
-               if ((!held) && (state == OFONO_CALL_STATE_HELD))
-                       held = c;
+static void _activecall_elapsed_update(Callscreen *ctx)
+{
+       Edje_Message_Float msgf = {0};
+       Evas_Object *ed;
+       double start, now, elapsed;
+       char buf[128] = "";
+
+       if (ofono_call_multiparty_get(ctx->calls.current))
+               start = ctx->multiparty.start;
+       else
+               start = ofono_call_start_time_get(ctx->calls.current);
+
+       if (start < 0) {
+               ERR("Unknown start time for call");
+               goto end;
        }
 
-       DBG("found=%p, state=%d, waiting=%p, held=%p",
-               found, found_state, waiting, held);
-       if (!found)
-               return;
+       now = ecore_loop_time_get();
+       elapsed =  now - start;
+       if (elapsed < 0) {
+               ERR("Time rewinded? %f - %f = %f", now, start, elapsed);
+               goto end;
+       }
+
+       msgf.val = elapsed;
+       if (elapsed > 3600)
+               snprintf(buf, sizeof(buf), "%02d:%02d:%02d",
+                               (int)elapsed / 3600,
+                               (int)elapsed % 3600 / 60,
+                               (int)elapsed % 60);
+       else
+               snprintf(buf, sizeof(buf), "%02d:%02d",
+                               (int)elapsed / 60,
+                               (int)elapsed % 60);
+
+end:
+       ed = elm_layout_edje_get(ctx->gui_activecall);
+       edje_object_message_send(ed, EDJE_MESSAGE_FLOAT, 1, &msgf);
+
+       elm_object_part_text_set(ctx->gui_activecall, "elm.text.elapsed", buf);
+}
+
+static void _call_update(Callscreen *ctx, unsigned int id, const OFono_Call *c)
+{
+       Evas_Object *o = ctx->self;
+       OFono_Call_State state = ofono_call_state_get(c);
+       const char *status_label = _call_state_str(state);
+       const char *status_id = _call_state_id(state);
+       char buf[128];
 
-       if (!ctx->calls.active) {
-               ctx->calls.active = found;
-               ctx->last_state = 0;
+       _call_text_set(ctx, id, "status", status_label);
+
+       snprintf(buf, sizeof(buf), "state,%u,%s", id, status_id);
+       elm_object_signal_emit(o, buf, "call");
+
+       snprintf(buf, sizeof(buf), "%s,%u,multiparty",
+                       ofono_call_multiparty_get(c) ? "show" : "hide", id);
+       elm_object_signal_emit(o, buf, "call");
+
+       if ((state == OFONO_CALL_STATE_ACTIVE) ||
+               (state == OFONO_CALL_STATE_HELD)) {
+               _call_elapsed_update(ctx, id, c);
+               snprintf(buf, sizeof(buf), "show,%u,elapsed", id);
+               elm_object_signal_emit(o, buf, "call");
+       } else {
+               Edje_Message_Float msgf;
+
+               _call_text_set(ctx, id, "elapsed", "");
+               snprintf(buf, sizeof(buf), "hide,%u,elapsed", id);
+               elm_object_signal_emit(o, buf, "call");
+
+               msgf.val = 0.0;
+               edje_object_message_send(elm_layout_edje_get(o),
+                               EDJE_MESSAGE_FLOAT, 10 + id, &msgf);
        }
-       if (!ctx->calls.waiting)
-               ctx->calls.waiting = waiting;
-       if (!ctx->calls.held)
-               ctx->calls.held = held;
+}
+
+static void _call_clear(Callscreen *ctx, unsigned int id)
+{
+       Evas_Object *o = ctx->self;
+       Edje_Message_Float msgf;
+       char buf[128];
+
+       _call_text_set(ctx, id, "name", "");
+       _call_text_set(ctx, id, "phone.type", "");
+       _call_text_set(ctx, id, "status", "");
+       _call_text_set(ctx, id, "elapsed", "");
 
-       if (ctx->calls.active)
-               gui_call_enter();
+       snprintf(buf, sizeof(buf), "state,%u,disconnected", id);
+       elm_object_signal_emit(o, buf, "call");
+
+       snprintf(buf, sizeof(buf), "hide,%u,multiparty", id);
+       elm_object_signal_emit(o, buf, "call");
+
+       snprintf(buf, sizeof(buf), "hide,%u,elapsed", id);
+       elm_object_signal_emit(o, buf, "call");
+
+       msgf.val = 0.0;
+       edje_object_message_send(elm_layout_edje_get(o),
+                                       EDJE_MESSAGE_FLOAT, 10 + id, &msgf);
 }
 
-static void _call_disconnected_done(Callscreen *ctx,
-                                       const char *reason __UNUSED__)
+static void _activecall_update(Callscreen *ctx)
 {
-       _calls_update(ctx);
+       OFono_Call_State state = ofono_call_state_get(ctx->calls.current);
+       char *contact = _call_name_get(ctx, ctx->calls.current);
+       const char *type = _call_type_get(ctx->calls.current);
+       const char *status_label = _call_state_str(state);
+       const char *status_id = _call_state_id(state);
+       Evas_Object *o = ctx->gui_activecall;
+       char buf[128];
+
+       elm_object_part_text_set(o, "elm.text.name", contact);
+       elm_object_part_text_set(o, "elm.text.phone.type", type);
+       elm_object_part_text_set(o, "elm.text.status", status_label);
+
+       snprintf(buf, sizeof(buf), "state,%s", status_id);
+       elm_object_signal_emit(o, buf, "call");
+
+       _activecall_elapsed_update(ctx);
+
+       free(contact);
+}
+
+static void _call_waiting_set(Callscreen *ctx, OFono_Call *c)
+{
+       Evas_Object *o = ctx->self;
+
+       if (!c) {
+               elm_object_part_text_set(o, "elm.text.waiting", "");
+               elm_object_signal_emit(o, "hide,waiting", "call");
+       } else {
+               char *name = _call_name_get(ctx, c);
+               elm_object_part_text_set(o, "elm.text.waiting", name);
+               elm_object_signal_emit(o, "show,waiting", "call");
+               free(name);
+       }
+
+       ctx->calls.waiting = c;
+}
+
+static void _call_incoming_set(Callscreen *ctx, OFono_Call *c)
+{
+       Evas_Object *o = ctx->self;
+
+       if (!c)
+               elm_object_signal_emit(o, "hide,answer", "call");
+       else
+               elm_object_signal_emit(o, "show,answer", "call");
 
-       if (!ctx->calls.list) {
+       ctx->calls.incoming = c;
+}
+
+static void _call_current_actions_update(Callscreen *ctx)
+{
+       Evas_Object *o = ctx->self;
+       OFono_Call_State s;
+
+       if (ctx->calls.current)
+               s = ofono_call_state_get(ctx->calls.current);
+       else
+               s = OFONO_CALL_STATE_DISCONNECTED;
+
+       if ((s == OFONO_CALL_STATE_ACTIVE) || (s == OFONO_CALL_STATE_HELD))
+               elm_object_signal_emit(o, "enable,actions", "call");
+       else
+               elm_object_signal_emit(o, "disable,actions", "call");
+}
+
+static void _call_current_set(Callscreen *ctx, OFono_Call *c)
+{
+       Evas_Object *o = ctx->self;
+       Eina_Bool need_activecall_update = EINA_FALSE;
+
+       DBG("was=%p, now=%p, first=%p, second=%p",
+               ctx->calls.current, c, ctx->calls.first, ctx->calls.second);
+
+       if (!c) {
+               elm_object_signal_emit(o, "active,disconnected", "call");
                if (ctx->gui_activecall) {
                        gui_activecall_set(NULL);
                        evas_object_del(ctx->gui_activecall);
                        ctx->gui_activecall = NULL;
                }
-               gui_call_exit();
+       } else {
+               if (!ctx->gui_activecall) {
+                       ctx->gui_activecall = gui_layout_add(o, "activecall");
+                       elm_object_signal_callback_add(
+                               ctx->gui_activecall, "clicked", "call",
+                               _on_active_call_clicked, ctx);
+                       gui_activecall_set(ctx->gui_activecall);
+                       need_activecall_update = EINA_TRUE;
+               } else if (ctx->calls.current != c)
+                       need_activecall_update = EINA_TRUE;
+       }
+
+       ctx->calls.current = c;
+
+       _call_current_actions_update(ctx);
+
+       if (need_activecall_update)
+               _activecall_update(ctx);
+}
+
+static void _call_first_set(Callscreen *ctx, OFono_Call *c)
+{
+       DBG("was=%p, now=%p", ctx->calls.first, c);
+
+       if (!c)
+               _call_clear(ctx, 1);
+       else {
+               _call_show(ctx, 1, c);
+               _call_update(ctx, 1, c);
        }
+
+       ctx->calls.first = c;
+}
+
+static void _call_second_set(Callscreen *ctx, OFono_Call *c)
+{
+       DBG("was=%p, now=%p", ctx->calls.second, c);
+
+       if (!c) {
+               _call_clear(ctx, 2);
+               elm_object_signal_emit(ctx->self, "calls,1", "call");
+       } else {
+               _call_show(ctx, 2, c);
+               _call_update(ctx, 2, c);
+               elm_object_signal_emit(ctx->self, "calls,2", "call");
+       }
+
+       ctx->calls.second = c;
+}
+
+static void _call_auto_place(Callscreen *ctx, OFono_Call *c)
+{
+       OFono_Call_State state = ofono_call_state_get(c);
+       Eina_Bool is_multiparty = ofono_call_multiparty_get(c);
+
+       DBG("ctx=%p, %p, %p, call=%p, state=%d, multiparty=%d",
+               ctx, ctx->calls.first, ctx->calls.second, c,
+               state, is_multiparty);
+
+       if (!ctx->calls.first) {
+               DBG("first call %p", c);
+               _call_first_set(ctx, c);
+               _call_current_set(ctx, c);
+               return;
+       }
+
+       if (is_multiparty) {
+               if (ofono_call_multiparty_get(ctx->calls.first)) {
+                       DBG("call %p is already part of first multiparty", c);
+                       return;
+               } else if (ctx->calls.second &&
+                               ofono_call_multiparty_get(ctx->calls.second)) {
+                       DBG("call %p is already part of second multiparty", c);
+                       return;
+               }
+       }
+
+       DBG("second call %p", c);
+       _call_second_set(ctx, c);
+       if (state == OFONO_CALL_STATE_ACTIVE)
+               _call_current_set(ctx, c);
+}
+
+static OFono_Call *_multiparty_find_other(const Callscreen *ctx,
+                                               const OFono_Call *c)
+{
+       OFono_Call *itr;
+       const Eina_List *n;
+       EINA_LIST_FOREACH(ctx->calls.list, n, itr) {
+               if (itr == c)
+                       continue;
+               if (!ofono_call_multiparty_get(itr))
+                       continue;
+               if (ofono_call_state_get(itr) == OFONO_CALL_STATE_DISCONNECTED)
+                       continue;
+               DBG("%p is another multiparty peer of %p", itr, c);
+               return itr;
+       }
+       DBG("no other multiparty peers of %p", c);
+       return NULL;
+}
+
+static void _call_auto_unplace(Callscreen *ctx, OFono_Call *c)
+{
+       Eina_Bool is_multiparty = ofono_call_multiparty_get(c);
+       OFono_Call *replacement = NULL;
+
+       DBG("ctx=%p, %p, %p, current=%p, call=%p, multiparty=%d",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               c, is_multiparty);
+
+       if (is_multiparty) {
+               replacement = _multiparty_find_other(ctx, c);
+               DBG("replacement=%p", replacement);
+       }
+
+       if (ctx->calls.first == c) {
+               if (replacement) {
+                       _call_first_set(ctx, replacement);
+                       if (ctx->calls.current == c)
+                               _call_current_set(ctx, replacement);
+                       return;
+               }
+
+               if (!ctx->calls.second) {
+                       DBG("no calls left");
+                       _call_first_set(ctx, NULL);
+                       _call_current_set(ctx, NULL);
+               } else {
+                       DBG("move second to first");
+                       _call_first_set(ctx, ctx->calls.second);
+                       _call_second_set(ctx, NULL);
+                       _call_current_set(ctx, ctx->calls.first);
+               }
+       } else if (ctx->calls.second == c) {
+               if (replacement) {
+                       _call_second_set(ctx, replacement);
+                       if (ctx->calls.current == c)
+                               _call_current_set(ctx, replacement);
+                       return;
+               }
+
+               DBG("remove second");
+               _call_second_set(ctx, NULL);
+               _call_current_set(ctx, ctx->calls.first);
+       }
+}
+
+static void _call_disconnected_done(Callscreen *ctx,
+                                       const char *reason __UNUSED__)
+{
+       _multiparty_update(ctx);
+
+       if (!ctx->calls.list)
+               gui_call_exit();
        ctx->disconnected.call = NULL;
 }
 
-static void _popup_close(void *data, Evas_Object *o __UNUSED__, void *event __UNUSED__)
+static void _popup_close(void *data, Evas_Object *o __UNUSED__,
+                               void *event __UNUSED__)
 {
        Callscreen *ctx = data;
 
@@ -283,7 +689,8 @@ static void _popup_close(void *data, Evas_Object *o __UNUSED__, void *event __UN
        _call_disconnected_done(ctx, "network");
 }
 
-static void _popup_redial(void *data, Evas_Object *o __UNUSED__, void *event __UNUSED__)
+static void _popup_redial(void *data, Evas_Object *o __UNUSED__,
+                               void *event __UNUSED__)
 {
        Callscreen *ctx = data;
 
@@ -298,31 +705,18 @@ static void _call_disconnected_show(Callscreen *ctx, OFono_Call *c,
        const char *number, *title;
        char msg[1024];
 
-       DBG("ctx=%p, active=%p, held=%p, waiting=%p, previous=%s, "
+       DBG("ctx=%p, %p, %p, current=%p, waiting=%p, previous=%s, "
                "disconnected=%p (%s)",
-               ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
-               ctx->disconnected.number, c, reason);
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, ctx->disconnected.number, c, reason);
 
        if (ctx->calls.waiting == c) {
-               ctx->calls.waiting = NULL;
-               elm_object_part_text_set(ctx->self, "elm.text.waiting", "");
-               elm_object_signal_emit(ctx->self, "hide,waiting", "call");
-               goto done;
-       }
-       if (ctx->calls.held == c) {
-               ctx->calls.held = NULL;
-               if (ofono_call_multiparty_get(c))
-                       goto done;
-               elm_object_part_text_set(ctx->self, "elm.text.held", "");
-               elm_object_signal_emit(ctx->self, "hide,held", "call");
-               _multiparty_private_available_update(ctx);
-               goto done;
+               _call_waiting_set(ctx, NULL);
+               goto done; /* do not ask to redial to waiting number */
        }
 
-       if ((ctx->calls.active) && (ctx->calls.active != c))
-               goto done;
-       ctx->calls.active = NULL;
-       ctx->last_state = 0;
+       _call_auto_unplace(ctx, c);
+
        ctx->disconnected.call = c;
 
        elm_object_signal_emit(ctx->self, "active,disconnected", "call");
@@ -359,6 +753,7 @@ static void _call_disconnected_show(Callscreen *ctx, OFono_Call *c,
        /* TODO: sound to notify user */
 
        return;
+
 done:
        _call_disconnected_done(ctx, reason);
 }
@@ -385,9 +780,9 @@ static void _on_pressed(void *data, Evas_Object *obj __UNUSED__,
                        const char *emission, const char *source __UNUSED__)
 {
        Callscreen *ctx = data;
-       DBG("ctx=%p, active=%p, held=%p, waiting=%p, signal: %s",
-               ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
-               emission);
+       DBG("ctx=%p, %p, %p, current=%p, waiting=%p, signal: %s",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, emission);
 
        EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "pressed,"));
        emission += strlen("pressed,");
@@ -399,9 +794,9 @@ static void _on_released(void *data, Evas_Object *obj __UNUSED__,
                                const char *source __UNUSED__)
 {
        Callscreen *ctx = data;
-       DBG("ctx=%p, active=%p, held=%p, waiting=%p, signal: %s",
-               ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
-               emission);
+       DBG("ctx=%p, %p, %p, current=%p, waiting=%p, signal: %s",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, emission);
 
        EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "released,"));
        emission += strlen("released,");
@@ -413,14 +808,15 @@ static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
 {
        Callscreen *ctx = data;
        const char *dtmf = NULL;
-       DBG("ctx=%p, active=%p, held=%p, waiting=%p, signal: %s",
-               ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
-               emission);
+
+       DBG("ctx=%p, %p, %p, current=%p, waiting=%p, signal: %s",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, emission);
 
        EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "clicked,"));
        emission += strlen("clicked,");
 
-       if ((emission[0] >= '0') && (emission[0] <= '9'))
+       if ((emission[0] >= '0') && (emission[0] <= '9') && (emission[1] == 0))
                dtmf = emission;
        else if (strcmp(emission, "star") == 0)
                dtmf = "*";
@@ -437,14 +833,14 @@ static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
        }
 
        if (strcmp(emission, "hangup") == 0) {
-               OFono_Call *c = ctx->calls.active;
+               OFono_Call *c = ctx->calls.current;
                if ((c) && (ofono_call_state_get(c) == OFONO_CALL_STATE_ACTIVE))
                        ofono_release_and_swap(NULL, NULL);
                else if (c)
                        ofono_call_hangup(c, NULL, NULL);
        } else if (strcmp(emission, "answer") == 0) {
-               if (ctx->calls.active)
-                       ofono_call_answer(ctx->calls.active, NULL, NULL);
+               if (ctx->calls.current)
+                       ofono_call_answer(ctx->calls.current, NULL, NULL);
        } else if (strcmp(emission, "mute") == 0) {
                Eina_Bool val = !ofono_mute_get();
                ofono_mute_set(val, NULL, NULL);
@@ -455,11 +851,9 @@ static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
        } else if (strcmp(emission, "add-call") == 0) {
                gui_call_exit();
        } else if (strcmp(emission, "merge") == 0) {
-               if (ctx->calls.held)
-                       ofono_multiparty_create(NULL, NULL);
+               ofono_multiparty_create(NULL, NULL);
        } else if (strcmp(emission, "swap") == 0) {
-               if (ctx->calls.held)
-                       ofono_swap_calls(NULL, NULL);
+               ofono_swap_calls(NULL, NULL);
        } else if (strcmp(emission, "waiting-hangup") == 0) {
                if (ctx->calls.waiting)
                        ofono_call_hangup(ctx->calls.waiting, NULL, NULL);
@@ -516,9 +910,6 @@ static void _ofono_changed(void *data)
        Evas_Object *ed;
        const char *sig;
 
-       if (!ctx->calls.list)
-               return;
-
        sig = ofono_mute_get() ? "toggle,on,mute" : "toggle,off,mute";
        elm_object_signal_emit(ctx->self, sig, "call");
 
@@ -531,25 +922,35 @@ static void _ofono_changed(void *data)
        edje_object_message_send(ed, EDJE_MESSAGE_FLOAT, 2, &msgf);
 }
 
-
 static void _call_added(void *data, OFono_Call *c)
 {
        Callscreen *ctx = data;
-       DBG("ctx=%p, active=%p, held=%p, waiting=%p, added=%p",
-               ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting, c);
+       OFono_Call_State state = ofono_call_state_get(c);
+
+       DBG("ctx=%p, %p, %p, current=%p, waiting=%p, added=%p(%d)",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, c, state);
 
        ctx->calls.list = eina_list_append(ctx->calls.list, c);
-       if (!ctx->calls.active) {
-               _calls_update(ctx);
-               gui_call_enter();
+
+       if (state == OFONO_CALL_STATE_WAITING)
+               _call_waiting_set(ctx, c);
+       else {
+               _call_auto_place(ctx, c);
+               if (state == OFONO_CALL_STATE_INCOMING)
+                       _call_incoming_set(ctx, c);
        }
+
+       gui_call_enter();
 }
 
 static void _call_removed(void *data, OFono_Call *c)
 {
        Callscreen *ctx = data;
-       DBG("ctx=%p, active=%p, held=%p, waiting=%p, removed=%p",
-               ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting, c);
+       DBG("ctx=%p, %p, %p, current=%p, waiting=%p, removed=%p",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, c);
+
        ctx->calls.list = eina_list_remove(ctx->calls.list, c);
        _call_disconnected_show(ctx, c, "local");
 }
@@ -557,290 +958,259 @@ static void _call_removed(void *data, OFono_Call *c)
 static Eina_Bool _on_elapsed_updater(void *data)
 {
        Callscreen *ctx = data;
-       double next, elapsed, start;
-       Edje_Message_Float msgf;
-       Evas_Object *ed, *activecall = ctx->gui_activecall;
-       char buf[128];
-
-       if (!ctx->calls.active)
-               goto stop;
-
-       if (ofono_call_multiparty_get(ctx->calls.active))
-               start = ctx->multiparty.start;
-       else
-               start = ofono_call_start_time_get(ctx->calls.active);
+       double next, now;
 
-       if (start < 0) {
-               ERR("Unknown start time for call");
-               goto stop;
+       if ((!ctx->calls.first) && (!ctx->calls.second)) {
+               ctx->elapsed_updater = NULL;
+               return EINA_FALSE;
        }
 
-       elapsed = ecore_loop_time_get() - start;
-       if (elapsed < 0) {
-               ERR("Time rewinded? %f - %f = %f", ecore_loop_time_get(), start,
-                       elapsed);
-               goto stop;
-       }
+       if (ctx->calls.first)
+               _call_elapsed_update(ctx, 1, ctx->calls.first);
+       if (ctx->calls.second)
+               _call_elapsed_update(ctx, 2, ctx->calls.second);
 
-       ed = elm_layout_edje_get(ctx->self);
-       msgf.val = elapsed;
-       edje_object_message_send(ed, EDJE_MESSAGE_FLOAT, 3, &msgf);
-
-       if (elapsed > 3600)
-               snprintf(buf, sizeof(buf), "%02d:%02d:%02d",
-                               (int)elapsed / 3600,
-                               (int)elapsed % 3600 / 60,
-                               (int)elapsed % 60);
-       else
-               snprintf(buf, sizeof(buf), "%02d:%02d",
-                               (int)elapsed / 60,
-                               (int)elapsed % 60);
-       elm_object_part_text_set(ctx->self, "elm.text.elapsed", buf);
-       if (activecall)
-               elm_object_part_text_set(activecall, "elm.text.elapsed", buf);
+       if (ctx->gui_activecall)
+               _activecall_elapsed_update(ctx);
 
-       next = 1.0 - (elapsed - (int)elapsed);
+       now = ecore_loop_time_get();
+       next = 1.0 - (now - (int)now);
        ctx->elapsed_updater = ecore_timer_add(next, _on_elapsed_updater, ctx);
        return EINA_FALSE;
-
-stop:
-       if (activecall) {
-               elm_object_part_text_set(activecall, "elm.text.elapsed", "");
-               elm_object_signal_emit(activecall, "hide,elapsed", "call");
-       }
-
-       elm_object_part_text_set(ctx->self, "elm.text.elapsed", "");
-       elm_object_signal_emit(ctx->self, "hide,elapsed", "call");
-       ctx->elapsed_updater = NULL;
-       return EINA_FALSE;
 }
 
-static char *_call_name_or_id(const OFono_Call *call)
+static Eina_Bool _call_multiparty_changed(const Callscreen *ctx,
+                                               const OFono_Call *c)
 {
-       const char *s = ofono_call_line_id_get(call);
-       Contact_Info *info = gui_contact_search(s, NULL);
-
-       if (info)
-               return strdup(contact_info_full_name_get(info));
-
-       return phone_format(ofono_call_line_id_get(call));
+       Eina_Bool current = ofono_call_multiparty_get(c);
+       Eina_Bool previous = !!eina_list_data_find(ctx->multiparty.calls, c);
+       return current ^ previous;
 }
 
-static char *_call_name_get(const Callscreen *ctx __UNUSED__,
-                               const OFono_Call *call)
+static inline int _call_priority_cmp(OFono_Call_State a, OFono_Call_State b)
 {
-       if (!ofono_call_multiparty_get(call))
-               return _call_name_or_id(call);
-
-       return strdup("Conference");
+       static const int state_priority[] = {
+               [OFONO_CALL_STATE_DISCONNECTED] = 0,
+               [OFONO_CALL_STATE_ACTIVE] = 6,
+               [OFONO_CALL_STATE_HELD] = 3,
+               [OFONO_CALL_STATE_DIALING] = 5,
+               [OFONO_CALL_STATE_ALERTING] = 4,
+               [OFONO_CALL_STATE_INCOMING] = 2,
+               [OFONO_CALL_STATE_WAITING] = 1
+       };
+       return state_priority[a] - state_priority[b];
 }
 
-static char *_call_type_get(const OFono_Call *call)
+static Eina_Bool _call_priority_higher_than_current(const Callscreen *ctx,
+                                                       const OFono_Call *c)
 {
-       const char *type;
-       const char *s = ofono_call_line_id_get(call);
-       Contact_Info *info = gui_contact_search(s, &type);
+       OFono_Call_State cur_state, new_state;
 
-       if (info) {
-               return strdup(type);
-       }
+       new_state = ofono_call_state_get(c);
+       if ((new_state == OFONO_CALL_STATE_DISCONNECTED) ||
+               (new_state == OFONO_CALL_STATE_WAITING))
+               return EINA_FALSE;
 
-       return NULL;
+       if (!ctx->calls.current)
+               return EINA_TRUE;
+
+       cur_state = ofono_call_state_get(ctx->calls.current);
+       return _call_priority_cmp(new_state, cur_state) > 0;
 }
 
-static void _call_changed(void *data, OFono_Call *c)
+static OFono_Call *_call_priority_find_higher(const Callscreen *ctx,
+                                               OFono_Call *c)
 {
-       Callscreen *ctx = data;
-       OFono_Call_State state;
-       Eina_Bool was_waiting, was_held, is_held;
-       const char *status, *sig = "hide,answer";
-       char *contact, *type;
+       OFono_Call_State first, second, query, higher_state;
+       OFono_Call *higher = NULL;
 
-       DBG("ctx=%p, active=%p, held=%p, waiting=%p, changed=%p",
-               ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting, c);
+       if (!ctx->calls.first)
+               return NULL;
 
-       was_waiting = !!ctx->calls.waiting;
+       query = ofono_call_state_get(c);
+       first = ofono_call_state_get(ctx->calls.first);
+       if (ctx->calls.second)
+               second = ofono_call_state_get(ctx->calls.second);
+       else
+               second = OFONO_CALL_STATE_DISCONNECTED;
+
+       if (_call_priority_cmp(first, query) > 0) {
+               higher = ctx->calls.first;
+               higher_state = first;
+       } else {
+               higher = c;
+               higher_state = query;
+       }
 
-       was_held = (ctx->calls.held) && (ctx->calls.held != ctx->calls.active);
+       if (_call_priority_cmp(second, higher_state) > 0) {
+               higher = ctx->calls.second;
+               higher_state = second;
+       }
 
-       _calls_update(ctx);
+       if (higher != c)
+               return higher;
 
-       if ((ctx->calls.waiting) && (!was_waiting)) {
-               contact = _call_name_get(ctx, ctx->calls.waiting);
-               elm_object_part_text_set(ctx->self, "elm.text.waiting",
-                                               contact);
-               elm_object_signal_emit(ctx->self, "show,waiting", "call");
-               free(contact);
-       } else if ((!ctx->calls.waiting) && (was_waiting)) {
-               elm_object_part_text_set(ctx->self, "elm.text.waiting", "");
-               elm_object_signal_emit(ctx->self, "hide,waiting", "call");
-       }
+       return NULL;
+}
 
-       is_held = (ctx->calls.held) && (ctx->calls.held != ctx->calls.active);
+static void _call_changed_waiting_update(Callscreen *ctx, OFono_Call *c)
+{
+       OFono_Call_State state = ofono_call_state_get(c);
+
+       if (state == OFONO_CALL_STATE_DISCONNECTED) {
+               DBG("waiting was disconnected");
+               _call_waiting_set(ctx, NULL);
+       } else if (state != OFONO_CALL_STATE_WAITING) {
+               _call_waiting_set(ctx, NULL);
+               DBG("waiting was answered");
+               _call_auto_place(ctx, c);
+       }
+}
 
-       if ((is_held) && (!was_held)) {
-               elm_object_signal_emit(ctx->self, "show,held", "call");
-               _multiparty_private_available_update(ctx);
-       } else if (((!is_held) && (was_held)) ||
-                       (ctx->calls.active == ctx->calls.held)) {
-               elm_object_part_text_set(ctx->self, "elm.text.held", "");
-               elm_object_signal_emit(ctx->self, "hide,held", "call");
-               _multiparty_private_available_update(ctx);
+static void _call_changed_current_update(Callscreen *ctx, OFono_Call *c)
+{
+       if (ctx->calls.current != c) {
+               if (_call_priority_higher_than_current(ctx, c)) {
+                       DBG("changed call %p is higher than previous %p",
+                               c, ctx->calls.current);
+                       _call_current_set(ctx, c);
+               } else {
+                       DBG("changed call %p is lower than previous %p",
+                               c, ctx->calls.current);
+               }
+       } else if (ctx->calls.current == c) {
+               OFono_Call *other = _call_priority_find_higher(ctx, c);
+               if (other) {
+                       DBG("other call %p is higher than changed %p",
+                               other, c);
+                       _call_current_set(ctx, other);
+               } else {
+                       DBG("changed call %p is still the current", c);
+                       _call_current_actions_update(ctx);
+               }
        }
+}
 
-       c = ctx->calls.active;
-       if (!c)
+static void _call_changed_multiparty_update(Callscreen *ctx, OFono_Call *c)
+{
+       if (!_call_multiparty_changed(ctx, c)) {
+               DBG("multiparty unchanged");
                return;
+       }
+
+       _multiparty_update(ctx);
 
-       contact = _call_name_get(ctx, c);
-       type = _call_type_get(c);
+       _call_show(ctx, 1, ctx->calls.first);
+       _call_update(ctx, 1, ctx->calls.first);
+       _activecall_update(ctx);
 
-       state = ofono_call_state_get(c);
-       switch (state) {
-       case OFONO_CALL_STATE_DISCONNECTED:
-               status = "Disconnected";
-               break;
-       case OFONO_CALL_STATE_ACTIVE:
-               status = "Active";
-               break;
-       case OFONO_CALL_STATE_HELD:
-               status = "On Hold";
-               break;
-       case OFONO_CALL_STATE_DIALING:
-               status = "Dialing...";
-               break;
-       case OFONO_CALL_STATE_ALERTING:
-               status = "Alerting...";
-               break;
-       case OFONO_CALL_STATE_INCOMING:
-               status = "Incoming...";
-               sig = "show,answer";
-               break;
-       case OFONO_CALL_STATE_WAITING:
-               status = "Waiting...";
-               break;
-       default:
-               status = "?";
+       if (ofono_call_state_get(c) == OFONO_CALL_STATE_DISCONNECTED) {
+               DBG("multiparty peer was disconnected");
+               return;
        }
 
-       if (!ctx->gui_activecall) {
-               ctx->gui_activecall = gui_layout_add(ctx->self, "activecall");
-               elm_object_signal_callback_add(ctx->gui_activecall,
-                                               "clicked", "call",
-                                               _on_active_call_clicked, ctx);
-               gui_activecall_set(ctx->gui_activecall);
+       if (ofono_call_multiparty_get(c)) {
+               if (ctx->calls.first &&
+                       (!ofono_call_multiparty_get(ctx->calls.first)))
+                       DBG("multiparty added to second display");
+               else {
+                       DBG("multiparty merged to first display, "
+                               "remove second display");
+                       _call_second_set(ctx, NULL);
+               }
+       } else if (ctx->calls.first != c) {
+               DBG("multiparty split, add peer as second display");
+               _call_second_set(ctx, c);
+       } else {
+               OFono_Call *other = _multiparty_find_other(ctx, c);
+               DBG("multiparty split, add another %p peer as second display",
+                       other);
+               _call_second_set(ctx, other);
        }
-       elm_object_part_text_set(ctx->gui_activecall, "elm.text.name", contact);
-       if (type)
-               elm_object_part_text_set(ctx->gui_activecall,
-                                               "elm.text.phone.type", type);
-       elm_object_part_text_set(ctx->gui_activecall, "elm.text.status",
-                                       status);
+}
 
+static void _call_changed_swap_merge_update(Callscreen *ctx)
+{
+       Evas_Object *o = ctx->self;
 
-       elm_object_part_text_set(ctx->self, "elm.text.name", contact);
-       elm_object_part_text_set(ctx->self, "elm.text.phone.type", type);
-       free(contact);
-       free(type);
+       if (!ctx->calls.first) {
+               DBG("no calls, disable swap, pause and merge");
+               elm_object_signal_emit(o, "disable,swap", "call");
+               elm_object_signal_emit(o, "disable,pause", "call");
+               elm_object_signal_emit(o, "disable,merge", "call");
+               return;
+       }
 
-       elm_object_part_text_set(ctx->self, "elm.text.status", status);
-       elm_object_signal_emit(ctx->self, sig, "call");
+       if (ctx->calls.second) {
+               DBG("two calls, enable swap and merge, disable pause");
+               elm_object_signal_emit(o, "enable,swap", "call");
+               elm_object_signal_emit(o, "disable,pause", "call");
+               elm_object_signal_emit(o, "enable,merge", "call");
+       } else {
+               const char *sig_swap, *sig_pause;
+               if (ofono_call_state_get(ctx->calls.first) ==
+                       OFONO_CALL_STATE_HELD) {
+                       sig_swap = "enable,swap";
+                       sig_pause = "disable,pause";
+               } else {
+                       sig_swap = "disable,swap";
+                       sig_pause = "enable,pause";
+               }
+               DBG("one call, disable merge and %s %s", sig_swap, sig_pause);
+               elm_object_signal_emit(o, sig_swap, "call");
+               elm_object_signal_emit(o, sig_pause, "call");
+               elm_object_signal_emit(o, "disable,merge", "call");
+       }
+}
 
-       sig = ofono_call_multiparty_get(c) ? "show,multiparty" :
-               "hide,multiparty";
-       elm_object_signal_emit(ctx->self, sig, "call");
-       elm_object_signal_emit(ctx->gui_activecall, sig, "call");
+static void _call_changed(void *data, OFono_Call *c)
+{
+       Callscreen *ctx = data;
+       OFono_Call_State state = ofono_call_state_get(c);
 
-       if (ctx->last_state != state) {
-               Eina_Bool have_updater = !!ctx->elapsed_updater;
-               Eina_Bool want_updater = EINA_FALSE;
+       DBG("BEGIN: ctx=%p, %p, %p, current=%p, waiting=%p, changed=%p (%d)",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, c, state);
 
-               switch (state) {
-               case OFONO_CALL_STATE_DISCONNECTED:
-                       sig = "state,disconnected";
-                       break;
-               case OFONO_CALL_STATE_ACTIVE:
-                       sig = "state,active";
-                       want_updater = EINA_TRUE;
-                       break;
-               case OFONO_CALL_STATE_HELD:
-                       sig = "state,held";
-                       break;
-               case OFONO_CALL_STATE_DIALING:
-                       sig = "state,dialing";
-                       break;
-               case OFONO_CALL_STATE_ALERTING:
-                       sig = "state,alerting";
-                       break;
-               case OFONO_CALL_STATE_INCOMING:
-                       sig = "state,incoming";
-                       break;
-               case OFONO_CALL_STATE_WAITING:
-                       sig = "state,waiting";
-                       break;
-               default:
-                       sig = NULL;
-               }
-               if (sig) {
-                       elm_object_signal_emit(ctx->self, sig, "call");
-                       elm_object_signal_emit(ctx->gui_activecall,
-                                               sig, "call");
-               }
-               ctx->last_state = state;
-
-               sig = NULL;
-               if (have_updater && !want_updater) {
-                       ecore_timer_del(ctx->elapsed_updater);
-                       ctx->elapsed_updater = NULL;
-                       sig = "hide,elapsed";
-                       elm_object_part_text_set(ctx->self, "elm.text.elapsed",
-                                                       "");
-               } else if (want_updater) {
-                       if (!have_updater)
-                               sig = "show,elapsed";
-                       else {
-                               ecore_timer_del(ctx->elapsed_updater);
-                               ctx->elapsed_updater = NULL;
-                       }
-                       _on_elapsed_updater(ctx);
-               }
-               if (sig) {
-                       elm_object_signal_emit(ctx->self, sig, "call");
-                       elm_object_signal_emit(ctx->gui_activecall,
-                                               sig, "call");
-               }
+       if (ctx->calls.waiting == c)
+               _call_changed_waiting_update(ctx, c);
+       else if (ctx->calls.incoming == c) {
+               if (state != OFONO_CALL_STATE_INCOMING)
+                       _call_incoming_set(ctx, NULL);
        }
 
-       if (is_held) {
-               contact = _call_name_get(ctx, ctx->calls.held);
-               elm_object_part_text_set(ctx->self, "elm.text.held", contact);
-               free(contact);
+       _call_changed_current_update(ctx, c);
+       _call_changed_multiparty_update(ctx, c);
 
-               if (ofono_call_multiparty_get(ctx->calls.held))
-                       sig = "show,held,multiparty";
-               else
-                       sig = "hide,held,multiparty";
-               elm_object_signal_emit(ctx->self, sig, "call");
-       }
+       if (ctx->calls.first == c)
+               _call_update(ctx, 1, c);
+       else if (ctx->calls.second == c)
+               _call_update(ctx, 2, c);
 
-       sig = is_held ? "enable,merge" : "disable,merge";
-       elm_object_signal_emit(ctx->self, sig, "call");
+       if (ctx->multiparty.calls)
+               _multiparty_private_available_update(ctx);
 
-       sig = ctx->calls.held ? "enable,swap" : "disable,swap";
-       elm_object_signal_emit(ctx->self, sig, "call");
+       if (ctx->calls.current) {
+               if (!ctx->elapsed_updater)
+                       _on_elapsed_updater(ctx);
+       } else if (ctx->elapsed_updater) {
+               ecore_timer_del(ctx->elapsed_updater);
+               ctx->elapsed_updater = NULL;
+       }
 
-       if (state == OFONO_CALL_STATE_DISCONNECTED)
-               _call_disconnected_show(ctx, c, "local");
+       _call_changed_swap_merge_update(ctx);
 
-       _ofono_changed(ctx);
+       DBG("END: ctx=%p, %p, %p, current=%p, waiting=%p, changed=%p (%d)",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, c, state);
 }
 
 static void _call_disconnected(void *data, OFono_Call *c, const char *reason)
 {
        Callscreen *ctx = data;
-       DBG("ctx=%p, active=%p, held=%p, waiting=%p, disconnected=%p (%s)",
-               ctx, ctx->calls.active, ctx->calls.held, ctx->calls.waiting,
-               c, reason);
+       DBG("ctx=%p, %p, %p, current=%p, waiting=%p, disconnected=%p (%s)",
+               ctx, ctx->calls.first, ctx->calls.second, ctx->calls.current,
+               ctx->calls.waiting, c, reason);
 
        EINA_SAFETY_ON_NULL_RETURN(reason);
        _call_disconnected_show(ctx, c, reason);
@@ -897,9 +1267,9 @@ Evas_Object *callscreen_add(Evas_Object *parent)
        evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN,
                                        _on_key_down, ctx);
 
-       elm_object_part_text_set(obj, "elm.text.name", "");
-       elm_object_part_text_set(obj, "elm.text.status", "");
-       elm_object_part_text_set(obj, "elm.text.elapsed", "");
+       _call_clear(ctx, 1);
+       _call_clear(ctx, 2);
+       elm_object_signal_emit(obj, "calls,1", "call");
 
        ctx->multiparty.sc = elm_scroller_add(obj);
        elm_scroller_policy_set(ctx->multiparty.sc, ELM_SCROLLER_POLICY_AUTO,