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 >abset_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, >abset_ri, >abset_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(>abset_box);
94 gtabset_box.border_width = 1; gtabset_box.border_shape = bs_rect;
95 /*gtabset_box.flags = 0;*/
96 gtabset_font = _GGadgetInitDefaultBox("GTabSet.",>abset_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 = >s->g;
111 if ( gts->g.handle_controlevent != NULL )
112 (gts->g.handle_controlevent)(>s->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,>s->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,>s->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,>s->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(>s->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(>s->g, i);
540 GTabSetRemetric(>s->g);
541 GGadgetRedraw(>s->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(>s->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(>s->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(>s->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 = >abset_funcs;
865 _GGadget_Create(>s->g,base,gd,data, gd->flags&gg_tabset_vert ? &gvtabset_box : >abset_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(>s->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,>s->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(>s->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( >s->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(>s->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( >abset_ri );
1103 }
1104