1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "gdraw.h"
31 #include "ggadgetP.h"
32 #include "gkeysym.h"
33 #include "gresource.h"
34 #include "gwidget.h"
35 #include "ustring.h"
36 
37 static GBox gtabset_box = GBOX_EMPTY; /* Don't initialize here */
38 static GBox gvtabset_box = GBOX_EMPTY; /* Don't initialize here */
39 static FontInstance *gtabset_font = NULL;
40 static int gtabset_inited = false;
41 
42 static int GTS_TABPADDING = 25;
43 
44 static GResInfo gtabset_ri, gvtabset_ri;
45 
46 static GResInfo gtabset_ri = {
47     &gvtabset_ri, &ggadget_ri, &gvtabset_ri, NULL,
48     &gtabset_box,
49     NULL,
50     NULL,
51     NULL,
52     N_("TabSet"),
53     N_("Tab Set"),
54     "GTabSet",
55     "Gdraw",
56     false,
57     omf_border_width|omf_border_shape,
58     NULL,
59     GBOX_EMPTY,
60     NULL,
61     NULL,
62     NULL
63 };
64 
65 /* Dummy gadget for styling vertical layout */
66 static GResInfo gvtabset_ri = {
67     NULL, &gtabset_ri, &gtabset_ri, NULL,
68     &gvtabset_box,
69     NULL,
70     NULL,
71     NULL,
72     N_("VerticalTabSet"),
73     N_("Vertical Tab Set"),
74     "GVTabSet",
75     "Gdraw",
76     false,
77     0,
78     NULL,
79     GBOX_EMPTY,
80     NULL,
81     NULL,
82     NULL
83 };
84 #define NEST_INDENT	4
85 
GTabSetInit()86 static void GTabSetInit() {
87 
88     if ( gtabset_inited )
89 return;
90 
91     GGadgetInit();
92 
93     _GGadgetCopyDefaultBox(&gtabset_box);
94     gtabset_box.border_width = 1; gtabset_box.border_shape = bs_rect;
95     /*gtabset_box.flags = 0;*/
96     gtabset_font = _GGadgetInitDefaultBox("GTabSet.",&gtabset_box,NULL);
97 
98     gvtabset_box = gtabset_box; /* needs this to figure inheritance */
99     _GGadgetInitDefaultBox("GVTabSet.",&gvtabset_box,NULL);
100 
101     gtabset_inited = true;
102 }
103 
GTabSetChanged(GTabSet * gts,int oldsel)104 static void GTabSetChanged(GTabSet *gts,int oldsel) {
105     GEvent e;
106 
107     e.type = et_controlevent;
108     e.w = gts->g.base;
109     e.u.control.subtype = et_radiochanged;
110     e.u.control.g = &gts->g;
111     if ( gts->g.handle_controlevent != NULL )
112 	(gts->g.handle_controlevent)(&gts->g,&e);
113     else
114 	GDrawPostEvent(&e);
115 }
116 
DrawLeftArrowTab(GWindow pixmap,GTabSet * gts,int x,int y)117 static int DrawLeftArrowTab(GWindow pixmap, GTabSet *gts, int x, int y ) {
118     Color fg = gts->g.box->main_foreground;
119     GPoint pts[5];
120     int retx = x + gts->arrow_width, cnt;
121 
122     if ( fg==COLOR_DEFAULT ) fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap));
123     GBoxDrawTabOutline(pixmap,&gts->g,x,y,gts->arrow_width,gts->rowh,false);
124     gts->haslarrow = true;
125 	y += (gts->rowh-gts->arrow_size)/2;
126 	x += (gts->arrow_width-gts->arrow_size/2)/2;
127 	cnt = 4;
128 	pts[0].y = (y+(gts->arrow_size-1)/2); 	pts[0].x = x;
129 	pts[1].y = y; 				pts[1].x = x + (gts->arrow_size-1)/2;
130 	pts[2].y = y+gts->arrow_size-1; 	pts[2].x = pts[1].x;
131 	pts[3] = pts[0];
132 	if ( !(gts->arrow_size&1 )) {
133 	    ++pts[3].y;
134 	    pts[4] = pts[0];
135 	    cnt = 5;
136 	}
137 	GDrawFillPoly(pixmap,pts,cnt,fg);
138 return( retx );
139 }
140 
DrawRightArrowTab(GWindow pixmap,GTabSet * gts,int x,int y)141 static int DrawRightArrowTab(GWindow pixmap, GTabSet *gts, int x, int y ) {
142     Color fg = gts->g.box->main_foreground;
143     GPoint pts[5];
144     int retx = x + gts->arrow_width, cnt;
145 
146     if ( fg==COLOR_DEFAULT ) fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap));
147     GBoxDrawTabOutline(pixmap,&gts->g,x,y,gts->arrow_width,gts->rowh,false);
148     gts->hasrarrow = true;
149 	y += (gts->rowh-gts->arrow_size)/2;
150 	x += (gts->arrow_width-gts->arrow_size/2)/2;
151 	cnt = 4;
152 	pts[0].y = (y+(gts->arrow_size-1)/2); 	pts[0].x = x + (gts->arrow_size-1)/2;
153 	pts[1].y = y; 				pts[1].x = x;
154 	pts[2].y = y+gts->arrow_size-1; 	pts[2].x = pts[1].x;
155 	pts[3] = pts[0];
156 	if ( !(gts->arrow_size&1 )) {
157 	    ++pts[3].y;
158 	    pts[4] = pts[0];
159 	    cnt = 5;
160 	}
161 	GDrawFillPoly(pixmap,pts,cnt,fg);
162 return( retx );
163 }
164 
DrawTab(GWindow pixmap,GTabSet * gts,int i,int x,int y)165 static int DrawTab(GWindow pixmap, GTabSet *gts, int i, int x, int y ) {
166     Color fg = gts->tabs[i].disabled?gts->g.box->disabled_foreground:gts->g.box->main_foreground;
167 
168     if ( fg==COLOR_DEFAULT ) fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap));
169     GBoxDrawTabOutline(pixmap,&gts->g,x,y,gts->tabs[i].width,gts->rowh,i==gts->sel);
170 
171     if ( (i==gts->sel) && (gts->g.box->flags&box_active_border_inner) ) {
172         GRect r;
173         r.x = x+2;
174         r.y = y+1;
175         r.width = gts->tabs[i].width-4;
176         r.height = gts->rowh-2;
177         GDrawFillRect(pixmap,&r,gts->g.box->active_border);
178     }
179 
180     int nx = x+(gts->tabs[i].width-gts->tabs[i].tw)/2;
181     int ny = y+gts->rowh-gts->ds;
182     int nx1 = GDrawDrawText(pixmap,nx,ny,gts->tabs[i].name,-1,fg);
183     if (gts->closable) {
184         nx1 += (GTS_TABPADDING/2-5);
185         Color xcol = GResourceFindColor("GTabSet.CloseColor",0xff0000);
186         GDrawDrawLine(pixmap,nx+nx1,ny,nx+nx1+10,ny-10,xcol);
187         GDrawDrawLine(pixmap,nx+nx1,ny-10,nx+nx1+10,ny,xcol);
188     }
189     gts->tabs[i].x = x;
190     x += gts->tabs[i].width;
191 return( x );
192 }
193 
gtabset_expose(GWindow pixmap,GGadget * g,GEvent * event)194 static int gtabset_expose(GWindow pixmap, GGadget *g, GEvent *event) {
195     GTabSet *gts = (GTabSet *) g;
196     int x,y,i,rd, dsel;
197     GRect old1, bounds;
198     int bw = GBoxBorderWidth(pixmap,g->box);
199     int yoff = ( gts->rcnt==1 ? bw : 0 );
200     Color fg;
201     int ni = GDrawPointsToPixels(pixmap,NEST_INDENT);
202 
203     if ( g->state == gs_invisible )
204 return( false );
205 
206     GDrawPushClip(pixmap,&g->r,&old1);
207 
208     GBoxDrawBackground(pixmap,&g->r,g->box,g->state,false);
209     bounds = g->r;
210 
211     if ( !gts->vertical ) {
212 	/* make room for tabs */
213 	bounds.y += gts->rcnt*gts->rowh+yoff-1;
214 	bounds.height -= gts->rcnt*gts->rowh+yoff-1;
215 	/* draw border around horizontal tabs only */
216 	GBoxDrawBorder(pixmap,&bounds,g->box,g->state,false);
217     }
218     else if ( g->state==gs_enabled ) {
219 	/* background for labels */
220 	GRect old2, vertListRect = g->r;
221 	vertListRect.width = gts->vert_list_width+bw-1;
222 	GDrawPushClip(pixmap,&vertListRect,&old2);
223 	GBoxDrawBackground(pixmap,&g->r,g->box,gs_pressedactive,false);
224 	GDrawPopClip(pixmap,&old2);
225     }
226 
227     GDrawSetFont(pixmap,gts->font);
228 
229     if ( gts->vertical ) {
230 	x = g->r.x + bw + 3;
231 	y = g->r.y + bw + 3;
232 	for ( i=gts->offtop; i<gts->tabcnt; ++i ) {
233 	    fg = gts->tabs[i].disabled?gts->g.box->disabled_foreground:gts->g.box->main_foreground;
234 	    if ( fg==COLOR_DEFAULT ) fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap));
235 	    if ( i==gts->sel ) {
236 		GRect r;
237 		r.x = x; r.y = y;
238 		r.width = gts->vert_list_width-10; r.height = gts->fh;
239 		GDrawFillRect(pixmap,&r,gts->g.box->active_border);
240 	    }
241 	    GDrawDrawText(pixmap,x+gts->tabs[i].nesting*ni,y + gts->as,gts->tabs[i].name,-1,fg);
242 	    y += gts->fh;
243 	}
244 	fg = gts->g.box->main_foreground;
245 	if ( fg==COLOR_DEFAULT ) fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap));
246 	GDrawDrawLine(pixmap,x + gts->vert_list_width-4, g->r.y + bw,
247 			     x + gts->vert_list_width-4, g->r.y + g->r.height - (bw+1),
248 			     fg);
249 	if ( gts->vsb != NULL )
250 	    gts->vsb->funcs->handle_expose(pixmap,gts->vsb,event);
251     } else {
252 	gts->haslarrow = gts->hasrarrow = false;
253 	if ( gts->scrolled ) {
254 	    x = g->r.x + GBoxBorderWidth(pixmap,gts->g.box);
255 	    y = g->r.y+yoff;
256 	    dsel = 0;
257 	    if ( gts->toff!=0 )
258 		x = DrawLeftArrowTab(pixmap,gts,x,y);
259 	    for ( i=gts->toff;
260 		    (i==gts->tabcnt-1 && x+gts->tabs[i].width < g->r.width) ||
261 		    (i<gts->tabcnt-1 && x+gts->tabs[i].width < g->r.width-gts->arrow_width ) ;
262 		    ++i ) {
263 		if ( i!=gts->sel )
264 		    x = DrawTab(pixmap,gts,i,x,y);
265 		else {
266 		    gts->tabs[i].x = x;
267 		    x += gts->tabs[i].width;
268 		    dsel = 1;
269 		}
270 	    }
271 	    if ( i!=gts->tabcnt ) {
272 		int p = gts->g.inner.x+gts->g.inner.width - gts->arrow_width - GTS_TABPADDING;
273 		if ( p>x ) x=p;
274 		x = DrawRightArrowTab(pixmap,gts,x,y);
275 		gts->tabs[i].x = 0x7fff;
276 	    }
277 	    /* This one draws on top of the others, must come last */
278 	    if ( dsel )
279 		DrawTab(pixmap,gts,gts->sel,gts->tabs[gts->sel].x, g->r.y + (gts->rcnt-1) * gts->rowh + yoff );
280 	} else {
281 	    /* r is real row, rd is drawn pos */
282 	    /* rd is 0 at the top of the ggadget */
283 	    /* r is 0 when it contains tabs[0], (the index in the rowstarts array) */
284 	    for ( rd = 0; rd<gts->rcnt; ++rd ) {
285 		int r = (gts->rcnt-1-rd+gts->active_row)%gts->rcnt;
286 		y = g->r.y + rd * gts->rowh + yoff;
287 		x = g->r.x + (gts->rcnt-1-rd) * gts->offset_per_row + GBoxBorderWidth(pixmap,gts->g.box);
288 		for ( i = gts->rowstarts[r]; i<gts->rowstarts[r+1]; ++i )
289 		    if ( i==gts->sel ) {
290 			gts->tabs[i].x = x;
291 			x += gts->tabs[i].width;
292 		    } else
293 			x = DrawTab(pixmap,gts,i,x,y);
294 	    }
295 	    /* This one draws on top of the others, must come last */
296 	    DrawTab(pixmap,gts,gts->sel,gts->tabs[gts->sel].x, g->r.y + (gts->rcnt-1) * gts->rowh + yoff );
297 	}
298     }
299     if ( gts->nested_expose )
300 	(gts->nested_expose)(pixmap,g,event);
301     GDrawPopClip(pixmap,&old1);
302 return( true );
303 }
304 
GTabSetRCnt(GTabSet * gts,int totwidth)305 static int GTabSetRCnt(GTabSet *gts, int totwidth) {
306     int i, off, r, width;
307     int bp = GBoxBorderWidth(gts->g.base,gts->g.box) + GDrawPointsToPixels(gts->g.base,5);
308 
309     width = totwidth;
310     for ( i = off = r = 0; i<gts->tabcnt; ++i ) {
311 	if ( off!=0 && width-(gts->tabs[i].tw+2*bp)< 0 ) {
312 	    off = 0; ++r;
313 	    width = totwidth;
314 	}
315 	width -= gts->tabs[i].width;
316 	gts->tabs[i].x = off;
317 	off ++;
318     }
319 return( r+1 );
320 }
321 
GTabSetGetLineWidth(GTabSet * gts,int r)322 static int GTabSetGetLineWidth(GTabSet *gts,int r) {
323     int i, width = 0;
324 
325     for ( i=gts->rowstarts[r]; i<gts->rowstarts[r+1]; ++i )
326 	width += gts->tabs[i].width;
327 return( width );
328 }
329 
GTabSetDistributePixels(GTabSet * gts,int r,int widthdiff)330 static void GTabSetDistributePixels(GTabSet *gts,int r, int widthdiff) {
331     int diff, off, i;
332 
333     diff = widthdiff/(gts->rowstarts[r+1]-gts->rowstarts[r]);
334     off = widthdiff-diff*(gts->rowstarts[r+1]-gts->rowstarts[r]);
335     for ( i=gts->rowstarts[r]; i<gts->rowstarts[r+1]; ++i ) {
336 	gts->tabs[i].width += diff;
337 	if ( off ) {
338 	    ++gts->tabs[i].width;
339 	    --off;
340 	}
341     }
342 }
343 
344 /* We have rearranged the rows of tabs. We want to make sure that each row */
345 /*  row is at least as wide as the row above it */
GTabSetFigureWidths(GTabSet * gts)346 static void GTabSetFigureWidths(GTabSet *gts) {
347     int bp = GBoxBorderWidth(gts->g.base,gts->g.box) + GDrawPointsToPixels(gts->g.base,5);
348     int i, rd;
349     int oldwidth=0, width;
350 
351     /* set all row widths to default values */
352     for ( i=0; i<gts->tabcnt; ++i ) {
353 	gts->tabs[i].width = gts->tabs[i].tw + 2*bp;
354     }
355     /* r is real row, rd is drawn pos */
356     /* rd is 0 at the top of the ggadget */
357     /* r is 0 when it contains tabs[0], (the index in the rowstarts array) */
358     if ( ( gts->filllines && gts->rcnt>1 ) || (gts->fill1line && gts->rcnt==1) ) {
359 	for ( rd = 0; rd<gts->rcnt; ++rd ) {
360 	    int r = (rd+gts->rcnt-1-gts->active_row)%gts->rcnt;
361 	    int totwidth = gts->g.r.width-2*GBoxBorderWidth(gts->g.base,gts->g.box) -
362 		    (gts->rcnt-1-rd)*gts->offset_per_row;
363 	    width = GTabSetGetLineWidth(gts,r);
364 	    GTabSetDistributePixels(gts,r,totwidth-width);
365 	}
366     } else {
367 	for ( rd = 0; rd<gts->rcnt; ++rd ) {		/* r is real row, rd is drawn pos */
368 	    int r = (rd+gts->rcnt-1-gts->active_row)%gts->rcnt;
369 	    width = GTabSetGetLineWidth(gts,r) + (gts->rcnt-1-rd)*gts->offset_per_row;
370 	    if ( rd==0 )
371 		oldwidth = width;
372 	    else if ( oldwidth>width )
373 		GTabSetDistributePixels(gts,r,oldwidth-width);
374 	    else
375 		oldwidth = width;
376 	}
377     }
378 }
379 
380 /* Something happened which would change how many things fit on a line */
381 /*  (initialization, change of font, addition or removal of tab, etc.) */
382 /* Figure out how many rows we need and then how best to divide the tabs */
383 /*  between those rows, and then the widths of each tab */
GTabSet_Remetric(GTabSet * gts)384 static void GTabSet_Remetric(GTabSet *gts) {
385     int bbp = GBoxBorderWidth(gts->g.base,gts->g.box);
386     int bp = bbp + GDrawPointsToPixels(gts->g.base,5);
387     int r, r2, width, i;
388     int as, ds, ld;
389     int ni = GDrawPointsToPixels(gts->g.base,NEST_INDENT), in;
390 
391     GDrawSetFont(gts->g.base,gts->font);
392     GDrawWindowFontMetrics(gts->g.base,gts->font,&as,&ds,&ld);
393     gts->as = as; gts->fh = as+ds;
394     gts->rowh = as+ds + bbp+GDrawPointsToPixels(gts->g.base,3);
395     gts->ds = ds + bbp+GDrawPointsToPixels(gts->g.base,1);
396     gts->arrow_size = as+ds;
397     gts->arrow_width = gts->arrow_size + 2*GBoxBorderWidth(gts->g.base,gts->g.box);
398     gts->vert_list_width = 0;
399 
400     for ( i=0; i<gts->tabcnt; ++i ) {
401 	gts->tabs[i].tw = GDrawGetTextWidth(gts->g.base,gts->tabs[i].name,-1);
402 	gts->tabs[i].width = gts->closable ? GTS_TABPADDING : 0;
403 	gts->tabs[i].width+= gts->tabs[i].tw + 2*bp;
404 	in = gts->tabs[i].nesting*ni;
405 	if ( gts->tabs[i].tw+in > gts->vert_list_width )
406 	    gts->vert_list_width = gts->tabs[i].tw+in;
407     }
408     if ( gts->vsb ) {
409 	gts->vert_list_width += gts->vsb->r.width;
410 	if ( gts->g.inner.height>26 ) {
411 	    int bp = GBoxBorderWidth(gts->g.base,gts->g.box);
412 	    GScrollBarSetBounds(gts->vsb,0,gts->tabcnt,(gts->g.r.height-2*bp-6)/gts->fh);
413 	}
414     }
415     gts->vert_list_width += 8;
416 
417     if ( gts->vertical ) {
418 	/* Nothing much to do */
419     } else if ( gts->scrolled ) {
420 	free(gts->rowstarts);
421 	gts->rowstarts = malloc(2*sizeof(int16));
422 	gts->rowstarts[0] = 0; gts->rowstarts[1] = gts->tabcnt;
423 	gts->rcnt = 1;
424     } else {
425 	width = gts->g.r.width-2*GBoxBorderWidth(gts->g.base,gts->g.box);
426 	r = GTabSetRCnt(gts,width);
427 	if ( gts->offset_per_row!=0 && r>1 )
428 	    while ( (r2 = GTabSetRCnt(gts,width-(r-1)*gts->offset_per_row))!=r )
429 		r = r2;
430 	free(gts->rowstarts);
431 	gts->rowstarts = malloc((r+1)*sizeof(int16));
432 	gts->rcnt = r;
433 	gts->rowstarts[r] = gts->tabcnt;
434 	for ( i=r=0; i<gts->tabcnt; ++i ) {
435 	    if ( gts->tabs[i].x==0 )
436 		gts->rowstarts[r++] = i;
437 	}
438 	/* if there is a single tab on the last line and there are more on */
439 	/*  the previous line, then things look nicer if we move one of the */
440 	/*  tabs from the previous line onto the last line */
441 	/*  Providing it fits, of course */
442 	if ( gts->rowstarts[r]-gts->rowstarts[r-1]==1 && r>1 &&
443 		gts->rowstarts[r-1]-gts->rowstarts[r-2]>1 &&
444 		gts->tabs[i-1].width+gts->tabs[i-2].width < width-(r-1)*gts->offset_per_row )
445 	    --gts->rowstarts[r-1];
446 
447 	GTabSetFigureWidths(gts);
448     }
449 }
450 
GTabSetChangeSel(GTabSet * gts,int sel,int sendevent)451 static void GTabSetChangeSel(GTabSet *gts, int sel,int sendevent) {
452     int i, width;
453     int oldsel = gts->sel;
454 
455     if ( sel==-2 )		/* left arrow */
456 	--gts->toff;
457     else if ( sel==-3 )
458 	++gts->toff;
459     else if ( sel<0 || sel>=gts->tabcnt || gts->tabs[sel].disabled )
460 return;
461     else {
462 	if ( gts->vertical )
463 	    gts->sel = sel;
464 	else {
465 	    for ( i=0; i<gts->rcnt && sel>=gts->rowstarts[i+1]; ++i );
466 	    if ( gts->active_row != i ) {
467 		gts->active_row = i;
468 		if ( gts->rcnt>1 && (!gts->filllines || gts->offset_per_row!=0))
469 		    GTabSetFigureWidths(gts);
470 	    }
471 	    gts->sel = sel;
472 	    if ( sel<gts->toff )
473 		gts->toff = sel;
474 	    else if ( gts->scrolled ) {
475 		for ( i=gts->toff; i<sel && gts->tabs[i].x!=0x7fff; ++i );
476 		if ( gts->tabs[i].x==0x7fff ) {
477 		    width = gts->g.r.width-2*gts->arrow_width;	/* it will have a left arrow */
478 		    if ( sel!=gts->tabcnt )
479 			width -= gts->arrow_width;		/* it might have a right arrow */
480 		    for ( i=sel; i>=0 && width-gts->tabs[i].width>=0; --i )
481 			width -= gts->tabs[i].width;
482 		    if ( ++i>sel ) i = sel;
483 		    gts->toff = i;
484 		}
485 	    }
486 	}
487 	if ( oldsel!=sel ) {
488 	    if ( sendevent )
489 		GTabSetChanged(gts,oldsel);
490 	    if ( gts->tabs[oldsel].w!=NULL )
491 		GDrawSetVisible(gts->tabs[oldsel].w,false);
492 	    if ( gts->tabs[gts->sel].w!=NULL )
493 		GDrawSetVisible(gts->tabs[gts->sel].w,true);
494 	}
495     }
496     _ggadget_redraw(&gts->g);
497 }
498 
gtabset_mouse(GGadget * g,GEvent * event)499 static int gtabset_mouse(GGadget *g, GEvent *event) {
500     GTabSet *gts = (GTabSet *) g;
501 
502     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
503 return( false );
504     if ( gts->nested_mouse!=NULL )
505 	if ( (gts->nested_mouse)(g,event))
506 return( true );
507 
508     if ( event->type == et_crossing ) {
509 return( true );
510     } else if ( event->type == et_mousemove ) {
511 return( true );
512     } else {
513 	int i = -1, sel= -1, l;
514 	if ( event->u.mouse.y < gts->g.r.y ||
515 		( !gts->vertical && event->u.mouse.y >= gts->g.inner.y ) ||
516 		( gts->vertical && event->u.mouse.x >= gts->g.inner.x ))
517 return( false );
518 	else if ( gts->vertical ) {
519 	    int y = g->r.y + GBoxBorderWidth(g->base,g->box) + 5;
520 	    sel = (event->u.mouse.y-y)/gts->fh + gts->offtop;
521 	    if ( sel<0 || sel>=gts->tabcnt )
522 return(false);
523 	} else if ( gts->scrolled ) {
524 	    if ( gts->haslarrow && event->u.mouse.x<gts->tabs[gts->toff].x )
525 		sel = -2;	/* left arrow */
526 	    else {
527 		for ( i=gts->toff;
528 			i<gts->tabcnt && event->u.mouse.x>=gts->tabs[i].x+gts->tabs[i].width;
529 			++i );
530 		if ( gts->hasrarrow && gts->tabs[i].x==0x7fff &&
531 			event->u.mouse.x>=gts->tabs[i-1].x+gts->tabs[i-1].width )
532 		    sel = -3;
533 		else
534 		    sel = i;
535 	    }
536         if ( i <= gts->tabcnt && i >= 0 )
537 		if ( gts->closable && event->type==et_mouseup && event->u.mouse.x>=gts->tabs[i].x+gts->tabs[i].width+(-GTS_TABPADDING/2-10) ) {
538 			TRACE("Closing tab %d\n", sel);
539 			GTabSetRemoveTabByPos(&gts->g, i);
540 			GTabSetRemetric(&gts->g);
541 			GGadgetRedraw(&gts->g);
542 			// If we removed a tab before us, the selected tab is now one behind.
543 			if (i < gts->sel) --gts->sel;
544 			GTabSetChangeSel(gts,gts->sel,true); /* this redraws the tab set */
545 			return true;
546 		} else if (event->type == et_mousedown && sel >= 0) {
547 			gts->oldsel = sel;
548 		} else if (event->type == et_mouseup && sel >= 0) {
549 			if ( sel != gts->oldsel && gts->movable ) {
550 				TRACE("Swapping tabs: A=%d & B=%d\n", gts->oldsel, sel);
551 				GTabSetSwapTabs(&gts->g, gts->oldsel, sel);
552 			}
553 			gts->oldsel = sel;
554 		}
555 	} else {
556 	    l = (event->u.mouse.y-gts->g.r.y)/gts->rowh;	/* screen row */
557 	    if ( l>=gts->rcnt ) l = gts->rcnt-1;		/* can happen on single line tabsets (there's extra space then) */
558 	    l = (gts->rcnt-1-l+gts->active_row)%gts->rcnt;	/* internal row number */
559 	    if ( event->u.mouse.x<gts->tabs[gts->rowstarts[l]].x )
560 		sel = -1;
561 	    else if ( event->u.mouse.x>=gts->tabs[gts->rowstarts[l+1]-1].x+gts->tabs[gts->rowstarts[l+1]-1].width )
562 		sel = -1;
563 	    else {
564 		for ( i=gts->rowstarts[l]; i<gts->rowstarts[l+1] &&
565 			event->u.mouse.x>=gts->tabs[i].x+gts->tabs[i].width; ++i );
566 		sel = i;
567 	    }
568 	}
569 	if ( event->type==et_mousedown ) {
570 	    gts->pressed = true;
571 	    gts->pressed_sel = sel;
572 	} else {
573 	    if ( gts->pressed && gts->pressed_sel == sel )
574 		GTabSetChangeSel(gts,sel,true);
575 	    gts->pressed = false;
576 	    gts->pressed_sel = -1;
577 	}
578     }
579 return( true );
580 }
581 
gtabset_key(GGadget * g,GEvent * event)582 static int gtabset_key(GGadget *g, GEvent *event) {
583     GTabSet *gts = (GTabSet *) g;
584     int i;
585 
586     if ( !g->takes_input || !g->takes_keyboard || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
587 return(false);
588     if ( event->type == et_charup )
589 return( true );
590 
591     if (event->u.chr.keysym == GK_Left || event->u.chr.keysym == GK_KP_Left ||
592 	    (event->u.chr.keysym == GK_Tab && (event->u.chr.state&ksm_shift)) ||
593 	    event->u.chr.keysym == GK_BackTab ||
594 	    event->u.chr.keysym == GK_Up || event->u.chr.keysym == GK_KP_Up ) {
595 	for ( i = gts->sel-1; i>0 && gts->tabs[i].disabled; --i );
596 	GTabSetChangeSel(gts,i,true);
597 return( true );
598     } else if (event->u.chr.keysym == GK_Right || event->u.chr.keysym == GK_KP_Right ||
599 	    event->u.chr.keysym == GK_Tab ||
600 	    event->u.chr.keysym == GK_Down || event->u.chr.keysym == GK_KP_Down ) {
601 	for ( i = gts->sel+1; i<gts->tabcnt-1 && gts->tabs[i].disabled; ++i );
602 	GTabSetChangeSel(gts,i,true);
603 return( true );
604     }
605 return( false );
606 }
607 
gtabset_focus(GGadget * g,GEvent * UNUSED (event))608 static int gtabset_focus(GGadget *g, GEvent *UNUSED(event)) {
609 
610     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active ))
611 return(false);
612 
613 return( true );
614 }
615 
gtabset_destroy(GGadget * g)616 static void gtabset_destroy(GGadget *g) {
617     GTabSet *gts = (GTabSet *) g;
618     int i;
619 
620     if ( gts==NULL )
621 return;
622     free(gts->rowstarts);
623     for ( i=0; i<gts->tabcnt; ++i ) {
624 	free(gts->tabs[i].name);
625 /* This has already been done */
626 /*	if ( gts->tabs[i].w!=NULL ) */
627 /*	    GDrawDestroyWindow(gts->tabs[i].w); */
628     }
629     free(gts->tabs);
630     _ggadget_destroy(g);
631 }
632 
GTabSetSetFont(GGadget * g,FontInstance * new)633 static void GTabSetSetFont(GGadget *g,FontInstance *new) {
634     GTabSet *gts = (GTabSet *) g;
635     gts->font = new;
636     GTabSet_Remetric(gts);
637 }
638 
GTabSetGetFont(GGadget * g)639 static FontInstance *GTabSetGetFont(GGadget *g) {
640     GTabSet *gts = (GTabSet *) g;
641 return( gts->font );
642 }
643 
_gtabset_redraw(GGadget * g)644 static void _gtabset_redraw(GGadget *g) {
645     GTabSet *gts = (GTabSet *) g;
646     int i;
647 
648     GDrawRequestExpose(g->base, &g->r, false);
649     i = gts->sel;
650     if ( gts->tabs[i].w!=NULL )
651 	GDrawRequestExpose(gts->tabs[i].w, NULL, false);
652 }
653 
_gtabset_move(GGadget * g,int32 x,int32 y)654 static void _gtabset_move(GGadget *g, int32 x, int32 y ) {
655     GTabSet *gts = (GTabSet *) g;
656     int i;
657     int32 nx = x+g->inner.x-g->r.x, ny = y+g->inner.y-g->r.y;
658 
659     for ( i=0; i<gts->tabcnt; ++i ) if ( gts->tabs[i].w!=NULL )
660 	GDrawMove(gts->tabs[i].w,nx,ny);
661     _ggadget_move(g,x,y);
662     if ( gts->vsb!=NULL ) {
663 	int bp = GBoxBorderWidth(gts->g.base,gts->g.box);
664 	GGadgetMove(gts->vsb,g->r.x +bp+ gts->vert_list_width - gts->vsb->r.width,
665 		    gts->g.r.y+bp);
666     }
667 }
668 
_gtabset_resize(GGadget * g,int32 width,int32 height)669 static void _gtabset_resize(GGadget *g, int32 width, int32 height ) {
670     GTabSet *gts = (GTabSet *) g;
671     int i;
672 
673     _ggadget_resize(g,width,height);
674     for ( i=0; i<gts->tabcnt; ++i ) if ( gts->tabs[i].w!=NULL )
675 	GDrawResize(gts->tabs[i].w,g->inner.width,g->inner.height);
676     if ( gts->vsb!=NULL ) {
677 	int off = gts->offtop;
678 	int bp = GBoxBorderWidth(gts->g.base,gts->g.box);
679 	GGadgetResize(gts->vsb,gts->vsb->r.width, gts->g.r.height-2*bp);
680 	GScrollBarSetBounds(gts->vsb,0,gts->tabcnt,(gts->g.r.height-2*bp-6)/gts->fh);
681 	if ( gts->offtop + (gts->g.r.height-2*bp-6)/gts->fh > gts->tabcnt )
682 	    off = gts->tabcnt - (gts->g.r.height-2*bp-6)/gts->fh;
683 	if ( off<0 )
684 	    off = 0;
685 	if ( off!=gts->offtop ) {
686 	    gts->offtop = off;
687 	    GScrollBarSetPos(gts->vsb, off );
688 	    GGadgetRedraw(&gts->g);
689 	}
690     }
691 }
692 
_gtabset_setvisible(GGadget * g,int visible)693 static void _gtabset_setvisible(GGadget *g,int visible) {
694     GTabSet *gts = (GTabSet *) g;
695 
696     _ggadget_setvisible(g,visible);
697     if ( gts->tabs[gts->sel].w!=NULL )
698 	GDrawSetVisible(gts->tabs[gts->sel].w, visible);
699     if ( gts->vsb!=NULL )
700 	GGadgetSetVisible(gts->vsb,visible);
701 }
702 
gtabset_FillsWindow(GGadget * g)703 static int gtabset_FillsWindow(GGadget *g) {
704 return( g->prev==NULL && _GWidgetGetGadgets(g->base)==g );
705 }
706 
gtabset_GetDesiredSize(GGadget * g,GRect * outer,GRect * inner)707 static void gtabset_GetDesiredSize(GGadget *g, GRect *outer, GRect *inner) {
708     GTabSet *gts = (GTabSet *) g;
709     int bp = GBoxBorderWidth(g->base,g->box);
710     GRect nested, test;
711     int i;
712 
713     memset(&nested,0,sizeof(nested));
714     for ( i=0; i<gts->tabcnt; ++i ) {
715 	GGadget *last = _GWidgetGetGadgets(gts->tabs[i].w);
716 	if ( last!=NULL ) {
717 	    while ( last->prev!=NULL )
718 		last=last->prev;
719 	    GGadgetGetDesiredSize(last,&test,NULL);
720 	    if ( GGadgetFillsWindow(last)) {
721 		test.width += 2*last->r.x;
722 		test.height += 2*last->r.y;
723 	    }
724 	    if ( test.width>nested.width ) nested.width = test.width;
725 	    if ( test.height>nested.height ) nested.height = test.height;
726 	}
727     }
728     if ( gts->vertical ) {
729 	if ( gts->vsb==NULL && gts->rcnt*gts->fh+10 > nested.height )
730 	    nested.height = gts->tabcnt*gts->fh+10;
731 	else if ( gts->vsb!=NULL && 2*gts->vsb->r.width + 20 > nested.height )
732 	    nested.height = 2*gts->vsb->r.width + 20;	/* Minimum size for scrollbar arrows, vaguely */
733     }
734 
735     if ( g->desired_width>=0 ) nested.width = g->desired_width - 2*bp;
736     if ( g->desired_height>=0 ) nested.height = g->desired_height - 2*bp;
737     if ( nested.width==0 ) nested.width = 100;
738     if ( nested.height==0 ) nested.height = 100;
739 
740     if ( inner != NULL )
741 	*inner = nested;
742     if ( gts->vertical ) {
743 	if ( outer != NULL ) {
744 	    *outer = nested;
745 	    outer->width += gts->vert_list_width + 2*bp;
746 	    outer->height += 2*bp;
747 	}
748     } else {
749 	if ( outer != NULL ) {
750 	    *outer = nested;
751 	    outer->width += 2*bp;
752 	    outer->height += gts->rcnt*gts->rowh + bp;
753 	}
754     }
755 }
756 
757 struct gfuncs gtabset_funcs = {
758     0,
759     sizeof(struct gfuncs),
760 
761     gtabset_expose,
762     gtabset_mouse,
763     gtabset_key,
764     NULL,
765     gtabset_focus,
766     NULL,
767     NULL,
768 
769     _gtabset_redraw,
770     _gtabset_move,
771     _gtabset_resize,
772     _gtabset_setvisible,
773     _ggadget_setenabled,		/* Doesn't work right */
774     _ggadget_getsize,
775     _ggadget_getinnersize,
776 
777     gtabset_destroy,
778 
779     NULL,
780     NULL,
781     NULL,
782     NULL,
783     NULL,
784     GTabSetSetFont,
785     GTabSetGetFont,
786 
787     NULL,
788     NULL,
789     NULL,
790     NULL,
791     NULL,
792     NULL,
793     NULL,
794     NULL,
795     NULL,
796     NULL,
797     NULL,
798 
799     gtabset_GetDesiredSize,
800     _ggadget_setDesiredSize,
801     gtabset_FillsWindow,
802     NULL
803 };
804 
sendtoparent_eh(GWindow gw,GEvent * event)805 static int sendtoparent_eh(GWindow gw, GEvent *event) {
806     switch ( event->type ) {
807       case et_controlevent: case et_char: case et_drop:
808 	event->w = GDrawGetParentWindow(gw);
809 	GDrawPostEvent(event);
810       break;
811       case et_resize:
812 	GDrawRequestExpose(gw,NULL,false);
813       break;
814     }
815 
816 return( true );
817 }
818 
gtabset_vscroll(GGadget * g,GEvent * event)819 static int gtabset_vscroll(GGadget *g, GEvent *event) {
820     enum sb sbt = event->u.control.u.sb.type;
821     GTabSet *gts = (GTabSet *) (g->data);
822     int loff = gts->offtop;
823     int page = (g->inner.height-6)/gts->fh- ((g->inner.height-6)/gts->fh>2?1:0);
824 
825     if ( sbt==et_sb_top )
826 	loff = 0;
827     else if ( sbt==et_sb_bottom ) {
828 	loff = gts->tabcnt - (gts->g.inner.height-6)/gts->fh;
829     } else if ( sbt==et_sb_up ) {
830 	--loff;
831     } else if ( sbt==et_sb_down ) {
832 	++loff;
833     } else if ( sbt==et_sb_uppage ) {
834 	loff -= page;
835     } else if ( sbt==et_sb_downpage ) {
836 	loff += page;
837     } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
838 	loff = event->u.control.u.sb.pos;
839     }
840     if ( loff + (gts->g.inner.height-6)/gts->fh > gts->tabcnt )
841 	loff = gts->tabcnt - (gts->g.inner.height-6)/gts->fh;
842     if ( loff<0 )
843 	loff = 0;
844     if ( loff!=gts->offtop ) {
845 	gts->offtop = loff;
846 	GScrollBarSetPos(gts->vsb, loff );
847 	GGadgetRedraw(&gts->g);
848     }
849 return( true );
850 }
851 
GTabSetCreate(struct gwindow * base,GGadgetData * gd,void * data)852 GGadget *GTabSetCreate(struct gwindow *base, GGadgetData *gd,void *data) {
853     GTabSet *gts = calloc(1,sizeof(GTabSet));
854     int i, bp;
855     GRect r;
856     GWindowAttrs childattrs;
857 
858     memset(&childattrs,0,sizeof(childattrs));
859     childattrs.mask = wam_events;
860     childattrs.event_masks = -1;
861 
862     if ( !gtabset_inited )
863 	GTabSetInit();
864     gts->g.funcs = &gtabset_funcs;
865     _GGadget_Create(&gts->g,base,gd,data, gd->flags&gg_tabset_vert ? &gvtabset_box  : &gtabset_box);
866     gts->font = gtabset_font;
867 
868     gts->g.takes_input = true; gts->g.takes_keyboard = true; gts->g.focusable = true;
869 
870     GDrawGetSize(base,&r);
871     if ( gd->pos.x <= 0 )
872 	gts->g.r.x = GDrawPointsToPixels(base,2);
873     if ( gd->pos.y <= 0 )
874 	gts->g.r.y = GDrawPointsToPixels(base,2);
875     if ( gd->pos.width<=0 )
876 	gts->g.r.width = r.width - gts->g.r.x - GDrawPointsToPixels(base,2);
877     if ( gd->pos.height<=0 ) {
878 	if ( gd->flags&gg_tabset_nowindow )
879 	    gts->g.r.height = GDrawPointsToPixels(base,20);
880 	else
881 	    gts->g.r.height = r.height - gts->g.r.y - GDrawPointsToPixels(base,26);
882     }
883 
884     for ( i=0; gd->u.tabs[i].text!=NULL; ++i );
885     gts->tabcnt = i;
886     gts->tabs = calloc(i, sizeof(struct tabs));
887     for ( i=0; gd->u.tabs[i].text!=NULL; ++i ) {
888 	if ( gd->u.tabs[i].text_in_resource )
889 	    gts->tabs[i].name = u_copy(GStringGetResource((intpt) (gd->u.tabs[i].text),NULL));
890 	else if ( gd->u.tabs[i].text_is_1byte )
891 	    gts->tabs[i].name = utf82u_copy((char *) (gd->u.tabs[i].text));
892 	else
893 	    gts->tabs[i].name = u_copy(gd->u.tabs[i].text);
894 	gts->tabs[i].disabled = gd->u.tabs[i].disabled;
895 	gts->tabs[i].nesting = gd->u.tabs[i].nesting;
896 	if ( gd->u.tabs[i].selected && !gts->tabs[i].disabled )
897 	    gts->sel = i;
898     }
899     if ( gd->flags&gg_tabset_scroll ) gts->scrolled = true;
900     if ( gd->flags&gg_tabset_filllines ) gts->filllines = true;
901     if ( gd->flags&gg_tabset_fill1line ) gts->fill1line = true;
902     if ( gd->flags&gg_tabset_vert ) gts->vertical = true;
903     gts->offset_per_row = GDrawPointsToPixels(base,2);
904     if ( gts->vertical && gts->scrolled ) {
905 	GGadgetData gd;
906 	memset(&gd,'\0',sizeof(gd));
907 	gd.pos.y = gts->g.r.y; gd.pos.height = gts->g.inner.height;
908 	gd.pos.width = GDrawPointsToPixels(gts->g.base,_GScrollBar_Width);
909 	gd.pos.x = gts->g.inner.x;
910 	gd.flags = (gts->g.state==gs_invisible?0:gg_visible)|gg_enabled|gg_pos_in_pixels|gg_sb_vert;
911 	gd.handle_controlevent = gtabset_vscroll;
912 	gts->vsb = GScrollBarCreate(gts->g.base,&gd,gts);
913 	gts->vsb->contained = true;
914     }
915     GTabSet_Remetric(gts);
916     _GGadget_FinalPosition(&gts->g,base,gd);
917 
918     bp = GBoxBorderWidth(base,gts->g.box);
919     gts->g.inner = gts->g.r;
920     if ( gts->vertical ) {
921 	gts->g.inner.x += bp+gts->vert_list_width; gts->g.inner.width -= 2*bp+gts->vert_list_width;
922 	gts->g.inner.y += bp; gts->g.inner.height -= 2*bp;
923     } else {
924 	gts->g.inner.x += bp; gts->g.inner.width -= 2*bp;
925 	gts->g.inner.y += gts->rcnt*gts->rowh+bp; gts->g.inner.height -= 2*bp+gts->rcnt*gts->rowh;
926     }
927     if ( gts->rcnt==1 ) {
928 	gts->g.inner.y += bp; gts->g.inner.height -= bp;
929     }
930     if ( gts->vsb!=NULL ) {
931 	GGadgetMove(gts->vsb, gts->g.r.x + bp + gts->vert_list_width - gts->vsb->r.width, gts->g.r.y + bp);
932 	GGadgetResize(gts->vsb, gts->vsb->r.width, gts->g.r.height-2*bp);
933 	if ( gts->g.inner.height>26 )
934 	    GScrollBarSetBounds(gts->vsb,0,gts->tabcnt,(gts->g.r.height-2*bp-6)/gts->fh);
935     }
936 
937     if ( gd->flags&gg_tabset_nowindow ) gts->nowindow = true;
938 
939     for ( i=0; gd->u.tabs[i].text!=NULL; ++i )
940 	if ( !(gd->flags&gg_tabset_nowindow)) {
941 	    gts->tabs[i].w = GDrawCreateSubWindow(base,&gts->g.inner,sendtoparent_eh,GDrawGetUserData(base),&childattrs);
942 	    if ( gd->u.tabs[i].gcd!=NULL )
943 		GGadgetsCreate(gts->tabs[i].w,gd->u.tabs[i].gcd);
944 	    if ( gts->sel==i && (gd->flags & gg_visible ))
945 		GDrawSetVisible(gts->tabs[i].w,true);
946 	} else
947 	    gts->tabs[i].w = NULL;
948 
949     if ( gd->flags & gg_group_end )
950 	_GGadgetCloseGroup(&gts->g);
951 
952     for ( i=0; gd->u.tabs[i].text!=NULL && !gd->u.tabs[i].selected; ++i );
953     if ( i!=0 && gd->u.tabs[i].text!=NULL )
954 	GTabSetChangeSel(gts,i,false);
955 
956 return( &gts->g );
957 }
958 
GTabSetGetTabCount(GGadget * g)959 int GTabSetGetTabCount(GGadget *g) {
960     GTabSet *gts = (GTabSet *) g;
961 return( gts->tabcnt );
962 }
963 
GTabSetGetSel(GGadget * g)964 int GTabSetGetSel(GGadget *g) {
965     GTabSet *gts = (GTabSet *) g;
966 return( gts->sel );
967 }
968 
GTabSetSetSel(GGadget * g,int sel)969 void GTabSetSetSel(GGadget *g,int sel) {
970     GTabSet *gts = (GTabSet *) g;
971     GTabSetChangeSel(gts,sel,false);
972 }
973 
GTabSetSetEnabled(GGadget * g,int pos,int enabled)974 void GTabSetSetEnabled(GGadget *g,int pos,int enabled) {
975     GTabSet *gts = (GTabSet *) g;
976 
977     if ( pos>=0 && pos<gts->tabcnt ) {
978 	gts->tabs[pos].disabled = !enabled;
979     }
980     GDrawRequestExpose(g->base, &g->r, false);
981 }
982 
GTabSetGetSubwindow(GGadget * g,int pos)983 GWindow GTabSetGetSubwindow(GGadget *g,int pos) {
984     GTabSet *gts = (GTabSet *) g;
985 
986     if ( pos>=0 && pos<gts->tabcnt )
987 return( gts->tabs[pos].w );
988 
989 return( NULL );
990 }
991 
GTabSetGetTabLines(GGadget * g)992 int GTabSetGetTabLines(GGadget *g) {
993     GTabSet *gts = (GTabSet *) g;
994 return( gts->rcnt );
995 }
996 
GTabSetSetClosable(GGadget * g,bool flag)997 void GTabSetSetClosable(GGadget *g, bool flag) {
998     GTabSet *gts = (GTabSet *) g;
999 
1000     gts->closable = flag;
1001 }
1002 
GTabSetSetMovable(GGadget * g,bool flag)1003 void GTabSetSetMovable(GGadget *g, bool flag) {
1004     GTabSet *gts = (GTabSet *) g;
1005 
1006     gts->movable = flag;
1007 }
1008 
GTabSetSetNestedExpose(GGadget * g,void (* ne)(GWindow,GGadget *,GEvent *))1009 void GTabSetSetNestedExpose(GGadget *g, void (*ne)(GWindow,GGadget *,GEvent *)) {
1010     GTabSet *gts = (GTabSet *) g;
1011     gts->nested_expose = ne;
1012 }
1013 
GTabSetSetNestedMouse(GGadget * g,int (* nm)(GGadget *,GEvent *))1014 void GTabSetSetNestedMouse(GGadget *g, int (*nm)(GGadget *,GEvent *)) {
1015     GTabSet *gts = (GTabSet *) g;
1016     gts->nested_mouse = nm;
1017 }
1018 
GTabSetSetRemoveSync(GGadget * g,void (* rs)(GWindow gw,int pos))1019 void GTabSetSetRemoveSync(GGadget *g, void (*rs)(GWindow gw, int pos)) {
1020     GTabSet *gts = (GTabSet *) g;
1021     gts->remove_sync = rs;
1022 }
1023 
GTabSetSetSwapSync(GGadget * g,void (* ss)(GWindow gw,int pos_a,int pos_b))1024 void GTabSetSetSwapSync(GGadget *g, void (*ss)(GWindow gw, int pos_a, int pos_b)) {
1025     GTabSet *gts = (GTabSet *) g;
1026     gts->swap_sync = ss;
1027 }
1028 
1029 // This function also adds tabs, a GTabSetAddTab of sorts.
GTabSetChangeTabName(GGadget * g,const char * name,int pos)1030 void GTabSetChangeTabName(GGadget *g, const char *name, int pos) {
1031     GTabSet *gts = (GTabSet *) g;
1032 
1033     if ( pos==gts->tabcnt && gts->nowindow ) {
1034 	gts->tabs = realloc(gts->tabs,(pos+1)*sizeof(struct tabs));
1035 	memset(&gts->tabs[pos],0,sizeof(struct tabs));
1036 	++gts->tabcnt;
1037     }
1038     if ( pos<gts->tabcnt ) {
1039 	free(gts->tabs[pos].name);
1040 	gts->tabs[pos].name = utf82u_copy(name);
1041     }
1042 }
1043 
GTabSetRemetric(GGadget * g)1044 void GTabSetRemetric(GGadget *g) {
1045     GTabSet *gts = (GTabSet *) g;
1046     GTabSet_Remetric(gts);
1047 }
1048 
GTabSetRemoveTabByPos(GGadget * g,int pos)1049 void GTabSetRemoveTabByPos(GGadget *g, int pos) {
1050     GTabSet *gts = (GTabSet *) g;
1051     int i;
1052 
1053     if ( gts->nowindow && pos>=0 && pos<gts->tabcnt && gts->tabcnt>1 ) {
1054 	free(gts->tabs[pos].name);
1055 	for ( i=pos+1; i<gts->tabcnt; ++i )
1056 	    gts->tabs[i-1] = gts->tabs[i];
1057 	--gts->tabcnt;
1058 	/* CharView keeps its own tabs array with its own private data.
1059 	 * This gives us a way to realign that array upon changes to ours. */
1060 	if (gts->remove_sync != NULL) (gts->remove_sync)(gts->g.base, pos);
1061 	if ( gts->sel==pos ) {
1062 	    if ( gts->sel==gts->tabcnt )
1063 		--gts->sel;
1064 	    GTabSetChanged(gts,pos);
1065 	}
1066     }
1067 }
1068 
GTabSetSwapTabs(GGadget * g,int pos_a,int pos_b)1069 void GTabSetSwapTabs(GGadget *g, int pos_a, int pos_b) {
1070     GTabSet *gts = (GTabSet *) g;
1071     struct tabs temp;
1072     temp = gts->tabs[pos_a];
1073     gts->tabs[pos_a] = gts->tabs[pos_b];
1074     gts->tabs[pos_b] = temp;
1075 	if (gts->swap_sync != NULL) (gts->swap_sync)(gts->g.base, pos_a, pos_b);
1076     if (gts->sel == pos_b)
1077     GTabSetSetSel(g, pos_a);	/* This does a redraw */
1078     else if (gts->sel == pos_a)
1079     GTabSetSetSel(g, pos_b);	/* This does a redraw */
1080     else
1081     GTabSetChanged(gts,gts->sel);
1082 }
1083 
GTabSetRemoveTabByName(GGadget * g,char * name)1084 void GTabSetRemoveTabByName(GGadget *g, char *name) {
1085     GTabSet *gts = (GTabSet *) g;
1086     int pos;
1087     unichar_t *uname = utf82u_copy(name);
1088 
1089     for ( pos=0; pos<gts->tabcnt; ++pos ) {
1090 	if ( u_strcmp(uname,gts->tabs[pos].name)==0 ) {
1091 	    GTabSetRemoveTabByPos(g,pos);
1092     break;
1093 	}
1094     }
1095 
1096     free(uname);
1097 }
1098 
_GTabSetRIHead(void)1099 GResInfo *_GTabSetRIHead(void) {
1100     if ( !gtabset_inited )
1101 	GTabSetInit();
1102 return( &gtabset_ri );
1103 }
1104