27f70353e39042d536844af0db67e1eccbafb1f1
[blender.git] / source / blender / editors / screen / area.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_blenlib.h"
35 #include "BLI_arithb.h"
36 #include "BLI_rand.h"
37
38 #include "BKE_context.h"
39 #include "BKE_screen.h"
40 #include "BKE_utildefines.h"
41
42 #include "ED_screen.h"
43 #include "ED_screen_types.h"
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47 #include "wm_subwindow.h"
48
49 #include "BIF_gl.h"
50 #include "BIF_glutil.h"
51
52 #include "UI_interface.h"
53 #include "UI_resources.h"
54 #include "UI_view2d.h"
55
56 #ifndef DISABLE_PYTHON
57 #include "BPY_extern.h"
58 #endif
59
60 #include "ED_util.h"
61
62 #include "screen_intern.h"
63
64 /* general area and region code */
65
66 static void region_draw_emboss(ARegion *ar)
67 {
68         short winx, winy;
69         
70         winx= ar->winrct.xmax-ar->winrct.xmin;
71         winy= ar->winrct.ymax-ar->winrct.ymin;
72         
73         /* set transp line */
74         glEnable( GL_BLEND );
75         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
76         
77         /* right  */
78         glColor4ub(0,0,0, 50);
79         sdrawline(winx, 0, winx, winy);
80         
81         /* bottom  */
82         glColor4ub(0,0,0, 80);
83         sdrawline(0, 0, winx, 0);
84         
85         /* top  */
86         glColor4ub(255,255,255, 60);
87         sdrawline(0, winy, winx, winy);
88
89         /* left  */
90         glColor4ub(255,255,255, 50);
91         sdrawline(0, 0, 0, winy);
92         
93         glDisable( GL_BLEND );
94 }
95
96 void ED_region_pixelspace(const bContext *C, ARegion *ar)
97 {
98         wmWindow *win= CTX_wm_window(C);
99         int width= ar->winrct.xmax-ar->winrct.xmin+1;
100         int height= ar->winrct.ymax-ar->winrct.ymin+1;
101         
102         wmOrtho2(win, -0.375, (float)width-0.375, -0.375, (float)height-0.375);
103         wmLoadIdentity(win);
104 }
105
106 void ED_region_do_listen(ARegion *ar, wmNotifier *note)
107 {
108         /* generic notes first */
109         switch(note->type) {
110                 case WM_NOTE_WINDOW_REDRAW:
111                 case WM_NOTE_GESTURE_REDRAW:
112                 case WM_NOTE_SCREEN_CHANGED:
113                         ED_region_tag_redraw(ar);
114                         break;
115                 default:
116                         if(ar->type->listener)
117                                 ar->type->listener(ar, note);
118         }
119 }
120
121 /* based on screen region draw tags, set draw tags in azones, and future region tabs etc */
122 void ED_area_overdraw_flush(bContext *C)
123 {
124         ScrArea *sa;
125         
126         for(sa= CTX_wm_screen(C)->areabase.first; sa; sa= sa->next) {
127                 ARegion *ar;
128                 
129                 for(ar= sa->regionbase.first; ar; ar= ar->next) {
130                         if(ar->do_draw) {
131                                 AZone *az;
132                                 
133                                 for(az= sa->actionzones.first; az; az= az->next) {
134                                         int xs= (az->x1+az->x2)/2, ys= (az->y1+az->y2)/2;
135                 
136                                         /* test if inside */
137                                         if(BLI_in_rcti(&ar->winrct, xs, ys)) {
138                                                 az->do_draw= 1;
139                                         }
140                                 }
141                         }
142                 }
143         }       
144 }
145
146 void ED_area_overdraw(bContext *C)
147 {
148         wmWindow *win= CTX_wm_window(C);
149         bScreen *screen= CTX_wm_screen(C);
150         ScrArea *sa;
151         
152         /* Draw AZones, in screenspace */
153         wm_subwindow_set(win, screen->mainwin);
154
155         glEnable( GL_BLEND );
156         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
157         
158         for(sa= screen->areabase.first; sa; sa= sa->next) {
159                 AZone *az;
160                 for(az= sa->actionzones.first; az; az= az->next) {
161                         if(az->do_draw) {
162                                 if(az->type==AZONE_TRI) {
163                                         glColor4ub(0, 0, 0, 70);
164                                         sdrawtrifill(az->x1, az->y1, az->x2, az->y2);
165                                 }
166                                 az->do_draw= 0;
167                         }
168                 }
169         }       
170         glDisable( GL_BLEND );
171         
172 }
173
174 void ED_region_do_draw(bContext *C, ARegion *ar)
175 {
176         wmWindow *win= CTX_wm_window(C);
177         ScrArea *sa= CTX_wm_area(C);
178         ARegionType *at= ar->type;
179         
180         wm_subwindow_set(win, ar->swinid);
181         
182         if(ar->swinid && at->draw) {
183                 UI_SetTheme(sa);
184                 at->draw(C, ar);
185                 UI_SetTheme(NULL);
186         }
187         else {
188                 float fac= 0.1*ar->swinid;
189                 
190                 fac= fac - (int)fac;
191                 
192                 glClearColor(0.5, fac, 1.0f-fac, 0.0); 
193                 glClear(GL_COLOR_BUFFER_BIT);
194                 
195                 /* swapbuffers indicator */
196                 fac= BLI_frand();
197                 glColor3f(fac, fac, fac);
198                 glRecti(20,  2,  30,  12);
199         }
200
201         if(sa)
202                 region_draw_emboss(ar);
203         
204         /* XXX test: add convention to end regions always in pixel space, for drawing of borders/gestures etc */
205         ED_region_pixelspace(C, ar);
206         
207         ar->do_draw= 0;
208 }
209
210 /* **********************************
211    maybe silly, but let's try for now
212    to keep do_draw tags protected
213    ********************************** */
214
215 void ED_region_tag_redraw(ARegion *ar)
216 {
217         if(ar)
218                 ar->do_draw= 1;
219 }
220
221 void ED_area_tag_redraw(ScrArea *sa)
222 {
223         ARegion *ar;
224         
225         if(sa)
226                 for(ar= sa->regionbase.first; ar; ar= ar->next)
227                         ar->do_draw= 1;
228 }
229
230
231
232 /* *************************************************************** */
233
234 /* dir is direction to check, not the splitting edge direction! */
235 static int rct_fits(rcti *rect, char dir, int size)
236 {
237         if(dir=='h') {
238                 return rect->xmax-rect->xmin - size;
239         }
240         else { // 'v'
241                 return rect->ymax-rect->ymin - size;
242         }
243 }
244
245 static void region_rect_recursive(ARegion *ar, rcti *remainder)
246 {
247         int prefsizex, prefsizey;
248         
249         if(ar==NULL)
250                 return;
251         
252         /* clear state flags first */
253         ar->flag &= ~RGN_FLAG_TOO_SMALL;
254         if(ar->next==NULL)
255                 ar->alignment= RGN_ALIGN_NONE;
256         
257         prefsizex= ar->type->minsizex;
258         prefsizey= ar->type->minsizey;
259         
260         /* hidden is user flag */
261         if(ar->flag & RGN_FLAG_HIDDEN);
262         /* XXX floating area region, not handled yet here */
263         else if(ar->alignment == RGN_ALIGN_FLOAT);
264         /* remainder is too small for any usage */
265         else if( rct_fits(remainder, 'v', 1)<0 || rct_fits(remainder, 'h', 1) < 0 ) {
266                 ar->flag |= RGN_FLAG_TOO_SMALL;
267         }
268         else if(ar->alignment==RGN_ALIGN_NONE) {
269                 /* typically last region */
270                 ar->winrct= *remainder;
271                 BLI_init_rcti(remainder, 0, 0, 0, 0);
272         }
273         else if(ar->alignment==RGN_ALIGN_TOP || ar->alignment==RGN_ALIGN_BOTTOM) {
274                 
275                 if( rct_fits(remainder, 'v', prefsizey) < 0 ) {
276                         ar->flag |= RGN_FLAG_TOO_SMALL;
277                 }
278                 else {
279                         int fac= rct_fits(remainder, 'v', prefsizey);
280
281                         if(fac < 0 )
282                                 prefsizey += fac;
283                         
284                         ar->winrct= *remainder;
285                         
286                         if(ar->alignment==RGN_ALIGN_TOP) {
287                                 ar->winrct.ymin= ar->winrct.ymax - prefsizey + 1;
288                                 remainder->ymax= ar->winrct.ymin - 1;
289                         }
290                         else {
291                                 ar->winrct.ymax= ar->winrct.ymin + prefsizey - 1;
292                                 remainder->ymin= ar->winrct.ymax + 1;
293                         }
294                 }
295         }
296         else if(ar->alignment==RGN_ALIGN_LEFT || ar->alignment==RGN_ALIGN_RIGHT) {
297                 
298                 if( rct_fits(remainder, 'h', prefsizex) < 0 ) {
299                         ar->flag |= RGN_FLAG_TOO_SMALL;
300                 }
301                 else {
302                         int fac= rct_fits(remainder, 'h', prefsizex);
303                         
304                         if(fac < 0 )
305                                 prefsizex += fac;
306                         
307                         ar->winrct= *remainder;
308                         
309                         if(ar->alignment==RGN_ALIGN_RIGHT) {
310                                 ar->winrct.xmin= ar->winrct.xmax - prefsizex + 1;
311                                 remainder->xmax= ar->winrct.xmin - 1;
312                         }
313                         else {
314                                 ar->winrct.xmax= ar->winrct.xmin + prefsizex - 1;
315                                 remainder->xmin= ar->winrct.xmax + 1;
316                         }
317                 }
318         }
319         else {
320                 /* percentage subdiv*/
321                 ar->winrct= *remainder;
322                 
323                 if(ar->alignment==RGN_ALIGN_HSPLIT) {
324                         if( rct_fits(remainder, 'h', prefsizex) > 4) {
325                                 ar->winrct.xmax= (remainder->xmin+remainder->xmax)/2;
326                                 remainder->xmin= ar->winrct.xmax+1;
327                         }
328                         else {
329                                 BLI_init_rcti(remainder, 0, 0, 0, 0);
330                         }
331                 }
332                 else {
333                         if( rct_fits(remainder, 'v', prefsizey) > 4) {
334                                 ar->winrct.ymax= (remainder->ymin+remainder->ymax)/2;
335                                 remainder->ymin= ar->winrct.ymax+1;
336                         }
337                         else {
338                                 BLI_init_rcti(remainder, 0, 0, 0, 0);
339                         }
340                 }
341         }
342         /* for speedup */
343         ar->winx= ar->winrct.xmax - ar->winrct.xmin + 1;
344         ar->winy= ar->winrct.ymax - ar->winrct.ymin + 1;
345         
346         region_rect_recursive(ar->next, remainder);
347 }
348
349 static void area_calc_totrct(ScrArea *sa, int sizex, int sizey)
350 {
351         
352         if(sa->v1->vec.x>0) sa->totrct.xmin= sa->v1->vec.x+1;
353         else sa->totrct.xmin= sa->v1->vec.x;
354         if(sa->v4->vec.x<sizex-1) sa->totrct.xmax= sa->v4->vec.x-1;
355         else sa->totrct.xmax= sa->v4->vec.x;
356         
357         if(sa->v1->vec.y>0) sa->totrct.ymin= sa->v1->vec.y+1;
358         else sa->totrct.ymin= sa->v1->vec.y;
359         if(sa->v2->vec.y<sizey-1) sa->totrct.ymax= sa->v2->vec.y-1;
360         else sa->totrct.ymax= sa->v2->vec.y;
361         
362         /* for speedup */
363         sa->winx= sa->totrct.xmax-sa->totrct.xmin+1;
364         sa->winy= sa->totrct.ymax-sa->totrct.ymin+1;
365 }
366
367
368 #define AZONESPOT               12
369 void area_azone_initialize(ScrArea *sa) 
370 {
371         AZone *az;
372         if(sa->actionzones.first==NULL) {
373                 /* set action zones - should these actually be ARegions? With these we can easier check area hotzones */
374                 /* (ton) for time being just area, ARegion split is not foreseen on user level */
375                 az= (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
376                 BLI_addtail(&(sa->actionzones), az);
377                 az->type= AZONE_TRI;
378                 az->pos= AZONE_SW;
379                 
380                 az= (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
381                 BLI_addtail(&(sa->actionzones), az);
382                 az->type= AZONE_TRI;
383                 az->pos= AZONE_NE;
384         }
385         
386         for(az= sa->actionzones.first; az; az= az->next) {
387                 if(az->pos==AZONE_SW) {
388                         az->x1= sa->v1->vec.x+1;
389                         az->y1= sa->v1->vec.y+1;
390                         az->x2= sa->v1->vec.x+AZONESPOT;
391                         az->y2= sa->v1->vec.y+AZONESPOT;
392                 } 
393                 else if (az->pos==AZONE_NE) {
394                         az->x1= sa->v3->vec.x;
395                         az->y1= sa->v3->vec.y;
396                         az->x2= sa->v3->vec.x-AZONESPOT;
397                         az->y2= sa->v3->vec.y-AZONESPOT;
398                 }
399         }
400 }
401
402 /* used for area initialize below */
403 static void region_subwindow(wmWindowManager *wm, wmWindow *win, ARegion *ar)
404 {
405         if(ar->flag & (RGN_FLAG_HIDDEN|RGN_FLAG_TOO_SMALL)) {
406                 if(ar->swinid)
407                         wm_subwindow_close(win, ar->swinid);
408                 ar->swinid= 0;
409         }
410         else if(ar->swinid==0)
411                 ar->swinid= wm_subwindow_open(win, &ar->winrct);
412         else 
413                 wm_subwindow_position(win, ar->swinid, &ar->winrct);
414 }
415
416 static void ed_default_handlers(wmWindowManager *wm, ListBase *handlers, int flag)
417 {
418         /* note, add-handler checks if it already exists */
419         
420         if(flag & ED_KEYMAP_UI) {
421                 UI_add_region_handlers(handlers);
422         }
423         if(flag & ED_KEYMAP_VIEW2D) {
424                 ListBase *keymap= WM_keymap_listbase(wm, "View2D", 0, 0);
425                 WM_event_add_keymap_handler(handlers, keymap);
426         }
427         if(flag & ED_KEYMAP_MARKERS) {
428                 ListBase *keymap= WM_keymap_listbase(wm, "Markers", 0, 0);
429                 WM_event_add_keymap_handler(handlers, keymap);
430         }
431 }
432
433
434 /* called in screen_refresh, or screens_init, also area size changes */
435 void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa)
436 {
437         ARegion *ar;
438         rcti rect;
439         
440         /* set typedefinitions */
441         sa->type= BKE_spacetype_from_id(sa->spacetype);
442         
443         if(sa->type==NULL) {
444                 sa->butspacetype= sa->spacetype= SPACE_VIEW3D;
445                 sa->type= BKE_spacetype_from_id(sa->spacetype);
446         }
447         
448         for(ar= sa->regionbase.first; ar; ar= ar->next)
449                 ar->type= ED_regiontype_from_id(sa->type, ar->regiontype);
450         
451         /* area sizes */
452         area_calc_totrct(sa, win->sizex, win->sizey);
453         
454         /* region rect sizes */
455         rect= sa->totrct;
456         region_rect_recursive(sa->regionbase.first, &rect);
457         
458         /* default area handlers */
459         ed_default_handlers(wm, &sa->handlers, sa->type->keymapflag);
460         /* checks spacedata, adds own handlers */
461         if(sa->type->init)
462                 sa->type->init(wm, sa);
463         
464         /* region windows, default and own handlers */
465         for(ar= sa->regionbase.first; ar; ar= ar->next) {
466                 region_subwindow(wm, win, ar);
467                 
468                 /* default region handlers */
469                 ed_default_handlers(wm, &ar->handlers, ar->type->keymapflag);
470
471                 if(ar->type->init)
472                         ar->type->init(wm, ar);
473                 
474         }
475         area_azone_initialize(sa);
476 }
477
478 /* externally called for floating regions like menus */
479 void ED_region_init(bContext *C, ARegion *ar)
480 {
481 //      ARegionType *at= ar->type;
482         
483         /* refresh can be called before window opened */
484         region_subwindow(CTX_wm_manager(C), CTX_wm_window(C), ar);
485         
486 }
487
488
489 /* sa2 to sa1, we swap spaces for fullscreen to keep all allocated data */
490 /* area vertices were set */
491 void area_copy_data(ScrArea *sa1, ScrArea *sa2, int swap_space)
492 {
493         Panel *pa1, *pa2, *patab;
494         ARegion *ar;
495         
496         sa1->headertype= sa2->headertype;
497         sa1->spacetype= sa2->spacetype;
498         sa1->butspacetype= sa2->butspacetype;
499         
500         if(swap_space) {
501                 SWAP(ListBase, sa1->spacedata, sa2->spacedata);
502                 /* exception: ensure preview is reset */
503 //              if(sa1->spacetype==SPACE_VIEW3D)
504 // XXX                  BIF_view3d_previewrender_free(sa1->spacedata.first);
505         }
506         else {
507                 BKE_spacedata_freelist(&sa1->spacedata);
508                 BKE_spacedata_copylist(&sa1->spacedata, &sa2->spacedata);
509         }
510         
511         BLI_freelistN(&sa1->panels);
512         BLI_duplicatelist(&sa1->panels, &sa2->panels);
513         
514         /* copy panel pointers */
515         for(pa1= sa1->panels.first; pa1; pa1= pa1->next) {
516                 
517                 patab= sa1->panels.first;
518                 pa2= sa2->panels.first;
519                 while(patab) {
520                         if( pa1->paneltab == pa2) {
521                                 pa1->paneltab = patab;
522                                 break;
523                         }
524                         patab= patab->next;
525                         pa2= pa2->next;
526                 }
527         }
528         
529         /* regions... XXX */
530         BLI_freelistN(&sa1->regionbase);
531         
532         for(ar= sa2->regionbase.first; ar; ar= ar->next) {
533                 ARegion *newar= BKE_area_region_copy(ar);
534                 BLI_addtail(&sa1->regionbase, newar);
535         }
536                 
537 #ifndef DISABLE_PYTHON
538         /* scripts */
539         BPY_free_scriptlink(&sa1->scriptlink);
540         sa1->scriptlink= sa2->scriptlink;
541         BPY_copy_scriptlink(&sa1->scriptlink);  /* copies internal pointers */
542 #endif
543 }
544
545 /* *********** Space switching code, local now *********** */
546 /* XXX make operator for this */
547
548 void area_newspace(bContext *C, ScrArea *sa, int type)
549 {
550         if(sa->spacetype != type) {
551                 SpaceType *st;
552                 SpaceLink *slold;
553                 SpaceLink *sl;
554
555                 ED_area_exit(C, sa);
556
557                 st= BKE_spacetype_from_id(type);
558                 slold= sa->spacedata.first;
559
560                 sa->spacetype= type;
561                 sa->butspacetype= type;
562                 sa->type= st;
563                 
564                 /* check previously stored space */
565                 for (sl= sa->spacedata.first; sl; sl= sl->next)
566                         if(sl->spacetype==type)
567                                 break;
568                 
569                 /* old spacedata... happened during work on 2.50, remove */
570                 if(sl && sl->regionbase.first==NULL) {
571                         st->free(sl);
572                         MEM_freeN(sl);
573                         sl= NULL;
574                 }
575                 
576                 if (sl) {
577                         
578                         /* swap regions */
579                         slold->regionbase= sa->regionbase;
580                         sa->regionbase= sl->regionbase;
581                         sl->regionbase.first= sl->regionbase.last= NULL;
582                         
583                         /* put in front of list */
584                         BLI_remlink(&sa->spacedata, sl);
585                         BLI_addhead(&sa->spacedata, sl);
586                 } 
587                 else {
588                         /* new space */
589                         if(st) {
590                                 sl= st->new();
591                                 BLI_addhead(&sa->spacedata, sl);
592                                 
593                                 /* swap regions */
594                                 if(slold)
595                                         slold->regionbase= sa->regionbase;
596                                 sa->regionbase= sl->regionbase;
597                                 sl->regionbase.first= sl->regionbase.last= NULL;
598                         }
599                 }
600                 
601                 ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
602                 
603                 /* tell WM to refresh, cursor types etc */
604                 WM_event_add_mousemove(C);
605         }
606 }
607
608 static char *windowtype_pup(void)
609 {
610         return(
611                    "Window type:%t" //14
612                    "|3D View %x1" //30
613                    
614                    "|%l" // 33
615                    
616                    "|Ipo Curve Editor %x2" //54
617                    "|Action Editor %x12" //73
618                    "|NLA Editor %x13" //94
619                    
620                    "|%l" //97
621                    
622                    "|UV/Image Editor %x6" //117
623                    
624                    "|Video Sequence Editor %x8" //143
625                    "|Timeline %x15" //163
626                    "|Audio Window %x11" //163
627                    "|Text Editor %x9" //179
628                    
629                    "|%l" //192
630                    
631                    
632                    "|User Preferences %x7" //213
633                    "|Outliner %x3" //232
634                    "|Buttons Window %x4" //251
635                    "|Node Editor %x16"
636                    "|%l" //254
637                    
638                    "|File Browser %x5" //290
639                    
640                    "|%l" //293
641                    
642                    "|Scripts Window %x14"//313
643                    );
644 }
645
646 static void spacefunc(struct bContext *C, void *arg1, void *arg2)
647 {
648         area_newspace(C, CTX_wm_area(C), CTX_wm_area(C)->butspacetype);
649         ED_area_tag_redraw(CTX_wm_area(C));
650 }
651
652 /* returns offset for next button in header */
653 int ED_area_header_standardbuttons(const bContext *C, uiBlock *block, int yco)
654 {
655         ScrArea *sa= CTX_wm_area(C);
656         uiBut *but;
657         int xco= 8;
658         
659         if(ED_screen_area_active(C)) uiBlockSetCol(block, TH_HEADER);
660         else uiBlockSetCol(block, TH_HEADERDESEL);
661         
662         but= uiDefIconTextButC(block, ICONTEXTROW, 0, ICON_VIEW3D, 
663                                                    windowtype_pup(), xco, yco, XIC+10, YIC, 
664                                                    &(sa->butspacetype), 1.0, SPACEICONMAX, 0, 0, 
665                                                    "Displays Current Window Type. "
666                                                    "Click for menu of available types.");
667         uiButSetFunc(but, spacefunc, NULL, NULL);
668         
669         xco += XIC + 14;
670         
671         uiBlockSetEmboss(block, UI_EMBOSSN);
672         if (sa->flag & HEADER_NO_PULLDOWN) {
673                 uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, 0, 
674                                                  ICON_DISCLOSURE_TRI_RIGHT,
675                                                  xco,yco,XIC,YIC-2,
676                                                  &(sa->flag), 0, 0, 0, 0, 
677                                                  "Show pulldown menus");
678         }
679         else {
680                 uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, 0, 
681                                                  ICON_DISCLOSURE_TRI_DOWN,
682                                                  xco,yco,XIC,YIC-2,
683                                                  &(sa->flag), 0, 0, 0, 0, 
684                                                  "Hide pulldown menus");
685         }
686         xco+=XIC;
687         
688         return xco;
689 }
690