7b49ad2d430b18287a8ebcf389cd06036aeb13d3
[framework/uifw/e17.git] / src / modules / mixer / app_mixer.c
1 #include "e_mod_main.h"
2
3 extern const char _e_mixer_Name[];
4
5 typedef struct E_Mixer_App_Dialog_Data
6 {
7    E_Mixer_System *sys;
8    const char *card;
9    const char *channel_name;
10    int lock_sliders;
11    Eina_List *cards;
12    Eina_List *channels_infos;
13    struct channel_info *channel_info;
14    E_Mixer_Channel_State state;
15
16    struct e_mixer_app_ui
17    {
18       Evas_Object *hlayout;
19       struct e_mixer_app_ui_cards
20       {
21          Evas_Object *frame;
22          Evas_Object *list;
23       } cards;
24       struct e_mixer_app_ui_channels
25       {
26          Evas_Object *frame;
27          Evas_Object *list;
28       } channels;
29       struct e_mixer_app_ui_channel_editor
30       {
31          Evas_Object *frame;
32          Evas_Object *label_card;
33          Evas_Object *card;
34          Evas_Object *label_channel;
35          Evas_Object *channel;
36          Evas_Object *label_type;
37          Evas_Object *type;
38          Evas_Object *label_left;
39          Evas_Object *left;
40          Evas_Object *label_right;
41          Evas_Object *right;
42          Evas_Object *mute;
43          Evas_Object *lock_sliders;
44       } channel_editor;
45    } ui;
46
47    struct
48    {
49       void *data;
50       void (*func)(E_Dialog *dialog, void *data);
51    } del;
52 } E_Mixer_App_Dialog_Data;
53
54 struct channel_info
55 {
56    int has_capture;
57    const char *name;
58    E_Mixer_Channel *id;
59    E_Mixer_App_Dialog_Data *app;
60 };
61
62 static void
63 _cb_changed_left(void *data, Evas_Object *obj __UNUSED__)
64 {
65    E_Mixer_App_Dialog_Data *app = data;
66    E_Mixer_Channel_State *state;
67
68    state = &app->state;
69    if (app->lock_sliders && (state->left != state->right))
70      {
71         state->right = state->left;
72         e_widget_slider_value_int_set(app->ui.channel_editor.right,
73                                       state->right);
74      }
75
76    e_mixer_system_set_volume(app->sys, app->channel_info->id,
77                              state->left, state->right);
78 }
79
80 static void
81 _cb_changed_right(void *data, Evas_Object *obj __UNUSED__)
82 {
83    E_Mixer_App_Dialog_Data *app = data;
84    E_Mixer_Channel_State *state;
85
86    state = &app->state;
87    if (app->lock_sliders && (state->right != state->left))
88      {
89         state->left = state->right;
90         e_widget_slider_value_int_set(app->ui.channel_editor.left,
91                                       state->left);
92      }
93
94    e_mixer_system_set_volume(app->sys, app->channel_info->id,
95                              state->left, state->right);
96 }
97
98 static void
99 _cb_changed_mute(void *data, Evas_Object *obj __UNUSED__)
100 {
101    E_Mixer_App_Dialog_Data *app = data;
102
103    e_mixer_system_set_mute(app->sys, app->channel_info->id, app->state.mute);
104 }
105
106 static void
107 _cb_changed_lock_sliders(void *data, Evas_Object *obj __UNUSED__)
108 {
109    E_Mixer_App_Dialog_Data *app = data;
110    E_Mixer_Channel_State *state;
111
112    if (!app->lock_sliders)
113      return;
114
115    state = &app->state;
116    if (state->left == state->right)
117      return;
118
119    state->left = state->right = (state->left + state->right) / 2;
120
121    e_widget_slider_value_int_set(app->ui.channel_editor.left, state->left);
122    e_widget_slider_value_int_set(app->ui.channel_editor.right, state->right);
123    e_mixer_system_set_volume(app->sys, app->channel_info->id,
124                              state->left, state->right);
125 }
126
127 static void
128 _update_channel_editor_state(E_Mixer_App_Dialog_Data *app, const E_Mixer_Channel_State state)
129 {
130    struct e_mixer_app_ui_channel_editor *ui = &app->ui.channel_editor;
131
132    e_widget_slider_value_int_set(ui->left, state.left);
133    e_widget_slider_value_int_set(ui->right, state.right);
134
135    if (e_mixer_system_can_mute(app->sys, app->channel_info->id))
136      {
137         e_widget_disabled_set(ui->mute, 0);
138         e_widget_check_checked_set(ui->mute, state.mute);
139      }
140    else
141      {
142         e_widget_disabled_set(ui->mute, 1);
143         e_widget_check_checked_set(ui->mute, 0);
144      }
145 }
146
147 static void
148 _populate_channel_editor(E_Mixer_App_Dialog_Data *app)
149 {
150    struct e_mixer_app_ui_channel_editor *ui = &app->ui.channel_editor;
151    E_Mixer_Channel_State state;
152    const char *card_name;
153
154    card_name = e_mixer_system_get_card_name(app->card);
155    e_widget_entry_text_set(ui->card, card_name);
156    eina_stringshare_del(card_name);
157
158    e_widget_entry_text_set(ui->channel, app->channel_name);
159
160    if (e_mixer_system_has_capture(app->sys, app->channel_info->id))
161      e_widget_entry_text_set(ui->type, _("Capture"));
162    else
163      e_widget_entry_text_set(ui->type, _("Playback"));
164
165    e_mixer_system_get_state(app->sys, app->channel_info->id, &state);
166    _update_channel_editor_state(app, state);
167
168    app->lock_sliders = (state.left == state.right);
169    e_widget_check_checked_set(ui->lock_sliders, app->lock_sliders);
170 }
171
172 static void
173 _cb_channel_selected(void *data)
174 {
175    struct channel_info *info = data;
176    E_Mixer_App_Dialog_Data *app;
177
178    app = info->app;
179    app->channel_info = info;
180    _populate_channel_editor(app);
181 }
182
183 static int
184 _channel_info_cmp(const void *data_a, const void *data_b)
185 {
186    const struct channel_info *a = data_a, *b = data_b;
187
188    if (a->has_capture < b->has_capture)
189      return -1;
190    else if (a->has_capture > b->has_capture)
191      return 1;
192
193    return strcmp(a->name, b->name);
194 }
195
196 static Eina_List *
197 _channels_info_new(E_Mixer_System *sys)
198 {
199    Eina_List *channels, *channels_infos, *l;
200
201    channels = e_mixer_system_get_channels(sys);
202    channels_infos = NULL;
203    for (l = channels; l; l = l->next)
204      {
205         struct channel_info *info;
206
207         info = malloc(sizeof(*info));
208         info->id = l->data;
209         info->name = e_mixer_system_get_channel_name(sys, info->id);
210         info->has_capture = e_mixer_system_has_capture(sys, info->id);
211
212         channels_infos = eina_list_append(channels_infos, info);
213      }
214    e_mixer_system_free_channels(channels);
215
216    return eina_list_sort(channels_infos, -1, _channel_info_cmp);
217 }
218
219 static void
220 _channels_info_free(Eina_List *list)
221 {
222    struct channel_info *info;
223
224    EINA_LIST_FREE(list, info)
225      {
226         eina_stringshare_del(info->name);
227         free(info);
228      }
229 }
230
231 static int
232 _cb_system_update(void *data, E_Mixer_System *sys __UNUSED__)
233 {
234    E_Mixer_App_Dialog_Data *app = data;
235    E_Mixer_Channel_State state;
236
237    if ((!app->sys) || (!app->channel_info))
238      return 1;
239
240    e_mixer_system_get_state(app->sys, app->channel_info->id, &state);
241    _update_channel_editor_state(app, state);
242
243    return 1;
244 }
245
246
247 static void
248 _populate_channels(E_Mixer_App_Dialog_Data *app)
249 {
250    Eina_List *l;
251    Evas_Object *ilist;
252    int header_input;
253    int i;
254
255    ilist = app->ui.channels.list;
256    edje_freeze();
257    e_widget_ilist_freeze(ilist);
258    e_widget_ilist_clear(ilist);
259
260    if (app->sys)
261      e_mixer_system_del(app->sys);
262    app->sys = e_mixer_system_new(app->card);
263    e_mixer_system_callback_set(app->sys, _cb_system_update, app);
264
265    eina_stringshare_del(app->channel_name);
266    app->channel_name = e_mixer_system_get_default_channel_name(app->sys);
267
268    if (app->channels_infos)
269      _channels_info_free(app->channels_infos);
270    app->channels_infos = _channels_info_new(app->sys);
271
272    if (app->channels_infos)
273      {
274         struct channel_info *info = app->channels_infos->data;
275         if (info->has_capture)
276           {
277              e_widget_ilist_header_append(ilist, NULL, _("Input"));
278              header_input = 1;
279              i = 1;
280           }
281         else
282           {
283              e_widget_ilist_header_append(ilist, NULL, _("Output"));
284              header_input = 0;
285              i = 1;
286           }
287      }
288
289    for (l = app->channels_infos; l; l = l->next, i++)
290      {
291         struct channel_info *info = l->data;
292
293         if ((!header_input) && info->has_capture)
294           {
295              e_widget_ilist_header_append(ilist, NULL, _("Input"));
296              header_input = 1;
297              i++;
298           }
299
300         info->app = app;
301         e_widget_ilist_append(ilist, NULL, info->name, _cb_channel_selected,
302                               info, info->name);
303         if (app->channel_name && info->name &&
304             (strcmp(app->channel_name, info->name) == 0))
305           {
306              e_widget_ilist_selected_set(ilist, i);
307              app->channel_info = info;
308           }
309      }
310
311    e_widget_ilist_go(ilist);
312    e_widget_ilist_thaw(ilist);
313    edje_thaw();
314 }
315
316 static void
317 select_card(E_Mixer_App_Dialog_Data *app)
318 {
319    _populate_channels(app);
320    e_widget_ilist_selected_set(app->ui.channels.list, 1);
321 }
322
323 static void
324 _cb_card_selected(void *data)
325 {
326    select_card(data);
327 }
328
329 static void
330 _create_cards(E_Dialog *dialog __UNUSED__, Evas *evas, E_Mixer_App_Dialog_Data *app)
331 {
332    struct e_mixer_app_ui_cards *ui = &app->ui.cards;
333    const char *card;
334    Eina_List *l;
335
336    app->card = e_mixer_system_get_default_card();
337    app->cards = e_mixer_system_get_cards();
338    if (eina_list_count(app->cards) < 2)
339      return;
340
341    ui->list = e_widget_ilist_add(evas, 32, 32, &app->card);
342    e_widget_size_min_set(ui->list, 180, 100);
343    e_widget_ilist_go(ui->list);
344    EINA_LIST_FOREACH(app->cards, l, card)
345      {
346         const char *card_name;
347
348         card_name = e_mixer_system_get_card_name(card);
349
350         e_widget_ilist_append(ui->list, NULL, card_name, _cb_card_selected,
351                               app, card);
352
353         eina_stringshare_del(card_name);
354      }
355
356    ui->frame = e_widget_framelist_add(evas, _("Cards"), 0);
357    e_widget_framelist_object_append(ui->frame, ui->list);
358    e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 0, 0.0);
359 }
360
361 static void
362 _create_channels(E_Dialog *dialog __UNUSED__, Evas *evas, E_Mixer_App_Dialog_Data *app)
363 {
364    struct e_mixer_app_ui_channels *ui = &app->ui.channels;
365    ui->list = e_widget_ilist_add(evas, 24, 24, &app->channel_name);
366    e_widget_size_min_set(ui->list, 180, 100);
367    e_widget_ilist_go(ui->list);
368
369    ui->frame = e_widget_framelist_add(evas, _("Channels"), 0);
370    e_widget_framelist_object_append(ui->frame, ui->list);
371    e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 1, 0.0);
372 }
373
374 static void
375 _create_channel_editor(E_Dialog *dialog __UNUSED__, Evas *evas, E_Mixer_App_Dialog_Data *app)
376 {
377    struct e_mixer_app_ui_channel_editor *ui = &app->ui.channel_editor;
378
379    ui->label_card = e_widget_label_add(evas, _("Card:"));
380    ui->card = e_widget_entry_add(evas, NULL, NULL, NULL, NULL);
381    e_widget_entry_readonly_set(ui->card, 1);
382
383    ui->label_channel = e_widget_label_add(evas, _("Channel:"));
384    ui->channel = e_widget_entry_add(evas, NULL, NULL, NULL, NULL);
385    e_widget_entry_readonly_set(ui->channel, 1);
386
387    ui->label_type = e_widget_label_add(evas, _("Type:"));
388    ui->type = e_widget_entry_add(evas, NULL, NULL, NULL, NULL);
389    e_widget_entry_readonly_set(ui->type, 1);
390
391    ui->label_left = e_widget_label_add(evas, _("Left:"));
392    ui->left = e_widget_slider_add(evas, 1, 0, "%3.0f", 0.0, 100.0, 10.0, 100.0,
393                                   NULL, &app->state.left, 150);
394    e_widget_on_change_hook_set(ui->left, _cb_changed_left, app);
395
396    ui->label_right = e_widget_label_add(evas, _("Right:"));
397    ui->right = e_widget_slider_add(evas, 1, 0, "%3.0f", 0.0, 100.0, 10.0, 100.0,
398                                    NULL, &app->state.right, 150);
399    e_widget_on_change_hook_set(ui->right, _cb_changed_right, app);
400
401    ui->mute = e_widget_check_add(evas, _("Mute"), &app->state.mute);
402    e_widget_on_change_hook_set(ui->mute, _cb_changed_mute, app);
403
404    ui->lock_sliders = e_widget_check_add(evas, _("Lock Sliders"),
405                                          &app->lock_sliders);
406    e_widget_on_change_hook_set(ui->lock_sliders, _cb_changed_lock_sliders, app);
407
408    ui->frame = e_widget_framelist_add(evas, _("Edit"), 0);
409    e_widget_framelist_object_append(ui->frame, ui->label_card);
410    e_widget_framelist_object_append(ui->frame, ui->card);
411    e_widget_framelist_object_append(ui->frame, ui->label_channel);
412    e_widget_framelist_object_append(ui->frame, ui->channel);
413    e_widget_framelist_object_append(ui->frame, ui->label_type);
414    e_widget_framelist_object_append(ui->frame, ui->type);
415    e_widget_framelist_object_append(ui->frame, ui->label_left);
416    e_widget_framelist_object_append(ui->frame, ui->left);
417    e_widget_framelist_object_append(ui->frame, ui->label_right);
418    e_widget_framelist_object_append(ui->frame, ui->right);
419    e_widget_framelist_object_append(ui->frame, ui->mute);
420    e_widget_framelist_object_append(ui->frame, ui->lock_sliders);
421
422    e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 1, 0.5);
423 }
424
425 static void
426 _create_ui(E_Dialog *dialog, E_Mixer_App_Dialog_Data *app)
427 {
428    struct e_mixer_app_ui *ui = &app->ui;
429    Evas *evas;
430    int mw, mh;
431
432    evas = e_win_evas_get(dialog->win);
433
434    ui->hlayout = e_widget_list_add(evas, 0, 1);
435    _create_cards(dialog, evas, app);
436    _create_channels(dialog, evas, app);
437    _create_channel_editor(dialog, evas, app);
438
439    if (ui->cards.list)
440      e_widget_ilist_selected_set(ui->cards.list, 0);
441    else
442      select_card(app);
443    e_widget_ilist_selected_set(ui->channels.list, 1);
444
445    e_widget_size_min_get(ui->hlayout, &mw, &mh);
446    if (mw < 300)
447      mw = 300;
448    if (mh < 200)
449      mh = 200;
450    e_dialog_content_set(dialog, ui->hlayout, mw, mh);
451 }
452
453 static void
454 _mixer_app_dialog_del(E_Dialog *dialog, E_Mixer_App_Dialog_Data *app)
455 {
456    if (app->del.func)
457      app->del.func(dialog, app->del.data);
458
459    eina_stringshare_del(app->card);
460    eina_stringshare_del(app->channel_name);
461    if (app->cards)
462      e_mixer_system_free_cards(app->cards);
463    if (app->channels_infos)
464      _channels_info_free(app->channels_infos);
465    e_mixer_system_del(app->sys);
466
467    e_util_defer_object_del(E_OBJECT(dialog));
468    dialog->data = NULL;
469    E_FREE(app);
470 }
471
472 static void
473 _cb_win_del(E_Win *win)
474 {
475    E_Dialog *dialog = win->data;
476    E_Mixer_App_Dialog_Data *app = dialog->data;
477    _mixer_app_dialog_del(dialog, app);
478 }
479
480 static void
481 _cb_dialog_dismiss(void *data, E_Dialog *dialog)
482 {
483    _mixer_app_dialog_del(dialog, data);
484 }
485
486 E_Dialog *
487 e_mixer_app_dialog_new(E_Container *con, void (*func)(E_Dialog *dialog, void *data), void *data)
488 {
489    E_Mixer_App_Dialog_Data *app;
490    E_Dialog *dialog;
491
492    dialog = e_dialog_new(con, _e_mixer_Name, "e_mixer_app_dialog");
493    if (!dialog)
494      return NULL;
495
496    app = E_NEW(E_Mixer_App_Dialog_Data, 1);
497    if (!app)
498      {
499         e_object_del(E_OBJECT(dialog));
500         return NULL;
501      }
502
503    dialog->data = app;
504    app->del.data = data;
505    app->del.func = func;
506
507    e_dialog_title_set(dialog, _(_e_mixer_Name));
508    e_dialog_resizable_set(dialog, 1);
509
510    e_win_delete_callback_set(dialog->win, _cb_win_del);
511
512    _create_ui(dialog, app);
513
514    e_dialog_button_add(dialog, _("Close"), NULL, _cb_dialog_dismiss, app);
515    e_dialog_button_focus_num(dialog, 1);
516    e_win_centered_set(dialog->win, 1);
517    e_dialog_show(dialog);
518    e_dialog_border_icon_set(dialog, "preferences-desktop-mixer");
519
520    // FIXME: what if module unloaded while mixer_app dialog up? bad!
521
522    return dialog;
523 }
524
525 static inline int
526 _find_card_by_name(E_Mixer_App_Dialog_Data *app, const char *card_name)
527 {
528    Eina_List *l;
529    int i;
530
531    for (i = 0, l = app->cards; l; i++, l = l->next)
532      if (strcmp(card_name, l->data) == 0)
533        return i;
534
535    return -1;
536 }
537
538 static inline int
539 _find_channel_by_name(E_Mixer_App_Dialog_Data *app, const char *channel_name)
540 {
541    struct channel_info *info;
542    Eina_List *l;
543    int i = 0;
544    int header_input;
545
546    if (app->channels_infos)
547      {
548         struct channel_info *info = app->channels_infos->data;
549
550         header_input = !!info->has_capture;
551         i = 1;
552      }
553
554    EINA_LIST_FOREACH(app->channels_infos, l, info)
555      {
556         if ((!header_input) && info->has_capture)
557           {
558              header_input = 1;
559              i++;
560           }
561
562         if (strcmp(channel_name, info->name) == 0)
563           return i;
564
565         ++i;
566      }
567
568    return -1;
569 }
570
571 int
572 e_mixer_app_dialog_select(E_Dialog *dialog, const char *card_name, const char *channel_name)
573 {
574    E_Mixer_App_Dialog_Data *app;
575    int n;
576
577    if (!dialog)
578      return 0;
579
580    app = dialog->data;
581    if (!app)
582      return 0;
583
584    n = _find_card_by_name(app, card_name);
585    if (n < 0)
586      return 0;
587    if (app->ui.cards.list)
588      e_widget_ilist_selected_set(app->ui.cards.list, n);
589
590    n = _find_channel_by_name(app, channel_name);
591    if (n < 0)
592      return 0;
593    e_widget_ilist_selected_set(app->ui.channels.list, n);
594
595    return 1;
596 }