Patch #4980, by Joshua Leung (aligorith)

This enables finding data in the Outliner.
Usage: Fkey (partial strings), CTRL+Fkey (partial strings, case sensitive).
SHIFT+Fkey to repeat a search, this cycles around.

Extra changes:
- button popups to enter strings now starts activated.
- outliner either shows for Armature the Bones, or Posechannels or
  Editbones,  depending the mode. Was needed to make searches meaningful.

Although Joshua did very good work on the key functions, there were a couple
of issues in his code, and problems in Outliner code, that didn't make it
all work nicely. So, this is quite a revised patch. :)

Full review log can be found in the patch tracker.
This commit is contained in:
2006-11-07 14:25:58 +00:00
parent 008d6987d0
commit f34bf8a0cd
8 changed files with 347 additions and 21 deletions

View File

@@ -132,8 +132,8 @@
#include "BKE_texture.h" // for open_plugin_tex
#include "BKE_utildefines.h" // SWITCH_INT DATA ENDB DNA1 O_BINARY GLOB USER TEST REND
#include "BIF_butspace.h" // for do_versions, patching event codes
#include "BIF_previewrender.h" // for struct RenderInfo
#include "BIF_butspace.h" // badlevel, for do_versions, patching event codes
#include "BIF_previewrender.h" // bedlelvel, for struct RenderInfo
#include "BLO_readfile.h"
#include "BLO_undofile.h"
#include "BLO_readblenfile.h" // streaming read pipe, for BLO_readblenfile BLO_readblenfilememory
@@ -3143,6 +3143,7 @@ static void lib_link_screen(FileData *fd, Main *main)
}
so->lockpoin= NULL;
so->tree.first= so->tree.last= NULL;
so->search_tse.id= newlibadr(fd, NULL, so->search_tse.id);
if(so->treestore) {
tselem= so->treestore->data;
@@ -3302,14 +3303,15 @@ void lib_link_screen_restore(Main *newmain, Scene *curscene)
else if(sl->spacetype==SPACE_OOPS) {
SpaceOops *so= (SpaceOops *)sl;
Oops *oops;
int a;
oops= so->oops.first;
while(oops) {
oops->id= restore_pointer_by_name(newmain, (ID *)oops->id, 0);
oops= oops->next;
}
so->lockpoin= NULL;
so->search_tse.id= restore_pointer_by_name(newmain, so->search_tse.id, 0);
if(so->treestore) {
TreeStore *ts= so->treestore;

View File

@@ -38,7 +38,7 @@ struct TreeStoreElem;
typedef struct TreeElement {
struct TreeElement *next, *prev, *parent;
ListBase subtree;
float xs, ys; // doe selection
float xs, ys; // do selection
int store_index; // offset in tree store
short flag, index; // flag for non-saved stuff, index for data arrays
short idcode; // from TreeStore id
@@ -72,6 +72,12 @@ typedef struct TreeElement {
#define TSE_VERSE_GEOM_NODE 17
/*#endif*/
/* outliner search flags */
#define OL_FIND 0
#define OL_FIND_CASE 1
#define OL_FIND_COMPLETE 2
#define OL_FIND_COMPLETE_CASE 3
/* button events */
#define OL_NAMEBUTTON 1
@@ -86,6 +92,7 @@ extern void outliner_select(struct ScrArea *sa);
extern void outliner_toggle_selected(struct ScrArea *sa);
extern void outliner_operation_menu(struct ScrArea *sa);
extern void outliner_page_up_down(struct ScrArea *sa, int up);
extern void outliner_find_panel(struct ScrArea *sa, int again, int flags);
#endif

View File

@@ -43,7 +43,7 @@ struct ID;
typedef struct TreeStoreElem {
short type, nr, flag, used;
ID *id;
struct ID *id;
} TreeStoreElem;
typedef struct TreeStore {
@@ -65,7 +65,7 @@ typedef struct Oops {
typedef struct OopsLink {
struct OopsLink *next, *prev;
short type, flag;
ID **idfrom;
struct ID **idfrom;
Oops *to, *from; /* from is for temp */
float xof, yof;
char name[12];

View File

@@ -36,6 +36,7 @@
#include "DNA_listBase.h"
#include "DNA_vec_types.h"
#include "DNA_oops_types.h" /* for TreeStoreElem */
/* Hum ... Not really nice... but needed for spacebuts. */
#include "DNA_view2d_types.h"
@@ -47,7 +48,6 @@ struct ImBuf;
struct Image;
struct SpaceIpo;
struct BlendHandle;
struct TreeStore;
struct RenderInfo;
struct bNodeTree;
struct uiBlock;
@@ -206,6 +206,12 @@ typedef struct SpaceOops {
ListBase tree;
struct TreeStore *treestore;
/* search stuff */
char search_string[32];
struct TreeStoreElem search_tse;
int search_flags, pad;
short type, outlinevis, storeflag;
short deps_flags;

View File

@@ -348,7 +348,67 @@ static uiBlock *oops_blockmenu(void *arg_unused)
return block;
}
static void do_oops_searchmenu(void *arg, int event)
{
int search_flags = OL_FIND, again = 0;
switch(event)
{
case 0: /* plain new find */
search_flags = OL_FIND;
break;
case 1: /* case sensitive */
search_flags = OL_FIND_CASE;
break;
case 2: /* full search */
search_flags = OL_FIND_COMPLETE;
break;
case 3: /* full case sensitive */
search_flags = OL_FIND_COMPLETE_CASE;
break;
case 4: /* again */
again = 1;
break;
default: /* nothing valid */
return;
}
/* run search */
outliner_find_panel(curarea, again, search_flags);
}
static uiBlock *oops_searchmenu(void *arg_unused)
{
uiBlock *block;
short yco= 0, menuwidth=120;
block= uiNewBlock(&curarea->uiblocks, "oops_searchmenu", UI_EMBOSSP, UI_HELV, curarea->headwin);
uiBlockSetButmFunc(block, do_oops_searchmenu, NULL);
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find|F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find (Case Sensitive)|Ctrl F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find Complete|Alt F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find Complete (Case Sensitive)|Ctrl Alt F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find Again|Shift F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 4, "");
if(curarea->headertype==HEADERTOP) {
uiBlockSetDirection(block, UI_DOWN);
}
else {
uiBlockSetDirection(block, UI_TOP);
uiBlockFlipOrder(block);
}
uiTextBoundsBlock(block, 50);
return block;
}
void oops_buttons(void)
{
@@ -405,6 +465,11 @@ void oops_buttons(void)
xco+= xmax;
}
else {
xmax= GetButStringLength("Search");
uiDefPulldownBut(block, oops_searchmenu, NULL, "Search", xco, -2, xmax-3, 24, "");
xco+= xmax;
}
}
uiBlockSetEmboss(block, UI_EMBOSS);

View File

@@ -127,6 +127,7 @@ extern ListBase session_list;
extern ListBase server_list;
#endif
/* ******************** PERSISTANT DATA ***************** */
static void outliner_storage_cleanup(SpaceOops *soops)
@@ -440,8 +441,9 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i
tenla->name= "Pose";
if(ob!=G.obedit) { // channels undefined in editmode, but we want the 'tenla' pose icon itself
int a= 0;
if(ob!=G.obedit && (ob->flag & OB_POSEMODE)) { // channels undefined in editmode, but we want the 'tenla' pose icon itself
int a= 0, const_index= 1000; /* ensure unique id for bone constraints */
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next, a++) {
ten= outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_POSE_CHANNEL, a);
ten->name= pchan->name;
@@ -453,12 +455,11 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i
bConstraint *con;
TreeElement *ten1;
TreeElement *tenla1= outliner_add_element(soops, &ten->subtree, ob, ten, TSE_CONSTRAINT_BASE, 0);
int a= 0;
char *str;
tenla1->name= "Constraints";
for(con= pchan->constraints.first; con; con= con->next, a++) {
ten1= outliner_add_element(soops, &tenla1->subtree, ob, tenla1, TSE_CONSTRAINT, a);
for(con= pchan->constraints.first; con; con= con->next, const_index++) {
ten1= outliner_add_element(soops, &tenla1->subtree, ob, tenla1, TSE_CONSTRAINT, const_index);
target= get_constraint_target(con, &str);
if(str && str[0]) ten1->name= str;
else if(target) ten1->name= target->id.name+2;
@@ -479,6 +480,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i
BLI_remlink(&tenla->subtree, ten);
par= (TreeElement *)pchan->parent->prev;
BLI_addtail(&par->subtree, ten);
ten->parent= par;
}
}
ten= nten;
@@ -704,10 +706,15 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i
BLI_remlink(&te->subtree, ten);
par= ebone->parent->temp;
BLI_addtail(&par->subtree, ten);
ten->parent= par;
}
ten= nten;
}
}
else {
/* do not extend Armature when we have posemode */
tselem= TREESTORE(te->parent);
if( GS(tselem->id->name)==ID_OB && ((Object *)tselem->id)->flag & OB_POSEMODE);
else {
Bone *curBone;
for (curBone=arm->bonebase.first; curBone; curBone=curBone->next){
@@ -715,6 +722,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i
}
}
}
}
break;
}
}
@@ -1031,6 +1039,46 @@ static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curleve
}
}
/* return 1 when levels were opened */
static int outliner_open_back(SpaceOops *soops, TreeElement *te)
{
TreeStoreElem *tselem;
int retval= 0;
for (te= te->parent; te; te= te->parent) {
tselem= TREESTORE(te);
if (tselem->flag & TSE_CLOSED) {
tselem->flag &= ~TSE_CLOSED;
retval= 1;
}
}
return retval;
}
static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found)
{
TreeElement *te;
TreeStoreElem *tselem;
for (te= lb->first; te; te= te->next) {
/* check if this tree-element was the one we're seeking */
if (te == teFind) {
*found= 1;
return;
}
/* try to see if sub-tree contains it then */
outliner_open_reveal(soops, &te->subtree, teFind, found);
if (*found) {
tselem= TREESTORE(te);
if (tselem->flag & TSE_CLOSED)
tselem->flag &= ~TSE_CLOSED;
return;
}
}
}
void outliner_one_level(struct ScrArea *sa, int add)
{
SpaceOops *soops= sa->spacedata.first;
@@ -1767,6 +1815,36 @@ void outliner_mouse_event(ScrArea *sa, short event)
outliner_select(sa);
}
/* recursive helper for function below */
static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
{
TreeStoreElem *tselem= TREESTORE(te);
/* store coord and continue, we need coordinates for elements outside view too */
te->xs= startx;
te->ys= *starty;
*starty-= OL_H;
if((tselem->flag & TSE_CLOSED)==0) {
TreeElement *ten;
for(ten= te->subtree.first; ten; ten= ten->next) {
outliner_set_coordinates_element(soops, ten, startx+OL_X, starty);
}
}
}
/* to retrieve coordinates with redrawing the entire tree */
static void outliner_set_coordinates(SpaceOops *soops)
{
TreeElement *te;
int starty= soops->v2d.tot.ymax-OL_H;
int startx= 0;
for(te= soops->tree.first; te; te= te->next) {
outliner_set_coordinates_element(soops, te, startx, &starty);
}
}
static TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, ID *id)
{
@@ -1804,6 +1882,158 @@ void outliner_show_active(struct ScrArea *sa)
}
}
void outliner_show_selected(struct ScrArea *sa)
{
SpaceOops *so= sa->spacedata.first;
TreeElement *te;
int ytop;
te= outliner_find_id(so, &so->tree, (ID *)OBACT);
if(te) {
/* make te->ys center of view */
ytop= te->ys + (so->v2d.mask.ymax-so->v2d.mask.ymin)/2;
if(ytop>0) ytop= 0;
so->v2d.cur.ymax= ytop;
so->v2d.cur.ymin= ytop-(so->v2d.mask.ymax-so->v2d.mask.ymin);
scrarea_queue_redraw(sa);
}
}
/* find next element that has this name */
static TreeElement *outliner_find_named(SpaceOops *soops, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound)
{
TreeElement *te, *tes;
for (te= lb->first; te; te= te->next) {
int found;
/* determine if match */
if(flags==OL_FIND)
found= strcasestr(te->name, name)!=NULL;
else if(flags==OL_FIND_CASE)
found= strstr(te->name, name)!=NULL;
else if(flags==OL_FIND_COMPLETE)
found= strcasecmp(te->name, name)==0;
else
found= strcmp(te->name, name)==0;
if(found) {
/* name is right, but is element the previous one? */
if (prev) {
if ((te != prev) && (*prevFound))
return te;
if (te == prev) {
*prevFound = 1;
}
}
else
return te;
}
tes= outliner_find_named(soops, &te->subtree, name, flags, prev, prevFound);
if(tes) return tes;
}
/* nothing valid found */
return NULL;
}
/* tse is not in the treestore, we use its contents to find a match */
static TreeElement *outliner_find_tse(SpaceOops *soops, TreeStoreElem *tse)
{
TreeStore *ts= soops->treestore;
TreeStoreElem *tselem;
int a;
if(tse->id==NULL) return NULL;
/* check if 'tse' is in treestore */
tselem= ts->data;
for(a=0; a<ts->usedelem; a++, tselem++) {
if(tselem->id==tse->id) {
if((tse->type==0 && tselem->type==0) || (tselem->type==tse->type && tselem->nr==tse->nr)) {
break;
}
}
}
if(tselem)
return outliner_find_tree_element(&soops->tree, a);
return NULL;
}
/* Called to find an item based on name.
*/
void outliner_find_panel(struct ScrArea *sa, int again, int flags)
{
SpaceOops *soops= sa->spacedata.first;
TreeElement *te= NULL;
TreeElement *last_find;
TreeStoreElem *tselem;
int ytop, prevFound=0;
char name[33];
/* get last found tree-element based on stored search_tse */
last_find= outliner_find_tse(soops, &soops->search_tse);
/* determine which type of search to do */
if (again && last_find) {
/* no popup panel - previous + user wanted to search for next after previous */
BLI_strncpy(name, soops->search_string, 33);
flags= soops->search_flags;
/* try to find matching element */
te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
if (te==NULL) {
/* no more matches after previous, start from beginning again */
prevFound= 1;
te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
}
}
else {
/* pop up panel - no previous, or user didn't want search after previous */
strcpy(name, "");
if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) {
te= outliner_find_named(soops, &soops->tree, name, flags, NULL, &prevFound);
}
else return; /* XXX RETURN! XXX */
}
/* do selection and reveil */
if (te) {
tselem= TREESTORE(te);
if (tselem) {
/* expand branches so that it will be visible, we need to get correct coordinates */
if( outliner_open_back(soops, te))
outliner_set_coordinates(soops);
/* deselect all visible, and select found element */
outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
tselem->flag |= TSE_SELECTED;
/* make te->ys center of view */
ytop= te->ys + (soops->v2d.mask.ymax-soops->v2d.mask.ymin)/2;
if(ytop>0) ytop= 0;
soops->v2d.cur.ymax= ytop;
soops->v2d.cur.ymin= ytop-(soops->v2d.mask.ymax-soops->v2d.mask.ymin);
/* store selection */
soops->search_tse= *tselem;
BLI_strncpy(soops->search_string, name, 33);
soops->search_flags= flags;
/* redraw */
scrarea_queue_redraw(sa);
}
}
else {
if (name) error("Not found: %s", name);
}
}
static int subtree_has_objects(SpaceOops *soops, ListBase *lb)
{
TreeElement *te;
@@ -2678,7 +2908,7 @@ static void outliner_draw_tree_element(SpaceOops *soops, TreeElement *te, int st
}
}
}
/* store coord and continue */
/* store coord and continue, we need coordinates for elements outside view too */
te->xs= startx;
te->ys= *starty;
te->xend= startx+offsx;
@@ -2774,9 +3004,9 @@ static void outliner_draw_tree(SpaceOops *soops)
for(te= soops->tree.first; te; te= te->next) {
outliner_draw_tree_element(soops, te, startx, &starty);
}
}
static void outliner_back(SpaceOops *soops)
{
int ystart;
@@ -2881,6 +3111,7 @@ static void outliner_buttons(uiBlock *block, SpaceOops *soops, ListBase *lb)
for(te= lb->first; te; te= te->next) {
tselem= TREESTORE(te);
if(tselem->flag & TSE_TEXTBUT) {
if(tselem->type == TSE_POSE_BASE) continue; // prevent crash when trying to rename 'pose' entry of armature
if(tselem->type==TSE_EBONE) len = sizeof(((EditBone*) 0)->name);
else if (tselem->type==TSE_MODIFIER) len = sizeof(((ModifierData*) 0)->name);

View File

@@ -4495,6 +4495,19 @@ static void winqreadoopsspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
else
outliner_toggle_visible(sa);
break;
case FKEY:
{
/* search */
int search_flags=0, again=0;
/* CTRL=case sensitive, SHIFT=find again, ALT=complete */
if (G.qual & LR_CTRLKEY) search_flags |= 1;
if (G.qual & LR_ALTKEY) search_flags |= 8;
if (G.qual & LR_SHIFTKEY) again = 1;
outliner_find_panel(sa, again, search_flags);
}
break;
case WKEY:
outliner_operation_menu(sa);
break;
@@ -5045,7 +5058,8 @@ void freespacelist(ScrArea *sa)
}
}
else if(sl->spacetype==SPACE_OOPS) {
free_oopspace((SpaceOops *)sl);
SpaceOops *so= (SpaceOops *) sl;
free_oopspace(so);
}
else if(sl->spacetype==SPACE_IMASEL) {
free_imasel((SpaceImaSel *)sl);

View File

@@ -306,11 +306,12 @@ short sbutton(char *var, float min, float max, char *str)
x1=mval[0]-150;
y1=mval[1]-20;
uiDefButC(block, TEX, 0, str, x1+5,y1+10,125,20, var,(float)min,(float)max, 0, 0, "");
uiDefButC(block, TEX, 2, str, x1+5,y1+10,125,20, var,(float)min,(float)max, 0, 0, "");
uiDefBut(block, BUT, 1, "OK", x1+136,y1+10,25,20, NULL, 0, 0, 0, 0, "");
uiBoundsBlock(block, 5);
mainqenter_ext(BUT_ACTIVATE, 2, 0); /* note, button id '2' is asking for errors some day! */
ret= uiDoBlocks(&listb, 0);
if(ret==UI_RETURN_OK) return 1;