2 * notion/ioncore/frame-tabs-recalc.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
5 * Copyright (c) Tomas Ebenlendr 2011.
7 * See the included file LICENSE for details.
10 #include <stdlib.h> /* qsort */
16 #include "frame-draw.h"
17 #include "frame-tabs-recalc.h"
20 /*{{{ Common functions */
24 * Temporarily store actual title widths (without truncation) in
25 * frame->titles[*].iw, when calculating tabs widths and bar width. After the
26 * algorithm returns it has to set frame->titles[*].iw to the proper values.
28 * This function is generic for all algorithms.
30 static void get_titles_text_width(WFrame
*frame
)
35 const char *displayname
;
37 /* Assume frame->bar_brush != NULL.
38 Assume FRAME_MCOUNT(frame) > 0 */
40 FRAME_MX_FOR_ALL(sub
, frame
, itmp
){
41 displayname
=region_displayname(sub
);
43 frame
->titles
[i
].iw
=0;
45 frame
->titles
[i
].iw
=grbrush_get_text_width(frame
->bar_brush
,
46 displayname
, strlen(displayname
));
53 /*{{{ Equal tab sizes algorithm
54 * This gives tabs equal sizes (up to 1 pixel).
56 static void frame_tabs_width_calculate_equal(WFrame
*frame
)
59 GrBorderWidths bdw
=GR_BORDER_WIDTHS_INIT
;
60 int i
,m
=frame
->titles_n
;
63 frame_bar_geom(frame
, &bg
);
64 grbrush_get_border_widths(frame
->bar_brush
, &bdw
);
67 w
=bg
.w
-bdw
.left
-bdw
.right
-(bdw
.tb_ileft
+bdw
.tb_iright
+bdw
.spacing
)*(m
-1);
71 frame
->titles
[i
].iw
=0;
74 /* Get i:th tab's portion of free area */
75 frame
->titles
[i
].iw
=((i
+1)*w
)/m
-(i
*w
)/m
;
80 Equal tab sizes algorithm.
81 Calculate size of the bar of a shaped (i.e., floating) frame.
83 static int frame_shaped_recalc_bar_size_equal(WFrame
*frame
)
85 int bar_w
, textw
=0, tmaxw
=frame
->tabs_params
.tab_min_w
, tmp
=0;
90 m
=FRAME_MCOUNT(frame
);
91 bar_w
=frame
->tabs_params
.bar_max_width_q
*REGION_GEOM(frame
).w
;
94 grbrush_get_border_widths(frame
->bar_brush
, &bdw
);
95 bdtotal
=((m
-1)*(bdw
.tb_ileft
+bdw
.tb_iright
+bdw
.spacing
)
98 get_titles_text_width(frame
);
100 if(frame
->titles
[i
].iw
>tmaxw
)
101 tmaxw
=frame
->titles
[i
].iw
;
103 if(bar_w
<frame
->tabs_params
.tab_min_w
&&
104 REGION_GEOM(frame
).w
>frame
->tabs_params
.tab_min_w
)
105 bar_w
=frame
->tabs_params
.tab_min_w
;
107 tmp
=bar_w
-bdtotal
-m
*tmaxw
;
110 /* No label truncation needed, good. See how much can be padded. */
112 if(tmp
>frame
->tabs_params
.requested_pad
)
113 tmp
=frame
->tabs_params
.requested_pad
;
114 bar_w
=(tmaxw
+tmp
*2)*m
+bdtotal
;
116 /* Some labels must be truncated */
119 if(bar_w
<frame
->tabs_params
.tab_min_w
) bar_w
=frame
->tabs_params
.tab_min_w
;
125 static bool tab_sizes_equal(WFrame
* frame
, bool complete
)
129 if(frame
->barmode
==FRAME_BAR_SHAPED
){
130 bar_w
=frame_shaped_recalc_bar_size_equal(frame
);
131 if((frame
->bar_w
!=bar_w
)){
136 frame_tabs_width_calculate_equal(frame
);
146 /*{{{ Proportional/Elastic tab sizes algorithms
147 * This gives tabs sizes proportional to the contained text.
150 /* Failsafes to 'equal' algorithm if there are more tabs.
151 * The result will be same in most cases of many tabs anyway.
153 #define PROPOR_MAX_TABS 30
155 static int intcompare (const void *a
, const void *b
) {
156 return *(int *)a
-*(int *)b
;
159 static bool tab_sizes_propor_elastic(WFrame
* frame
, bool complete
, bool proportional
)
161 int bar_w
, titles_total
=0;
162 int titles_padded_total1
=0, titles_padded_total2
=0;
164 GrBorderWidths bdw
=GR_BORDER_WIDTHS_INIT
;
165 int i
, m
=frame
->titles_n
;
166 int iw
, min_w
=0, max_w
, w
;
167 int sorted_sizes
[PROPOR_MAX_TABS
];
170 if (m
>PROPOR_MAX_TABS
)
171 return tab_sizes_equal(frame
, complete
);
174 frame_bar_geom(frame
, &bg
);
175 grbrush_get_border_widths(frame
->bar_brush
, &bdw
);
177 if (frame
->barmode
==FRAME_BAR_SHAPED
) {
178 bar_w
=frame
->tabs_params
.bar_max_width_q
*REGION_GEOM(frame
).w
;
184 get_titles_text_width(frame
);
186 /* Calculate thresholds. */
188 iw
=sorted_sizes
[i
]=frame
->titles
[i
].iw
;
190 (iw
<frame
->tabs_params
.propor_tab_min_w
?
191 frame
->tabs_params
.propor_tab_min_w
: iw
);
192 titles_padded_total1
+=
193 (iw
< frame
->tabs_params
.propor_tab_min_w
-2*frame
->tabs_params
.requested_pad
?
194 frame
->tabs_params
.propor_tab_min_w
: iw
+2*frame
->tabs_params
.requested_pad
);
195 titles_padded_total2
+=
196 (iw
< frame
->tabs_params
.tab_min_w
-2*frame
->tabs_params
.requested_pad
?
197 frame
->tabs_params
.tab_min_w
: iw
+2*frame
->tabs_params
.requested_pad
);
199 bdtotal
=((m
-1)*(bdw
.tb_ileft
+bdw
.tb_iright
+bdw
.spacing
)
200 +bdw
.right
+bdw
.left
);
203 /* Do different things based on thresholds. */
204 if (m
*frame
->tabs_params
.propor_tab_min_w
>=w
) {
205 /*Equal sizes (tiny tabs). Base width is zero.*/
207 frame
->titles
[i
].iw
=0;
208 } else if (titles_total
>=w
) {
209 /*Truncate long tabs.*/
210 qsort(sorted_sizes
, m
, sizeof(int), intcompare
);
211 min_w
=frame
->tabs_params
.propor_tab_min_w
;
212 max_w
=sorted_sizes
[m
-1];titles_total
=0;
214 if (sorted_sizes
[i
]>frame
->tabs_params
.propor_tab_min_w
)
216 titles_total
+=frame
->tabs_params
.propor_tab_min_w
;
219 if (titles_total
+sorted_sizes
[i
]*(m
-i
)>=w
) {
220 max_w
=(w
-titles_total
)/(m
-i
);
223 titles_total
+=sorted_sizes
[i
];
226 if (frame
->titles
[i
].iw
>max_w
)
227 frame
->titles
[i
].iw
=max_w
;
228 } else if (titles_padded_total1
>=w
) {
229 /*Just a little padding.
230 *Pad equally, no tab shorter than tabs_params.propor_tab_min_w-1.
232 max_w
=frame
->tabs_params
.propor_tab_min_w
;
234 qsort(sorted_sizes
, m
, sizeof(int), intcompare
);
235 titles_total
=m
*max_w
;
238 (sorted_sizes
[i
]>=max_w
)){
239 titles_total
+=sorted_sizes
[i
];
243 /*Test padding by max_w-sorted_sizes[i].*/
244 if(titles_total
-(m
-i
-1)*sorted_sizes
[i
]>=w
){
245 min_w
=(titles_total
-w
)/(m
-i
-1);
248 titles_total
+=sorted_sizes
[i
];
251 /* After expanding to min_w: equal padding will extend short tabs to
252 * required size (+- 1pixel).
255 } else if (titles_padded_total2
>=w
) {
256 /* Expand as many short tabs as possible to equal size,
257 * they will be shorter than tabs_params.tab_min_w anyway.
258 * Long tabs should be padded by 2*tabs_params.requested_pad.
261 qsort(sorted_sizes
, m
, sizeof(int), intcompare
);
262 min_w
=0;titles_total
=2*m
*frame
->tabs_params
.requested_pad
;
264 if (sorted_sizes
[i
]*(i
+1)+titles_total
<=w
){
265 min_w
=(w
-titles_total
)/(i
+1);
268 titles_total
+=sorted_sizes
[i
];
270 /* After expanding to min_w: it should remain
271 * 2*m*tabs_params.requested_pad +- m
273 } else if (frame
->barmode
==FRAME_BAR_SHAPED
) {
275 w
=titles_padded_total2
;
277 min_w
=frame
->tabs_params
.tab_min_w
-2*frame
->tabs_params
.requested_pad
;
278 /* After expanding to min_w: it should remain
279 * 2*m*tabs_params.requested_pad exactly
283 /* Pad equally, no tab shorter than tabs_params.tab_min_w-1. */
284 max_w
=frame
->tabs_params
.tab_min_w
;
287 /* Expand as many short tabs as possible to equal size,
288 * Long tabs should be padded by 2*tabs_params.requested_pad.
295 if (frame
->titles
[i
].iw
<min_w
)
296 frame
->titles
[i
].iw
=min_w
;
300 /* Calculate remaining space */
303 titles_total
+=frame
->titles
[i
].iw
;
305 /* Distribute remaining space equally up to 1 pixel */
307 frame
->titles
[i
].iw
+=((i
+1)*w
)/m
-(i
*w
)/m
;
309 if((frame
->bar_w
!=bar_w
)){
317 static bool tab_sizes_proportional(WFrame
* frame
, bool complete
)
319 return tab_sizes_propor_elastic(frame
, complete
, TRUE
);
322 static bool tab_sizes_elastic(WFrame
* frame
, bool complete
)
324 return tab_sizes_propor_elastic(frame
, complete
, FALSE
);
331 DECLFUNPTRMAP(TabCalcPtr
);
333 static StringTabCalcPtrMap frame_tabs_width_algorithms
[] = {
334 { "equal", tab_sizes_equal
},
335 { "elastic", tab_sizes_elastic
},
336 { "proportional", tab_sizes_proportional
},
340 static TabCalcPtr default_frame_tabs_width_algorithm
=tab_sizes_equal
;
344 static void param_init(TabCalcParams
*pars
)
347 pars
->propor_tab_min_w
=50;
348 pars
->bar_max_width_q
=0.95;
349 pars
->requested_pad
=10;
350 pars
->alg
=default_frame_tabs_width_algorithm
;
353 void frame_tabs_calc_brushes_updated(WFrame
*frame
)
357 TabCalcParams
*pars
=&(frame
->tabs_params
);
361 if(grbrush_get_extra(frame
->brush
, "floatframe_tab_min_w",
362 'i', &(pars
->tab_min_w
)) ||
363 grbrush_get_extra(frame
->brush
, "frame_tab_min_w",
364 'i', &(pars
->tab_min_w
))){
365 if(pars
->tab_min_w
<=0)
369 if(grbrush_get_extra(frame
->brush
, "frame_propor_tab_min_w",
370 'i', &(pars
->propor_tab_min_w
))){
371 if(pars
->propor_tab_min_w
<=0)
372 pars
->propor_tab_min_w
=1;
375 if(grbrush_get_extra(frame
->brush
, "floatframe_bar_max_w_q",
376 'd', &(pars
->bar_max_width_q
))){
377 if(pars
->bar_max_width_q
<=0.0 || pars
->bar_max_width_q
>1.0)
378 pars
->bar_max_width_q
=1.0;
381 if(grbrush_get_extra(frame
->brush
, "frame_tab_padding",
382 'i', &(pars
->requested_pad
))){
383 if(pars
->requested_pad
<=0)
384 pars
->requested_pad
=1;
387 if(grbrush_get_extra(frame
->brush
, "frame_tab_width_alg",
389 alg
=STRINGFUNPTRMAP_VALUE(TabCalcPtr
,
390 frame_tabs_width_algorithms
,
394 frame
->tabs_params
.alg
=alg
;
398 void frame_tabs_width_recalc_init(WFrame
*frame
)
400 param_init(&(frame
->tabs_params
));