update for beta release
[framework/uifw/e17.git] / src / modules / conf_randr / e_int_config_randr_arrangement.c
1 #include "e_int_config_randr.h"
2 #include "e_randr.h"
3 #include "Ecore_X.h"
4
5 #ifndef  ECORE_X_RANDR_1_2
6 #define ECORE_X_RANDR_1_2   ((1 << 16) | 2)
7 #endif
8 #ifndef  ECORE_X_RANDR_1_3
9 #define ECORE_X_RANDR_1_3   ((1 << 16) | 3)
10 #endif
11
12 #ifndef  Ecore_X_Randr_Unset
13 #define Ecore_X_Randr_Unset -1
14 #endif
15
16 #define DOUBLECLICK_TIMEOUT 0.2
17 #define CRTC_THUMB_SIZE_W   300
18 #define CRTC_THUMB_SIZE_H   300
19
20 Eina_Bool                dialog_subdialog_arrangement_create_data(E_Config_Dialog_Data *e_config_runtime_info);
21 Evas_Object             *dialog_subdialog_arrangement_basic_create_widgets(Evas *canvas);
22 Eina_Bool                dialog_subdialog_arrangement_basic_check_changed(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
23 Eina_Bool                dialog_subdialog_arrangement_basic_apply_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
24 void                     dialog_subdialog_arrangement_free_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
25 static inline Eina_List *_dialog_subdialog_arrangement_neighbors_get(Evas_Object *obj);
26 static void              _dialog_subdialog_arrangement_determine_positions_recursive(Evas_Object *obj);
27
28 //static inline E_Config_Randr_Dialog_Output_Dialog_Data *_dialog_subdialog_arrangement_output_dialog_data_new        (E_Randr_Crtc_Info *crtc_info, E_Randr_Output_Info *output_info);
29 static inline void       _dialog_subdialog_arrangement_suggestion_add(Evas *evas);
30 static inline void       _dialog_subdialog_arrangement_make_suggestion(Evas_Object *obj);
31 static void              _dialog_subdialog_arrangement_smart_class_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h);
32 static Evas_Object      *_dialog_subdialog_arrangement_output_add(Evas *canvas, E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data);
33 static void              _dialog_subdialog_arrangement_output_mouse_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
34 static void              _dialog_subdialog_arrangement_output_mouse_move_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
35 static void              _dialog_subdialog_arrangement_output_mouse_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
36
37 // Function for the resolutions subdialog interaction
38 extern void              dialog_subdialog_resolutions_update_list(Evas_Object *crtc);
39 // Function for the orientation subdialog interaction
40 extern void              dialog_subdialog_orientation_update_radio_buttons(Evas_Object *crtc);
41 extern void              dialog_subdialog_orientation_update_edje(Evas_Object *crtc);
42 // Functions for the orientation subdialog interaction
43 extern void              dialog_subdialog_policies_update_radio_buttons(Evas_Object *crtc);
44
45 static Evas_Smart_Class screen_setup_smart_class = EVAS_SMART_CLASS_INIT_NAME_VERSION("EvasObjectSmartScreenSetup");
46 static Evas_Smart *screen_setup_smart = NULL;
47
48 extern E_Config_Dialog_Data *e_config_runtime_info;
49 extern char _theme_file_path[];
50
51 static void
52 _dialog_subdialog_arrangement_output_dialog_data_fill(E_Config_Randr_Dialog_Output_Dialog_Data *odd)
53 {
54    if (!odd) return;
55
56    if (odd->crtc)
57      {
58         //already enabled screen
59         odd->previous_pos.x = odd->crtc->geometry.x;
60         odd->previous_pos.y = odd->crtc->geometry.y;
61         odd->previous_mode = odd->crtc->current_mode;
62      }
63    else if (odd->output)
64      {
65         //disabled monitor
66         //try to get a mode from the preferred list, else use default list
67         if (!(odd->preferred_mode = (Ecore_X_Randr_Mode_Info *)eina_list_data_get(eina_list_last(odd->output->preferred_modes))))
68           {
69              if (odd->output->modes)
70                odd->preferred_mode = (Ecore_X_Randr_Mode_Info *)eina_list_data_get(eina_list_last(odd->output->modes));
71              else
72                odd->preferred_mode = NULL;
73           }
74
75         odd->previous_pos.x = Ecore_X_Randr_Unset;
76         odd->previous_pos.y = Ecore_X_Randr_Unset;
77      }
78
79    odd->new_pos.x = Ecore_X_Randr_Unset;
80    odd->new_pos.y = Ecore_X_Randr_Unset;
81 }
82
83 Eina_Bool
84 dialog_subdialog_arrangement_create_data(E_Config_Dialog_Data *data)
85 {
86    Eina_List *iter;
87    E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data;
88    char *disabled_output_width, *disabled_output_height;
89    Evas_Object *display;
90
91    EINA_LIST_FOREACH(data->output_dialog_data_list, iter, dialog_data)
92      {
93         _dialog_subdialog_arrangement_output_dialog_data_fill(dialog_data);
94      }
95
96    if(!(disabled_output_width = edje_file_data_get(_theme_file_path, "disabled_output_width")))
97      disabled_output_width = "1024";
98    if(!(disabled_output_height = edje_file_data_get(_theme_file_path, "disabled_output_height")))
99      disabled_output_height = "768";
100
101    data->gui.subdialogs.arrangement.disabled_output_size.w = atoi(disabled_output_width);
102    data->gui.subdialogs.arrangement.disabled_output_size.h = atoi(disabled_output_height);
103
104    return EINA_TRUE;
105 }
106
107 //IMPROVABLE: Clean up properly if instances can't be created
108 Evas_Object *
109 dialog_subdialog_arrangement_basic_create_widgets(Evas *canvas)
110 {
111    Evas_Object *subdialog, *crtc;
112    E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data;
113    Eina_List *iter;
114
115    if (!canvas || !e_config_runtime_info || !e_config_runtime_info->output_dialog_data_list) return NULL;
116
117    //initialize smart object
118    evas_object_smart_clipped_smart_set(&screen_setup_smart_class);
119    screen_setup_smart_class.resize = _dialog_subdialog_arrangement_smart_class_resize;
120    screen_setup_smart = evas_smart_class_new(&screen_setup_smart_class);
121
122    subdialog = evas_object_smart_add(canvas, screen_setup_smart);
123    e_config_runtime_info->gui.subdialogs.arrangement.clipper = evas_object_smart_clipped_clipper_get(subdialog);
124    fprintf(stderr, "CONF_RANDR: Arrangement subdialog added (%p).\n", subdialog);
125
126    //only use information we can restore.
127    EINA_LIST_FOREACH(e_config_runtime_info->output_dialog_data_list, iter, output_dialog_data)
128      {
129         if ((!output_dialog_data->crtc && !output_dialog_data->output))
130           continue;
131         crtc = _dialog_subdialog_arrangement_output_add(canvas, output_dialog_data);
132
133         if (!crtc) continue;
134         evas_object_show(crtc);
135
136         evas_object_event_callback_add (crtc, EVAS_CALLBACK_MOUSE_DOWN, _dialog_subdialog_arrangement_output_mouse_down_cb, NULL);
137         evas_object_event_callback_add (crtc, EVAS_CALLBACK_MOUSE_MOVE, _dialog_subdialog_arrangement_output_mouse_move_cb, NULL);
138         evas_object_event_callback_add (crtc, EVAS_CALLBACK_MOUSE_UP, _dialog_subdialog_arrangement_output_mouse_up_cb, NULL);
139
140         evas_object_smart_member_add(crtc, subdialog);
141         fprintf(stderr, "CONF_RANDR: CRTC representation (%p) added to arrangement subdialog (%p).\n", crtc, subdialog);
142      }
143
144    e_config_runtime_info->gui.subdialogs.arrangement.smart_parent = subdialog;
145
146    return subdialog;
147 }
148
149 static Evas_Object *
150 _dialog_subdialog_arrangement_output_add(Evas *canvas, E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data)
151 {
152    E_Randr_Output_Info *output_info;
153    Evas_Object *output;
154    const char *output_name = NULL;
155
156    if (!canvas || !output_dialog_data || !e_config_runtime_info) return NULL;
157
158    output = edje_object_add(canvas);
159
160    //set instance data for output
161    evas_object_data_set(output, "output_info", output_dialog_data);
162
163    //set theme for monitor representation
164    EINA_SAFETY_ON_FALSE_GOTO(edje_object_file_set(output, _theme_file_path, "e/conf/randr/dialog/subdialog/arrangement/output"), _dialog_subdialog_arrangement_output_add_edje_set_fail);
165    //indicate monitor state
166    if (!output_dialog_data->crtc || (output_dialog_data->crtc && !output_dialog_data->previous_mode))
167      edje_object_signal_emit(output, "disabled", "e");
168    else
169      edje_object_signal_emit(output, "enabled", "e");
170    //for now use deskpreview widget as background of output, maybe change this to
171    //live image from comp module
172    output_dialog_data->bg = e_widget_deskpreview_add(canvas, 1, 1);
173    edje_object_part_swallow(output, "e.swallow.content", output_dialog_data->bg);
174
175    //Try to get the name of the monitor connected to the output's last output via edid
176    //else use the output's name
177    if (output_dialog_data->crtc)
178      output_info = (E_Randr_Output_Info *)eina_list_data_get(eina_list_last(output_dialog_data->crtc->outputs));
179    else
180      output_info = output_dialog_data->output;
181    if (output_info)
182      {
183         if (ecore_x_randr_edid_has_valid_header(output_info->edid, output_info->edid_length))
184           output_name = ecore_x_randr_edid_display_name_get(output_info->edid, output_info->edid_length);
185         else if (output_info->name)
186           output_name = output_info->name;
187      }
188    if (output_name)
189      edje_object_part_text_set(output, "output_txt", output_name);
190
191    //set output orientation
192    dialog_subdialog_orientation_update_edje(output);
193    return output;
194
195 _dialog_subdialog_arrangement_output_add_edje_set_fail:
196    evas_object_del(output);
197    return NULL;
198 }
199
200 static void
201 _dialog_subdialog_arrangement_smart_class_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
202 {
203    Evas_Object *output;
204    Evas_Coord real_sum_w = 0, real_sum_h = 0;
205    Eina_Rectangle parent_geo, new_geo;
206    Evas_Coord_Point offset = {.x = 0, .y = 0};
207    Evas_Coord offset_x_max = 0;
208    float scaling_factor = 0.1;
209    Eina_List *lst, *itr;
210    const E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data;
211
212    evas_object_geometry_get(obj, &parent_geo.x, &parent_geo.y, &parent_geo.w, &parent_geo.h);
213    fprintf(stderr, "CONF_RANDR: Arrangement dialog shall be resized to %d x %d\n", w, h);
214    fprintf(stderr, "CONF_RANDR: Arrangement dialog Smart object geo: %d x %d, %d x %d\n", parent_geo.x, parent_geo.y, parent_geo.w, parent_geo.h);
215    if ((w < 1) || (h < 1)) return;
216
217    lst = evas_object_smart_members_get(obj);
218    //Calc average aspect ratio from all available monitors
219    EINA_LIST_FOREACH(lst, itr, output)
220      {
221         if (output == e_config_runtime_info->gui.subdialogs.arrangement.clipper) continue;
222         output_dialog_data = evas_object_data_get(output, "output_info");
223         if (!output_dialog_data) continue;
224         if ((!output_dialog_data->previous_mode) && (!output_dialog_data->preferred_mode)) continue;
225         if (output_dialog_data->previous_mode)
226           {
227              real_sum_w += output_dialog_data->previous_mode->width;
228              real_sum_h += output_dialog_data->previous_mode->height;
229           }
230         else if (output_dialog_data->preferred_mode)
231           {
232              real_sum_w += output_dialog_data->preferred_mode->width;
233              real_sum_h += output_dialog_data->preferred_mode->height;
234           }
235         else
236           {
237              real_sum_w += e_config_runtime_info->gui.subdialogs.arrangement.disabled_output_size.w;
238              real_sum_h += e_config_runtime_info->gui.subdialogs.arrangement.disabled_output_size.h;
239           }
240      }
241
242    scaling_factor = (((float)parent_geo.w / (float)real_sum_w) < ((float)parent_geo.h / (float)real_sum_h)) ? ((float)parent_geo.w / (float)real_sum_w) : ((float)parent_geo.h / (float)real_sum_h);
243    scaling_factor *= e_scale;
244
245    EINA_LIST_FOREACH(lst, itr, output)
246      {
247         //Skip elements that are either the clipped smart object or falsely added
248         //to the list of outputs (which should not happen)
249         if (output == e_config_runtime_info->gui.subdialogs.arrangement.clipper) continue;
250         output_dialog_data = evas_object_data_get(output, "output_info");
251         if (!output_dialog_data) continue;
252         if (output_dialog_data->previous_mode)
253           {
254              new_geo.w = (int)((float)output_dialog_data->previous_mode->width * scaling_factor);
255              new_geo.h = (int)((float)output_dialog_data->previous_mode->height * scaling_factor);
256           }
257         else if (output_dialog_data->preferred_mode)
258           {
259              new_geo.w = (int)((float)output_dialog_data->preferred_mode->width * scaling_factor);
260              new_geo.h = (int)((float)output_dialog_data->preferred_mode->height * scaling_factor);
261           }
262         else
263           {
264              new_geo.w = (int)((float)e_config_runtime_info->gui.subdialogs.arrangement.disabled_output_size.w * scaling_factor);
265              new_geo.h = (int)((float)e_config_runtime_info->gui.subdialogs.arrangement.disabled_output_size.h * scaling_factor);
266              fprintf(stderr, "CONF_RANDR: Neither mode nor preferred mode are avavailable for %x. Using %dx%d.\n", (output_dialog_data->crtc ? output_dialog_data->crtc->xid : output_dialog_data->output->xid), e_config_runtime_info->gui.subdialogs.arrangement.disabled_output_size.w, e_config_runtime_info->gui.subdialogs.arrangement.disabled_output_size.h);
267           }
268         if ((new_geo.w <= 0) || (new_geo.h <= 0))
269           {
270              //this is an effect, occuring during dialog closing.
271              //If we don't return here, e_thumb will segfault!
272              return;
273           }
274         if ((output_dialog_data->previous_pos.x == Ecore_X_Randr_Unset) || (output_dialog_data->previous_pos.y == Ecore_X_Randr_Unset))
275           {
276              //this is a non enabled monitor
277              new_geo.x = parent_geo.x + parent_geo.w - new_geo.w - offset.x;
278              new_geo.y = parent_geo.y + offset.y;
279              offset.y = new_geo.y + new_geo.h;
280              if (offset_x_max < new_geo.w)
281                {
282                   //adopt new max value for x
283                   offset_x_max = new_geo.w;
284                }
285              if ((offset.y + new_geo.h) > (parent_geo.y + parent_geo.h))
286                {
287                   //reset offset.y and adjust offset.x
288                   offset.y = 0;
289                   offset.x += offset_x_max;
290                }
291           }
292         else
293           {
294              new_geo.x = ((int)((float)output_dialog_data->previous_pos.x * scaling_factor)) + parent_geo.x;
295              new_geo.y = ((int)((float)output_dialog_data->previous_pos.y * scaling_factor)) + parent_geo.y;
296           }
297         //resize edje element
298         evas_object_resize(output, new_geo.w, new_geo.h);
299         //also resize bg
300         e_thumb_icon_size_set(output_dialog_data->bg, new_geo.w, new_geo.h); //need to clarify the usage of e_thumb. Usable without e_thumb_icon_file_set??!!
301         evas_object_move(output, new_geo.x, new_geo.y);
302         fprintf(stderr, "CONF_RANDR: output representation %p was resized to %d x %d\n", output, new_geo.w, new_geo.h);
303         fprintf(stderr, "CONF_RANDR: output representation %p was moved to %d x %d\n", output, new_geo.x, new_geo.y);
304      }
305 }
306
307 static void
308 _dialog_subdialog_arrangement_output_mouse_down_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
309 {
310    Evas_Object *element = NULL;
311    Eina_List *iter;
312    Eina_Bool crtc_selected = EINA_FALSE;
313
314    EINA_LIST_FOREACH(evas_object_smart_members_get(evas_object_smart_parent_get(obj)), iter, element)
315      {
316         if (e_config_runtime_info->gui.subdialogs.arrangement.clipper == obj) continue;
317         if (element != obj)
318           edje_object_signal_emit(element, "deselect", "e");
319         else
320           {
321              edje_object_signal_emit(element, "select", "e");
322              //update data for other dialogs
323              e_config_runtime_info->gui.selected_eo = obj;
324
325              //update resolutions list
326              dialog_subdialog_resolutions_update_list(obj);
327
328              //update orientation radio buttons
329              dialog_subdialog_orientation_update_radio_buttons(obj);
330
331              //update policy radio buttons
332              dialog_subdialog_policies_update_radio_buttons(obj);
333
334              crtc_selected = EINA_TRUE;
335           }
336      }
337    if (!crtc_selected)
338      {
339         //update data for other dialogs
340         e_config_runtime_info->gui.selected_eo = NULL;
341
342         //update resolutions list
343         dialog_subdialog_resolutions_update_list(NULL);
344
345         //update orientation radio buttons
346         dialog_subdialog_orientation_update_radio_buttons(NULL);
347
348         //update policy radio buttons
349         dialog_subdialog_policies_update_radio_buttons(NULL);
350      }
351
352    evas_object_geometry_get(obj, &e_config_runtime_info->gui.subdialogs.arrangement.previous_pos.x, &e_config_runtime_info->gui.subdialogs.arrangement.previous_pos.y, NULL, NULL);
353 }
354
355 static void
356 _dialog_subdialog_arrangement_output_mouse_move_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info)
357 {
358    Evas_Event_Mouse_Move *ev = event_info;
359    Eina_Rectangle geo, parent;
360    Evas_Coord_Point delta, new;
361
362    if (ev->buttons != 1) return;
363    evas_object_geometry_get (obj, &geo.x, &geo.y, &geo.w, &geo.h);
364    evas_object_geometry_get (evas_object_smart_parent_get(obj), &parent.x, &parent.y, &parent.w, &parent.h);
365    delta.x = ev->cur.canvas.x - ev->prev.canvas.x;
366    delta.y = ev->cur.canvas.y - ev->prev.canvas.y;
367
368    new.x = geo.x + delta.x;
369    new.y = geo.y + delta.y;
370    //respect container borders
371    if (new.x < parent.x + 1)
372      new.x = parent.x + 1;
373    else if (new.x > parent.x + parent.w - geo.w)
374      new.x = parent.x + parent.w - geo.w;
375    if (new.y < parent.y + 1)
376      new.y = parent.y + 1;
377    else if (new.y > parent.y + parent.h - geo.h)
378      new.y = parent.y + parent.h - geo.h;
379    //only take action if position changed
380    if ((geo.x != new.x) || (geo.y != new.y))
381      {
382         evas_object_move(obj, new.x, new.y);
383         _dialog_subdialog_arrangement_make_suggestion(obj);
384      }
385 }
386
387 static void
388 _dialog_subdialog_arrangement_output_mouse_up_cb(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
389 {
390    Evas_Coord_Point coords;
391
392    if (evas_object_visible_get(e_config_runtime_info->gui.subdialogs.arrangement.suggestion))
393      {
394         edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, "hide", "e");
395         evas_object_geometry_get(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, &coords.x, &coords.y, NULL, NULL);
396         evas_object_move(obj, coords.x, coords.y);
397      }
398    else
399      {
400         evas_object_move(obj, e_config_runtime_info->gui.subdialogs.arrangement.previous_pos.x, e_config_runtime_info->gui.subdialogs.arrangement.previous_pos.y);
401      }
402 }
403
404 void
405 _dialog_subdialog_arrangement_suggestion_add(Evas *evas)
406 {
407    const char *theme_data_item = NULL;
408
409    e_config_runtime_info->gui.subdialogs.arrangement.suggestion = edje_object_add(evas);
410    edje_object_file_set(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, _theme_file_path, "e/conf/randr/dialog/subdialog/arrangement/suggestion");
411    if ((theme_data_item = edje_object_data_get(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, "distance_min")))
412      e_config_runtime_info->gui.subdialogs.arrangement.suggestion_dist_min = MIN(MAX(atoi(theme_data_item), 0), 100);
413    else
414      e_config_runtime_info->gui.subdialogs.arrangement.suggestion_dist_min = 20;
415 }
416
417 void
418 _dialog_subdialog_arrangement_make_suggestion(Evas_Object *obj)
419 {
420    Eina_List *li, *crtcs = evas_object_smart_members_get(evas_object_smart_parent_get(obj));
421    Evas_Object *crtc = NULL;
422    Eina_Rectangle p_geo, geo, crtc_geo, s_geo;
423    int dxa = 10000, dya = 10000, tmp, min_dist;
424
425    if (!obj) return;
426
427    if (!e_config_runtime_info->gui.subdialogs.arrangement.suggestion)
428      {
429         _dialog_subdialog_arrangement_suggestion_add(evas_object_evas_get(obj));
430         evas_object_show(e_config_runtime_info->gui.subdialogs.arrangement.suggestion);
431      }
432
433    min_dist = e_config_runtime_info->gui.subdialogs.arrangement.suggestion_dist_min;
434
435    evas_object_geometry_get(evas_object_smart_parent_get(obj), &p_geo.x, &p_geo.y, &p_geo.w, &p_geo.h);
436    evas_object_geometry_get(obj, &geo.x, &geo.y, &geo.w, &geo.h);
437
438    s_geo.x = geo.x;
439    s_geo.y = geo.y;
440    s_geo.w = geo.w;
441    s_geo.h = geo.h;
442
443    //compare possible positions
444    //aritifical (relative) 0x0 element
445    tmp = s_geo.x;
446    if ((tmp < dxa) && (tmp < min_dist))
447      {
448         s_geo.x = p_geo.x;
449         dxa = tmp;
450      }
451    tmp = s_geo.y;
452    if ((tmp < dya) && (tmp < min_dist))
453      {
454         s_geo.y = p_geo.y;
455         dya = tmp;
456      }
457    //iterate crtc list
458    EINA_LIST_FOREACH(crtcs, li, crtc)
459      {
460         if ((crtc == obj) || (crtc == e_config_runtime_info->gui.subdialogs.arrangement.clipper)) continue;
461         evas_object_geometry_get(crtc, &crtc_geo.x, &crtc_geo.y, &crtc_geo.w, &crtc_geo.h);
462         //X-Axis
463         tmp = abs(s_geo.x - crtc_geo.x);
464         if ((tmp < dxa) && (tmp < min_dist))
465           {
466              s_geo.x = crtc_geo.x;
467              dxa = abs(s_geo.x - crtc_geo.x);
468           }
469
470         tmp = abs(s_geo.x - (crtc_geo.x + crtc_geo.w));
471         if ((tmp < dxa) && (tmp < min_dist))
472           {
473              s_geo.x = (crtc_geo.x + crtc_geo.w);
474              dxa = tmp;
475           }
476
477         tmp = abs((s_geo.x + s_geo.w) - (crtc_geo.x - 1));
478         if ((tmp < dxa) && (tmp < min_dist))
479           {
480              s_geo.x = (crtc_geo.x - s_geo.w);
481              dxa = tmp;
482           }
483
484         tmp = abs((s_geo.x + s_geo.w) - (crtc_geo.x + crtc_geo.w));
485         if ((tmp < dxa) && (tmp < min_dist))
486           {
487              s_geo.x = (crtc_geo.x + crtc_geo.w - s_geo.w);
488              dxa = tmp;
489           }
490
491         //Y-Axis
492         tmp = abs(s_geo.y - crtc_geo.y);
493         if ((tmp < dya) && (tmp < min_dist))
494           {
495              s_geo.y = crtc_geo.y;
496              dya = abs(s_geo.y - crtc_geo.y);
497           }
498
499         tmp = abs(s_geo.y - (crtc_geo.y + crtc_geo.h));
500         if ((tmp < dya) && (tmp < min_dist))
501           {
502              s_geo.y = (crtc_geo.y + crtc_geo.h);
503              dya = tmp;
504           }
505
506         tmp = abs((s_geo.y + s_geo.h) - (crtc_geo.y - 1));
507         if ((tmp < dya) && (tmp < min_dist))
508           {
509              s_geo.y = (crtc_geo.y - s_geo.h);
510              dya = tmp;
511           }
512
513         tmp = abs((s_geo.y + s_geo.h) - (crtc_geo.y + crtc_geo.h));
514         if ((tmp < dya) && (tmp < min_dist))
515           {
516              s_geo.y = (crtc_geo.y + crtc_geo.h - s_geo.h);
517              dya = tmp;
518           }
519      }
520
521    if ((s_geo.x != geo.x) && (s_geo.y != geo.y))
522      {
523         if (s_geo.x < p_geo.x) s_geo.x = p_geo.x;
524         if ((s_geo.x + s_geo.w) > (p_geo.x + p_geo.w)) s_geo.x = ((p_geo.x + p_geo.w) - s_geo.w);
525         if (s_geo.y < p_geo.y) s_geo.y = p_geo.y;
526         if ((s_geo.y + s_geo.h) > (p_geo.y + p_geo.h)) s_geo.y = ((p_geo.y + p_geo.h) - s_geo.h);
527
528         if (!evas_object_visible_get(e_config_runtime_info->gui.subdialogs.arrangement.suggestion))
529           {
530              evas_object_show(e_config_runtime_info->gui.subdialogs.arrangement.suggestion);
531              edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, "show", "e");
532           }
533
534         evas_object_resize(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, s_geo.w, s_geo.h);
535         evas_object_move(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, s_geo.x, s_geo.y);
536      }
537    else
538      {
539         edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, "hide", "e");
540         evas_object_hide(e_config_runtime_info->gui.subdialogs.arrangement.suggestion);
541      }
542 }
543
544 void
545 dialog_subdialog_arrangement_free_data(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
546 {
547    E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data;
548
549    EINA_SAFETY_ON_NULL_RETURN(cfdata);
550
551    EINA_LIST_FREE(cfdata->output_dialog_data_list, dialog_data)
552      {
553         if (!dialog_data) continue;
554         if (dialog_data->bg)
555           {
556              evas_object_del(dialog_data->bg);
557              dialog_data->bg = NULL;
558           }
559         free(dialog_data);
560      }
561 }
562
563 static Eina_List *
564 _dialog_subdialog_arrangement_neighbors_get(Evas_Object *obj)
565 {
566    Evas_Object *smart_parent, *crtc;
567    Eina_List *crtcs, *iter, *neighbors = NULL;
568    Eina_Rectangle geo, neighbor_geo;
569    E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data, *neighbor_info;
570
571    smart_parent = evas_object_smart_parent_get(obj);
572    crtcs = evas_object_smart_members_get(smart_parent);
573
574    EINA_SAFETY_ON_FALSE_RETURN_VAL((dialog_data = evas_object_data_get(obj, "output_info")), NULL);
575    evas_object_geometry_get(obj, &geo.x, &geo.y, &geo.w, &geo.h);
576    EINA_LIST_FOREACH(crtcs, iter, crtc)
577      {
578         if ((crtc == obj)
579             || (crtc == e_config_runtime_info->gui.subdialogs.arrangement.clipper)) continue;
580         evas_object_geometry_get(crtc, &neighbor_geo.x, &neighbor_geo.y, &neighbor_geo.w, &neighbor_geo.h);
581         if (!(neighbor_info = evas_object_data_get(crtc, "output_info"))) continue;
582         if (!neighbor_info->previous_mode) continue;
583
584         if (((geo.x + geo.w) == neighbor_geo.x)
585             || (geo.x == (neighbor_geo.x + neighbor_geo.w))
586             || (geo.x == neighbor_geo.x)
587             || ((geo.x + geo.w) == (neighbor_geo.x + neighbor_geo.w))
588             || ((geo.y + geo.h) == neighbor_geo.y)
589             || (geo.y == (neighbor_geo.y + neighbor_geo.h))
590             || (geo.y == neighbor_geo.y)
591             || ((geo.y + geo.h) == (neighbor_geo.y + neighbor_geo.h)))
592           {
593              neighbors = eina_list_append(neighbors, crtc);
594           }
595      }
596
597    return neighbors;
598 }
599
600 static void
601 _dialog_subdialog_arrangement_determine_positions_recursive(Evas_Object *obj)
602 {
603    Eina_List *neighbors, *iter;
604    Evas_Object *smart_parent, *crtc;
605    E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data, *neighbor_info;
606    Eina_Rectangle geo, neighbor_geo, smart_geo;
607
608    // Each object is seen as a tree. All its edges are compared to their
609    // neighbors and wandered recusively.
610    EINA_SAFETY_ON_NULL_RETURN(obj);
611
612    smart_parent = e_config_runtime_info->gui.subdialogs.arrangement.smart_parent;
613    evas_object_geometry_get(smart_parent, &smart_geo.x, &smart_geo.y, &smart_geo.w, &smart_geo.h);
614    //fprintf(stderr, "CONF_RANDR: Smart Parent is at %dx%d\n", smart_geo.x, smart_geo.y);
615    neighbors = _dialog_subdialog_arrangement_neighbors_get(obj);
616
617    EINA_SAFETY_ON_FALSE_RETURN((dialog_data = evas_object_data_get(obj, "output_info")));
618    evas_object_geometry_get(obj, &geo.x, &geo.y, &geo.w, &geo.h);
619
620    //fprintf(stderr, "CONF_RANDR: Traversed element (%p) is at %dx%d\n", obj, geo.x, geo.y);
621    if (geo.x == e_config_runtime_info->gui.subdialogs.arrangement.relative_zero.x) dialog_data->new_pos.x = 0;
622    if (geo.y == e_config_runtime_info->gui.subdialogs.arrangement.relative_zero.y) dialog_data->new_pos.y = 0;
623
624    if ((dialog_data->new_pos.x != 0) || (dialog_data->new_pos.y != 0))
625      {
626         // Find neighbor object we can calculate our own coordinates from
627         EINA_LIST_FOREACH(neighbors, iter, crtc)
628           {
629              evas_object_geometry_get(crtc, &neighbor_geo.x, &neighbor_geo.y, &neighbor_geo.w, &neighbor_geo.h);
630              if (!(neighbor_info = evas_object_data_get(crtc, "output_info"))) continue;
631
632              evas_object_geometry_get(crtc, &neighbor_geo.x, &neighbor_geo.y, &neighbor_geo.w, &neighbor_geo.h);
633
634              if ((dialog_data->new_pos.x == Ecore_X_Randr_Unset) && (neighbor_info->new_pos.x != Ecore_X_Randr_Unset))
635                {
636                   if ((geo.x + geo.w) == neighbor_geo.x)
637                     {
638                        dialog_data->new_pos.x = neighbor_info->new_pos.x - dialog_data->previous_mode->width;
639                     }
640
641                   if (geo.x == (neighbor_geo.x + neighbor_geo.w))
642                     {
643                        dialog_data->new_pos.x = neighbor_info->new_pos.x + neighbor_info->previous_mode->width;
644                     }
645
646                   if (geo.x == neighbor_geo.x)
647                     {
648                        dialog_data->new_pos.x = neighbor_info->new_pos.x;
649                     }
650
651                   if ((geo.x + geo.w) == (neighbor_geo.x + neighbor_geo.w))
652                     {
653                        dialog_data->new_pos.x = (neighbor_info->new_pos.x + neighbor_info->previous_mode->width) - dialog_data->previous_mode->width;
654                     }
655                }
656
657              if ((dialog_data->new_pos.y == Ecore_X_Randr_Unset) && (neighbor_info->new_pos.y != Ecore_X_Randr_Unset))
658                {
659                   if ((geo.y + geo.h) == neighbor_geo.y)
660                     {
661                        dialog_data->new_pos.y = neighbor_info->new_pos.y - dialog_data->previous_mode->height;
662                     }
663
664                   if (geo.y == (neighbor_geo.y + neighbor_geo.h))
665                     {
666                        dialog_data->new_pos.y = neighbor_info->new_pos.y + neighbor_info->previous_mode->height;
667                     }
668
669                   if (geo.y == neighbor_geo.y)
670                     {
671                        dialog_data->new_pos.y = neighbor_info->new_pos.y;
672                     }
673
674                   if ((geo.y + geo.h) == (neighbor_geo.y + neighbor_geo.h))
675                     {
676                        dialog_data->new_pos.y = (neighbor_info->new_pos.y + neighbor_info->previous_mode->height) - dialog_data->previous_mode->height;
677                     }
678                }
679              if ((dialog_data->new_pos.x != Ecore_X_Randr_Unset)
680                  && (dialog_data->new_pos.y != Ecore_X_Randr_Unset))
681                {
682                   //fprintf(stderr, "CONF_RANDR: Determined position for %p: %dx%d\n", obj, dialog_data->new_pos.x, dialog_data->new_pos.y);
683                   break;
684                }
685           }
686      }
687    if ((dialog_data->new_pos.x != Ecore_X_Randr_Unset) || (dialog_data->new_pos.y != Ecore_X_Randr_Unset))
688      {
689         //Only wander all neighbors recursively, if they can use the current
690         //element as reference for their position
691         EINA_LIST_FOREACH(neighbors, iter, crtc)
692           {
693              neighbor_info = evas_object_data_get(crtc, "output_info");
694              if ((neighbor_info->new_pos.x == Ecore_X_Randr_Unset)
695                  || (neighbor_info->new_pos.y == Ecore_X_Randr_Unset))
696                {
697                   //fprintf(stderr, "CONF_RANDR: Now going to travel %p.\n", crtc);
698                   _dialog_subdialog_arrangement_determine_positions_recursive(crtc);
699                }
700           }
701      }
702    eina_list_free(neighbors);
703 }
704
705 Eina_Bool
706 dialog_subdialog_arrangement_basic_apply_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata __UNUSED__)
707 {
708    Eina_List *crtcs, *iter;
709    Evas_Object *smart_parent, *crtc, *top_left = NULL;
710    Eina_Rectangle geo, smart_geo;
711    E_Config_Randr_Dialog_Output_Dialog_Data *odd;
712    Evas_Coord_Point relz = { .x = 10000, .y = 10000};
713    Eina_Bool arrangement_failed = EINA_FALSE;
714
715    smart_parent = e_config_runtime_info->gui.subdialogs.arrangement.smart_parent;
716    evas_object_geometry_get(smart_parent, &smart_geo.x, &smart_geo.y, &smart_geo.w, &smart_geo.h);
717    crtcs = evas_object_smart_members_get(smart_parent);
718
719    //Create virtual borders around the displayed representations by finding
720    //relative x and y as virtual 0x0
721    EINA_LIST_FOREACH(crtcs, iter, crtc)
722      {
723         if (crtc == e_config_runtime_info->gui.subdialogs.arrangement.clipper) continue;
724         //Already reset values for upcoming calculation
725         if (!(odd = evas_object_data_get(crtc, "output_info"))) continue;
726         odd->new_pos.x = Ecore_X_Randr_Unset;
727         odd->new_pos.y = Ecore_X_Randr_Unset;
728         odd = NULL;
729
730         //See whether this element is closer to 0x0 than any before
731         evas_object_geometry_get(crtc, &geo.x, &geo.y, &geo.w, &geo.h);
732         if (geo.x < relz.x)
733           {
734              relz.x = geo.x;
735              top_left = crtc;
736           }
737         if (geo.y < relz.y)
738           {
739              relz.y = geo.y;
740              top_left = crtc;
741           }
742      }
743    e_config_runtime_info->gui.subdialogs.arrangement.relative_zero.x = relz.x;
744    e_config_runtime_info->gui.subdialogs.arrangement.relative_zero.y = relz.y;
745    if (top_left) _dialog_subdialog_arrangement_determine_positions_recursive(top_left);
746
747    EINA_LIST_FOREACH(crtcs, iter, crtc)
748      {
749         if ((crtc == e_config_runtime_info->gui.subdialogs.arrangement.clipper) || !(odd = evas_object_data_get(crtc, "output_info")) || !odd->crtc
750             || ((odd->new_pos.x == Ecore_X_Randr_Unset) || (odd->new_pos.y == Ecore_X_Randr_Unset))) continue;
751         if ((odd->previous_pos.x != odd->new_pos.x) || (odd->previous_pos.y != odd->new_pos.y))
752           {
753              fprintf(stderr, "CONF_RANDR: CRTC %x is moved to %dx%d\n", odd->crtc->xid, odd->new_pos.x, odd->new_pos.y);
754              if (!ecore_x_randr_crtc_pos_set(cfd->con->manager->root, odd->crtc->xid, odd->new_pos.x, odd->new_pos.y))
755                {
756                   arrangement_failed = EINA_TRUE;
757                   break;
758                }
759           }
760      }
761    if (arrangement_failed)
762      return EINA_FALSE;
763    else
764      {
765         ecore_x_randr_screen_reset(cfd->con->manager->root);
766         return EINA_TRUE;
767      }
768 }
769
770 Eina_Bool
771 dialog_subdialog_arrangement_basic_check_changed(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
772 {
773    Eina_List *iter;
774    E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data;
775
776    EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, output_dialog_data)
777      {
778         if ((output_dialog_data->previous_pos.x != output_dialog_data->new_pos.x)
779             || (output_dialog_data->previous_pos.y != output_dialog_data->new_pos.y)
780             ) return EINA_TRUE;
781      }
782    return EINA_FALSE;
783 }
784
785 void
786 dialog_subdialog_arrangement_keep_changes(E_Config_Dialog_Data *cfdata)
787 {
788    E_Config_Randr_Dialog_Output_Dialog_Data *odd;
789    Eina_List *iter;
790
791    if (!cfdata) return;
792
793    EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd)
794      {
795         if (!odd->crtc || ((odd->new_pos.x == Ecore_X_Randr_Unset) || (odd->new_pos.y == Ecore_X_Randr_Unset))) continue;
796         odd->previous_pos.x = odd->new_pos.x;
797         odd->previous_pos.y = odd->new_pos.y;
798         odd->new_pos.x = Ecore_X_Randr_Unset;
799         odd->new_pos.y = Ecore_X_Randr_Unset;
800      }
801 }
802
803 void
804 dialog_subdialog_arrangement_discard_changes(E_Config_Dialog_Data *cfdata)
805 {
806    E_Config_Randr_Dialog_Output_Dialog_Data *odd;
807    Eina_List *iter;
808
809    if (!cfdata) return;
810
811    EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd)
812      {
813         if (!odd->crtc || ((odd->previous_pos.x == Ecore_X_Randr_Unset) || (odd->previous_pos.y == Ecore_X_Randr_Unset))) continue;
814         if (ecore_x_randr_crtc_pos_set(cfdata->manager->root, odd->crtc->xid, odd->previous_pos.x, odd->previous_pos.y))
815           {
816              odd->new_pos.x = odd->previous_pos.x;
817              odd->new_pos.y = odd->previous_pos.y;
818              odd->previous_pos.x = Ecore_X_Randr_Unset;
819              odd->previous_pos.y = Ecore_X_Randr_Unset;
820              ecore_x_randr_screen_reset(cfdata->manager->root);
821           }
822      }
823 }
824