Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Fri, 6 Jul 2018 12:41:14 +0000 (14:41 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 6 Jul 2018 12:41:52 +0000 (14:41 +0200)
1  2 
source/blender/editors/space_outliner/outliner_edit.c
source/blender/editors/space_outliner/outliner_intern.h
source/blender/editors/space_outliner/outliner_ops.c
source/blender/editors/space_outliner/outliner_select.c
source/blender/editors/space_outliner/outliner_tools.c

index 1c78931b44984704fbe63328b6037a284488ff6f,3708b653ec4209bed0671fb8fb005df3a2cef9c4..d8a5ba533c40d0d6b4f721a3027168ef7450f83c
@@@ -150,45 -147,8 +150,45 @@@ TreeElement *outliner_dropzone_find(con
        return NULL;
  }
  
 +
  /* ************************************************************** */
 -/* Click Activated */
 +
 +/* Highlight --------------------------------------------------- */
 +
 +static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
 +{
 +      ARegion *ar = CTX_wm_region(C);
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]);
 +
 +      TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, my);
 +      bool changed = false;
 +
 +      if (!hovered_te || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED)) {
-               changed = outliner_set_flag(&soops->tree, TSE_HIGHLIGHTED, false);
++              changed = outliner_flag_set(&soops->tree, TSE_HIGHLIGHTED, false);
 +              if (hovered_te) {
 +                      hovered_te->store_elem->flag |= TSE_HIGHLIGHTED;
 +                      changed = true;
 +              }
 +      }
 +
 +      if (changed) {
 +              ED_region_tag_redraw_no_rebuild(ar);
 +      }
 +
 +      return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
 +}
 +
 +void OUTLINER_OT_highlight_update(wmOperatorType *ot)
 +{
 +      ot->name = "Update Highlight";
 +      ot->idname = "OUTLINER_OT_highlight_update";
 +      ot->description = "Update the item highlight based on the current mouse position";
 +
 +      ot->invoke = outliner_highlight_update;
 +
 +      ot->poll = ED_operator_outliner_active;
 +}
  
  /* Toggle Open/Closed ------------------------------------------- */
  
@@@ -850,11 -743,7 +850,11 @@@ int outliner_flag_is_any_test(ListBase 
        return 0;
  }
  
 -void outliner_flag_set(ListBase *lb, short flag, short set)
 +/**
 + * Set or unset \a flag for all outliner elements in \a lb and sub-trees.
 + * \return if any flag was modified.
 + */
- bool outliner_set_flag(ListBase *lb, short flag, short set)
++bool outliner_flag_set(ListBase *lb, short flag, short set)
  {
        TreeElement *te;
        TreeStoreElem *tselem;
  
        for (te = lb->first; te; te = te->next) {
                tselem = TREESTORE(te);
 -              if (set == 0) tselem->flag &= ~flag;
 -              else tselem->flag |= flag;
 -              outliner_flag_set(&te->subtree, flag, set);
 +              has_flag = (tselem->flag & flag);
 +              if (set == 0) {
 +                      if (has_flag) {
 +                              tselem->flag &= ~flag;
 +                              changed = true;
 +                      }
 +              }
 +              else if (!has_flag) {
 +                      tselem->flag |= flag;
 +                      changed = true;
 +              }
-               changed |= outliner_set_flag(&te->subtree, flag, set);
++              changed |= outliner_flag_set(&te->subtree, flag, set);
        }
 +
 +      return changed;
  }
  
  /* Restriction Columns ------------------------------- */
@@@ -946,14 -997,15 +946,14 @@@ static int outliner_toggle_selected_exe
        ARegion *ar = CTX_wm_region(C);
        Scene *scene = CTX_data_scene(C);
  
-       if (outliner_has_one_flag(&soops->tree, TSE_SELECTED, 1))
-               outliner_set_flag(&soops->tree, TSE_SELECTED, 0);
+       if (outliner_flag_is_any_test(&soops->tree, TSE_SELECTED, 1))
+               outliner_flag_set(&soops->tree, TSE_SELECTED, 0);
        else
-               outliner_set_flag(&soops->tree, TSE_SELECTED, 1);
+               outliner_flag_set(&soops->tree, TSE_SELECTED, 1);
  
 -      soops->storeflag |= SO_TREESTORE_REDRAW;
 -
 +      DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE);
        WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 -      ED_region_tag_redraw(ar);
 +      ED_region_tag_redraw_no_rebuild(ar);
  
        return OPERATOR_FINISHED;
  }
index 0ab222088410df94ebbb5f45fff1da2fd9bb3415,49647a0d838cad6223d9e879c2a259ebdb0631bf..0763933b1027471091ab7edc05b42b557b988637
@@@ -245,8 -172,8 +245,8 @@@ void outliner_do_object_operation
  
  int common_restrict_check(struct bContext *C, struct Object *ob);
  
- int outliner_has_one_flag(ListBase *lb, short flag, const int curlevel);
- bool outliner_set_flag(ListBase *lb, short flag, short set);
+ int outliner_flag_is_any_test(ListBase *lb, short flag, const int curlevel);
 -void outliner_flag_set(ListBase *lb, short flag, short set);
++bool outliner_flag_set(ListBase *lb, short flag, short set);
  
  void object_toggle_visibility_cb(
          struct bContext *C, struct ReportList *reports, struct Scene *scene,
index b061e8537c2eea60f6cbf3691dfdbbbfeee45e46,9574e82505fc7d3ba3962f42d2f09339ec92afeb..760e28530381543bfb9e2d44de65c42decc6e642
  #include "WM_api.h"
  #include "WM_types.h"
  
 +#include "ED_screen.h"
 +
  #include "outliner_intern.h"
  
-       outliner_set_flag(&soops->tree, TSE_HIGHLIGHTED, false);
 +typedef struct OutlinerDragDropTooltip {
 +      TreeElement *te;
 +      void *handle;
 +} OutlinerDragDropTooltip;
 +
 +enum {
 +      OUTLINER_ITEM_DRAG_CANCEL,
 +      OUTLINER_ITEM_DRAG_CONFIRM,
 +};
 +
 +static bool outliner_item_drag_drop_poll(bContext *C)
 +{
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      return ED_operator_outliner_active(C) &&
 +             /* Only collection display modes supported for now. Others need more design work */
 +             ELEM(soops->outlinevis, SO_VIEW_LAYER, SO_LIBRARIES);
 +}
 +
 +static TreeElement *outliner_item_drag_element_find(SpaceOops *soops, ARegion *ar, const wmEvent *event)
 +{
 +      /* note: using EVT_TWEAK_ events to trigger dragging is fine,
 +       * it sends coordinates from where dragging was started */
 +      const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]);
 +      return outliner_find_item_at_y(soops, &soops->tree, my);
 +}
 +
 +static void outliner_item_drag_end(wmWindow *win, OutlinerDragDropTooltip *data)
 +{
 +      MEM_SAFE_FREE(data->te->drag_data);
 +
 +      if (data->handle) {
 +              WM_draw_cb_exit(win, data->handle);
 +      }
 +
 +      MEM_SAFE_FREE(data);
 +}
 +
 +static void outliner_item_drag_get_insert_data(
 +        const SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged,
 +        TreeElement **r_te_insert_handle, TreeElementInsertType *r_insert_type)
 +{
 +      TreeElement *te_hovered;
 +      float view_mval[2];
 +
 +      UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
 +      te_hovered = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
 +
 +      if (te_hovered) {
 +              /* mouse hovers an element (ignoring x-axis), now find out how to insert the dragged item exactly */
 +
 +              if (te_hovered == te_dragged) {
 +                      *r_te_insert_handle = te_dragged;
 +              }
 +              else if (te_hovered != te_dragged) {
 +                      const float margin = UI_UNIT_Y * (1.0f / 4);
 +
 +                      *r_te_insert_handle = te_hovered;
 +                      if (view_mval[1] < (te_hovered->ys + margin)) {
 +                              if (TSELEM_OPEN(TREESTORE(te_hovered), soops)) {
 +                                      /* inserting after a open item means we insert into it, but as first child */
 +                                      if (BLI_listbase_is_empty(&te_hovered->subtree)) {
 +                                              *r_insert_type = TE_INSERT_INTO;
 +                                      }
 +                                      else {
 +                                              *r_insert_type = TE_INSERT_BEFORE;
 +                                              *r_te_insert_handle = te_hovered->subtree.first;
 +                                      }
 +                              }
 +                              else {
 +                                      *r_insert_type = TE_INSERT_AFTER;
 +                              }
 +                      }
 +                      else if (view_mval[1] > (te_hovered->ys + (3 * margin))) {
 +                              *r_insert_type = TE_INSERT_BEFORE;
 +                      }
 +                      else {
 +                              *r_insert_type = TE_INSERT_INTO;
 +                      }
 +              }
 +      }
 +      else {
 +              /* mouse doesn't hover any item (ignoring x-axis), so it's either above list bounds or below. */
 +
 +              TreeElement *first = soops->tree.first;
 +              TreeElement *last = soops->tree.last;
 +
 +              if (view_mval[1] < last->ys) {
 +                      *r_te_insert_handle = last;
 +                      *r_insert_type = TE_INSERT_AFTER;
 +              }
 +              else if (view_mval[1] > (first->ys + UI_UNIT_Y)) {
 +                      *r_te_insert_handle = first;
 +                      *r_insert_type = TE_INSERT_BEFORE;
 +              }
 +              else {
 +                      BLI_assert(0);
 +              }
 +      }
 +}
 +
 +static void outliner_item_drag_handle(
 +        SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged)
 +{
 +      TreeElement *te_insert_handle;
 +      TreeElementInsertType insert_type;
 +
 +      outliner_item_drag_get_insert_data(soops, ar, event, te_dragged, &te_insert_handle, &insert_type);
 +
 +      if (!te_dragged->reinsert_poll &&
 +          /* there is no reinsert_poll, so we do some generic checks (same types and reinsert callback is available) */
 +          (TREESTORE(te_dragged)->type == TREESTORE(te_insert_handle)->type) &&
 +          te_dragged->reinsert)
 +      {
 +              /* pass */
 +      }
 +      else if (te_dragged == te_insert_handle) {
 +              /* nothing will happen anyway, no need to do poll check */
 +      }
 +      else if (!te_dragged->reinsert_poll ||
 +               !te_dragged->reinsert_poll(te_dragged, &te_insert_handle, &insert_type))
 +      {
 +              te_insert_handle = NULL;
 +      }
 +      te_dragged->drag_data->insert_type = insert_type;
 +      te_dragged->drag_data->insert_handle = te_insert_handle;
 +}
 +
 +/**
 + * Returns true if it is a collection and empty.
 + */
 +static bool is_empty_collection(TreeElement *te)
 +{
 +      Collection *collection = outliner_collection_from_tree_element(te);
 +
 +      if (!collection) {
 +              return false;
 +      }
 +
 +      return BLI_listbase_is_empty(&collection->gobject) &&
 +             BLI_listbase_is_empty(&collection->children);
 +}
 +
 +static bool outliner_item_drag_drop_apply(
 +        Main *bmain,
 +        Scene *scene,
 +        SpaceOops *soops,
 +        OutlinerDragDropTooltip *data,
 +        const wmEvent *event)
 +{
 +      TreeElement *dragged_te = data->te;
 +      TreeElement *insert_handle = dragged_te->drag_data->insert_handle;
 +      TreeElementInsertType insert_type = dragged_te->drag_data->insert_type;
 +
 +      if ((insert_handle == dragged_te) || !insert_handle) {
 +              /* No need to do anything */
 +      }
 +      else if (dragged_te->reinsert) {
 +              BLI_assert(!dragged_te->reinsert_poll || dragged_te->reinsert_poll(dragged_te, &insert_handle,
 +                                                                                 &insert_type));
 +              /* call of assert above should not have changed insert_handle and insert_type at this point */
 +              BLI_assert(dragged_te->drag_data->insert_handle == insert_handle &&
 +                         dragged_te->drag_data->insert_type == insert_type);
 +
 +              /* If the collection was just created and you moved objects/collections inside it,
 +               * it is strange to have it closed and we not see the newly dragged elements. */
 +              const bool should_open_collection = (insert_type == TE_INSERT_INTO) && is_empty_collection(insert_handle);
 +
 +              dragged_te->reinsert(bmain, scene, soops, dragged_te, insert_handle, insert_type, event);
 +
 +              if (should_open_collection && !is_empty_collection(insert_handle)) {
 +                      TREESTORE(insert_handle)->flag &= ~TSE_CLOSED;
 +              }
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEvent *event)
 +{
 +      Main *bmain = CTX_data_main(C);
 +      Scene *scene = CTX_data_scene(C);
 +      ARegion *ar = CTX_wm_region(C);
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      OutlinerDragDropTooltip *data = op->customdata;
 +      TreeElement *te_dragged = data->te;
 +      int retval = OPERATOR_RUNNING_MODAL;
 +      bool redraw = false;
 +      bool skip_rebuild = true;
 +
 +      switch (event->type) {
 +              case EVT_MODAL_MAP:
 +                      if (event->val == OUTLINER_ITEM_DRAG_CONFIRM) {
 +                              if (outliner_item_drag_drop_apply(bmain, scene, soops, data, event)) {
 +                                      skip_rebuild = false;
 +                              }
 +                              retval = OPERATOR_FINISHED;
 +                      }
 +                      else if (event->val == OUTLINER_ITEM_DRAG_CANCEL) {
 +                              retval = OPERATOR_CANCELLED;
 +                      }
 +                      else {
 +                              BLI_assert(0);
 +                      }
 +                      WM_event_add_mousemove(C); /* update highlight */
 +                      outliner_item_drag_end(CTX_wm_window(C), data);
 +                      redraw = true;
 +                      break;
 +              case MOUSEMOVE:
 +                      outliner_item_drag_handle(soops, ar, event, te_dragged);
 +                      redraw = true;
 +                      break;
 +      }
 +
 +      if (redraw) {
 +              if (skip_rebuild) {
 +                      ED_region_tag_redraw_no_rebuild(ar);
 +              }
 +              else {
 +                      ED_region_tag_redraw(ar);
 +              }
 +      }
 +
 +      return retval;
 +}
 +
 +static const char *outliner_drag_drop_tooltip_get(
 +        const TreeElement *te_float)
 +{
 +      const char *name = NULL;
 +
 +      const TreeElement *te_insert = te_float->drag_data->insert_handle;
 +      if (te_float && outliner_is_collection_tree_element(te_float)) {
 +              if (te_insert == NULL) {
 +                      name = TIP_("Move collection");
 +              }
 +              else {
 +                      switch (te_float->drag_data->insert_type) {
 +                              case TE_INSERT_BEFORE:
 +                                      if (te_insert->prev && outliner_is_collection_tree_element(te_insert->prev)) {
 +                                              name = TIP_("Move between collections");
 +                                      }
 +                                      else {
 +                                              name = TIP_("Move before collection");
 +                                      }
 +                                      break;
 +                              case TE_INSERT_AFTER:
 +                                      if (te_insert->next && outliner_is_collection_tree_element(te_insert->next)) {
 +                                              name = TIP_("Move between collections");
 +                                      }
 +                                      else {
 +                                              name = TIP_("Move after collection");
 +                                      }
 +                                      break;
 +                              case TE_INSERT_INTO:
 +                                      name = TIP_("Move inside collection");
 +                                      break;
 +                      }
 +              }
 +      }
 +      else if ((TREESTORE(te_float)->type == 0) && (te_float->idcode == ID_OB)) {
 +              name = TIP_("Move to collection (Ctrl to link)");
 +      }
 +
 +      return name;
 +}
 +
 +static void outliner_drag_drop_tooltip_cb(const wmWindow *win, void *vdata)
 +{
 +      OutlinerDragDropTooltip *data = vdata;
 +      const char *tooltip;
 +
 +      int cursorx, cursory;
 +      int x, y;
 +
 +      tooltip = outliner_drag_drop_tooltip_get(data->te);
 +      if (tooltip == NULL) {
 +              return;
 +      }
 +
 +      cursorx = win->eventstate->x;
 +      cursory = win->eventstate->y;
 +
 +      x = cursorx + U.widget_unit;
 +      y = cursory - U.widget_unit;
 +
 +      /* Drawing. */
 +      const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
 +
 +      const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f};
 +      const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f};
 +
 +      GPU_blend(true);
 +      UI_fontstyle_draw_simple_backdrop(fstyle, x, y, tooltip, col_fg, col_bg);
 +      GPU_blend(false);
 +}
 +
 +static int outliner_item_drag_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 +{
 +      ARegion *ar = CTX_wm_region(C);
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      TreeElement *te_dragged = outliner_item_drag_element_find(soops, ar, event);
 +
 +      if (!te_dragged) {
 +              return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
 +      }
 +
 +      OutlinerDragDropTooltip *data = MEM_mallocN(sizeof(OutlinerDragDropTooltip), __func__);
 +      data->te = te_dragged;
 +
 +      op->customdata = data;
 +      te_dragged->drag_data = MEM_callocN(sizeof(*te_dragged->drag_data), __func__);
 +      /* by default we don't change the item position */
 +      te_dragged->drag_data->insert_handle = te_dragged;
 +      /* unset highlighted tree element, dragged one will be highlighted instead */
++      outliner_flag_set(&soops->tree, TSE_HIGHLIGHTED, false);
 +
 +      ED_region_tag_redraw_no_rebuild(ar);
 +
 +      WM_event_add_modal_handler(C, op);
 +
 +      data->handle = WM_draw_cb_activate(CTX_wm_window(C), outliner_drag_drop_tooltip_cb, data);
 +
 +      return OPERATOR_RUNNING_MODAL;
 +}
 +
 +/**
 + * Notes about Outliner Item Drag 'n Drop:
 + * Right now only collections display mode is supported. But ideally all/most modes would support this. There are
 + * just some open design questions that have to be answered: do we want to allow mixing order of different data types
 + * (like render-layers and objects)? Would that be a purely visual change or would that have any other effect? ...
 + */
 +static void OUTLINER_OT_item_drag_drop(wmOperatorType *ot)
 +{
 +      ot->name = "Drag and Drop";
 +      ot->idname = "OUTLINER_OT_item_drag_drop";
 +      ot->description = "Change the hierarchical position of an item by repositioning it using drag and drop";
 +
 +      ot->invoke = outliner_item_drag_drop_invoke;
 +      ot->modal = outliner_item_drag_drop_modal;
 +
 +      ot->poll = outliner_item_drag_drop_poll;
 +
 +      ot->flag = OPTYPE_UNDO;
 +}
 +
  
  /* ************************** registration **********************************/
  
index bc514914b8152b5fb1b19b140864a708ba694365,e40ba8670496c6445149594d23aa744e86dcc866..7ab13f369538b5940f9211395c5d3706ddcc291d
@@@ -1019,44 -968,52 +1019,44 @@@ static void do_outliner_item_activate_t
  }
  
  /**
 - * Activates tree items, also handles clicking on arrows.
 + * \param extend: Don't deselect other items, only modify \a te.
 + * \param toggle: Select \a te when not selected, deselect when selected.
   */
 -static bool do_outliner_item_activate_from_cursor(
 -        bContext *C, Scene *scene, ARegion *ar, SpaceOops *soops,
 -        TreeElement *te, bool extend, bool recursive, const float mval[2])
 +void outliner_item_select(SpaceOops *soops, const TreeElement *te, const bool extend, const bool toggle)
  {
 -      if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
 -              TreeStoreElem *tselem = TREESTORE(te);
 -              bool openclose = false;
 -
 -              /* open close icon */
 -              if ((te->flag & TE_ICONROW) == 0) {               // hidden icon, no open/close
 -                      if (mval[0] > te->xs && mval[0] < te->xs + UI_UNIT_X)
 -                              openclose = true;
 -              }
 +      TreeStoreElem *tselem = TREESTORE(te);
 +      const short new_flag = toggle ? (tselem->flag ^ TSE_SELECTED) : (tselem->flag | TSE_SELECTED);
  
 -              if (openclose) {
 -                      /* all below close/open? */
 -                      if (extend) {
 -                              tselem->flag &= ~TSE_CLOSED;
 -                              outliner_flag_set(&te->subtree, TSE_CLOSED, !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1));
 -                      }
 -                      else {
 -                              if (tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
 -                              else tselem->flag |= TSE_CLOSED;
 +      if (extend == false) {
-               outliner_set_flag(&soops->tree, TSE_SELECTED, false);
++              outliner_flag_set(&soops->tree, TSE_SELECTED, false);
 +      }
 +      tselem->flag = new_flag;
 +}
  
 -                      }
 +static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_children)
 +{
 +      TreeStoreElem *tselem = TREESTORE(te);
 +      if (toggle_children) {
 +              tselem->flag &= ~TSE_CLOSED;
  
-               const bool all_opened = !outliner_has_one_flag(&te->subtree, TSE_CLOSED, 1);
-               outliner_set_flag(&te->subtree, TSE_CLOSED, all_opened);
 -                      return true;
 -              }
 -              /* name and first icon */
 -              else if (mval[0] > te->xs + UI_UNIT_X && mval[0] < te->xend) {
 -                      do_outliner_item_activate_tree_element(
 -                              C, scene, soops,
 -                              te, tselem,
 -                              extend, recursive);
 -                      return true;
 -              }
++              const bool all_opened = !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1);
++              outliner_flag_set(&te->subtree, TSE_CLOSED, all_opened);
        }
 -
 -      for (te = te->subtree.first; te; te = te->next) {
 -              if (do_outliner_item_activate_from_cursor(C, scene, ar, soops, te, extend, recursive, mval)) {
 -                      return true;
 -              }
 +      else {
 +              tselem->flag ^= TSE_CLOSED;
        }
 -      return false;
 +}
 +
 +static bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x)
 +{
 +      return ((te->flag & TE_ICONROW) == 0) && (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X);
 +}
 +
 +static bool outliner_is_co_within_restrict_columns(const SpaceOops *soops, const ARegion *ar, float view_co_x)
 +{
 +      return ((soops->outlinevis != SO_DATA_API) &&
 +              !(soops->flag & SO_HIDE_RESTRICTCOLS) &&
 +              (view_co_x > ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX));
  }
  
  /**
index b23ea8e4daeb8a0f84990503fe0591f306c2ff18,2707e13059819135665e92ca94d2598e2e5687f7..190ef9fa229c2db1f02d10de31991b3e95f7c457
@@@ -1817,14 -1860,13 +1817,14 @@@ static int do_outliner_operation_event(
                /* select object that's clicked on and popup context menu */
                if (!(tselem->flag & TSE_SELECTED)) {
  
-                       if (outliner_has_one_flag(&soops->tree, TSE_SELECTED, 1))
-                               outliner_set_flag(&soops->tree, TSE_SELECTED, 0);
+                       if (outliner_flag_is_any_test(&soops->tree, TSE_SELECTED, 1))
+                               outliner_flag_set(&soops->tree, TSE_SELECTED, 0);
  
                        tselem->flag |= TSE_SELECTED;
 -                      /* redraw, same as outliner_select function */
 -                      soops->storeflag |= SO_TREESTORE_REDRAW;
 -                      ED_region_tag_redraw(ar);
 +
 +                      /* Only redraw, don't rebuild here because TreeElement pointers will
 +                       * become invalid and operations will crash. */
 +                      ED_region_tag_redraw_no_rebuild(ar);
                }
  
                set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);