Released version 3-2014010505
[notion.git] / ioncore / frame-tabs-recalc.c
blobf2b6679ba5d77725a70fbd0ccc130a8333cc7596
1 /*
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.
8 */
9 #include <string.h>
10 #include <stdlib.h> /* qsort */
12 #include "common.h"
13 #include "names.h"
14 #include "frame.h"
15 #include "framep.h"
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)
32 int i=0;
33 WLListIterTmp itmp;
34 WRegion *sub;
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);
42 if(displayname==NULL)
43 frame->titles[i].iw=0;
44 else
45 frame->titles[i].iw=grbrush_get_text_width(frame->bar_brush,
46 displayname, strlen(displayname));
47 i++;
51 /*}}}*/
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)
58 WRectangle bg;
59 GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
60 int i,m=frame->titles_n;
61 uint w;
63 frame_bar_geom(frame, &bg);
64 grbrush_get_border_widths(frame->bar_brush, &bdw);
66 /* Remove borders */
67 w=bg.w-bdw.left-bdw.right-(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)*(m-1);
69 if(w<=0){
70 for(i=0;i<m;i++)
71 frame->titles[i].iw=0;
72 }else{
73 for(i=0;i<m;i++)
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;
86 GrBorderWidths bdw;
87 uint bdtotal;
88 int i, m;
90 m=FRAME_MCOUNT(frame);
91 bar_w=frame->tabs_params.bar_max_width_q*REGION_GEOM(frame).w;
93 if(m>0){
94 grbrush_get_border_widths(frame->bar_brush, &bdw);
95 bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)
96 +bdw.right+bdw.left);
98 get_titles_text_width(frame);
99 for(i=0;i<m;i++)
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;
109 if(tmp>0){
110 /* No label truncation needed, good. See how much can be padded. */
111 tmp/=m*2;
112 if(tmp>frame->tabs_params.requested_pad)
113 tmp=frame->tabs_params.requested_pad;
114 bar_w=(tmaxw+tmp*2)*m+bdtotal;
115 }else{
116 /* Some labels must be truncated */
118 }else{
119 if(bar_w<frame->tabs_params.tab_min_w) bar_w=frame->tabs_params.tab_min_w;
122 return bar_w;
125 static bool tab_sizes_equal(WFrame * frame, bool complete)
127 int bar_w;
129 if(frame->barmode==FRAME_BAR_SHAPED){
130 bar_w=frame_shaped_recalc_bar_size_equal(frame);
131 if((frame->bar_w!=bar_w)){
132 frame->bar_w=bar_w;
133 complete=TRUE;
136 frame_tabs_width_calculate_equal(frame);
137 return complete;
144 /*}}}*/
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;
163 WRectangle bg;
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];
168 uint bdtotal;
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;
179 } else {
180 bar_w=bg.w;
181 frame->bar_w=bar_w;
184 get_titles_text_width(frame);
186 /* Calculate thresholds. */
187 for (i=0;i<m;i++) {
188 iw=sorted_sizes[i]=frame->titles[i].iw;
189 titles_total+=
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);
201 w=bar_w-bdtotal;
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.*/
206 for(i=0;i<m;i++)
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;
213 for(i=0;i<m;i++){
214 if (sorted_sizes[i]>frame->tabs_params.propor_tab_min_w)
215 break;
216 titles_total+=frame->tabs_params.propor_tab_min_w;
218 for(;i<m;i++){
219 if (titles_total+sorted_sizes[i]*(m-i)>=w) {
220 max_w=(w-titles_total)/(m-i);
221 break;
223 titles_total+=sorted_sizes[i];
225 for(i=0;i<m;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;
233 equal_pad:
234 qsort(sorted_sizes, m, sizeof(int), intcompare);
235 titles_total=m*max_w;
236 i=m-1;min_w=0;
237 while((i>=0) &&
238 (sorted_sizes[i]>=max_w)){
239 titles_total+=sorted_sizes[i];
240 i--;
242 while(i>=0){
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);
246 break;
248 titles_total+=sorted_sizes[i];
249 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.
260 equal_tab:
261 qsort(sorted_sizes, m, sizeof(int), intcompare);
262 min_w=0;titles_total=2*m*frame->tabs_params.requested_pad;
263 for(i=m-1;i>=0;i--){
264 if (sorted_sizes[i]*(i+1)+titles_total<=w){
265 min_w=(w-titles_total)/(i+1);
266 break;
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) {
274 /* Shorter bar. */
275 w=titles_padded_total2;
276 bar_w=w+bdtotal;
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
281 } else {
282 if(proportional){
283 /* Pad equally, no tab shorter than tabs_params.tab_min_w-1. */
284 max_w=frame->tabs_params.tab_min_w;
285 goto equal_pad;
286 }else{
287 /* Expand as many short tabs as possible to equal size,
288 * Long tabs should be padded by 2*tabs_params.requested_pad.
290 goto equal_tab;
293 if (min_w>0) {
294 for(i=0;i<m;i++)
295 if (frame->titles[i].iw<min_w)
296 frame->titles[i].iw=min_w;
300 /* Calculate remaining space */
301 titles_total=0;
302 for(i=0;i<m;i++)
303 titles_total+=frame->titles[i].iw;
304 w-=titles_total;
305 /* Distribute remaining space equally up to 1 pixel */
306 for(i=0;i<m;i++)
307 frame->titles[i].iw+=((i+1)*w)/m-(i*w)/m;
309 if((frame->bar_w!=bar_w)){
310 frame->bar_w=bar_w;
311 return TRUE;
313 return complete;
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);
329 /*}}}*/
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 },
337 END_STRINGPTRMAP
340 static TabCalcPtr default_frame_tabs_width_algorithm=tab_sizes_equal;
344 static void param_init(TabCalcParams *pars)
346 pars->tab_min_w=100;
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)
355 char *str;
356 TabCalcPtr alg;
357 TabCalcParams *pars=&(frame->tabs_params);
359 param_init(pars);
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)
366 pars->tab_min_w=1;
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",
388 's', &str)){
389 alg=STRINGFUNPTRMAP_VALUE(TabCalcPtr,
390 frame_tabs_width_algorithms,
391 str, NULL);
393 if(alg!=NULL)
394 frame->tabs_params.alg=alg;
398 void frame_tabs_width_recalc_init(WFrame *frame)
400 param_init(&(frame->tabs_params));