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