diff lwlib/xlwtabs.c @ 3094:ad2f4ae9895b

[xemacs-hg @ 2005-11-26 11:45:47 by stephent] Xft merge. <87k6ev4p8q.fsf@tleepslib.sk.tsukuba.ac.jp>
author stephent
date Sat, 26 Nov 2005 11:46:25 +0000
parents facf3239ba30
children 174eb4da74fb
line wrap: on
line diff
--- a/lwlib/xlwtabs.c	Fri Nov 25 22:51:38 2005 +0000
+++ b/lwlib/xlwtabs.c	Sat Nov 26 11:46:25 2005 +0000
@@ -18,13 +18,6 @@
  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  Boston, MA 02111-1307, USA.  */
 
- /* Synched up with: Tabs.c 1.27.  
-
- #### This file contains essential XEmacs related fixes to the original
- verison of the Tabs widget. Be VERY careful about syncing if you ever
- update to a more recent version. In general this is probably now a
- bad idea. */
-
  /*
  * Tabs.c - Index Tabs composite widget
  *
@@ -56,6 +49,18 @@
  * the frame.
  */
 
+ /* Synched up with: Tabs.c 1.27.  
+
+ This file contains essential XEmacs-related fixes to the original
+ version of the Tabs widget. Be VERY careful about syncing if you ever
+ update to a more recent version. In general this is probably now a
+ bad idea.
+
+ #### We need to check that various windows (the  whole widget, or a single
+ tab) are of "reasonable" size, ie, we need to try for more sanity in the
+ geometry management routines.
+ */
+
 /*
  * TODO: min child height = tab height
  */
@@ -67,11 +72,17 @@
 #include	<X11/IntrinsicP.h>
 #include	<X11/StringDefs.h>
 
+/* #### This may be risky, lwlib-internal.h redefines abort() */
+#include	"lwlib-fonts.h"
+#include	"lwlib-colors.h"
 #include	"lwlib-internal.h"
 #include	"../src/xmu.h"
 #include	"xlwtabsP.h"
 #include	"xlwgcs.h"
 
+#define XFT_USE_HEIGHT_NOT_ASCENT_DESCENT 0
+
+/* #### These should probably be resources. */
 #define	MIN_WID		10
 #define	MIN_HGT		10
 #define	INDENT		3	/* tabs indented from edge by this much */
@@ -138,6 +149,12 @@
 	offset(selectInsensitive), XtRImmediate, (XtPointer) True},
   {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
 	offset(font), XtRString, (XtPointer) XtDefaultFont},
+#ifdef USE_XFT_TABS
+  /* #### Maybe use "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1" here? */
+  {XtNxftFont, XtCXftFont, XtRString, sizeof (String),
+	offset(renderFontSpec), XtRString,
+        (XtPointer) "AirCut-16" /* XtDefaultFont */},
+#endif
   {XtNinternalWidth, XtCWidth, XtRDimension, sizeof(Dimension),
 	offset(internalWidth), XtRImmediate, (XtPointer)4 },
   {XtNinternalHeight, XtCHeight, XtRDimension, sizeof(Dimension),
@@ -400,6 +417,27 @@
 	 ((TabsConstraints)((tab)->core.constraints))->tabs.visible)
 
 
+
+static int debug_tabs = 0;	/* increase for more verbosity */
+
+#ifdef USE_XFT_TABS
+/* #### duplicated from xlwmenu.c -- CLEAN THIS SHIT UP!
+   Undeclared so define at top. */
+#define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
+		   ? ((unsigned long) (x)) : ((unsigned long) (y)))
+
+static int
+x_xft_text_width (Display *dpy, XftFont *xft_font, FcChar8 *run, int len)
+{
+  static XGlyphInfo glyphinfo;	/* #### static? */
+
+  XftTextExtents8 (dpy,
+		   xft_font,
+		   run, len, &glyphinfo);
+  return glyphinfo.xOff;
+}
+#endif
+
 /****************************************************************
  *
  * Member Procedures
@@ -436,11 +474,26 @@
      */
     newTw->tabs.tab_height = 2 * newTw->tabs.internalHeight + SHADWID ;
 
-    if( newTw->tabs.font != NULL )
+#ifdef USE_XFT_TABS
+    /* must get font here
+       to do this right, we should add a new Xt Resource type +
+       conversion function
+    */
+    newTw->tabs.renderFont =
+      xft_open_font_by_name (XtDisplay ((Widget) newTw),
+			     newTw->tabs.renderFontSpec);
+    if (newTw->tabs.renderFont != NULL) 
+#if XFT_USE_HEIGHT_NOT_ASCENT_DESCENT
+      newTw->tabs.tab_height += newTw->tabs.renderFont->height;
+#else
+      newTw->tabs.tab_height += newTw->tabs.renderFont->ascent +
+				newTw->tabs.renderFont->descent;
+#endif /* XFT_USE_HEIGHT_NOT_ASCENT_DESCENT */
+#else  /* ! USE_XFT_TABS */
+    if (newTw->tabs.font != NULL)
       newTw->tabs.tab_height += newTw->tabs.font->max_bounds.ascent +
-				newTw->tabs.font->max_bounds.descent ;
-
-    /* GC allocation is deferred until XtRealize() */
+				newTw->tabs.font->max_bounds.descent;
+#endif /* ! USE_XFT_TABS */
 
     /* if size not explicitly set, set it to our preferred size now. */
 
@@ -614,16 +667,30 @@
 	Widget	*childP ;
 	int	i ;
 
-
-	if( tw->tabs.font != curtw->tabs.font  ||
-	    tw->tabs.internalWidth != curtw->tabs.internalWidth ||
-	    tw->tabs.internalHeight != curtw->tabs.internalHeight )
+	if(
+#ifdef USE_XFT_TABS
+            tw->tabs.renderFont != curtw->tabs.renderFont  ||
+#else
+	    tw->tabs.font != curtw->tabs.font  ||
+#endif
+            tw->tabs.internalWidth != curtw->tabs.internalWidth ||
+	    tw->tabs.internalHeight != curtw->tabs.internalHeight)
 	{
-	  tw->tabs.tab_height = 2 * tw->tabs.internalHeight + SHADWID ;
+	  tw->tabs.tab_height = 2 * tw->tabs.internalHeight + SHADWID;
 
-	  if( tw->tabs.font != NULL )
+#ifdef USE_XFT_TABS
+	  if (tw->tabs.renderFont != NULL)
+#if XFT_USE_HEIGHT_NOT_ASCENT_DESCENT
+	    tw->tabs.tab_height += tw->tabs.renderFont->height;
+#else
+	    tw->tabs.tab_height += tw->tabs.renderFont->ascent +
+				   tw->tabs.renderFont->descent;
+#endif /* XFT_USE_HEIGHT_NOT_ASCENT_DESCENT */
+#else  /* ! USE_XFT_TABS */
+	  if (tw->tabs.font != NULL)
 	    tw->tabs.tab_height += tw->tabs.font->max_bounds.ascent +
-				   tw->tabs.font->max_bounds.descent ;
+				   tw->tabs.font->max_bounds.descent;
+#endif /* ! USE_XFT_TABS */
 
 	  /* Tab size has changed.  Resize all tabs and request a new size */
 	  for(i=0, childP=tw->composite.children;
@@ -640,7 +707,12 @@
 
 	if( tw->core.background_pixel != curtw->core.background_pixel ||
 	    tw->core.background_pixmap != curtw->core.background_pixmap ||
-	    tw->tabs.font != curtw->tabs.font )
+#ifdef USE_XFT_TABS
+	    tw->tabs.renderFont != curtw->tabs.renderFont
+#else
+	    tw->tabs.font != curtw->tabs.font
+#endif
+	    )
 	  if( XtIsRealized(new_) )
 	  {
 	    TabsFreeGCs(tw) ;
@@ -755,83 +827,167 @@
 
 
 /*
- * Return preferred size.  Happily accept anything >= our preferred size.
- * (TODO: is that the right thing to do?  Should we always return "almost"
- * if offered more than we need?)
+ * Return status, with preferred size in PREFERRED.
+ *
+ * According to the X Toolkit Intrinsics manual
+ *   XtGeometryYes    = accept INTENDED without change
+ *   XtGeometryNo     = request to stay _exactly_ the same
+ *   XtGeometryAlmost = suggest PREFERRED as a compromise
+ * and the PREFERRED argument must be filled in completely (ie, any fields
+ * whose bits are set in the request_mode mask must correspond to the
+ * preferred geometry, which must be consistent with the return value).
+ *
+ * Assuming horizontal orientation, in XEmacs, we should always accept if
+ * the width is more than we need.  There's no problem if there are only a
+ * couple of tabs packed to the left.  OTOH there's probably something wrong
+ * if we're offered a height more than 1.5x or 2x the preferred height.
+ * (#### Do tab controls do vertical?)
  */
 
+/* compute the height above which we complain */
+#define TAB_HEIGHT_TOLERANCE(x) (2*x)
+
 static XtGeometryResult
-TabsQueryGeometry(Widget w,
-	XtWidgetGeometry *intended, XtWidgetGeometry *preferred)
+TabsQueryGeometry (Widget w,
+		   XtWidgetGeometry *intended,
+		   XtWidgetGeometry *preferred)	/* RETURN */
 {
-    register TabsWidget tw = (TabsWidget)w ;
-    XtGeometryMask mode = intended->request_mode ;
+    TabsWidget tw = (TabsWidget) w;
+    XtGeometryMask mode = intended->request_mode;
+    
+    preferred->request_mode = CWWidth | CWHeight;
+    PreferredSize (tw, &preferred->width, &preferred->height, NULL, NULL);
 
-    preferred->request_mode = CWWidth | CWHeight ;
-    PreferredSize(tw, &preferred->width, &preferred->height, NULL,NULL) ;
+    /* If width is big enough, accept it. */
+    if ((mode & CWWidth) && intended->width >= preferred->width)
+      preferred->width = intended->width;
 
-    if( (!(mode & CWWidth) || intended->width == w->core.width)  &&
-        (!(mode & CWHeight) || intended->height == w->core.height) )
-      return XtGeometryNo ;
+    /* If height is within range, accept it.
+       #### If too tall, we could offer a compromise at TAB_HEIGHT_TOLERANCE.
+       Should we? */
+    if ((mode & CWHeight) && intended->height >= preferred->height
+	&& intended->height <= TAB_HEIGHT_TOLERANCE (preferred->height))
+      preferred->height = intended->height;
 
-    if( (!(mode & CWWidth) || intended->width >= preferred->width)  &&
-	(!(mode & CWHeight) || intended->height >= preferred->height) )
+    /* Compute return value. */
+    if (preferred->width == ((mode & CWWidth) ? intended->width
+					      : w->core.width)
+	&& preferred->height == ((mode & CWHeight) ? intended->height
+						   : w->core.height))
       return XtGeometryYes;
-
-    return XtGeometryAlmost;
+    else if (preferred->width == w->core.width
+	     && preferred->height == w->core.height)
+      return XtGeometryNo;
+    else
+      return XtGeometryAlmost;
 }
 
 
 
 /*
- * Geometry Manager; called when a child wants to be resized.
+ * Geometry Manager; called when TAB (a child) wants to be resized.
+ *
+ * According to the X Toolkit Intrinsics manual
+ *   XtGeometryDone   = accept REQUEST and do it (#### check this)
+ *   XtGeometryYes    = accept REQUEST without change
+ *   XtGeometryNo     = refuse REQUEST (ie, stay _exactly_ the same)
+ *   XtGeometryAlmost = suggest REPLY as a compromise
  */
 
 static XtGeometryResult
-TabsGeometryManager(Widget w, XtWidgetGeometry *req, XtWidgetGeometry *reply)
+TabsGeometryManager (Widget tab,
+		     XtWidgetGeometry *request,
+		     XtWidgetGeometry *reply) /* RETURN */
 {
-	TabsWidget		tw = (TabsWidget) XtParent(w);
-	Dimension		s = SHADWID ;
-	TabsConstraints		tab = (TabsConstraints)w->core.constraints;
-	XtGeometryResult	result ;
-	Dimension		rw, rh ;
+	TabsWidget	 control = (TabsWidget) XtParent(tab);
+	Dimension	 s = SHADWID;
+	TabsConstraints	 constraint = (TabsConstraints) tab->core.constraints;
+	XtGeometryResult result, best_offer = XtGeometryYes;
+	Dimension	 rw, rh;
+
+	static int debug_count = 0;
+        static int debug_mask = 1;
+
+	/* Position request cannot be satisfied, so if tabs are not resizable,
+	   no nontrivial request can be satisfied: return XGeometryNo. */
+	if (!constraint->tabs.resizable)
+	  return XtGeometryNo;
+
+	fprintf (stderr, "Urk! label is resizable!\n");
 
-	/* Position request always denied */
+	/* Assume we will refuse these; toggle iff we accept them.
+	   Reply won't specify any fields not in the request. */
+	reply->request_mode = request->request_mode;
+	reply->x = tab->core.x;
+	reply->y = tab->core.y;
 
-	if( ((req->request_mode & CWX) && req->x != w->core.x) ||
-	    ((req->request_mode & CWY) && req->y != w->core.y) ||
-	    !tab->tabs.resizable )
-	  return XtGeometryNo ;
+	/* If a position request would result in a change, best offer is
+	   XtGeometryAlmost.  Otherwise toggle reply->request_mode. */
+	if ((request->request_mode & CWX) && request->x != tab->core.x)
+	  best_offer = XtGeometryAlmost;
+	else
+	  reply->request_mode &= ~CWX;
+	if ((request->request_mode & CWY) && request->y != tab->core.y)
+	  best_offer = XtGeometryAlmost;
+	else
+	  reply->request_mode &= ~CWY;
 
-	/* Make all three fields in the request valid */
-	if( !(req->request_mode & CWWidth) )
-	    req->width = w->core.width;
-	if( !(req->request_mode & CWHeight) )
-	    req->height = w->core.height;
-	if( !(req->request_mode & CWBorderWidth) )
-	    req->border_width = w->core.border_width;
+	/* Make all three fields in the reply valid */
+	reply->width = (request->request_mode & CWWidth)
+		       ? request->width : tab->core.width;
+	reply->height = (request->request_mode & CWHeight)
+			? request->height : tab->core.height;
+	reply->border_width = (request->request_mode & CWBorderWidth)
+			      ? request->border_width : tab->core.border_width;
+
+	/* check if we can already offer a compromise */
+	if (best_offer == XtGeometryAlmost &&
+	    reply->width == tab->core.width &&
+	    reply->height == tab->core.height &&
+	    reply->border_width == tab->core.border_width)
+	  {
+	    reply->request_mode &= ~(CWWidth | CWHeight | CWBorderWidth);
+	    return best_offer;
+	  }
 
-	if( req->width == w->core.width &&
-	    req->height == w->core.height &&
-	    req->border_width == w->core.border_width )
-	  return XtGeometryNo ;
+#ifndef DONT_DEBUG_REQUESTS
+#define DBG_REQUEST_PRINT(name,field,size)				\
+do {									\
+  if (reply->field > size)						\
+    {									\
+      if (++debug_count == debug_mask)					\
+	{								\
+	  debug_mask <<= 1;						\
+	  fprintf (stderr, "ridiculous %s request #%d: %d > %d\n",	\
+		   name, debug_count, reply->field, size);		\
+	}								\
+      reply->field = tab->core.field;					\
+    }									\
+} while (0)
 
-	rw = req->width + 2 * req->border_width ;
-	rh = req->height + 2 * req->border_width ;
+	DBG_REQUEST_PRINT ("width",width,1024);
+	DBG_REQUEST_PRINT ("height",height,768);
+	DBG_REQUEST_PRINT ("border_width",border_width,30);
+#undef DBG_REQUEST_PRINT
+#endif
+
+	rw = reply->width + 2 * reply->border_width;
+	rh = reply->height + 2 * reply->border_width;
 
 	/* find out how big the children want to be now */
-	MaxChild(tw, w, rw, rh) ;
+	MaxChild (control, tab, rw, rh);
 
 
 	/* Size changes must see if the new size can be accommodated.
-	 * The Tabs widget keeps all of its children the same
-	 * size.  A request to shrink will be accepted only if the
+	 * The Tabs widget keeps all of its children the same height, but
+	 * widths may vary.
+	 * A request to shrink will be accepted only if the
 	 * new size is still big enough for all other children.  A
 	 * request to shrink that is not big enough for all children
 	 * returns an "almost" response with the new proposed size
 	 * or a "no" response if unable to shrink at all.
 	 *
-	 * A request to grow will be accepted only if the Tabs parent can
+	 * A request to grow will be accepted only if the Tabs control can
 	 * grow to accommodate.
 	 *
 	 * TODO:
@@ -840,25 +996,33 @@
 	 * for another day.
 	 */
 
-	if (req->request_mode & (CWWidth | CWHeight | CWBorderWidth))
+	if (request->request_mode & (CWWidth | CWHeight | CWBorderWidth))
 	{
 	  Dimension	cw,ch ;		/* children's preferred size */
 	  Dimension	aw,ah ;		/* available size we can give child */
 	  Dimension	th ;		/* space used by tabs */
 	  Dimension	wid,hgt ;	/* Tabs widget size */
+	  int check_nrows;
 
-	  cw = tw->tabs.max_cw ;
-	  ch = tw->tabs.max_ch ;
+	  cw = control->tabs.max_cw ;
+	  ch = control->tabs.max_ch ;
 
 	  /* find out what *my* resulting preferred size would be */
-
-	  PreferredSize2(tw, cw, ch, &wid, &hgt) ;
+	  /* #### this whole API is wrong; what should happen is
+	     1. app should hint as to #rows and/or aspect ratio
+	     2. tab control should attempt to layout in current space
+	     3. if not all tabs fit, should request resize to achieve
+		layout hints
+	     Probably can and should cache preferred size in widget, with
+	     cache cleared when labels or core size changes. */
+	  PreferredSize2(control, cw, ch, &wid, &hgt) ;
 
 	  /* Would my size change?  If so, ask to be resized. */
 
-	  if( wid != tw->core.width || hgt != tw->core.height )
+	  if (wid != control->core.width || hgt != control->core.height)
 	  {
-	    Dimension	oldWid = tw->core.width, oldHgt = tw->core.height ;
+	    Dimension oldWid = control->core.width,
+		      oldHgt = control->core.height;
 	    XtWidgetGeometry	myrequest, myreply ;
 
 	    myrequest.width = wid ;
@@ -870,10 +1034,11 @@
 	     * offer the child a compromise, then make this a query only.
 	     */
 
-	    if( (req->request_mode & XtCWQueryOnly)  || rw < cw || rh < ch )
-	      myrequest.request_mode |= XtCWQueryOnly ;
+	    if ((request->request_mode & XtCWQueryOnly) || rw < cw || rh < ch)
+	      myrequest.request_mode |= XtCWQueryOnly;
 
-	    result = XtMakeGeometryRequest((Widget)tw, &myrequest, &myreply) ;
+	    result = XtMakeGeometryRequest ((Widget) control,
+					    &myrequest, &myreply);
 
 	    /* !$@# Athena Box widget changes the core size even if QueryOnly
 	     * is set.  I'm convinced this is a bug.  At any rate, to work
@@ -881,81 +1046,104 @@
 	     * query geometry request.  This is only partly effective,
 	     * as there may be other boxes further up the tree.
 	     */
-	    if( myrequest.request_mode & XtCWQueryOnly ) {
-	      tw->core.width = oldWid ;
-	      tw->core.height = oldHgt ;
+	    if (myrequest.request_mode & XtCWQueryOnly) {
+	      control->core.width = oldWid;
+	      control->core.height = oldHgt;
 	    }
 
 	    /* based on the parent's response, determine what the
 	     * resulting Tabs widget size would be.
 	     */
 
-	    switch( result ) {
+	    switch (result) {
 	      case XtGeometryYes:
 	      case XtGeometryDone:
-		tw->tabs.needs_layout = True ;
-		break ;
+		control->tabs.needs_layout = True;
+		break;
 
 	      case XtGeometryNo:
-		wid = tw->core.width ;
-		hgt = tw->core.height ;
-		break ;
+		wid = control->core.width;
+		hgt = control->core.height;
+		break;
 
 	      case XtGeometryAlmost:
-		wid = myreply.width ;
-		hgt = myreply.height ;
-		tw->tabs.needs_layout = True ;
-		break ;
+		wid = myreply.width;
+		hgt = myreply.height;
+		control->tabs.needs_layout = True;
+		break;
 	    }
 	  }
 
 	  /* Within the constraints imposed by the parent, what is
 	   * the max size we can give the child?
 	   */
-	  (void) TabLayout(tw, wid, hgt, &th, True) ;
-	  aw = wid - 2*s ;
-	  ah = hgt - th - 2*s ;
+	  check_nrows = TabLayout (control, wid, hgt, &th, True);
+	  aw = wid - 2*s;
+	  if (check_nrows == 1)
+	    {
+	      ah = hgt - th - 2*s;
+	    }
+	  else
+	    {
+	      /* this rarely gets triggered, but when it does it seems to
+		 get triggered forever after */
+	      int n = control->composite.num_children;
+	      ah = control->tabs.tab_height;
+	      if (debug_tabs > 0)
+		fprintf (stderr, "Kludging around %d != 1 rows,"
+			 " #children = %d, total height %d, using %d.\n",
+			 check_nrows, n, th, ah);
+	    }
 
 	  /* OK, make our decision.  If requested size is >= max sibling
 	   * preferred size, AND requested size <= available size, then
 	   * we accept.  Otherwise, we offer a compromise.
 	   */
 
-	  if( rw == aw && rh == ah )
+	  if (rw == aw && rh == ah)
 	  {
 	    /* Acceptable.  If this wasn't a query, change *all* children
 	     * to this size.
 	     */
-	    if( req->request_mode & XtCWQueryOnly )
-	      return XtGeometryYes ;
+	    if (request->request_mode & XtCWQueryOnly)
+	      {
+		control->tabs.needs_layout = False;
+		return XtGeometryYes ;
+	      }
 	    else
 	    {
-	      Widget	*childP = tw->composite.children ;
-	      int	i,bw ;
-	      w->core.border_width = req->border_width ;
-	      for(i=TabsNumChildren (tw); --i >= 0; ++childP)
-		if( TabVisible(*childP) )
+	      Widget	*childP = control->composite.children;
+	      int	i, bw;
+	      tab->core.border_width = request->border_width;
+	      for (i = TabsNumChildren (control); --i >= 0; ++childP)
+		if (TabVisible (*childP))
 		{
-		  bw = (*childP)->core.border_width ;
-		  XtConfigureWidget(*childP, s,tw->tabs.tab_total+s,
-			    rw-2*bw, rh-2*bw, bw) ;
+		  bw = (*childP)->core.border_width;
+		  XtConfigureWidget (*childP, s, control->tabs.tab_total+s,
+				     rw-2*bw, rh-2*bw, bw);
 		}
 #ifdef	COMMENT
 	      /* TODO: under what conditions will we need to redraw? */
-	      XClearWindow(XtDisplay((Widget)tw), XtWindow((Widget)tw)) ;
-	      XtClass(tw)->core_class.expose((Widget)tw,NULL,NULL) ;
+	      XClearWindow (XtDisplay ((Widget) control),
+			    XtWindow ((Widget) control));
+	      XtClass (control)->core_class.expose ((Widget)control,
+						    NULL, NULL);
 #endif	/* COMMENT */
-	      return XtGeometryDone ;
+	      return XtGeometryDone;
 	    }
 	  }
 
 	  /* Cannot grant child's request.  Describe what we *can* do
 	   * and return counter-offer.
 	   */
-	  reply->width  = aw - 2 * req->border_width ;
-	  reply->height = ah - 2 * req->border_width ;
-	  reply->border_width = req->border_width ;
-	  reply->request_mode = CWWidth | CWHeight | CWBorderWidth ;
+	  control->tabs.needs_layout = False;
+	  reply->width  = aw - 2 * request->border_width ;
+	  reply->height = ah - 2 * request->border_width ;
+	  reply->request_mode &=
+	    ~((reply->border_width == tab->core.border_width
+	       ? CWBorderWidth : 0)
+	      |(reply->width == tab->core.width ? CWWidth : 0)
+	      |(reply->height == tab->core.height ? CWHeight : 0));
 	  return XtGeometryAlmost ;
 	}
 
@@ -1428,6 +1616,8 @@
 	GC	gc ;
 	int	x,y ;
 
+	if (debug_tabs > 1) fprintf (stderr, "DrawTab called.\n");
+
  	if( !XtIsRealized((Widget)tw))
  	  return ;
 
@@ -1440,55 +1630,142 @@
 	  Window	win = XtWindow((Widget)tw) ;
 	  String	lbl = tab->tabs.label != NULL ?
 			      tab->tabs.label : XtName(child) ;
+#ifdef USE_XFT_TABS
+	  XftColor color;
+	  XftColor colorBG;
+	  Colormap cmap = tw->core.colormap;
+	  Visual *visual;
+	  int ignored;
 
-	  if( XtIsSensitive(child) )
+	  visual_info_from_widget ((Widget) tw, &visual, &ignored);
+	  colorBG = xft_convert_color (dpy, cmap, visual,
+				       tw->core.background_pixel, 0);
+#endif
+
+	  if (debug_tabs > 2)
+	    fprintf (stderr, "(Re)drawing labels.\n");
+
+	  if (XtIsSensitive(child))
 	  {
-	    gc = tw->tabs.foregroundGC ;
-	    XSetForeground(dpy, gc, tab->tabs.foreground) ;
+	    gc = tw->tabs.foregroundGC;
+#ifdef USE_XFT_TABS
+	    color = xft_convert_color (dpy, cmap, visual,
+				       tab->tabs.foreground, 0);
+#else
+	    XSetForeground(dpy, gc, tab->tabs.foreground);
+#endif
 	  }
 	  else
 	  {
 	    /* grey pixel allocation deferred until now */
-	    if( !tab->tabs.greyAlloc )
+	    if (!tab->tabs.greyAlloc)
 	    {
-	      if( tw->tabs.be_nice_to_cmap || tw->core.depth == 1 )
-		tab->tabs.grey = tab->tabs.foreground ;
+	      if (tw->tabs.be_nice_to_cmap || tw->core.depth == 1)
+		tab->tabs.grey = tab->tabs.foreground;
 	      else
-		tab->tabs.grey = AllocGreyPixel((Widget)tw,
+		tab->tabs.grey = AllocGreyPixel ((Widget) tw,
 					tab->tabs.foreground,
 					tw->core.background_pixel,
-					tw->tabs.insensitive_contrast ) ;
-	      tab->tabs.greyAlloc = True ;
+					tw->tabs.insensitive_contrast);
+	      tab->tabs.greyAlloc = True;
 	    }
-	    gc = tw->tabs.greyGC ;
-	    XSetForeground(dpy, gc, tab->tabs.grey) ;
+	    gc = tw->tabs.greyGC;
+#ifdef USE_XFT_TABS
+	    color = xft_convert_color (dpy, cmap, visual, tab->tabs.grey, 0);
+#else
+	    XSetForeground(dpy, gc, tab->tabs.grey);
+#endif
 	  }
 
-	  x = tab->tabs.x ;
-	  y = tab->tabs.y ;
-	  if( child == tw->tabs.topWidget )
-	    y -= TABLDELTA ;
+	  x = tab->tabs.x;
+	  y = tab->tabs.y;
+	  if (child == tw->tabs.topWidget)
+	    y -= TABLDELTA;
 
-	  if( tab->tabs.left_bitmap != None && tab->tabs.lbm_width > 0 )
+	  if (tab->tabs.left_bitmap != None && tab->tabs.lbm_width > 0)
 	  {
-	    if( tab->tabs.lbm_depth == 1 )
+	    if (tab->tabs.lbm_depth == 1)
 	      XCopyPlane(dpy, tab->tabs.left_bitmap, win,gc,
 		0,0, tab->tabs.lbm_width, tab->tabs.lbm_height,
-		x+tab->tabs.lbm_x, y+tab->tabs.lbm_y, 1L) ;
+		x+tab->tabs.lbm_x, y+tab->tabs.lbm_y, 1L);
 	    else
 	      XCopyArea(dpy, tab->tabs.left_bitmap, win,gc,
 		0,0, tab->tabs.lbm_width, tab->tabs.lbm_height,
-		x+tab->tabs.lbm_x, y+tab->tabs.lbm_y) ;
+		x+tab->tabs.lbm_x, y+tab->tabs.lbm_y);
 	  }
 
-	  if( lbl != NULL && tw->tabs.font != NULL )
-	    XDrawString(dpy,win,gc,
-	      x+tab->tabs.l_x, y+tab->tabs.l_y,
-	      lbl, (int)strlen(lbl)) ;
+	  if (lbl != NULL &&
+#ifdef USE_XFT_TABS
+	      tw->tabs.renderFont != NULL
+#else
+	      tw->tabs.font != NULL
+#endif
+	      )
+	    {
+#ifdef USE_XFT_TABS
+	      XftDraw *xftDraw = XftDrawCreate (dpy, win, visual, cmap);
+	      XftFont *renderFont = tw->tabs.renderFont;
+	      XGlyphInfo glyphinfo;
+	      XftColor colorDBG;
+	      XftColorAllocName (dpy, visual, cmap, "wheat", &colorDBG);
+	      XftTextExtents8 (dpy, renderFont, lbl, (int) strlen (lbl),
+			       &glyphinfo);
+	      /* #### unnecessary? for the moment, give visual extent */
+	      /* draw background rect */
+#if 1
+	      if (debug_tabs > 2)
+		{
+		  fprintf (stderr, "background color:  pixel=%08lx, r=%04x,"
+			   " g=%04x, b=%04x, alpha=%04x.\n",
+			   colorDBG.pixel, colorDBG.color.red,
+			   colorDBG.color.green, colorDBG.color.blue,
+			   colorDBG.color.alpha);
+ 		  fprintf (stderr, "label geometry: x=%d, y=%d, xOff=%d,"
+ 			   " yOff=%d, width=%d, height=%d\n",
+ 			   glyphinfo.x, glyphinfo.y, glyphinfo.xOff,
+ 			   glyphinfo.yOff, glyphinfo.width, glyphinfo.height);
+ 		}
+	      XftDrawRect (xftDraw, &colorDBG,
+			   /* left, top, width, height */
+			   x+tab->tabs.l_x-glyphinfo.x,
+			   y+tab->tabs.l_y-glyphinfo.y,
+			   glyphinfo.width, glyphinfo.height);
+#endif
+	      /* draw text */
+	      if (debug_tabs > 2)
+		{
+		  FcValue name;
+		  FcValue size;
+		  FcPatternGet (renderFont->pattern, FC_FAMILY, 0, &name);
+		  FcPatternGet (renderFont->pattern, FC_SIZE, 0, &size);
+		  fprintf (stderr, "label:             %s.\n", lbl);
+		  fprintf (stderr, "foreground color:  pixel=%08lx, r=%04x,"
+				   " g=%04x, b=%04x, alpha=%04x.\n",
+			   color.pixel, color.color.red, color.color.green,
+			   color.color.blue, color.color.alpha);
+		  fprintf (stderr, "extent:            x=%d, y=%d, xOffset=%d,"
+				   " yOffset=%d, height=%d, width=%d.\n",
+			   glyphinfo.x, glyphinfo.y, glyphinfo.xOff,
+			   glyphinfo.yOff, glyphinfo.height, glyphinfo.width);
+		  fprintf (stderr, "font:              name=%s-%.1f,"
+				   " height=%d, ascent=%d, descent=%d.\n",
+			   name.u.s, size.u.d, renderFont->height,
+			   renderFont->ascent, renderFont->descent);
+		}
+	      XftDrawString8 (xftDraw, &color, renderFont,
+			      x+tab->tabs.l_x, y+tab->tabs.l_y,
+			      lbl, (int) strlen (lbl));
+	      XftDrawDestroy (xftDraw);
+#else
+	      XDrawString(dpy,win,gc,
+			  x+tab->tabs.l_x, y+tab->tabs.l_y,
+			  lbl, (int)strlen(lbl));
+#endif
+	    }
 	}
 
-	if( child == tw->tabs.hilight )
-	  DrawHighlight(tw, child, False) ;
+	if (child == tw->tabs.hilight)
+	  DrawHighlight(tw, child, False);
 }
 
 
@@ -1689,9 +1966,13 @@
 	TabsConstraints tab = (TabsConstraints) w->core.constraints ;
 	TabsWidget	tw = (TabsWidget)XtParent(w) ;
 	String		lbl = tab->tabs.label != NULL ?
-				tab->tabs.label : XtName(w) ;
-	XFontStruct	*font = tw->tabs.font ;
-	int		iw = tw->tabs.internalWidth ;
+				tab->tabs.label : XtName(w);
+#ifdef USE_XFT_TABS
+	XftFont		*font = tw->tabs.renderFont;
+#else
+	XFontStruct	*font = tw->tabs.font;
+#endif
+	int		iw = tw->tabs.internalWidth;
 
 	tab->tabs.width = iw + SHADWID*2 ;
 	tab->tabs.l_x = tab->tabs.lbm_x = SHADWID + iw ;
@@ -1705,10 +1986,27 @@
 
 	if( lbl != NULL && font != NULL )
 	{
-	  tab->tabs.width += XTextWidth( font, lbl, (int)strlen(lbl) ) + iw ;
+#ifdef USE_XFT_TABS
+	  tab->tabs.width += x_xft_text_width (XtDisplay(tw), font,
+						 lbl, (int)strlen(lbl)) + iw;
+	  tab->tabs.l_y = (tw->tabs.tab_height
+			   + tw->tabs.renderFont->ascent
+			   /* #### how can this subtraction be correct? */
+			   - tw->tabs.renderFont->descent)/2;
+	  if (debug_tabs > 2)
+	    fprintf (stderr, "tab:  height=%d, width=%d, baseline=%d.\n",
+		     tw->tabs.tab_height, tab->tabs.width, tab->tabs.l_y);
+	  if (debug_tabs > 1)
+	    fprintf (stderr, "font: height=%d, ascent=%d, descent=%d.\n",
+		     tw->tabs.renderFont->height,
+		     tw->tabs.renderFont->ascent,
+		     tw->tabs.renderFont->descent);
+#else
+	  tab->tabs.width += XTextWidth (font, lbl, (int)strlen(lbl)) + iw;
 	  tab->tabs.l_y = (tw->tabs.tab_height +
 		 tw->tabs.font->max_bounds.ascent -
-		 tw->tabs.font->max_bounds.descent)/2 ;
+		 tw->tabs.font->max_bounds.descent)/2;
+#endif
 	}
 }
 
@@ -1723,19 +2021,32 @@
 	 *
 	 * TODO: if they require more than two rows and the total height:width
 	 * ratio is more than 2:1, then try something else.
+	 * Gaak!  This is actually already done in PreferredSize()!
+	 *
+	 * TODO SOONER: for reasons unclear, some applications (specifically
+	 * XEmacs) give a nominal geometry (in the core record) which doesn't
+	 * make much sense (eg, may be smaller than some of the tab children).
+	 * This results in bizarre values for DISPLAY_ROWS and REPLY_HEIGHT.
+	 * Specify a way to say "tell me what you really want" (eg, with WID
+	 * and/or HGT == 0 or == Dimension_MAX), and use it where appropriate.
+	 * LATE-BREAKING LOSE: This happens in PreferredSize(), not XEmacs!
+	 *
+	 * TODO EVEN SOONER: some applications lay out the tab control by
+	 * repeatedly querying until a fixed width and height has been filled
+	 * by the tabs (XEmacs).  There should be an API to cache this?
 	 */
 
 static	int
 TabLayout(TabsWidget tw, 
-	  Dimension wid, 
-	  Dimension hgt, 
+	  Dimension wid,	/* if 0, use core.width as guess */
+	  Dimension hgt,	/* if 0, use core.height as guess */
 	  Dimension *reply_height, Bool query_only)
 {
 	int		i, row, done = 0, display_rows = 0 ;
 	int		num_children = tw->composite.num_children ;
 	Widget		*childP ;
 	Dimension	w ;
-	Position	x,y ;
+	Position	x,y ;	/* #### gaak, these are dimensions! */
 	TabsConstraints	tab ;
 
 	/* Algorithm: loop through children, assign X positions.  If a tab
@@ -1750,22 +2061,33 @@
 	  row = 0 ;
 	  x = INDENT ;
 	  y = 0 ;
-	  wid -= INDENT ;
+	  /* If wid or hgt is 0, we want to guess our own dimensions.
+	     Currently the guessing functions are broken....
+	     #### When PreferredSize*() get fixed, fix this too. */
+	  if (debug_tabs > 0)
+	    fprintf (stderr, "arg=%d,", wid);
+	  wid = (wid ? wid : tw->core.width) - INDENT ;
+	  hgt = hgt ? hgt : tw->core.height;
+	  if (debug_tabs > 0)
+	    fprintf (stderr, "wid=%d: x,w,y=", wid);
 	  for(i=num_children, childP=tw->composite.children; --i >= 0; ++childP)
 	    if( XtIsManaged(*childP) )
 	    {
 	      tab = (TabsConstraints) (*childP)->core.constraints ;
 	      w = tab->tabs.width ;
 
+	      if (debug_tabs > 0)
+		fprintf (stderr, "%d,%d,%d;", x, w, y);
 	      if( x + w > wid ) {			/* new row */
- 		if (y + tw->tabs.tab_height > hgt && !done)
+		/* #### algorithm is not robust to wid < child's width */
+		++row;
+		x = INDENT ;
+		y += tw->tabs.tab_height ;
+ 		if (y > hgt && !done)
 		  {
 		    display_rows = row;
 		    done = 1;
 		  }
-		++row;
-		x = INDENT ;
-		y += tw->tabs.tab_height ;
 	      }
 	      if( !query_only ) {
 		tab->tabs.x = x ;
@@ -1777,10 +2099,11 @@
 		tab->tabs.visible = 1;
 
 	    }
+	  if (debug_tabs > 0)
+	    fprintf (stderr, "\n");
 	  /* If there was only one row, increase the height by TABDELTA */
 	  if( ++display_rows == 1 )
 	  {
-	    row++;
 	    y = TABDELTA ;
 	    if( !query_only )
 	      for(i=num_children, childP=tw->composite.children;
@@ -1791,6 +2114,7 @@
 		  tab->tabs.y = y ;
 		}
 	  }
+	  row++;
 	  y += tw->tabs.tab_height ;
 	}
 	else
@@ -1802,6 +2126,12 @@
 	  tw->tabs.realRows = row;
 	}
 
+	if (debug_tabs > 0 && (row > 1 || display_rows > 1))
+	  fprintf (stderr, "tab: %d display rows, #children = %d,"
+		   " total height %d, total rows %d%s.\n",
+		   display_rows, num_children, y, row,
+		   query_only ? " (query)" : "");
+
 	if( reply_height != NULL )
 	  *reply_height = y ;
 
@@ -1822,8 +2152,8 @@
 
 
 
-	/* Find max preferred child size.  Returned sizes include child
-	 * border widths.  If except is non-null, don't ask that one.
+	/* Find max preferred child size and store in control widget.
+	 * If except is non-null, don't ask that one.
 	 */
 
 static	void
@@ -2009,7 +2339,10 @@
 	int		nrows ;
 
 	if( tw->composite.num_children > 0 )
-	  nrows = TabLayout(tw, wid, hgt, &th, True) ;
+	  /* used to be wid, hgt not 0, 0 but that's obviously wrong
+	     since TabLayout wants dimensions of control parent but
+	     wid, hgt are dimensions of some child */
+	  nrows = TabLayout(tw, 0, 0, &th, True) ;
 	else {
 	  th = 0 ;
 	  nrows = 0 ;
@@ -2091,17 +2424,29 @@
 	Widget		w = (Widget) tw;
 	XGCValues	values ;
 
-	values.background = tw->core.background_pixel ;
-	values.font = tw->tabs.font->fid ;
-	values.line_style = LineOnOffDash ;
-	values.line_style = LineSolid ;
+	values.background = tw->core.background_pixel;
+	values.font =
+#ifdef USE_XFT_TABS
+	  None;
+#else
+	  tw->tabs.font->fid;
+#endif
+	values.line_style = LineOnOffDash;
+	values.line_style = LineSolid;
 
 	tw->tabs.foregroundGC =
 	  XtAllocateGC(w, w->core.depth,
-	    GCBackground|GCFont|GCLineStyle, &values,
-	    GCForeground,
-	    GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
-		GCDashList|GCArcMode) ;
+#ifndef USE_XFT_TABS
+		       GCFont|
+#endif
+		       GCBackground|GCLineStyle,
+		       &values,
+		       GCForeground,
+#ifdef USE_XFT_TABS
+		       GCFont|
+#endif
+		       GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
+		       GCDashList|GCArcMode);
 }
 
 static	void
@@ -2110,30 +2455,49 @@
 	Widget		w = (Widget) tw;
 	XGCValues	values ;
 
-	values.background = tw->core.background_pixel ;
-	values.font = tw->tabs.font->fid ;
+	values.background = tw->core.background_pixel;
+	values.font =
+#ifdef USE_XFT_TABS
+	  None;
+#else
+	  tw->tabs.font->fid;
+#endif
 #ifdef HAVE_XMU
-	if( tw->tabs.be_nice_to_cmap || w->core.depth == 1)
+	if (tw->tabs.be_nice_to_cmap || w->core.depth == 1)
 	{
-	  values.fill_style = FillStippled ;
+	  values.fill_style = FillStippled;
 	  tw->tabs.grey50 =
-	  values.stipple = XmuCreateStippledPixmap(XtScreen(w), 1L, 0L, 1) ;
+	  values.stipple = XmuCreateStippledPixmap(XtScreen(w), 1L, 0L, 1);
 
 	  tw->tabs.greyGC =
 	    XtAllocateGC(w, w->core.depth,
-	      GCBackground|GCFont|GCStipple|GCFillStyle, &values,
+#ifndef USE_XFT_TABS
+	      GCFont|
+#endif
+	      GCBackground|GCStipple|GCFillStyle, &values,
 	      GCForeground,
+#ifdef USE_XFT_TABS
+	      GCFont|
+#endif
 	      GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
-		  GCDashList|GCArcMode) ;
+		  GCDashList|GCArcMode);
 	}
 	else
 #endif
 	{
 	  tw->tabs.greyGC =
 	    XtAllocateGC(w, w->core.depth,
-	      GCFont, &values,
+#ifdef USE_XFT_TABS
+	      0L,
+#else
+	      GCFont,
+#endif
+	      &values,
 	      GCForeground,
+#ifdef USE_XFT_TABS
+	      GCFont|
+#endif
 	      GCBackground|GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
-		  GCDashList|GCArcMode) ;
+		  GCDashList|GCArcMode);
 	}
 }