|
From: Tomáš E. <eb...@dr...> - 2011-02-07 03:51:15
|
From: Tomáš Ebenlendr <eb...@uc...>
Proportional and elastic algorithms for calculating widths of tabs.
---
ioncore/frame-tabs-recalc.c | 195 +++++++++++++++++++++++++++++++++++++++++++
ioncore/frame-tabs-recalc.h | 10 ++-
2 files changed, 204 insertions(+), 1 deletions(-)
diff --git a/ioncore/frame-tabs-recalc.c b/ioncore/frame-tabs-recalc.c
index b6aba01..f2b6679 100644
--- a/ioncore/frame-tabs-recalc.c
+++ b/ioncore/frame-tabs-recalc.c
@@ -7,6 +7,7 @@
* See the included file LICENSE for details.
*/
#include <string.h>
+#include <stdlib.h> /* qsort */
#include "common.h"
#include "names.h"
@@ -142,10 +143,197 @@ static bool tab_sizes_equal(WFrame * frame, bool complete)
/*}}}*/
+/*{{{ Proportional/Elastic tab sizes algorithms
+ * This gives tabs sizes proportional to the contained text.
+ */
+
+/* Failsafes to 'equal' algorithm if there are more tabs.
+ * The result will be same in most cases of many tabs anyway.
+ */
+#define PROPOR_MAX_TABS 30
+
+static int intcompare (const void *a, const void *b) {
+ return *(int *)a-*(int *)b;
+}
+
+static bool tab_sizes_propor_elastic(WFrame * frame, bool complete, bool proportional)
+{
+ int bar_w, titles_total=0;
+ int titles_padded_total1=0, titles_padded_total2=0;
+ WRectangle bg;
+ GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
+ int i, m=frame->titles_n;
+ int iw, min_w=0, max_w, w;
+ int sorted_sizes[PROPOR_MAX_TABS];
+ uint bdtotal;
+
+ if (m>PROPOR_MAX_TABS)
+ return tab_sizes_equal(frame, complete);
+
+
+ frame_bar_geom(frame, &bg);
+ grbrush_get_border_widths(frame->bar_brush, &bdw);
+
+ if (frame->barmode==FRAME_BAR_SHAPED) {
+ bar_w=frame->tabs_params.bar_max_width_q*REGION_GEOM(frame).w;
+ } else {
+ bar_w=bg.w;
+ frame->bar_w=bar_w;
+ }
+
+ get_titles_text_width(frame);
+
+ /* Calculate thresholds. */
+ for (i=0;i<m;i++) {
+ iw=sorted_sizes[i]=frame->titles[i].iw;
+ titles_total+=
+ (iw<frame->tabs_params.propor_tab_min_w ?
+ frame->tabs_params.propor_tab_min_w : iw);
+ titles_padded_total1+=
+ (iw < frame->tabs_params.propor_tab_min_w-2*frame->tabs_params.requested_pad ?
+ frame->tabs_params.propor_tab_min_w : iw+2*frame->tabs_params.requested_pad);
+ titles_padded_total2+=
+ (iw < frame->tabs_params.tab_min_w-2*frame->tabs_params.requested_pad ?
+ frame->tabs_params.tab_min_w : iw+2*frame->tabs_params.requested_pad);
+ }
+ bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)
+ +bdw.right+bdw.left);
+ w=bar_w-bdtotal;
+
+ /* Do different things based on thresholds. */
+ if (m*frame->tabs_params.propor_tab_min_w>=w) {
+ /*Equal sizes (tiny tabs). Base width is zero.*/
+ for(i=0;i<m;i++)
+ frame->titles[i].iw=0;
+ } else if (titles_total>=w) {
+ /*Truncate long tabs.*/
+ qsort(sorted_sizes, m, sizeof(int), intcompare);
+ min_w=frame->tabs_params.propor_tab_min_w;
+ max_w=sorted_sizes[m-1];titles_total=0;
+ for(i=0;i<m;i++){
+ if (sorted_sizes[i]>frame->tabs_params.propor_tab_min_w)
+ break;
+ titles_total+=frame->tabs_params.propor_tab_min_w;
+ }
+ for(;i<m;i++){
+ if (titles_total+sorted_sizes[i]*(m-i)>=w) {
+ max_w=(w-titles_total)/(m-i);
+ break;
+ }
+ titles_total+=sorted_sizes[i];
+ }
+ for(i=0;i<m;i++)
+ if (frame->titles[i].iw>max_w)
+ frame->titles[i].iw=max_w;
+ } else if (titles_padded_total1>=w) {
+ /*Just a little padding.
+ *Pad equally, no tab shorter than tabs_params.propor_tab_min_w-1.
+ */
+ max_w=frame->tabs_params.propor_tab_min_w;
+equal_pad:
+ qsort(sorted_sizes, m, sizeof(int), intcompare);
+ titles_total=m*max_w;
+ i=m-1;min_w=0;
+ while((i>=0) &&
+ (sorted_sizes[i]>=max_w)){
+ titles_total+=sorted_sizes[i];
+ i--;
+ }
+ while(i>=0){
+ /*Test padding by max_w-sorted_sizes[i].*/
+ if(titles_total-(m-i-1)*sorted_sizes[i]>=w){
+ min_w=(titles_total-w)/(m-i-1);
+ break;
+ }
+ titles_total+=sorted_sizes[i];
+ i--;
+ }
+ /* After expanding to min_w: equal padding will extend short tabs to
+ * required size (+- 1pixel).
+ */
+
+ } else if (titles_padded_total2>=w) {
+ /* Expand as many short tabs as possible to equal size,
+ * they will be shorter than tabs_params.tab_min_w anyway.
+ * Long tabs should be padded by 2*tabs_params.requested_pad.
+ */
+equal_tab:
+ qsort(sorted_sizes, m, sizeof(int), intcompare);
+ min_w=0;titles_total=2*m*frame->tabs_params.requested_pad;
+ for(i=m-1;i>=0;i--){
+ if (sorted_sizes[i]*(i+1)+titles_total<=w){
+ min_w=(w-titles_total)/(i+1);
+ break;
+ }
+ titles_total+=sorted_sizes[i];
+ }
+ /* After expanding to min_w: it should remain
+ * 2*m*tabs_params.requested_pad +- m
+ */
+ } else if (frame->barmode==FRAME_BAR_SHAPED) {
+ /* Shorter bar. */
+ w=titles_padded_total2;
+ bar_w=w+bdtotal;
+ min_w=frame->tabs_params.tab_min_w-2*frame->tabs_params.requested_pad;
+ /* After expanding to min_w: it should remain
+ * 2*m*tabs_params.requested_pad exactly
+ */
+ } else {
+ if(proportional){
+ /* Pad equally, no tab shorter than tabs_params.tab_min_w-1. */
+ max_w=frame->tabs_params.tab_min_w;
+ goto equal_pad;
+ }else{
+ /* Expand as many short tabs as possible to equal size,
+ * Long tabs should be padded by 2*tabs_params.requested_pad.
+ */
+ goto equal_tab;
+ }
+ }
+ if (min_w>0) {
+ for(i=0;i<m;i++)
+ if (frame->titles[i].iw<min_w)
+ frame->titles[i].iw=min_w;
+ }
+
+
+ /* Calculate remaining space */
+ titles_total=0;
+ for(i=0;i<m;i++)
+ titles_total+=frame->titles[i].iw;
+ w-=titles_total;
+ /* Distribute remaining space equally up to 1 pixel */
+ for(i=0;i<m;i++)
+ frame->titles[i].iw+=((i+1)*w)/m-(i*w)/m;
+
+ if((frame->bar_w!=bar_w)){
+ frame->bar_w=bar_w;
+ return TRUE;
+ }
+ return complete;
+}
+
+
+static bool tab_sizes_proportional(WFrame * frame, bool complete)
+{
+ return tab_sizes_propor_elastic(frame, complete, TRUE);
+}
+
+static bool tab_sizes_elastic(WFrame * frame, bool complete)
+{
+ return tab_sizes_propor_elastic(frame, complete, FALSE);
+}
+
+
+
+/*}}}*/
+
DECLFUNPTRMAP(TabCalcPtr);
static StringTabCalcPtrMap frame_tabs_width_algorithms[] = {
{ "equal", tab_sizes_equal },
+ { "elastic", tab_sizes_elastic },
+ { "proportional", tab_sizes_proportional },
END_STRINGPTRMAP
};
@@ -156,6 +344,7 @@ static TabCalcPtr default_frame_tabs_width_algorithm=tab_sizes_equal;
static void param_init(TabCalcParams *pars)
{
pars->tab_min_w=100;
+ pars->propor_tab_min_w=50;
pars->bar_max_width_q=0.95;
pars->requested_pad=10;
pars->alg=default_frame_tabs_width_algorithm;
@@ -177,6 +366,12 @@ void frame_tabs_calc_brushes_updated(WFrame *frame)
pars->tab_min_w=1;
}
+ if(grbrush_get_extra(frame->brush, "frame_propor_tab_min_w",
+ 'i', &(pars->propor_tab_min_w))){
+ if(pars->propor_tab_min_w<=0)
+ pars->propor_tab_min_w=1;
+ }
+
if(grbrush_get_extra(frame->brush, "floatframe_bar_max_w_q",
'd', &(pars->bar_max_width_q))){
if(pars->bar_max_width_q<=0.0 || pars->bar_max_width_q>1.0)
diff --git a/ioncore/frame-tabs-recalc.h b/ioncore/frame-tabs-recalc.h
index 2472aac..2c60701 100644
--- a/ioncore/frame-tabs-recalc.h
+++ b/ioncore/frame-tabs-recalc.h
@@ -43,10 +43,18 @@ DECLSTRUCT(TabCalcParams){
TabCalcPtr alg;
/* Maximum size of shaped bar. */
double bar_max_width_q;
- /* Requested size of the tab. */
+ /* Minimum width of a tab in shaped frame.
+ * For 'proportional' and 'elastic' algorithms also minimum width of a tab
+ * provided that no title has to be truncated
+ */
int tab_min_w;
/* Requested empty space to be added before and after text. */
int requested_pad;
+ /* Minimum width of a tab for 'proportional' and 'elastic' algorithms.
+ * Long titles will be truncated instead of shortening a short tab below
+ * this length.
+ */
+ int propor_tab_min_w;
};
void frame_tabs_calc_brushes_updated(WFrame *frame);
--
1.7.1
|