Enlightenment CVS committal Author : dj2 Project : e17 Module : libs/ewl
Dir : e17/libs/ewl/src/lib Modified Files: ewl_mvc.c Log Message: - re-work the mvc selection storage, split and merge =================================================================== RCS file: /cvs/e/e17/libs/ewl/src/lib/ewl_mvc.c,v retrieving revision 1.26 retrieving revision 1.27 diff -u -3 -r1.26 -r1.27 --- ewl_mvc.c 17 Aug 2007 16:12:33 -0000 1.26 +++ ewl_mvc.c 18 Aug 2007 18:20:15 -0000 1.27 @@ -6,28 +6,32 @@ #include "ewl_macros.h" #include "ewl_debug.h" -/* XXX There is a bit of behaviour that maybe considered a bug in here. If - * the user has a list of say 4 items. The user clicks on item 2 then - * control clicks on item one, then shift clicks on item 4. Item 2 will now - * be in the selected list twice. A rm _should_ remove both instances, but - * this could be confusing for the user. - * - * The only solution is to do a selected_is for each index that is added to - * the selected list, and for each range, check if that range intersects - * anything else in the selected list. This could be slow and painful. - * - * Leaving it with the same item possibly in the list multiple times for - * now. - */ +static void ewl_mvc_selected_clear_private(Ewl_MVC *mvc); +static unsigned int ewl_mvc_selected_goto(Ewl_MVC *mvc, + unsigned int row, unsigned int column); +static void ewl_mvc_selected_insert(Ewl_MVC *mvc, Ewl_Model *model, + void *data, Ewl_Selection *sel, + unsigned int row, unsigned int column); +static void ewl_mvc_selected_range_split(Ewl_MVC *mvc, + Ewl_Selection_Range *range, + unsigned int row, unsigned int column); +static int ewl_mvc_selection_intersects(Ewl_Selection_Range *range, + Ewl_Selection *sel); +static int ewl_mvc_selection_contained(Ewl_Selection_Range *a, + Ewl_Selection_Range *b); +static int ewl_mvc_line_intersects(int astart, int aend, int bstart, int bend); +static void ewl_mvc_range_merge(Ecore_List *list, Ewl_Model *model, void *data, + Ewl_Selection_Range *range, Ewl_Selection_Range *cur); +static Ewl_Selection *ewl_mvc_selection_make(Ewl_Model *model, void *data, + int top, int left, + int bottom, int right); -static void ewl_mvc_highlight_do(Ewl_MVC *mvc, Ewl_Container *c, - Ewl_Selection *sel, Ewl_Widget *w); static void ewl_mvc_selected_change_notify(Ewl_MVC *mvc); -static void ewl_mvc_selected_rm_item(Ewl_MVC *mvc, Ewl_Selection *sel, - unsigned int row, unsigned int column); +static void ewl_mvc_highlight_do(Ewl_MVC *mvc, Ewl_Container *c, + Ewl_Selection *sel, Ewl_Widget *w); +static void ewl_mvc_cb_highlight_destroy(Ewl_Widget *w, void *ev, void *data); static void ewl_mvc_cb_sel_free(void *data); -static void ewl_mvc_cb_highlight_destroy(Ewl_Widget *w, void *ev, void *data); /** * @param mvc: The MVC to initialize @@ -76,7 +80,7 @@ mvc->cb.view_change(mvc); ewl_mvc_dirty_set(mvc, TRUE); - + DLEAVE_FUNCTION(DLEVEL_STABLE); } @@ -142,7 +146,7 @@ * @return Returns the current model set into the MVC widget * @brief Retrieves the model set into the MVC widget */ -Ewl_Model * +Ewl_Model * ewl_mvc_model_get(Ewl_MVC *mvc) { DENTER_FUNCTION(DLEVEL_STABLE); @@ -166,10 +170,10 @@ DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); mvc->data = data; - ewl_mvc_dirty_set(mvc, TRUE); /* new data, clear out the old selection list */ ewl_mvc_selected_clear(mvc); + ewl_mvc_dirty_set(mvc, TRUE); DLEAVE_FUNCTION(DLEVEL_STABLE); } @@ -239,6 +243,9 @@ DCHECK_PARAM_PTR("mvc", mvc); DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); + if (mvc->selection_mode == mode) + DRETURN(DLEVEL_STABLE); + mvc->selection_mode = mode; if (mode == EWL_SELECTION_MODE_NONE) { @@ -271,11 +278,27 @@ /** * @param mvc: The mvc to clear * @return Returns no value - * @brief clears the selection list + * @brief clears the selection list */ void ewl_mvc_selected_clear(Ewl_MVC *mvc) { + DENTER_FUNCTION(DLEVEL_STABLE); + DCHECK_PARAM_PTR("mvc", mvc); + DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); + + if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) + DRETURN(DLEVEL_STABLE); + + ewl_mvc_selected_clear_private(mvc); + ewl_mvc_selected_change_notify(mvc); + + DLEAVE_FUNCTION(DLEVEL_STABLE); +} + +static void +ewl_mvc_selected_clear_private(Ewl_MVC *mvc) +{ Ewl_Selection *sel; DENTER_FUNCTION(DLEVEL_STABLE); @@ -283,13 +306,11 @@ DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) - DRETURN(DLEVEL_STABLE); + DRETURN(DLEVEL_STABLE); - while ((sel = ecore_list_first_remove(mvc->selected))) + while ((sel = ecore_list_remove_first(mvc->selected))) ewl_mvc_cb_sel_free(sel); - ewl_mvc_selected_change_notify(mvc); - DLEAVE_FUNCTION(DLEVEL_STABLE); } @@ -304,7 +325,8 @@ ewl_mvc_selected_list_set(Ewl_MVC *mvc, Ecore_List *list) { Ewl_Selection *sel; - + int count = 0; + DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("mvc", mvc); DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); @@ -312,19 +334,22 @@ if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) DRETURN(DLEVEL_STABLE); - while ((sel = ecore_list_first_remove(mvc->selected))) - ewl_mvc_cb_sel_free(sel); + count = ewl_mvc_selected_count_get(mvc); + ewl_mvc_selected_clear_private(mvc); if (!list || (ecore_list_count(list) == 0)) + { + if (count > 0) ewl_mvc_selected_change_notify(mvc); DRETURN(DLEVEL_STABLE); + } - sel = ecore_list_first_remove(list); - ecore_list_append(mvc->selected, sel); + ewl_mvc_selected_insert(mvc, NULL, NULL, + ecore_list_remove_first(list), 0, 0); if (mvc->selection_mode == EWL_SELECTION_MODE_MULTI) { while ((sel = ecore_list_first_remove(list))) - ecore_list_append(mvc->selected, sel); + ewl_mvc_selected_insert(mvc, NULL, NULL, sel, 0, 0); } ewl_mvc_selected_change_notify(mvc); @@ -346,7 +371,7 @@ DCHECK_TYPE_RET("mvc", mvc, EWL_MVC_TYPE, NULL); if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) - DRETURN_PTR(NULL, DLEVEL_STABLE); + DRETURN_PTR(NULL, DLEVEL_STABLE); DRETURN_PTR(mvc->selected, DLEVEL_STABLE); } @@ -354,7 +379,7 @@ /** * @param mvc: The MVC to set the list into * @param model: The model to use for this data. If NULL the model from the - * MVC will be used + * MVC will be used * @param data: The parent data containing the index selection * @param srow: The start row * @param scolumn: The start column @@ -364,7 +389,7 @@ * @brief Sets the given range, inclusive, as selected in the mvc */ void -ewl_mvc_selected_range_add(Ewl_MVC *mvc, Ewl_Model *model, void *data, +ewl_mvc_selected_range_add(Ewl_MVC *mvc, Ewl_Model *model, void *data, unsigned int srow, unsigned int scolumn, unsigned int erow, unsigned int ecolumn) { @@ -377,7 +402,7 @@ DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) - DRETURN(DLEVEL_STABLE); + DRETURN(DLEVEL_STABLE); if (model) mod = model; else mod = ewl_mvc_model_get(mvc); @@ -398,12 +423,14 @@ } if (mvc->selection_mode == EWL_SELECTION_MODE_SINGLE) - sel = ewl_mvc_selection_index_new(mod, data, srow, scolumn); + ewl_mvc_selected_insert(mvc, mod, data, NULL, srow, scolumn); else - sel = ewl_mvc_selection_range_new(mod, data, srow, scolumn, + { + sel = ewl_mvc_selection_range_new(mod, data, srow, scolumn, erow, ecolumn); + ewl_mvc_selected_insert(mvc, NULL, NULL, sel, 0, 0); + } - ecore_list_append(mvc->selected, sel); ewl_mvc_selected_change_notify(mvc); DLEAVE_FUNCTION(DLEVEL_STABLE); @@ -420,21 +447,17 @@ * @brief Sets the given index as selected */ void -ewl_mvc_selected_set(Ewl_MVC *mvc, Ewl_Model *model, void *data, +ewl_mvc_selected_set(Ewl_MVC *mvc, Ewl_Model *model, void *data, unsigned int row, unsigned int column) { - Ewl_Selection *sel; - DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("mvc", mvc); DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) - DRETURN(DLEVEL_STABLE); - - while ((sel = ecore_list_first_remove(mvc->selected))) - ewl_mvc_cb_sel_free(sel); + DRETURN(DLEVEL_STABLE); + ewl_mvc_selected_clear_private(mvc); ewl_mvc_selected_add(mvc, model, data, row, column); DLEAVE_FUNCTION(DLEVEL_STABLE); @@ -451,10 +474,9 @@ * @brief Adds the given index to the selected list */ void -ewl_mvc_selected_add(Ewl_MVC *mvc, Ewl_Model *model, void *data, +ewl_mvc_selected_add(Ewl_MVC *mvc, Ewl_Model *model, void *data, unsigned int row, unsigned int column) { - Ewl_Selection *si; Ewl_Model *mod; DENTER_FUNCTION(DLEVEL_STABLE); @@ -467,8 +489,7 @@ if (model) mod = model; else mod = ewl_mvc_model_get(mvc); - si = ewl_mvc_selection_index_new(mod, data, row, column); - ecore_list_append(mvc->selected, si); + ewl_mvc_selected_insert(mvc, mod, data, NULL, row, column); ewl_mvc_selected_change_notify(mvc); DLEAVE_FUNCTION(DLEVEL_STABLE); @@ -532,7 +553,7 @@ * @brief Removes the given index from the list of selected indices */ void -ewl_mvc_selected_rm(Ewl_MVC *mvc, void *data __UNUSED__, unsigned int row, +ewl_mvc_selected_rm(Ewl_MVC *mvc, void *data __UNUSED__, unsigned int row, unsigned int column) { Ewl_Selection *sel; @@ -542,43 +563,20 @@ DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) - DRETURN(DLEVEL_STABLE); + DRETURN(DLEVEL_STABLE); - /* We walk the entire list. The reason for this is that you can have - * the same cell in the list multiple times. This can happen if - * they've single selected something, then did a multiselection over - * top of it again. */ - ecore_list_first_goto(mvc->selected); - while ((sel = ecore_list_current(mvc->selected))) + if (ewl_mvc_selected_goto(mvc, row, column)) { - if (sel->type == EWL_SELECTION_TYPE_INDEX) - { - Ewl_Selection_Idx *si; + sel = ecore_list_current(mvc->selected); - si = EWL_SELECTION_IDX(sel); - if ((si->row == row) && (si->column == column)) - { - ewl_mvc_selected_rm_item(mvc, sel, row, column); - continue; - } - } + if (sel->type == EWL_SELECTION_TYPE_INDEX) + ecore_list_remove(mvc->selected); else - { - Ewl_Selection_Range *si; + ewl_mvc_selected_range_split(mvc, + EWL_SELECTION_RANGE(sel), row, column); - si = EWL_SELECTION_RANGE(sel); - if ((si->start.row <= row) - && (si->end.row >= row) - && (si->start.column <= column) - && (si->end.column >= column)) - { - ewl_mvc_selected_rm_item(mvc, sel, row, column); - continue; - } - } - ecore_list_next(mvc->selected); + ewl_mvc_selected_change_notify(mvc); } - ewl_mvc_selected_change_notify(mvc); DLEAVE_FUNCTION(DLEVEL_STABLE); } @@ -588,6 +586,8 @@ * @return Returns the number of items selected in the MVC * @brief Retrives the number of items selected in the widget */ +/* XXX We might want to just store this to cut down the time + * to calculate it */ unsigned int ewl_mvc_selected_count_get(Ewl_MVC *mvc) { @@ -599,7 +599,7 @@ DCHECK_TYPE_RET("mvc", mvc, EWL_MVC_TYPE, 0); if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) - DRETURN_INT(0, DLEVEL_STABLE); + DRETURN_INT(0, DLEVEL_STABLE); /* make sure we only return 1 or 0 for the single select case */ if (mvc->selection_mode == EWL_SELECTION_MODE_SINGLE) @@ -623,7 +623,6 @@ r = EWL_SELECTION_RANGE(sel); rows = (r->end.row - r->start.row) + 1; columns = (r->end.column - r->start.column) + 1; - count += (rows * columns); } } @@ -631,6 +630,48 @@ DRETURN_INT(count, DLEVEL_STABLE); } +/* This will look through the list and see if the given row/column is in there. + * If it is there it will return TRUE and leave the list pointing to the matching + * node. Returns FALSE otherwise, list should be at either the end, or the item + * past where this item would be (so the insertion point for a create) + */ +static unsigned int +ewl_mvc_selected_goto(Ewl_MVC *mvc, unsigned int row, unsigned int column) +{ + Ewl_Selection *sel; + + DENTER_FUNCTION(DLEVEL_STABLE); + DCHECK_PARAM_PTR_RET("mvc", mvc, FALSE); + DCHECK_TYPE_RET("mvc", mvc, EWL_MVC_TYPE, FALSE); + + ecore_list_first_goto(mvc->selected); + while ((sel = ecore_list_current(mvc->selected))) + { + if (sel->type == EWL_SELECTION_TYPE_INDEX) + { + Ewl_Selection_Idx *idx; + idx = EWL_SELECTION_IDX(sel); + if ((idx->row == row) && (idx->column == column)) + DRETURN_INT(TRUE, DLEVEL_STABLE); + } + else + { + Ewl_Selection_Range *r; + + r = EWL_SELECTION_RANGE(sel); + + /* see if we match within the range */ + if ((r->start.row <= row) && (r->start.column <= column) + && (r->end.row >= row) + && (r->end.column >= column)) + DRETURN_INT(TRUE, DLEVEL_STABLE); + } + ecore_list_next(mvc->selected); + } + + DRETURN_INT(FALSE, DLEVEL_STABLE); +} + /** * @param mvc: The MVC to work with * @param data: UNUSED @@ -640,107 +681,408 @@ * @brief Checks if the given index is selected or not. */ unsigned int -ewl_mvc_selected_is(Ewl_MVC *mvc, void *data __UNUSED__, unsigned int row, - unsigned int column) +ewl_mvc_selected_is(Ewl_MVC *mvc, void *data __UNUSED__, unsigned int row, + unsigned int column) { - Ewl_Selection *sel; - unsigned int ret = FALSE; - DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR_RET("mvc", mvc, FALSE); DCHECK_TYPE_RET("mvc", mvc, EWL_MVC_TYPE, FALSE); if (mvc->selection_mode == EWL_SELECTION_MODE_NONE) - DRETURN_INT(FALSE, DLEVEL_STABLE); + DRETURN_INT(FALSE, DLEVEL_STABLE); - ecore_list_first_goto(mvc->selected); - while ((sel = ecore_list_next(mvc->selected))) + DRETURN_INT(ewl_mvc_selected_goto(mvc, row, column), DLEVEL_STABLE); +} + +static void +ewl_mvc_selected_insert(Ewl_MVC *mvc, Ewl_Model *model, void *data, + Ewl_Selection *sel, unsigned int row, unsigned int column) +{ + Ewl_Selection_Range *range; + Ewl_Selection *cur; + Ecore_List *intersections; + + DENTER_FUNCTION(DLEVEL_STABLE); + DCHECK_PARAM_PTR("mvc", mvc); + + if (!sel) + sel = EWL_SELECTION(ewl_mvc_selection_index_new(model, + data, row, column)); + + /* if this is an index and the index is already selected + * then we're done. Otherwise, just insert the item into the list + * and be done with it. */ + if (sel->type == EWL_SELECTION_TYPE_INDEX) { - if (sel->type == EWL_SELECTION_TYPE_INDEX) + Ewl_Selection_Idx *idx; + + idx = EWL_SELECTION_IDX(sel); + if (ewl_mvc_selected_goto(mvc, idx->row, idx->column)) + { + FREE(sel); + DRETURN(DLEVEL_STABLE); + } + + ecore_list_append(mvc->selected, sel); + DRETURN(DLEVEL_STABLE); + } + + /* We've got a range we're trying to insert from here onwards */ + + /* we store the intersections away and handle them later as + * we don't want to be fiddling the list while we walk it + * (this process will add more items to the list that we + * don't want to check again for intersections */ + intersections = ecore_list_new(); + ecore_list_free_cb_set(intersections, ECORE_FREE_CB(free)); + + range = EWL_SELECTION_RANGE(sel); + ecore_list_goto_first(mvc->selected); + while ((cur = ecore_list_current(mvc->selected))) + { + if (ewl_mvc_selection_intersects(range, cur)) { - Ewl_Selection_Idx *si; + ecore_list_remove(mvc->selected); + + /* just free indexes as their covered by the + * range and don't need to be re-inserted */ + if (cur->type == EWL_SELECTION_TYPE_INDEX) + { + FREE(cur); + } + else + ecore_list_append(intersections, cur); + + } + ecore_list_next(mvc->selected); + } - si = EWL_SELECTION_IDX(sel); - if ((si->row == row) && (si->column == column)) + /* if we intersect nothing just add ourselves to the list + * and be done with it */ + if (ecore_list_count(intersections) == 0) + ecore_list_insert(mvc->selected, range); + else + { + Ewl_Selection_Range *ptr; + + while ((ptr = ecore_list_remove_first(intersections))) + { + /* if range is contained inside current then + * this can be the only intersection. we add + * current to the list, destroy range and + * are done + * + * We can't do this inside + * ewl_mvc_range_merge() as we free range in + * this case and keep ptr. This is backwards + * to what's expected by _merge() + */ + if (ewl_mvc_selection_contained(ptr, range)) { - ret = TRUE; + ecore_list_append(mvc->selected, ptr); + FREE(range); + range = NULL; break; } + ewl_mvc_range_merge(mvc->selected, model, data, range, ptr); } - else + if (range) ecore_list_append(mvc->selected, range); + } + ecore_list_destroy(intersections); + + DLEAVE_FUNCTION(DLEVEL_STABLE); +} + +/* This will take range and cur and split cur up so there is no overlap + * between the two ranges. The @a range will _not_ be changed by this. We + * will append into the list as needed. @a cur maybe freed by this operation + * if it is no longer needed */ +static void +ewl_mvc_range_merge(Ecore_List *list, Ewl_Model *model, void *data, + Ewl_Selection_Range *range, Ewl_Selection_Range *cur) +{ + Ewl_Selection *sel; + + DENTER_FUNCTION(DLEVEL_STABLE); + DCHECK_PARAM_PTR("list", list); + DCHECK_PARAM_PTR("range", range); + DCHECK_PARAM_PTR("cur", cur); + + /* if the new pointer is totaly in range + * then delete the original one and keep the + * range */ + if (ewl_mvc_selection_contained(range, cur)) + { + FREE(cur); + DRETURN(DLEVEL_STABLE); + } + + /* see if this is a merge of the two along one of the sides */ + if (((range->start.row == cur->start.row) + && (range->end.row == cur->end.row)) + || ((range->start.column == cur->start.column) + && (range->end.column == cur->end.column))) + { + range->start.row = MIN(range->start.row, cur->start.row); + range->start.column = MIN(range->start.column, cur->start.column); + range->end.row = MAX(range->end.row, cur->end.row); + range->end.column = MAX(range->end.column, cur->end.column); + + FREE(cur); + DRETURN(DLEVEL_STABLE); + } + + /* not merged and not overlapped we're going to need to split @a cur + * apart in order for this to mesh together + * + * We're going to split @a cur into, at most, 4 parts + * + * 1 + * - - - - - ------- - - - - + * 2 | R | 4 + * | | + * -------- - - - - + * | 3 + * + * | + */ + + /* find everything above (case 1) */ + if (cur->start.row < range->start.row) + { + sel = ewl_mvc_selection_make(model, data, cur->start.row, + cur->start.column, + range->start.row - 1, + cur->end.column); + ecore_list_append(list, sel); + } + + /* find everything left (case 2) */ + if (cur->start.column < range->start.column) + { + sel = ewl_mvc_selection_make(model, data, + MAX(range->start.row, cur->start.row), + cur->start.column, + cur->end.row, + range->start.column - 1); + ecore_list_append(list, sel); + } + + /* find everything below (case 3) */ + if (cur->end.row > range->end.row) + { + sel = ewl_mvc_selection_make(model, data, range->end.row + 1, + MAX(range->start.column, cur->start.column), + cur->end.row, + cur->end.column); + ecore_list_append(list, sel); + } + + /* find everything left (case 4) */ + if (cur->end.column > range->end.column) + { + sel = ewl_mvc_selection_make(model, data, + MAX(range->start.row, cur->start.row), + range->end.column + 1, + MIN(range->end.row, cur->end.row), + cur->end.column); + ecore_list_append(list, sel); + } + FREE(cur); + + DLEAVE_FUNCTION(DLEVEL_STABLE); +} + +static Ewl_Selection * +ewl_mvc_selection_make(Ewl_Model *model, void *data, int top, int left, + int bottom, int right) +{ + Ewl_Selection *sel; + + DENTER_FUNCTION(DLEVEL_STABLE); + + if ((top != bottom) || (left != right)) + { + sel = EWL_SELECTION(ewl_mvc_selection_range_new(model, + data, top, left, bottom, right)); + } + else + sel = EWL_SELECTION(ewl_mvc_selection_index_new(model, + data, top, left)); + + DRETURN_PTR(sel, DLEVEL_STABLE); +} + +/* This determins if there there is an intersection point between @a range + * and @a sel. Returns TRUE if they intersect, FALSE otherwise. + */ +static int +ewl_mvc_selection_intersects(Ewl_Selection_Range *range, Ewl_Selection *sel) +{ + DENTER_FUNCTION(DLEVEL_STABLE); + DCHECK_PARAM_PTR_RET("range", range, FALSE); + DCHECK_PARAM_PTR_RET("sel", sel, FALSE); + + if (sel->type == EWL_SELECTION_TYPE_INDEX) + { + Ewl_Selection_Idx *idx; + + idx = EWL_SELECTION_IDX(sel); + if ((range->start.row <= idx->row) && (range->end.row >= idx->row) + && (range->start.column <= idx->column) + && (range->end.column >= idx->column)) { - Ewl_Selection_Range *si; + DRETURN_INT(TRUE, DLEVEL_STABLE); + } + } + else + { + Ewl_Selection_Range *cur; + cur = EWL_SELECTION_RANGE(sel); - si = EWL_SELECTION_RANGE(sel); - if ((si->start.row <= row) - && (si->end.row >= row) - && (si->start.column <= column) - && (si->end.column >= column)) + /* is one range completely inside another */ + if ((ewl_mvc_selection_contained(range, cur)) + || (ewl_mvc_selection_contained(cur, range))) + DRETURN_INT(TRUE, DLEVEL_STABLE); + + /* if the columns intersect and the rows intersect then the + * boxes intersect */ + if (ewl_mvc_line_intersects(cur->start.row, cur->end.row, + range->start.row, range->end.row) + || ewl_mvc_line_intersects(range->start.row, range->end.row, + cur->start.row, cur->end.row)) + { + if (ewl_mvc_line_intersects(cur->start.column, cur->end.column, + range->start.column, range->end.column) + || ewl_mvc_line_intersects(range->start.column, + range->end.column, cur->start.column, + cur->end.column)) { - ret = TRUE; - break; + DRETURN_INT(TRUE, DLEVEL_STABLE); } } } - DRETURN_INT(ret, DLEVEL_STABLE); + DRETURN_INT(FALSE, DLEVEL_STABLE); } -/** - * @param model: The model to work with this data - * @param data: The parent data containing the index selection - * @param row: The row to create the index selection for - * @param column: The column to create the index for - * @return Returns a new Ewl_Selection_Idx based on the @a row and @a column - * @brief Creates a new index selection based on given values +/* + * this checks the following: + * astart <= bstart <= aend + * or astart <= bend <= aend + * or bstart <= astart <= bend + * or bstart <= aend <= bend + * + * Returns TRUE if any of the above match, FALSE otherwise */ -Ewl_Selection * -ewl_mvc_selection_index_new(Ewl_Model *model, void *data, unsigned int row, - unsigned int column) +static int +ewl_mvc_line_intersects(int astart, int aend, int bstart, int bend) { - Ewl_Selection_Idx *sel; + DENTER_FUNCTION(DLEVEL_STABLE); + if ((((astart <= bstart) && (bstart <= aend)) + || ((astart <= bend) && (bend <= aend))) + || (((bstart <= astart) && (astart <= bend)) + || ((bstart <= aend) && (aend <= bend)))) + DRETURN_INT(TRUE, DLEVEL_STABLE); + + DRETURN_INT(FALSE, DLEVEL_STABLE); +} + +/* checks if range @a b is contained completely within range @a a. + * Returns TRUE if contained. FALSE otherwise + */ +static int +ewl_mvc_selection_contained(Ewl_Selection_Range *a, Ewl_Selection_Range *b) +{ DENTER_FUNCTION(DLEVEL_STABLE); + DCHECK_PARAM_PTR_RET("a", a, FALSE); + DCHECK_PARAM_PTR_RET("b", b, FALSE); - sel = NEW(Ewl_Selection_Idx, 1); - sel->sel.model = model; - sel->sel.type = EWL_SELECTION_TYPE_INDEX; - sel->sel.data = data; - sel->row = row; - sel->column = column; + if ((a->start.column <= b->start.column) + && (b->start.column <= a->end.column) + && (a->start.column <= b->end.column) + && (b->end.column <= a->end.column) + && (a->start.row <= b->start.row) + && (b->start.row <= a->end.row) + && (a->start.row <= b->end.row) + && (b->end.row <= a->end.row)) + DRETURN_INT(TRUE, DLEVEL_STABLE); - DRETURN_PTR(sel, DLEVEL_STABLE); + DRETURN_INT(FALSE, DLEVEL_STABLE); } -/** - * @param model: The model to work with this data - * @param data: The data that we're working with - * @param srow: The start row - * @param scolumn: The start column - * @param erow: The end row - * @param ecolumn: The end column - * @return Returns a new Ewl_Selection_Range based on given values - * @brief Creates a new range selection based on given values +/* split the range into, at most, 4 ranges. This will be done similar to the + * merge for range intersections. + * + * ----------------------------- + * | 1 | + * | | + * |- - - - - - - - - - - - - | + * | |X- - - - - - | 4 will be a single line + * | 2 | + * | | 3 | + * | | + * ----------------------------- */ -Ewl_Selection * -ewl_mvc_selection_range_new(Ewl_Model *model, void *data, unsigned int srow, - unsigned int scolumn, unsigned int erow, - unsigned int ecolumn) +static void +ewl_mvc_selected_range_split(Ewl_MVC *mvc, Ewl_Selection_Range *range, + unsigned int row, unsigned int column) { - Ewl_Selection_Range *sel; + Ewl_Selection *sel; + Ewl_Model *model; + void *data; DENTER_FUNCTION(DLEVEL_STABLE); + DCHECK_PARAM_PTR("mvc", mvc); + DCHECK_PARAM_PTR("range", range); + DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); - sel = NEW(Ewl_Selection_Range, 1); - sel->sel.model = model; - sel->sel.type = EWL_SELECTION_TYPE_RANGE; - sel->sel.data = data; - sel->start.row = srow; - sel->start.column = scolumn; - sel->end.row = erow; - sel->end.column = ecolumn; + /* make life easier by removing the range */ + ecore_list_remove(mvc->selected); + model = EWL_SELECTION(range)->model; + data = EWL_SELECTION(range)->data; - DRETURN_PTR(sel, DLEVEL_STABLE); + /* we have something above, case 1 */ + if (range->start.row < row) + { + sel = ewl_mvc_selection_make(model, data, range->start.row, + range->start.column, + row - 1, + range->end.column); + ecore_list_append(mvc->selected, sel); + } + + /* something left, case 2 */ + if (range->start.column < column) + { + sel = ewl_mvc_selection_make(model, data, row, + range->start.column, + range->end.row, + column - 1); + ecore_list_append(mvc->selected, sel); + } + + /* something below, case 3 */ + if (range->end.row > row) + { + sel = ewl_mvc_selection_make(model, data, row + 1, column, + range->end.row, + range->end.column); + ecore_list_append(mvc->selected, sel); + } + + /* something right, case 4 */ + if (range->end.column > row) + { + sel = ewl_mvc_selection_make(model, data, row, column + 1, + row, range->end.column); + ecore_list_append(mvc->selected, sel); + } + + FREE(range); + + DLEAVE_FUNCTION(DLEVEL_STABLE); } /** @@ -754,7 +1096,7 @@ * @brief Handles the click of the given cell */ void -ewl_mvc_handle_click(Ewl_MVC *mvc, Ewl_Model *model, void *data, +ewl_mvc_handle_click(Ewl_MVC *mvc, Ewl_Model *model, void *data, unsigned int row, unsigned int column) { unsigned int modifiers; @@ -765,8 +1107,16 @@ DCHECK_PARAM_PTR("mvc", mvc); DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); - if (ewl_mvc_selection_mode_get(mvc) == EWL_SELECTION_MODE_MULTI) - multi_select = TRUE; + switch (ewl_mvc_selection_mode_get(mvc)) + { + case EWL_SELECTION_MODE_NONE: + DRETURN(DLEVEL_STABLE); + case EWL_SELECTION_MODE_MULTI: + multi_select = TRUE; + break; + default: + break; + } if (model) mod = model; else mod = ewl_mvc_model_get(mvc); @@ -782,9 +1132,9 @@ unsigned int srow, scolumn; Ewl_Model *smod; - /* A shift will add the current position into a - * range with the last selected item. If the - * last selected is a range, it will take the + /* A shift will add the current position into a + * range with the last selected item. If the + * last selected is a range, it will take the * start position */ sel = ecore_list_last_goto(mvc->selected); if (sel->type == EWL_SELECTION_TYPE_INDEX) @@ -796,17 +1146,10 @@ srow = idx->row; scolumn = idx->column; smod = EWL_SELECTION(idx)->model; - - if (sel->highlight) - { - ewl_widget_destroy(sel->highlight); - sel->highlight = NULL; - } } else { Ewl_Selection_Range *idx; - unsigned int i, k; idx = EWL_SELECTION_RANGE(sel); sdata = sel->data; @@ -814,34 +1157,6 @@ scolumn = idx->start.column; smod = EWL_SELECTION(idx)->model; - if (sel->highlight) - { - Ewl_Widget *w; - - while ((w = ecore_list_first_remove( - sel->highlight))) - ewl_widget_destroy(w); - } - - /* XXX this is not good. We probably want to - * find a better way to determine duplicates - * then what this is doing. - * - * determine if any of the range's widgets - * are in the list already. if so we need - * to remove them from the range. - * Do a selected_rm on all the duplicate - * points until we have no duplicates */ - for (i = srow; i <= row; i++) - { - for (k = scolumn; k <= column; k++) - { - if (ewl_mvc_selected_is(mvc, data, i, k)) - ewl_mvc_selected_rm(mvc, - data, i, k); - } - } - } ecore_list_remove(mvc->selected); @@ -875,7 +1190,7 @@ */ void ewl_mvc_highlight(Ewl_MVC *mvc, Ewl_Container *c, - Ewl_Widget *(*widget)(Ewl_MVC *mvc, void *data, unsigned int row, + Ewl_Widget *(*widget)(Ewl_MVC *mvc, void *data, unsigned int row, unsigned int column)) { Ewl_Selection *sel; @@ -885,7 +1200,7 @@ DCHECK_PARAM_PTR("widget", widget); DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); - if (!mvc->selected || !REALIZED(mvc)) + if (!mvc->selected || !REALIZED(mvc)) DRETURN(DLEVEL_STABLE); ecore_list_first_goto(mvc->selected); @@ -912,7 +1227,7 @@ idx = EWL_SELECTION_RANGE(sel); for (i = idx->start.row; i <= idx->end.row; i++) { - for (k = idx->start.column; + for (k = idx->start.column; k <= idx->end.column; k++) { w = widget(mvc, sel->data, i, k); @@ -926,6 +1241,39 @@ DLEAVE_FUNCTION(DLEVEL_STABLE); } +static void +ewl_mvc_highlight_do(Ewl_MVC *mvc __UNUSED__, Ewl_Container *c, + Ewl_Selection *sel, Ewl_Widget *w) +{ + Ewl_Widget *h; + + DENTER_FUNCTION(DLEVEL_STABLE); + DCHECK_PARAM_PTR("c", c); + DCHECK_PARAM_PTR("sel", sel); + DCHECK_PARAM_PTR("w", w); + DCHECK_TYPE("c", c, EWL_CONTAINER_TYPE); + DCHECK_TYPE("w", w, EWL_WIDGET_TYPE); + + h = ewl_highlight_new(); + ewl_highlight_follow_set(EWL_HIGHLIGHT(h), w); + ewl_container_child_append(EWL_CONTAINER(c), h); + ewl_callback_prepend(h, EWL_CALLBACK_DESTROY, + ewl_mvc_cb_highlight_destroy, sel); + ewl_widget_show(h); + + if (sel->type == EWL_SELECTION_TYPE_INDEX) + sel->highlight = h; + else + { + if (!sel->highlight) + sel->highlight = ecore_list_new(); + + ecore_list_append(sel->highlight, h); + } + + DLEAVE_FUNCTION(DLEVEL_STABLE); +} + /** * @internal * @param mvc: The MVC to set the callback into @@ -986,186 +1334,6 @@ DLEAVE_FUNCTION(DLEVEL_STABLE); } -/* This will remove @a sel from the mvc widget @a mvc. Then, if @a sel is a - * range selection it will remove @a row/@a column from the given selection - * and add up to 4 new ranges into the @a mvc widget. - * - * The range remove works like this: - * - first allocate as much space off the top to the first range. - * - this will go from the top left corner, to the selected (row - 1) - * and far right edge. - * - second, from the deletion row, far left to bottom row and (deletion column - 1) - * make this the second range - * - third, from the deletoin row, (deletion column + 1) to the bottom - * right corner this is the third range - * - fourth, from deletion row + 1, deletion column to bottom row, - * deletion column, this is the fourth range - * - * If a range would be only one item, we make it an Ewl_Selection_Index as - * needed. Steps can be skipped if they would result in a zero item range. - */ -static void -ewl_mvc_selected_rm_item(Ewl_MVC *mvc, Ewl_Selection *sel, unsigned int row, - unsigned int column) -{ - Ewl_Selection_Range *si; - - DENTER_FUNCTION(DLEVEL_STABLE); - DCHECK_PARAM_PTR("mvc", mvc); - DCHECK_PARAM_PTR("sel", sel); - DCHECK_TYPE("mvc", mvc, EWL_MVC_TYPE); - - ecore_list_goto(mvc->selected, sel); - ecore_list_remove(mvc->selected); - - /* done if this is an index */ - if (sel->type != EWL_SELECTION_TYPE_RANGE) - { - if (sel->highlight) - { - ewl_widget_destroy(sel->highlight); - sel->highlight = NULL; - } - - DRETURN(DLEVEL_STABLE); - } - - /* Clear out the highlights. - * - * XXX Might want to make this smarter and move the highlight widgets - * into the correct range arrays as needed - */ - if (sel->highlight) - { - Ewl_Widget *w; - - while ((w = ecore_list_first_remove(sel->highlight))) - ewl_widget_destroy(w); - } - - si = EWL_SELECTION_RANGE(sel); - - /* find top cells */ - if (row > si->start.row) - { - Ewl_Selection *n; - unsigned int erow; - - erow = (row - 1); - - /* one item left in the grouping */ - if ((((si->start.row - erow) + 1) * - ((si->start.column - si->end.column) + 1)) == 1) - n = ewl_mvc_selection_index_new(sel->model, sel->data, - si->start.row, - si->start.column); - - else - n = ewl_mvc_selection_range_new(sel->model, sel->data, - si->start.row, - si->start.column, - erow, si->end.column); - - ecore_list_append(mvc->selected, n); - } - - /* find left cells */ - if (column > si->start.column) - { - Ewl_Selection *n; - unsigned int ecolumn; - - ecolumn = (column - 1); - if ((((si->end.row - row) + 1) * - ((si->start.column - ecolumn) + 1)) == 1) - n = ewl_mvc_selection_index_new(sel->model, sel->data, - row, si->start.column); - - else - n = ewl_mvc_selection_range_new(sel->model, sel->data, - row, si->start.column, - si->end.row, ecolumn); - - ecore_list_append(mvc->selected, n); - } - - /* find right cells */ - if (column < si->end.column) - { - Ewl_Selection *n; - unsigned int scolumn; - - scolumn = column + 1; - if ((((si->end.row - row) + 1) * - ((scolumn - si->end.column) + 1)) == 1) - n = ewl_mvc_selection_index_new(sel->model, sel->data, - row, si->end.column); - - else - n = ewl_mvc_selection_range_new(sel->model, sel->data, - row, scolumn, - si->end.row, - si->end.column); - - ecore_list_append(mvc->selected, n); - } - - /* find bottom cells */ - if (row < si->end.row) - { - Ewl_Selection *n; - unsigned int srow; - - srow = row + 1; - if ((((srow - si->end.row) + 1) * - ((column - column) + 1)) == 1) - n = ewl_mvc_selection_index_new(sel->model, sel->data, - si->end.row, column); - - else - n = ewl_mvc_selection_range_new(sel->model, sel->data, - srow, column, - si->end.row, column); - - ecore_list_append(mvc->selected, n); - } - - DLEAVE_FUNCTION(DLEVEL_STABLE); -} - -static void -ewl_mvc_highlight_do(Ewl_MVC *mvc __UNUSED__, Ewl_Container *c, - Ewl_Selection *sel, Ewl_Widget *w) -{ - Ewl_Widget *h; - - DENTER_FUNCTION(DLEVEL_STABLE); - DCHECK_PARAM_PTR("c", c); - DCHECK_PARAM_PTR("sel", sel); - DCHECK_PARAM_PTR("w", w); - DCHECK_TYPE("c", c, EWL_CONTAINER_TYPE); - DCHECK_TYPE("w", w, EWL_WIDGET_TYPE); - - h = ewl_highlight_new(); - ewl_highlight_follow_set(EWL_HIGHLIGHT(h), w); - ewl_container_child_append(EWL_CONTAINER(c), h); - ewl_callback_prepend(h, EWL_CALLBACK_DESTROY, - ewl_mvc_cb_highlight_destroy, sel); - ewl_widget_show(h); - - if (sel->type == EWL_SELECTION_TYPE_INDEX) - sel->highlight = h; - else - { - if (!sel->highlight) - sel->highlight = ecore_list_new(); - - ecore_list_append(sel->highlight, h); - } - - DLEAVE_FUNCTION(DLEVEL_STABLE); -} - static void ewl_mvc_cb_sel_free(void *data) { @@ -1205,7 +1373,6 @@ sel = data; if (sel->type == EWL_SELECTION_TYPE_INDEX) sel->highlight = NULL; - else { Ewl_Widget *cur; @@ -1214,9 +1381,64 @@ cur = ecore_list_current(sel->highlight); if (cur == w) ecore_list_remove(sel->highlight); } - + DLEAVE_FUNCTION(DLEVEL_STABLE); } +/** + * @param model: The model to work with this data + * @param data: The parent data containing the index selection + * @param row: The row to create the index selection for + * @param column: The column to create the index for + * @return Returns a new Ewl_Selection_Idx based on the @a row and @a column + * @brief Creates a new index selection based on given values + */ +Ewl_Selection * +ewl_mvc_selection_index_new(Ewl_Model *model, void *data, unsigned int row, + unsigned int column) +{ + Ewl_Selection_Idx *sel; + + DENTER_FUNCTION(DLEVEL_STABLE); + + sel = NEW(Ewl_Selection_Idx, 1); + sel->sel.model = model; + sel->sel.type = EWL_SELECTION_TYPE_INDEX; + sel->sel.data = data; + sel->row = row; + sel->column = column; + + DRETURN_PTR(sel, DLEVEL_STABLE); +} + +/** + * @param model: The model to work with this data + * @param data: The data that we're working with + * @param srow: The start row + * @param scolumn: The start column + * @param erow: The end row + * @param ecolumn: The end column + * @return Returns a new Ewl_Selection_Range based on given values + * @brief Creates a new range selection based on given values + */ +Ewl_Selection * +ewl_mvc_selection_range_new(Ewl_Model *model, void *data, unsigned int srow, + unsigned int scolumn, unsigned int erow, + unsigned int ecolumn) +{ + Ewl_Selection_Range *sel; + DENTER_FUNCTION(DLEVEL_STABLE); + + sel = NEW(Ewl_Selection_Range, 1); + sel->sel.model = model; + sel->sel.type = EWL_SELECTION_TYPE_RANGE; + sel->sel.data = data; + sel->start.row = srow; + sel->start.column = scolumn; + sel->end.row = erow; + sel->end.column = ecolumn; + + DRETURN_PTR(sel, DLEVEL_STABLE); +} ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ _______________________________________________ enlightenment-cvs mailing list enlightenment-cvs@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/enlightenment-cvs