home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / xfe / outline.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  45.9 KB  |  1,636 lines

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* 
  19.    outline.c --- the outline widget hack.
  20.    Created: Terry Weissman <terry@netscape.com>, 24-Jun-95.
  21.  */
  22.  
  23.  
  24. #include "mozilla.h"
  25. #include "xfe.h"
  26. #include <msgcom.h>
  27.  
  28. #include "outline.h"
  29. #include "dragdrop.h"
  30.  
  31. #include <Xfe/Xfe.h>            /* for xfe widgets and utilities */
  32.  
  33. #ifndef _STDC_
  34. #define _STDC_ 1
  35. #define HACKED_STDC 1
  36. #endif
  37.  
  38. #define XmLBACKWARDS_COMPATIBILITY
  39.  
  40. #include <XmL/GridP.h>
  41. #include <Xm/Label.h>        /* For the drag window pixmap label */
  42.  
  43. #ifdef HACKED_STDC
  44. #undef HACKED_STDC
  45. #undef _STDC_
  46. #endif
  47.  
  48. #include "icons.h"
  49. #include "icondata.h"
  50.  
  51. #ifdef FREEIF
  52. #undef FREEIF
  53. #endif
  54. #define FREEIF(obj) do { if (obj) { XP_FREE (obj); obj = 0; }} while (0)
  55.  
  56. #define PIXMAP_WIDTH 18        /* #### */
  57. #define PIXMAP_INDENT 10
  58.  
  59. #define LINE_AREA_WIDTH 7
  60.  
  61. #define FLIPPY_WIDTH 14        /* #### */
  62.  
  63. #define MIN_COLUMN_WIDTH 10    /* So that the user can manipulate it. */
  64. #define MAX_COLUMN_WIDTH 10000    /* So that we don't overflow any 16-bit
  65.                    numbers. */
  66.  
  67. static Widget DataWidget = NULL;
  68.  
  69. typedef struct fe_OutlineInfo {
  70.   Widget widget;
  71.   Widget drag_widget;
  72.   int numcolumns;
  73.   int numrows;
  74.   fe_OutlineGetDataFunc datafunc;
  75.   fe_OutlineClickFunc clickfunc;
  76.   fe_OutlineIconClickFunc iconfunc;
  77.   MSG_Pane *pane;
  78.   void* closure;
  79.  
  80.   int lastx;
  81.   int lasty;
  82.   Time lastdowntime;
  83.   Time lastuptime;
  84.   XP_Bool ignoreevents;
  85.   EventMask activity;
  86.  
  87.   /* indented column specific stuff. */
  88.   int *column_indented;    /* the columns that are to be indented. */
  89.   int indent_amount;        /* the amount to indent each subline. */
  90.  
  91.   /* drag specific stuff. */
  92.   XP_Bool drag_enabled;
  93.   fe_icon *drag_icon;
  94.   void* dragscrolltimer;
  95.   int dragscrolldir;
  96.   int dragrow;
  97.   XP_Bool dragrowbox;
  98.   GC draggc;
  99.   fe_dnd_Type drag_type;
  100.  
  101.   XP_Bool allowiconresize;
  102.   int iconcolumnwidth;
  103.   int* columnwidths;
  104.   int* columnIndex;        /* Which column to display where.  If
  105.                    columnIndex[1] == 3, then display the third
  106.                    column's data in column 1.  Column zero is
  107.                    the icon column.*/
  108.   char** posinfo;        /* Pointer to a string where we keep the
  109.                    current column ordering and widths, in the
  110.                    form to be saved in the preferences file.*/
  111.   int dragcolumn;
  112.   Pixmap dragColumnPixmap;
  113.   unsigned char clickrowtype;
  114.   const char** headers;        /* What headers to display on the columns.
  115.                    Entry zero is for the icon column. */
  116.   XP_Bool* headerhighlight;
  117.  
  118.   void* visibleTimer;
  119.   int visibleLine;
  120.  
  121.   fe_OutlineDesc Data;
  122.   int DataRow;
  123. } fe_OutlineInfo;
  124.  
  125.  
  126. static fe_icon pixmapFlippyFolded = { 0 };
  127. static fe_icon pixmapFlippyExpanded = { 0 };
  128.  
  129. #if 0
  130. static fe_icon pixmapMarked = { 0 };
  131. static fe_icon pixmapUnmarked = { 0 };
  132. #endif
  133.  
  134. static fe_dnd_Source dragsource = {0};
  135.  
  136. /* static variables to make the conversion from our style
  137.    to fontlist tags easier (and less of a memory problem.) */
  138.  
  139. static char *ITALIC_STYLE = "ITALIC";
  140. static char *BOLD_STYLE = "BOLD";
  141. static char *DEFAULT_STYLE = XmFONTLIST_DEFAULT_TAG; /* Needs to be this for i18n to work. */
  142.  
  143. static char *
  144. fe_outline_style_to_tag(fe_OutlineTextStyle style)
  145. {
  146.   switch (style) 
  147.     {
  148.     case FE_OUTLINE_Italic:
  149.       return ITALIC_STYLE;
  150.     case FE_OUTLINE_Bold:
  151.       return BOLD_STYLE;
  152.     case FE_OUTLINE_Default:
  153.       return DEFAULT_STYLE;
  154.     default:
  155.       XP_ASSERT(0);
  156.       return DEFAULT_STYLE;
  157.     }
  158. }
  159.  
  160. static void
  161. fe_make_outline_drag_widget (fe_OutlineInfo *info,
  162.                  fe_OutlineDesc *data, int x, int y)
  163. {
  164. #if 0
  165.   Display *dpy = XtDisplay (info->widget);
  166.   XmString str;
  167.   int str_w, str_h;
  168. #endif
  169.   Visual *v = 0;
  170.   Colormap cmap = 0;
  171.   Cardinal depth = 0;
  172.   Widget label;
  173.   XmFontList fontList;
  174.   /*fe_icon *icon;*/
  175.   Widget shell;
  176.   Pixmap dragPixmap;
  177.  
  178.   if (dragsource.widget) return;
  179.  
  180.   shell = info->widget;
  181.   while (XtParent(shell) && !XtIsShell(shell)) {
  182.     shell = XtParent(shell);
  183.   }
  184.  
  185.   XtVaGetValues (shell, XtNvisual, &v, XtNcolormap, &cmap,
  186.          XtNdepth, &depth, 0);
  187.  
  188.   XtVaGetValues (info->widget, XmNfontList, &fontList, NULL);
  189.  
  190.   /* ### free the string
  191.      str = XmStringCreateLocalized(data->str[5]);
  192.      str_w = XmStringWidth (fontList, str);
  193.      str_h = XmStringHeight (fontList, str);
  194.      */
  195.  
  196.   dragsource.type = info->clickrowtype == XmCONTENT ? info->drag_type : FE_DND_COLUMN;
  197.   dragsource.closure = info->widget;
  198.  
  199.   if (info->dragColumnPixmap)
  200.     {
  201.       XFreePixmap(XtDisplay(info->widget), info->dragColumnPixmap);
  202.       info->dragColumnPixmap = 0;
  203.     }
  204.  
  205.   if (dragsource.type == FE_DND_COLUMN) {
  206.     XRectangle cellrect;
  207.     XGCValues gcv;
  208.  
  209.     XmLGridRowColumnToXY(info->widget, 
  210.              XmHEADING, 0, 
  211.              XmCONTENT, info->dragcolumn, 
  212.              True, &cellrect);
  213.  
  214.     /* if the column extends past the right hand side of the widget, clip
  215.        it. */
  216.     {
  217.       Dimension clipwidth;
  218.       Widget vertscrollbar;
  219.  
  220.       XtVaGetValues(info->widget,
  221.             XmNverticalScrollBar, &vertscrollbar,
  222.             NULL);
  223.  
  224.       clipwidth = XfeWidth(info->widget);
  225.  
  226.       if (vertscrollbar && XtIsManaged(vertscrollbar))
  227.     clipwidth -= XfeWidth(vertscrollbar);
  228.  
  229.       if (cellrect.x + cellrect.width > (int)clipwidth)
  230.     cellrect.width = clipwidth - cellrect.x;
  231.     }
  232.  
  233. #ifndef DONT_DRAG_COLUMNS
  234.     cellrect.height = XfeHeight(info->widget) - cellrect.y;
  235. #endif
  236.  
  237.     if (!info->draggc)
  238.       info->draggc = XCreateGC(XtDisplay(info->widget),
  239.                    XtWindow(info->widget),
  240.                    0, &gcv);
  241.  
  242.     dragPixmap = info->dragColumnPixmap = XCreatePixmap(XtDisplay(info->widget),
  243.                             XtWindow(info->widget),
  244.                             cellrect.width,
  245.                             cellrect.height,
  246.                             depth);
  247.  
  248.     XCopyArea(XtDisplay(info->widget), XtWindow(info->widget),
  249.           info->dragColumnPixmap, info->draggc,
  250.           cellrect.x, cellrect.y, cellrect.width, cellrect.height,
  251.           0,0);
  252.  
  253.     dragsource.widget = XtVaCreateWidget ("drag_win",
  254.                       overrideShellWidgetClass,
  255.                       info->widget,
  256.                       XmNwidth, cellrect.width,
  257.                       XmNheight, cellrect.height,
  258.                       XmNvisual, v,
  259.                       XmNcolormap, cmap,
  260.                       XmNdepth, depth,
  261.                       XmNborderWidth, 0,
  262.                       NULL);
  263.   } else {
  264.  
  265.     XP_ASSERT(info->drag_icon != NULL);
  266.     if (info->drag_icon == NULL) return;
  267.  
  268.     dragPixmap = info->drag_icon->pixmap;
  269.  
  270.     dragsource.widget = XtVaCreateWidget ("drag_win",
  271.                       overrideShellWidgetClass,
  272.                       info->widget,
  273.                       XmNwidth, info->drag_icon->width,
  274.                       XmNheight, info->drag_icon->height,
  275.                       XmNvisual, v,
  276.                       XmNcolormap, cmap,
  277.                       XmNdepth, depth,
  278.                       XmNborderWidth, 0,
  279.                       NULL);
  280.   }
  281.  
  282.  
  283.   label = XtVaCreateManagedWidget ("label",
  284.                    xmLabelWidgetClass,
  285.                    dragsource.widget,
  286.                    XmNlabelType, XmPIXMAP,
  287.                    XmNlabelPixmap, dragPixmap,
  288.                    NULL);
  289.  
  290.   /* XmStringFree(str); */
  291. }
  292.  
  293.  
  294. static void
  295. fe_destroy_outline_drag_widget (void)
  296. {
  297.   if (!dragsource.widget) return;
  298.   XtDestroyWidget (dragsource.widget);
  299.   dragsource.widget = NULL;
  300. }
  301.  
  302. static fe_OutlineInfo*
  303. fe_get_info(Widget outline)
  304. {
  305.   fe_OutlineInfo* result = NULL;
  306.   XtVaGetValues(outline, XmNuserData, &result, 0);
  307.   assert(result->widget == outline);
  308.   return result;
  309. }
  310.  
  311.  
  312. static void
  313. PixmapDraw(Widget w, Pixmap pixmap, Pixmap mask,
  314.        int pixmapWidth, int pixmapHeight, unsigned char alignment, GC gc,
  315.        XRectangle *rect, XRectangle *clipRect)
  316. {
  317.   Display *dpy;
  318.   Window win;
  319.   int needsClip;
  320.   int x, y, width, height;
  321.  
  322.   if (pixmap == XmUNSPECIFIED_PIXMAP) return;
  323.   if (rect->width <= 4 || rect->height <= 4) return;
  324.   if (clipRect->width < 3 || clipRect->height < 3) return;
  325.   if (!XtIsRealized(w)) return;
  326.   dpy = XtDisplay(w);
  327.   win = XtWindow(w);
  328.  
  329.   width = pixmapWidth;
  330.   height = pixmapHeight;
  331.   if (!width || !height) {
  332.     alignment = XmALIGNMENT_TOP_LEFT;
  333.     width = clipRect->width - 4;
  334.     height = clipRect->height - 4;
  335.   }
  336.  
  337.   if (alignment == XmALIGNMENT_TOP_LEFT ||
  338.       alignment == XmALIGNMENT_LEFT ||
  339.       alignment == XmALIGNMENT_BOTTOM_LEFT) {
  340.     x = rect->x + 2;
  341.   } else if (alignment == XmALIGNMENT_TOP ||
  342.          alignment == XmALIGNMENT_CENTER ||
  343.          alignment == XmALIGNMENT_BOTTOM) {
  344.     x = rect->x + ((int)rect->width - width) / 2;
  345.   } else {
  346.     x = rect->x + rect->width - width - 2;
  347.   }
  348.  
  349.   if (alignment == XmALIGNMENT_TOP ||
  350.       alignment == XmALIGNMENT_TOP_LEFT ||
  351.       alignment == XmALIGNMENT_TOP_RIGHT) {
  352.     y = rect->y + 2;
  353.   } else if (alignment == XmALIGNMENT_LEFT ||
  354.          alignment == XmALIGNMENT_CENTER ||
  355.          alignment == XmALIGNMENT_RIGHT) {
  356.     y = rect->y + ((int)rect->height - height) / 2;
  357.   } else {
  358.     y = rect->y + rect->height - height - 2;
  359.   }
  360.  
  361.   needsClip = 1;
  362.   if (clipRect->x <= x &&
  363.       clipRect->y <= y &&
  364.       clipRect->x + clipRect->width >= x + width &&
  365.       clipRect->y + clipRect->height >= y + height) {
  366.     needsClip = 0;
  367.   }
  368.  
  369.   if (needsClip) {
  370.     XSetClipRectangles(dpy, gc, 0, 0, clipRect, 1, Unsorted);
  371.   } else if (mask) {
  372.     XSetClipMask(dpy, gc, mask);
  373.     XSetClipOrigin(dpy, gc, x, y);
  374.   }
  375.   XSetGraphicsExposures(dpy, gc, False);
  376.   XCopyArea(dpy, pixmap, win, gc, 0, 0, width, height, x, y);
  377.   XSetGraphicsExposures(dpy, gc, True);
  378.   if (needsClip || mask) {
  379.     XSetClipMask(dpy, gc, None);
  380.   }
  381. }
  382.  
  383.  
  384.  
  385.  
  386. extern XmString fe_StringChopCreate(char* message, char* tag,
  387.                     XmFontList font_list, int maxwidth);
  388.  
  389. static void
  390. fe_outline_celldraw(Widget widget, XtPointer clientData, XtPointer callData)
  391. {
  392.   fe_OutlineInfo* info = (fe_OutlineInfo*) clientData;
  393.   XmLGridCallbackStruct* call = (XmLGridCallbackStruct*) callData;
  394.   XmLGridDrawStruct *draw = call->drawInfo;
  395.   Boolean drawSelected = draw->drawSelected;
  396.   XmLCGridRow *row;
  397.   XmLCGridColumn *column;
  398.   unsigned char alignment;
  399.   Pixel fg;
  400.   XmFontList fontList;
  401.   XmString str = NULL;
  402.   const char* ptr;
  403.   fe_icon* data = NULL;
  404.   XRectangle r, textRect;
  405.   int sourcecol;
  406.  
  407.   if (call->rowType != XmCONTENT) return;
  408.   
  409.   row = XmLGridGetRow(widget, call->rowType, call->row);
  410.   column = XmLGridGetColumn(widget, call->columnType, call->column);
  411.  
  412.   /* fill in useful things into the data sent to the datafunc. */
  413.   info->Data.column_headers = (const char **)info->headers;
  414.   info->Data.numcolumns = info->numcolumns;
  415.   info->Data.selected = (XP_Bool)drawSelected;
  416.  
  417.   if (DataWidget != widget || call->row != info->DataRow) {
  418.     if (!(*info->datafunc)(widget, info->closure, call->row, &info->Data, 
  419.                0)) {
  420.       info->DataRow = -1;
  421.       return;
  422.     }
  423.     DataWidget = widget;
  424.     info->DataRow = call->row;
  425.   }
  426.  
  427.   sourcecol = info->columnIndex[call->column];
  428.  
  429.   if (sourcecol == 0) 
  430.     {
  431.       /* Draw the flippy, if any. */
  432.       if (info->Data.flippy != FE_OUTLINE_Leaf) 
  433.         {
  434.           switch (info->Data.flippy) {
  435.           case FE_OUTLINE_Folded:    data = &pixmapFlippyFolded; break;
  436.           case FE_OUTLINE_Expanded:    data = &pixmapFlippyExpanded; break;
  437.           default:
  438.         XP_ASSERT(0);
  439.           }
  440.           r = *draw->cellRect;
  441.           r.width = data->width;
  442.           PixmapDraw(widget, data->pixmap, data->mask, data->width, data->height,
  443.              XmALIGNMENT_LEFT, draw->gc, &r, call->clipRect);
  444.         }
  445.   
  446. #if 0
  447.       /* Draw the indented icon */
  448.       r = *draw->cellRect;
  449.       data = fe_outline_lookup_icon(Data.icon);
  450.       XP_ASSERT(data);
  451.       if (!data) return;
  452.     
  453.       r.x += Data.depth * PIXMAP_INDENT + FLIPPY_WIDTH;
  454.       r.width = data->width;
  455.       if (r.x + r.width > draw->cellRect->x + draw->cellRect->width) {
  456.     char buf[10];
  457.     XRectangle rect;
  458.     rect = *draw->cellRect;
  459.     rect.width -= r.width;
  460.     PR_snprintf(buf, sizeof (buf), "%d", Data.depth);
  461.         XtVaGetValues(widget,
  462.               XmNrowPtr, row,
  463.               XmNcolumnPtr, column,
  464.               XmNcellForeground, &fg,
  465.               XmNcellFontList, &fontList,
  466.               NULL);
  467.         str = XmStringCreate(buf, DEFAULT_STYLE);
  468.         XSetForeground(XtDisplay(widget), draw->gc,
  469.                drawSelected ? draw->selectForeground : fg);
  470.         XmLStringDraw(widget, str, draw->stringDirection, fontList,
  471.               XmALIGNMENT_RIGHT, draw->gc, &rect, &rect);
  472.         r.x = draw->cellRect->x + draw->cellRect->width - r.width;
  473.       }
  474.       PixmapDraw(widget, data->pixmap, data->mask, data->width, data->height,
  475.          XmALIGNMENT_LEFT, draw->gc, &r, call->clipRect);
  476. #endif
  477.  
  478.     } 
  479.   else /* sourcecol != 0 */
  480.     {
  481.       ptr = info->Data.str[sourcecol - 1];
  482.  
  483.       /* if we're the indented column, we push everything over by the indented
  484.      amount -- the depth * the indent_amount */
  485.       if (info->column_indented[call->column] & FE_OUTLINE_IndentedColumn)
  486.     {
  487.       Dimension indent_amount = info->Data.depth * info->indent_amount;
  488.  
  489.       draw->cellRect->x += indent_amount;
  490.       draw->cellRect->width -= indent_amount;
  491.     }
  492.  
  493.       /* if there's an icon, we need to push the text over. */
  494.       if (info->Data.type[sourcecol - 1] & FE_OUTLINE_Icon)
  495.     {
  496.           PixmapDraw(widget, info->Data.icons[sourcecol - 1]->pixmap, 
  497.              info->Data.icons[sourcecol - 1]->mask, 
  498.              info->Data.icons[sourcecol - 1]->width, 
  499.              info->Data.icons[sourcecol - 1]->height,
  500.              XmALIGNMENT_LEFT, draw->gc, draw->cellRect,
  501.              call->clipRect);
  502.  
  503.       textRect.x = draw->cellRect->x + info->Data.icons[sourcecol - 1]->width + 4;
  504.       textRect.y = draw->cellRect->y;
  505.       textRect.width = draw->cellRect->width - info->Data.icons[sourcecol - 1]->height - 4;
  506.       textRect.height = draw->cellRect->height;
  507.     }
  508.       else
  509.     {
  510.       textRect = *draw->cellRect;
  511.     }
  512.  
  513.       if (info->Data.type[sourcecol - 1] & FE_OUTLINE_String)
  514.         {
  515.           XtVaGetValues(widget,
  516.                 XmNrowPtr, row,
  517.                 XmNcolumnPtr, column,
  518.                 XmNcellForeground, &fg,
  519.                 XmNcellAlignment, &alignment,
  520.                 XmNcellFontList, &fontList,
  521.                 NULL);
  522.           if (call->clipRect->width > 4) 
  523.             {
  524.           /* Impose some spacing between columns.  What a hack. ### */
  525.           call->clipRect->width -= 4;
  526.  
  527.           if (info->Data.type[sourcecol - 1] == FE_OUTLINE_ChoppedString) 
  528.                 {
  529.               str = fe_StringChopCreate((char*) ptr, fe_outline_style_to_tag(info->Data.style),
  530.                         fontList,
  531.                             call->clipRect->width);
  532.             } 
  533.               else 
  534.                 {
  535.               str = XmStringCreate((char *) ptr, fe_outline_style_to_tag(info->Data.style));
  536.             }
  537.           
  538.           XSetForeground(XtDisplay(widget), draw->gc,
  539.                      drawSelected ? draw->selectForeground : fg);
  540.           XmLStringDraw(widget, str, draw->stringDirection, fontList, alignment,
  541.                     draw->gc, &textRect, call->clipRect);
  542.           call->clipRect->width += 4;
  543.             }
  544.         } 
  545.     }
  546.   if (call->row == info->dragrow && sourcecol > 0) 
  547.     {
  548.       int y;
  549.       int x2 = draw->cellRect->x + draw->cellRect->width - 1;
  550.       XP_Bool rightedge = FALSE;
  551.       if (call->column == info->numcolumns) 
  552.         {
  553.           rightedge = TRUE;
  554.           if (str) 
  555.             {
  556.           int xx = draw->cellRect->x + XmStringWidth(fontList, str) + 4;
  557.           if (x2 > xx) x2 = xx;
  558.             }
  559.         }
  560.       if (info->draggc == NULL) 
  561.         {
  562.           XGCValues gcv;
  563. #if 0
  564.           Pixel foreground;
  565. #endif
  566.           gcv.foreground = fg;
  567.           info->draggc = XCreateGC(XtDisplay(widget), XtWindow(widget),
  568.                        GCForeground, &gcv);
  569.         }
  570.       y = draw->cellRect->y + draw->cellRect->height - 1;
  571.       XDrawLine(XtDisplay(widget), XtWindow(widget), info->draggc,
  572.             draw->cellRect->x, y, x2, y);
  573.       if (info->dragrowbox) 
  574.         {
  575.           int y0 = draw->cellRect->y;
  576.           if (call->column == 1) 
  577.             {
  578.           XDrawLine(XtDisplay(widget), XtWindow(widget), info->draggc,
  579.                 draw->cellRect->x, y0, draw->cellRect->x, y);
  580.             }
  581.           if (rightedge) 
  582.             {
  583.           XDrawLine(XtDisplay(widget), XtWindow(widget), info->draggc,
  584.                 x2, y0, x2, y);
  585.             }    
  586.           XDrawLine(XtDisplay(widget), XtWindow(widget), info->draggc,
  587.             draw->cellRect->x, y0, x2, y0);
  588.     
  589.         }
  590.     }
  591.   if (str) XmStringFree(str);
  592. }
  593.  
  594.  
  595. static void
  596. fe_outline_click(Widget widget, XtPointer clientData, XtPointer callData)
  597. {
  598.   fe_OutlineInfo* info = (fe_OutlineInfo*) clientData;
  599.   XmLGridCallbackStruct* call = (XmLGridCallbackStruct*) callData;
  600.   XEvent* event;
  601.   int row;
  602.   unsigned int state;
  603.   int sourcecol = info->columnIndex[call->column];
  604.  
  605.   event = call->event;
  606.   if (!event)
  607.     state = 0;
  608.   else if (event->type == ButtonPress || event->type == ButtonRelease)
  609.     state = event->xbutton.state;
  610.   else if (event->type == KeyPress || event->type == KeyRelease)
  611.     state = event->xkey.state;
  612.   else
  613.     state = 0;
  614.  
  615.   if (call->rowType == XmHEADING) 
  616.     row = -1;
  617.   else
  618.     row = call->row;
  619.   (*info->clickfunc)(widget, info->closure, row,
  620.              sourcecol - 1,
  621.              info->headers ? info->headers[sourcecol] : NULL,
  622.              1, call->reason == XmCR_ACTIVATE ? 2 : 1,
  623.              (state & ShiftMask) != 0, (state & ControlMask) != 0,
  624.              0);
  625. }
  626.  
  627.  
  628. static int last_motion_x = 0;
  629. static int last_motion_y = 0;
  630.  
  631. static void
  632. UpdateData (Widget widget, fe_OutlineInfo *info, int row)
  633. {
  634.   if (widget != DataWidget || row != info->DataRow) {
  635.     if (!(*info->datafunc)(widget, info->closure, row, &info->Data, 0)) {
  636.       return;
  637.     }
  638.     DataWidget = widget;
  639.     info->DataRow = row;
  640.   }
  641. }
  642.  
  643. static XP_Bool
  644. RowIsSelected(fe_OutlineInfo* info, int row)
  645. {
  646.   int* position;
  647.   int n = XmLGridGetSelectedRowCount(info->widget);
  648.   XP_Bool result = FALSE;
  649.   int i;
  650.   if (n > 0) {
  651.     position = XP_ALLOC(sizeof(int) * n);
  652.     if (position) {
  653.       XmLGridGetSelectedRows(info->widget, position, n);
  654.       for (i=0 ; i<n ; i++) {
  655.     if (position[i] == row) {
  656.       result = TRUE;
  657.       break;
  658.     }
  659.       }
  660.       XP_FREE(position);
  661.     }
  662.   }
  663.   return result;
  664. }
  665.  
  666.  
  667. static void
  668. fe_outline_visible_timer(void* closure)
  669. {
  670.   fe_OutlineInfo* info = (fe_OutlineInfo*) closure;
  671.   info->visibleTimer = NULL;
  672.   if (info->visibleLine >= 0) {
  673.     /* First and check that the user isn't still busy with his mouse.  If
  674.        he is, then we'll do this stuff when activity goes to 0. */
  675.     if (info->activity == 0) {
  676.       fe_OutlineMakeVisible(info->widget, info->visibleLine);
  677.       info->visibleLine = -1;
  678.     }
  679.   }
  680. }
  681.  
  682.  
  683. static void
  684. SendClick(fe_OutlineInfo* info, XEvent* event, XP_Bool only_if_not_selected)
  685. {
  686.   int x = event->xbutton.x;
  687.   int y = event->xbutton.y;
  688.   int numclicks = 1;
  689.   unsigned char rowtype;
  690.   unsigned char coltype;
  691.   int row;
  692.   int column;
  693.   unsigned int state = 0;
  694.  
  695.   if (!only_if_not_selected &&
  696.       abs(x - info->lastx) < 5 && abs(y - info->lasty) < 5 &&
  697.       (info->lastdowntime - info->lastuptime <=
  698.        XtGetMultiClickTime(XtDisplay(info->widget)))) {
  699.     numclicks = 2;        /* ### Allow triple clicks? */
  700.   }
  701.   info->lastx = x;
  702.   info->lasty = y;
  703.  
  704.   if (XmLGridXYToRowColumn(info->widget, x, y,
  705.                &rowtype, &row, &coltype, &column) >= 0) {
  706.     if (rowtype == XmHEADING) row = -1;
  707.     if (coltype == XmCONTENT) {
  708.       info->activity = ButtonPressMask;
  709.       info->ignoreevents = True;
  710.       if (column < 1 && !only_if_not_selected && row >= 0) {
  711.     UpdateData(info->widget, info, row);
  712.     if (1 /*### Pixel compare the click with where we drew icon*/) {
  713.       if (numclicks == 1) {
  714.         (*info->iconfunc)(info->widget, info->closure, row, 0);
  715.       }
  716.     }
  717.     return;
  718.       }
  719.       state = event->xbutton.state;
  720.       if (!only_if_not_selected || !RowIsSelected(info, row)) {
  721.     int sourcecol = info->columnIndex[column];
  722.     if (numclicks == 1) {
  723.       /* The user just clicked.  If he's in the middle of a double-click,
  724.          then we don't want calls to fe_OutlineMakeVisible to cause things
  725.          to scroll before the double-click finishes.  So, we set a
  726.          timer to go off; if fe_OutlineMakeVisible gets called before the
  727.          timer expires, we put off the call until the timer goes off. */
  728.       if (info->visibleTimer) {
  729.         FE_ClearTimeout(info->visibleTimer);
  730.       }
  731.       info->visibleTimer =
  732.         FE_SetTimeout(fe_outline_visible_timer, info,
  733.               XtGetMultiClickTime(XtDisplay(info->widget)) + 10);
  734.     }    
  735.     if (row != -1)
  736.       fe_OutlineSelect(info->widget, row, True);
  737.  
  738.     (*info->clickfunc)(info->widget, info->closure, row, sourcecol - 1,
  739.                info->headers ? info->headers[sourcecol] : NULL,
  740.                event->xbutton.button, numclicks,
  741.                (state & ShiftMask) != 0,
  742.                (state & ControlMask) != 0, 0);
  743.  
  744.       }
  745.     }
  746.   }
  747. }
  748.  
  749.  
  750.  
  751. static void
  752. ButtonEvent(Widget widget, XtPointer closure, XEvent* event, Boolean* c)
  753. {
  754.   fe_OutlineInfo* info = (fe_OutlineInfo*) closure;
  755.   int x = event->xbutton.x;
  756.   int y = event->xbutton.y;
  757.   unsigned char rowtype;
  758.   unsigned char coltype;
  759.   int row;
  760.   int column;
  761.  
  762.   info->ignoreevents = False;
  763.  
  764.   switch (event->type) {
  765.   case ButtonPress:
  766.     /* Always ignore btn3. Btn3 is for popups. - dp */
  767.     if (event->xbutton.button == 3) break;
  768.  
  769.     if (XmLGridXYToRowColumn(info->widget, x, y,
  770.                  &rowtype, &row, &coltype, &column) >= 0) {
  771.       if (rowtype == XmHEADING) {
  772.     if (XmLGridPosIsResize(info->widget, x, y)) {
  773.       return;
  774.     }
  775.       }
  776.       info->clickrowtype = rowtype;
  777.       info->dragcolumn = column;
  778.       info->activity |= ButtonPressMask;
  779.       info->ignoreevents = True;
  780.     }
  781.     last_motion_x = x;
  782.     last_motion_y = y;
  783.     info->lastdowntime = event->xbutton.time;
  784.     break;
  785.  
  786.   case ButtonRelease:
  787.  
  788.     /* fe_SetCursor (info->context, False); */
  789.  
  790.     if (info->activity & ButtonPressMask) {
  791.       if (info->activity & PointerMotionMask) {
  792.     /* handle the drop */
  793.  
  794.     fe_dnd_DoDrag(&dragsource, event, FE_DND_DROP);
  795.     fe_dnd_DoDrag(&dragsource, event, FE_DND_END);
  796.  
  797.     fe_destroy_outline_drag_widget();
  798.  
  799.       } else {
  800.     SendClick(info, event, FALSE);
  801.       }
  802.     }
  803.     info->lastuptime = event->xbutton.time;
  804.     info->activity = 0;
  805.     if (info->visibleLine >= 0 && info->visibleTimer == NULL) {
  806.       fe_OutlineMakeVisible(info->widget, info->visibleLine);
  807.       info->visibleLine = -1;
  808.     }
  809.  
  810.     break;
  811.  
  812.   case MotionNotify:
  813.     if (!info->drag_enabled)
  814.       break;
  815.  
  816.     if (!(info->activity & PointerMotionMask) &&
  817.     (abs(x - last_motion_x) < 5 && abs(y - last_motion_y) < 5)) {
  818.       /* We aren't yet dragging, and the mouse hasn't moved enough for
  819.      this to be considered a drag. */
  820.       break;
  821.     }
  822.  
  823.  
  824.     if (info->activity & ButtonPressMask) {
  825.       /* ok, the pointer moved while a button was held.
  826.        * we're gonna drag some stuff.
  827.        */
  828.       info->ignoreevents = True;
  829.       if (!(info->activity & PointerMotionMask)) {
  830.     if (info->drag_type == FE_DND_NONE &&
  831.         info->clickrowtype == XmCONTENT) {
  832.       /* We don't do drag'n'drop in this context.  Do any any visibility
  833.          scrolling that we may have been putting off til the end of user
  834.          activity. */
  835.       info->activity = 0;
  836.       if (info->visibleLine >= 0 && info->visibleTimer == NULL) {
  837.         fe_OutlineMakeVisible(info->widget, info->visibleLine);
  838.         info->visibleLine = -1;
  839.       }
  840.       break;
  841.     }
  842.  
  843.     /* First time.  If the item we're pointing at isn't
  844.        selected, deliver a click message for it (which ought to
  845.        select it.) */
  846.       
  847.     if (info->clickrowtype == XmCONTENT) {
  848.       /* Hack things so we click where the mouse down was, not where
  849.          the first notify event was.  Bleah. */
  850.       event->xbutton.x = last_motion_x;
  851.       event->xbutton.y = last_motion_y;
  852.       SendClick(info, event, TRUE);
  853.       event->xbutton.x = x;
  854.       event->xbutton.y = y;
  855.     }
  856.  
  857.     /* Create a drag source. */
  858.     fe_make_outline_drag_widget (info, &info->Data, x, y);
  859.     fe_dnd_DoDrag(&dragsource, event, FE_DND_START);
  860.     info->activity |= PointerMotionMask;
  861.       }
  862.     
  863.       fe_dnd_DoDrag(&dragsource, event, FE_DND_DRAG);
  864.  
  865.       /* Now, force all the additional mouse motion events that are
  866.      lingering around on the server to come to us, so that Xt can
  867.      compress them away.  Yes, XSync really does improve performance
  868.      in this case, not hurt it. */
  869.       XSync(XtDisplay(info->widget), False);
  870.  
  871.     }
  872.     last_motion_x = x;
  873.     last_motion_y = y;
  874.     break;
  875.   }
  876.   if (info->ignoreevents) *c = False;    
  877. }
  878.  
  879. static void
  880. fe_outline_destroy_cb(Widget widget, XtPointer clientData, XtPointer callData)
  881. {
  882.   fe_OutlineInfo *info = (fe_OutlineInfo*)clientData;
  883.  
  884.   /* first we must have something to free... */
  885.   XP_ASSERT(info);
  886.  
  887.   /* we must also be sure we're freeing the right stuff. */
  888.   XP_ASSERT(info->widget == widget);
  889.  
  890.   FREEIF(info->Data.type);
  891.   FREEIF(info->Data.icons);
  892.   FREEIF(info->Data.str);
  893.  
  894.   FREEIF(info->columnIndex);
  895.   FREEIF(info->column_indented);
  896.   FREEIF(info->headerhighlight);
  897.  
  898.   XP_FREE(info);
  899. }
  900.  
  901. static void
  902. fe_set_default_column_widths(Widget widget) {
  903.   fe_OutlineInfo* info = fe_get_info(widget);
  904.   int i;
  905.   short avgwidth, avgheight;
  906.   XmFontList fontList;
  907.   XtVaGetValues(widget, XmNfontList, &fontList, NULL);
  908.   XmLFontListGetDimensions(fontList, &avgwidth, &avgheight, TRUE);
  909.  
  910.   for (i=0 ; i<info->numcolumns ; i++) {
  911.     int width = info->columnwidths[i] * avgwidth;
  912.     if (width < MIN_COLUMN_WIDTH) width = MIN_COLUMN_WIDTH;
  913.     if (width > MAX_COLUMN_WIDTH) width = MAX_COLUMN_WIDTH;
  914.     info->columnIndex[i] = i;
  915.  
  916.     if (i == 0) info->iconcolumnwidth = width;
  917.  
  918.     XtVaSetValues(widget,
  919.           XmNcolumn, i,
  920.           XmNcolumnSizePolicy, XmCONSTANT,
  921.           XmNcolumnWidth, width,
  922.           NULL);
  923.   }
  924. }
  925.  
  926. Widget
  927. fe_GridCreate(MWContext* context, Widget parent, String name,
  928.           ArgList av, Cardinal ac,
  929.           int maxindentdepth,
  930.           int numcolumns, int* columnwidths,
  931.           fe_OutlineGetDataFunc datafunc,
  932.           fe_OutlineClickFunc clickfunc,
  933.           fe_OutlineIconClickFunc iconfunc,
  934.           void* closure,
  935.           char** posinfo, int tag, XP_Bool isOutline)
  936. {
  937.   Widget result;
  938.   fe_OutlineInfo* info;
  939.  
  940.   XP_ASSERT(numcolumns >= 0);
  941.  
  942.   info = XP_NEW_ZAP(fe_OutlineInfo);
  943.   if (info == NULL) return NULL; /* ### */
  944.  
  945.   XtSetArg(av[ac], XmNuserData, info); ac++;
  946.  
  947.   info->numcolumns = numcolumns;
  948.  
  949.   info->datafunc = datafunc;
  950.   info->clickfunc = clickfunc;
  951.   info->iconfunc = iconfunc;
  952.   info->closure = closure;
  953.   info->dragrow = -1;
  954.   info->posinfo = posinfo;
  955.   info->visibleLine = -1;
  956.   info->DataRow = -1;
  957.  
  958.   info->Data.type = XP_CALLOC(numcolumns, sizeof(fe_OutlineType));
  959.   info->Data.icons = XP_CALLOC(numcolumns, sizeof(fe_icon*));
  960.   info->Data.str = XP_CALLOC(numcolumns, sizeof(char*));
  961.  
  962.   info->lastx = -999;
  963.   info->columnwidths = columnwidths;
  964.  
  965.   XtSetArg(av[ac], XmNselectionPolicy, XmSELECT_MULTIPLE_ROW); ac++;
  966.  
  967.   if (isOutline) {
  968.     columnwidths[numcolumns - 1] = 9999; /* Make the last column always really
  969.                         wide, so we always are ready to
  970.                         show something no matter how wide
  971.                         the window gets. */
  972.  
  973.     if ((context->type == MWContextMail 
  974.      && fe_globalPrefs.mail_pane_style == FE_PANES_HORIZONTAL)
  975.     || ((context->type == MWContextNews
  976.          && fe_globalPrefs.news_pane_style == FE_PANES_HORIZONTAL))) {
  977.       XtSetArg(av[ac], XmNhorizontalSizePolicy, XmCONSTANT); ac++;
  978.     }
  979.     else {
  980.       XtSetArg(av[ac], XmNhorizontalSizePolicy, XmVARIABLE); ac++;
  981.     }
  982.   } else {
  983.     XtSetArg(av[ac], XmNhorizontalSizePolicy, XmCONSTANT); ac++;
  984.   }
  985.  
  986.   result = XmLCreateGrid(parent, name, av, ac);
  987.   info->widget = result;
  988.  
  989.   XtAddCallback(result, XmNdestroyCallback, fe_outline_destroy_cb, info);
  990.  
  991.   XtVaSetValues(result,
  992.         XmNcellDefaults, True,
  993.         XmNcellLeftBorderType, XmBORDER_NONE,
  994.         XmNcellRightBorderType, XmBORDER_NONE,
  995.         XmNcellTopBorderType, XmBORDER_NONE,
  996.         XmNcellBottomBorderType, XmBORDER_NONE,
  997.         XmNcellAlignment, XmALIGNMENT_LEFT,
  998.         0);
  999.  
  1000.   XmLGridAddColumns(result, XmCONTENT, -1, info->numcolumns);
  1001.  
  1002.   info->allowiconresize = (maxindentdepth > 0);
  1003.  
  1004.   info->columnIndex = XP_CALLOC(numcolumns, sizeof(int));
  1005.  
  1006.   fe_set_default_column_widths(result);
  1007.  
  1008.   XtInsertEventHandler(result, ButtonPressMask | ButtonReleaseMask
  1009.                | PointerMotionMask, False,
  1010.                ButtonEvent, info, XtListHead);
  1011.  
  1012.   XtAddCallback(result, XmNcellDrawCallback, fe_outline_celldraw, info);
  1013.   /*  XtAddCallback(result, XmNselectCallback, fe_outline_click, info);
  1014.       XtAddCallback(result, XmNactivateCallback, fe_outline_click, info);*/
  1015.  
  1016.   /* initialize the column indentation stuff. */
  1017.   info->column_indented = XP_CALLOC(numcolumns, sizeof(int *));
  1018.   info->indent_amount = 10;
  1019.  
  1020.   /* initialize the drag and drop stuff. */
  1021.   fe_OutlineDisableDrag(result);
  1022.   info->dragColumnPixmap = 0;
  1023.  
  1024.   if (!pixmapFlippyFolded.pixmap) {
  1025.     Pixel pixel;
  1026.     XtVaGetValues(result, XmNbackground, &pixel, NULL);
  1027.  
  1028.     fe_MakeIcon(context, pixel, &pixmapFlippyFolded, NULL,
  1029.         HFolder.width, HFolder.height,
  1030.         HFolder.mono_bits, HFolder.color_bits, HFolder.mask_bits,
  1031.         FALSE);
  1032.     fe_MakeIcon(context, pixel, &pixmapFlippyExpanded, NULL,
  1033.         HFolderO.width, HFolderO.height,
  1034.         HFolderO.mono_bits, HFolderO.color_bits, HFolderO.mask_bits,
  1035.         FALSE);
  1036. #if 0 
  1037.     /* no marks. */
  1038.     fe_MakeIcon(context, pixel, &pixmapMarked, NULL,
  1039.         HMarked.width, HMarked.height,
  1040.         HMarked.mono_bits, HMarked.color_bits, HMarked.mask_bits,
  1041.         FALSE);
  1042.     fe_MakeIcon(context, pixel, &pixmapUnmarked, NULL,
  1043.         HUMarked.width, HUMarked.height,
  1044.         HUMarked.mono_bits, HUMarked.color_bits, HUMarked.mask_bits,
  1045.         FALSE);
  1046. #endif
  1047.   }
  1048.  
  1049.   return result;
  1050. }
  1051.  
  1052.  
  1053.  
  1054. static void
  1055. fe_outline_remember_columns(Widget widget)
  1056. {
  1057.   fe_OutlineInfo* info = fe_get_info(widget);
  1058.   XmLCGridColumn* col;
  1059.   int i;
  1060.   Dimension width;
  1061.   char* ptr;
  1062.   int length = 0;
  1063.   FREEIF(*(info->posinfo));
  1064.   for (i=0 ; i < info->numcolumns ; i++) {
  1065.     length += strlen(info->headers[i]) + 14;
  1066.   }
  1067.   *(info->posinfo) = XP_ALLOC(length);
  1068.   ptr = *(info->posinfo);
  1069.   for (i=0 ; i<info->numcolumns ; i++) {
  1070.     col = XmLGridGetColumn(widget, XmCONTENT, i);
  1071.     XtVaGetValues(widget, XmNcolumnPtr, col, XmNcolumnWidth, &width, 0);
  1072.     PR_snprintf(ptr, *(info->posinfo) + length - ptr,
  1073.         "%s:%d;", info->headers[info->columnIndex[i]],
  1074.         (int) width);
  1075.     ptr += strlen(ptr);
  1076.   }
  1077.   if (ptr > *(info->posinfo)) ptr[-1] = '\0';
  1078. }
  1079.  
  1080.  
  1081. static void
  1082. fe_outline_resize(Widget widget, XtPointer clientData, XtPointer callData)
  1083. {
  1084.   /* The user has resized a column.  Unfortunately, if they resize it
  1085.      to width 0, they will never be able to grab it to resize it
  1086.      bigger.  So, we will implement a minimum width per column. */
  1087.  
  1088.   fe_OutlineInfo* info = (fe_OutlineInfo*) clientData;
  1089.   XmLGridCallbackStruct* call = (XmLGridCallbackStruct*) callData;
  1090.   XmLCGridColumn* col;
  1091.   Dimension width;
  1092.   if (call->reason != XmCR_RESIZE_COLUMN) return;
  1093.   if (!info->allowiconresize && call->column == 0) {
  1094.     XtVaSetValues(widget, XmNcolumn, call->column, XmNcolumnWidth,
  1095.           info->iconcolumnwidth, 0);
  1096.   } else {
  1097.     col = XmLGridGetColumn(widget, call->columnType, call->column);
  1098.     XtVaGetValues(widget, XmNcolumnPtr, col, XmNcolumnWidth, &width, 0);
  1099.     if (width < MIN_COLUMN_WIDTH) {
  1100.       XtVaSetValues(widget, XmNcolumn, call->column,
  1101.             XmNcolumnWidth, MIN_COLUMN_WIDTH, 0);
  1102.     }
  1103.   }
  1104.   fe_outline_remember_columns(widget);
  1105. }
  1106.  
  1107. void
  1108. fe_OutlineSetMaxDepth(Widget outline, int maxdepth)
  1109. {
  1110.   fe_OutlineInfo* info = fe_get_info(outline);
  1111.   int value = (maxdepth - 1) * PIXMAP_INDENT + PIXMAP_WIDTH + FLIPPY_WIDTH;
  1112.   XP_ASSERT(!info->allowiconresize);
  1113.   XP_ASSERT(maxdepth > 0);
  1114.   if (value != info->iconcolumnwidth) {
  1115.     info->iconcolumnwidth = value;
  1116.     XtVaSetValues(outline, XmNcolumn, 0, XmNcolumnWidth, value, 0);
  1117.   }
  1118. }
  1119.  
  1120.  
  1121. static XmString
  1122. fe_outline_get_header(char *widget, char *header, XP_Bool highlight)
  1123. {
  1124.   char        clas[512];
  1125.   XrmDatabase    db;
  1126.   char        name[512];
  1127.   char        *type;
  1128.   XrmValue    value;
  1129.   XmString    xms;
  1130.  
  1131.   db = XtDatabase(fe_display);
  1132.   (void) PR_snprintf(clas, sizeof(clas), "%s.MailNewsColumns.Pane.Column",
  1133.              fe_progclass);
  1134.   (void) PR_snprintf(name, sizeof(name), "%s.mailNewsColumns.%s.%s",
  1135.              fe_progclass, widget, header);
  1136.   if (XrmGetResource(db, name, clas, &type, &value))
  1137.     {
  1138.       xms = XmStringCreate((char *) value.addr,
  1139.                highlight ? BOLD_STYLE : DEFAULT_STYLE);
  1140.     }
  1141.   else
  1142.     {
  1143.       xms = XmStringCreate(header,
  1144.                highlight ? BOLD_STYLE : DEFAULT_STYLE);
  1145.     }
  1146.  
  1147.   return xms;
  1148. }
  1149.  
  1150.  
  1151. void 
  1152. fe_OutlineSetHeaders(Widget widget, fe_OutlineHeaderDesc *desc)
  1153. {                /* Fix i18n in here ### */
  1154.   fe_OutlineInfo* info = fe_get_info(widget);
  1155.   int i;
  1156.   XmString str;
  1157.   char* ptr;
  1158.   char* ptr2;
  1159.   char* ptr3;
  1160.   int value;
  1161.   int width;
  1162.  
  1163.   ptr = *info->posinfo;
  1164.   for (i=0 ; i<info->numcolumns ; i++) {
  1165.     if (ptr == NULL) {
  1166.       FREEIF(*info->posinfo);
  1167.       break;
  1168.     }
  1169.     ptr2 = XP_STRCHR(ptr, ';');
  1170.     if (ptr2) *ptr2 = '\0';
  1171.     ptr3 = XP_STRCHR(ptr, ':');
  1172.     if (!ptr3) {
  1173.       FREEIF(*info->posinfo);
  1174.       break;
  1175.     }
  1176.     *ptr3 = '\0';
  1177.     for (value = 0 ; value < info->numcolumns ; value++) {
  1178.       if (strcmp(desc->header_strings[value], ptr) == 0) break;
  1179.     }
  1180.     if (value > info->numcolumns) {
  1181.       FREEIF(*info->posinfo);
  1182.       break;
  1183.     }
  1184.     info->columnIndex[i] = value;
  1185.     width = atoi(ptr3 + 1);
  1186.     *ptr3 = ':';
  1187.     if (i == info->numcolumns) width = MAX_COLUMN_WIDTH; /* Last column is
  1188.                                 always huge.*/
  1189.     if (width < MIN_COLUMN_WIDTH) width = MIN_COLUMN_WIDTH;
  1190.     if (width > MAX_COLUMN_WIDTH) width = MAX_COLUMN_WIDTH;
  1191.     XtVaSetValues(widget,
  1192.           XmNcolumn, i,
  1193.           XmNcolumnSizePolicy, XmCONSTANT,
  1194.           XmNcolumnWidth, width,
  1195.           0);
  1196.     if (ptr2) *ptr2++ = ';';
  1197.     ptr = ptr2;
  1198.   }
  1199.  
  1200.   if (*info->posinfo) {
  1201.     /* Check that every column was mentioned, and no duplicates occurred. */
  1202.     int* check = XP_CALLOC(info->numcolumns, sizeof(int));
  1203.     for (i=0 ; i<info->numcolumns ; i++) check[i] = 0;
  1204.     for (i=0 ; i<info->numcolumns ; i++) {
  1205.       int w = info->columnIndex[i];
  1206.       if (w < 0 || w > info->numcolumns || check[w]) {
  1207.     FREEIF(*info->posinfo);
  1208.     break;
  1209.       }
  1210.       check[w] = 1;
  1211.     }
  1212.     XP_FREE(check);
  1213.   }
  1214.  
  1215.   if (!*info->posinfo) fe_set_default_column_widths(widget);
  1216.  
  1217.   info->headers = desc->header_strings;
  1218.   info->headerhighlight = (XP_Bool*)
  1219.     XP_ALLOC((info->numcolumns) * sizeof(XP_Bool));
  1220.   XP_MEMSET(info->headerhighlight, 0,
  1221.         (info->numcolumns) * sizeof(XP_Bool));
  1222.   XmLGridAddRows(widget, XmHEADING, 0, 1);
  1223.   for (i=0 ; i<info->numcolumns ; i++) {
  1224.     char* name = (char *) desc->header_strings[info->columnIndex[i]];
  1225.  
  1226.     if (desc->type[info->columnIndex[i]] & FE_OUTLINE_String) 
  1227.       {
  1228.     str = fe_outline_get_header(XtName(widget), name, 0);
  1229.     XtVaSetValues(widget, XmNcolumn, i, XmNrowType, XmHEADING, XmNrow, 0,
  1230.               XmNcellLeftBorderType, XmBORDER_LINE,
  1231.               XmNcellRightBorderType, XmBORDER_LINE,
  1232.               XmNcellTopBorderType, XmBORDER_LINE,
  1233.               XmNcellBottomBorderType, XmBORDER_LINE,
  1234.               XmNcellString, str, 0);
  1235.     XmStringFree(str);
  1236.       } 
  1237.     else if (desc->type[info->columnIndex[i]] & FE_OUTLINE_Icon) 
  1238.       {
  1239.     XtVaSetValues(widget, XmNcolumn, i, XmNrowType, XmHEADING, XmNrow, 0,
  1240.               XmNcellLeftBorderType, XmBORDER_LINE,
  1241.               XmNcellRightBorderType, XmBORDER_LINE,
  1242.               XmNcellTopBorderType, XmBORDER_LINE,
  1243.               XmNcellBottomBorderType, XmBORDER_LINE,
  1244.               XmNcellType, XmPIXMAP_CELL, XmNcellPixmap, desc->icons[info->columnIndex[i]]->pixmap,
  1245.               0);
  1246.       }
  1247.    
  1248.     /* keep track of the column information -- whether or not to indent, draw
  1249.        lines, etc. */
  1250.     info->column_indented[i] = desc->type[info->columnIndex[i]];
  1251.   }
  1252.   XtVaSetValues(widget, XmNallowColumnResize, True, 0);
  1253.   XtAddCallback(widget, XmNresizeCallback, fe_outline_resize, info);
  1254. }
  1255.  
  1256.  
  1257. void
  1258. fe_OutlineChangeHeaderLabel(Widget widget, const char* headername,
  1259.                 const char* label)
  1260. {
  1261.   fe_OutlineInfo* info = fe_get_info(widget);
  1262.   int i;
  1263.   int j;
  1264.   XmString str;
  1265.   if (label == NULL) label = headername;
  1266.   for (i=0 ; i<info->numcolumns ; i++) {
  1267.     unsigned char celltype;
  1268.     XtVaGetValues(widget, XmNcellType, &celltype, 0);
  1269.  
  1270.     if (XP_STRCMP(info->headers[i], headername) == 0) {
  1271.       for (j=0 ; j<info->numcolumns ; j++) {
  1272.     if (info->columnIndex[j] != i) continue;
  1273.     str = XmStringCreate((char*/*NOOP*/)label,
  1274.                  info->headerhighlight[i] ? BOLD_STYLE : DEFAULT_STYLE);
  1275.     XtVaSetValues(widget, XmNcolumn, j, XmNrowType, XmHEADING, XmNrow, 0,
  1276.               XmNcellString, str, 
  1277.               XmNcellType, XmSTRING_CELL,
  1278.               XmNcellEditable, False, /* hmm.. */
  1279.               0);
  1280.     XmStringFree(str);
  1281.     return;
  1282.       }
  1283.     }
  1284.   }
  1285.   XP_ASSERT(0);
  1286. }
  1287.  
  1288.  
  1289. void
  1290. fe_OutlineSetHeaderHighlight(Widget widget, const char* header, XP_Bool value)
  1291. {
  1292.   fe_OutlineInfo* info = fe_get_info(widget);
  1293.   int i, j;
  1294.   XmString str;
  1295.   for (i=0 ; i<info->numcolumns ; i++) {
  1296.     if (XP_STRCMP(info->headers[i], header) == 0) {
  1297.       if (info->headerhighlight[i] == value) return;
  1298.       info->headerhighlight[i] = value;
  1299.       for (j=0 ; j<info->numcolumns ; j++) {
  1300.     if (info->columnIndex[j] == i) {
  1301.       str = fe_outline_get_header(XtName(widget), (char *)info->headers[i], value);
  1302.       XtVaSetValues(widget, XmNcolumn, j, XmNrowType, XmHEADING,
  1303.             XmNrow, 0, XmNcellString, str, 0);
  1304.       XmStringFree(str);
  1305.       return;
  1306.     }
  1307.       }
  1308.       XP_ASSERT(0);
  1309.     }
  1310.   }
  1311.   /* should we really do this?
  1312.    - toshok 
  1313.   XP_ASSERT(0);
  1314.   */
  1315. }
  1316.  
  1317.  
  1318.  
  1319. void
  1320. fe_OutlineChange(Widget widget, int first, int length, int newnumrows)
  1321. {
  1322.   fe_OutlineInfo* info = fe_get_info(widget);
  1323.   int i;
  1324.   info->DataRow = -1;
  1325.   if (newnumrows != info->numrows) {
  1326.     if (newnumrows > info->numrows) {
  1327.       XmLGridAddRows(widget, XmCONTENT, -1, newnumrows - info->numrows);
  1328.     } else {
  1329.       XmLGridDeleteRows(widget, XmCONTENT, newnumrows,
  1330.             info->numrows - newnumrows);
  1331.     }
  1332.     info->numrows = newnumrows;
  1333.     length = newnumrows - first;
  1334.   }
  1335.   if (first == 0 && length == newnumrows) {
  1336.     XmLGridRedrawAll(widget);
  1337.   } else {
  1338.     for (i=first ; i<first + length ; i++) {
  1339.       XmLGridRedrawRow(widget, XmCONTENT, i);
  1340.     }
  1341.   }
  1342.   XFlush(XtDisplay(widget));
  1343. }
  1344.  
  1345.  
  1346. void
  1347. fe_OutlineSelect(Widget widget, int row, Boolean exclusive)
  1348. {
  1349.   if (exclusive) XmLGridDeselectAllRows(widget, False);
  1350.   XmLGridSelectRow(widget, row, False);
  1351. }
  1352.  
  1353.  
  1354. void
  1355. fe_OutlineUnselect(Widget widget, int row)
  1356. {
  1357.   XmLGridDeselectRow(widget, row, False);
  1358. }
  1359.  
  1360. void
  1361. fe_OutlineUnselectAll(Widget widget)
  1362. {
  1363.   XmLGridDeselectAllRows(widget, False);
  1364. }
  1365.  
  1366.  
  1367. void
  1368. fe_OutlineMakeVisible(Widget widget, int visible)
  1369. {
  1370.   fe_OutlineInfo* info = fe_get_info(widget);
  1371.   int firstrow, lastrow;
  1372.   XRectangle rect;
  1373.   Dimension height, shadowthickness;
  1374.   if (visible < 0) return;
  1375.   if (info->visibleTimer) {
  1376.     info->visibleLine = visible;
  1377.     return;
  1378.   }
  1379.   XtVaGetValues(widget, XmNscrollRow, &firstrow, XmNheight, &height,
  1380.         XmNshadowThickness, &shadowthickness, NULL);
  1381.   height -= shadowthickness;
  1382.   for (lastrow = firstrow + 1 ; ; lastrow++) {
  1383.     if (XmLGridRowColumnToXY(widget, XmCONTENT, lastrow, XmCONTENT, 0,
  1384.                  True, &rect) < 0)
  1385.       {
  1386.     break;
  1387.       }
  1388.     if (rect.y + rect.height >= (int)height) break;
  1389.   }
  1390.   if (visible >= firstrow && visible < lastrow) return;
  1391.   firstrow = visible - ((lastrow - firstrow) / 2);
  1392.   if (firstrow < 0) firstrow = 0;
  1393.   XtVaSetValues(widget, XmNscrollRow, firstrow, 0);
  1394. }
  1395.  
  1396.  
  1397. int
  1398. fe_OutlineGetSelection(Widget outline, MSG_ViewIndex* sellist, int sizesellist)
  1399. {
  1400.   int count = XmLGridGetSelectedRowCount(outline);
  1401.   if (sellist && count > 0) {
  1402.     int status;
  1403.     XP_ASSERT(count <= sizesellist);
  1404.     if (sizeof(MSG_ViewIndex) == sizeof(int)) {
  1405.       status = XmLGridGetSelectedRows(outline, (int*) sellist, count);
  1406.     } else {
  1407.       int* positions = (int*) XP_ALLOC(count * sizeof(int));
  1408.       int i;
  1409.       status = XmLGridGetSelectedRows(outline, positions, count);
  1410.       if (status == 0) {
  1411.     for (i=0 ; i<count ; i++) {
  1412.       sellist[i] = positions[i];
  1413.     }
  1414.       }
  1415.       XP_FREE(positions);
  1416.     }
  1417.     if (status < 0) return status;
  1418.   }
  1419.   return count;
  1420. }
  1421.  
  1422.  
  1423. int
  1424. fe_OutlineRootCoordsToRow(Widget outline, int x, int y, XP_Bool* nearbottom)
  1425. {
  1426.   Position rootx;
  1427.   Position rooty;
  1428.   int row;
  1429.   int column;
  1430.   unsigned char rowtype;
  1431.   unsigned char coltype;
  1432.   XtTranslateCoords(outline, 0, 0, &rootx, &rooty);
  1433.   x -= rootx;
  1434.   y -= rooty;
  1435.   if (x < 0 || y < 0 ||
  1436.       x >= XfeWidth(outline) || y >= XfeHeight(outline)) {
  1437.     return -1;
  1438.   }
  1439.   if (XmLGridXYToRowColumn(outline, x, y, &rowtype, &row,
  1440.                &coltype, &column) < 0) {
  1441.     return -1;
  1442.   }
  1443.   if (rowtype != XmCONTENT || coltype != XmCONTENT) return -1;
  1444.   if (nearbottom) {
  1445.     int row2;
  1446.     *nearbottom = (XmLGridXYToRowColumn(outline, x, y + 5, &rowtype, &row2,
  1447.                     &coltype, &column) >= 0 &&
  1448.            row2 > row);
  1449.   }
  1450.   return row;
  1451. }
  1452.  
  1453.  
  1454. #define SCROLLMARGIN 50
  1455. #define INITIALWAIT 500
  1456. #define REPEATINTERVAL 100
  1457.  
  1458. static void
  1459. fe_outline_drag_scroll(void* closure)
  1460. {
  1461.   fe_OutlineInfo* info = (fe_OutlineInfo*) closure;
  1462.   int row;
  1463.   info->dragscrolltimer = FE_SetTimeout(fe_outline_drag_scroll, info,
  1464.                     REPEATINTERVAL);
  1465.   XtVaGetValues(info->widget, XmNscrollRow, &row, 0);
  1466.   row += info->dragscrolldir;
  1467.   XtVaSetValues(info->widget, XmNscrollRow, row, 0);
  1468. }
  1469.  
  1470. void
  1471. fe_OutlineHandleDragEvent(Widget outline, XEvent* event, fe_dnd_Event type,
  1472.               fe_dnd_Source* source)
  1473. {
  1474.   fe_OutlineInfo* info = fe_get_info(outline);
  1475.   XP_Bool doscroll = FALSE;
  1476.   Position rootx;
  1477.   Position rooty;
  1478.   int x, y;
  1479.   unsigned char rowtype;
  1480.   unsigned char coltype;
  1481.   int row;
  1482.   int column;
  1483.   XmLCGridColumn* col;
  1484.   Dimension width;
  1485.   int i;
  1486.   int delta;
  1487.   int tmp;
  1488.   int total;
  1489.  
  1490.   if (source->type == FE_DND_COLUMN) {
  1491.     if (type == FE_DND_DROP && source->closure == outline) 
  1492.       {
  1493.     /* first we make sure that the drop happens within a valid row/column
  1494.        pair. */
  1495.  
  1496.     XtTranslateCoords(outline, 0, 0, &rootx, &rooty);
  1497.  
  1498.     x = event->xbutton.x_root - rootx;
  1499.     y = event->xbutton.y_root - rooty;
  1500.     
  1501.     if (XmLGridXYToRowColumn(info->widget, x, y,
  1502.                  &rowtype, &row, &coltype, &column) < 0)
  1503.       return;
  1504.     
  1505.     if (column != info->dragcolumn) 
  1506.       {
  1507.         /* Get rid of the hack that makes the last column really wide.  Make
  1508.            it be exactly as wide as it appears, so that if it no longer
  1509.            ends up as the last column, it has a reasonable width. */
  1510.         total = XfeWidth(outline);
  1511.         for (i=0 ; i<info->numcolumns ; i++) 
  1512.           {
  1513.         col = XmLGridGetColumn(outline, XmCONTENT, i);
  1514.         XtVaGetValues(outline,
  1515.                   XmNcolumnPtr, col,
  1516.                   XmNcolumnWidth, &width, 0
  1517.                   );
  1518.         total -= ((int) width); /* Beware any unsigned lossage... */
  1519.           }
  1520.         if (total < MIN_COLUMN_WIDTH) total = MIN_COLUMN_WIDTH;
  1521.         width = total;
  1522.         if (width > MAX_COLUMN_WIDTH) width = MAX_COLUMN_WIDTH;
  1523.         XtVaSetValues(outline, XmNcolumn, info->numcolumns - 1,
  1524.               XmNcolumnWidth, width, 0);
  1525.  
  1526.         if (info->dragcolumn < column) 
  1527.           {
  1528.         delta = 1;
  1529.           } 
  1530.         else 
  1531.           {
  1532.         delta = -1;
  1533.           }
  1534.  
  1535.         /* save off the column number we dragged */
  1536.         tmp = info->columnIndex[info->dragcolumn];
  1537.  
  1538.         /* move all the other columns out of the way. */
  1539.         for (i=info->dragcolumn ; i != column ; i += delta) 
  1540.           {
  1541.         info->columnIndex[i] = info->columnIndex[i + delta];
  1542.           }
  1543.         
  1544.         /* replace the column we dragged in the place we dropped
  1545.            it. */
  1546.         info->columnIndex[column] = tmp;
  1547.         
  1548.         XmLGridMoveColumns(outline, column, info->dragcolumn, 1);
  1549.         
  1550.         /* Now restore the hack of having the last column be wide. */
  1551.         XtVaSetValues(outline, XmNcolumn, info->numcolumns - 1,
  1552.               XmNcolumnWidth, MAX_COLUMN_WIDTH, 0);
  1553.         
  1554.         fe_outline_remember_columns(outline);
  1555.       }
  1556.       }
  1557.  
  1558.     return;
  1559.   }
  1560.   
  1561.   if (type != FE_DND_DRAG && type != FE_DND_END) return;
  1562.  
  1563.   if (type == FE_DND_DRAG) {
  1564.     XtTranslateCoords(outline, 0, 0, &rootx, &rooty);
  1565.     x = event->xbutton.x_root - rootx;
  1566.     y = event->xbutton.y_root - rooty;
  1567.     doscroll = (x >= 0 && x < XfeWidth(outline) &&
  1568.         ((y < 0 && y >= -SCROLLMARGIN) ||
  1569.          (y >= XfeHeight(outline) &&
  1570.           y < XfeHeight(outline) + SCROLLMARGIN)));
  1571.     info->dragscrolldir = y > XfeHeight(outline) / 2 ? 1 : -1;
  1572.   }
  1573.   if (!doscroll) {
  1574.     if (info->dragscrolltimer) {
  1575.       FE_ClearTimeout(info->dragscrolltimer);
  1576.       info->dragscrolltimer = NULL;
  1577.     }
  1578.   } else {
  1579.     if (!info->dragscrolltimer) {
  1580.       info->dragscrolltimer = FE_SetTimeout(fe_outline_drag_scroll, info,
  1581.                         INITIALWAIT);
  1582.     }
  1583.   }
  1584. }
  1585.  
  1586.  
  1587. void
  1588. fe_OutlineSetDragFeedback(Widget outline, int row, XP_Bool usebox)
  1589. {
  1590.   fe_OutlineInfo* info = fe_get_info(outline);
  1591.   int old = info->dragrow;
  1592.   if (old == row && info->dragrowbox == usebox) return;
  1593.   info->dragrowbox = usebox;
  1594.   info->dragrow = row;
  1595.   if (old >= 0) XmLGridRedrawRow(outline, XmCONTENT, old);
  1596.   if (row >= 0 && row != old) XmLGridRedrawRow(outline, XmCONTENT, row);
  1597. }
  1598.  
  1599.  
  1600. Widget
  1601. fe_OutlineCreate(MWContext* context, Widget parent, String name,
  1602.          ArgList av, Cardinal ac,
  1603.          int maxindentdepth,
  1604.          int numcolumns, int* columnwidths,
  1605.          fe_OutlineGetDataFunc datafunc,
  1606.          fe_OutlineClickFunc clickfunc,
  1607.          fe_OutlineIconClickFunc iconfunc,
  1608.          void* closure,
  1609.          char** posinfo, int tag)
  1610. {
  1611.   /*int i;*/
  1612.  
  1613.   return fe_GridCreate(context, parent, name, av, ac, maxindentdepth,
  1614.                numcolumns, columnwidths, datafunc, clickfunc, 
  1615.                iconfunc, closure, posinfo, tag, True); 
  1616.   /* last var = isOutline: attach TRUE for Outline, attach FALSE for Grid */
  1617. }
  1618.  
  1619. void fe_OutlineEnableDrag(Widget outline, fe_icon *dragicon, fe_dnd_Type dragtype)
  1620. {
  1621.   fe_OutlineInfo* info = fe_get_info(outline);
  1622.  
  1623.   info->drag_enabled = True;
  1624.   info->drag_icon = dragicon;
  1625.   info->drag_type = dragtype;
  1626. }
  1627.  
  1628. void fe_OutlineDisableDrag(Widget outline)
  1629. {
  1630.   fe_OutlineInfo* info = fe_get_info(outline);
  1631.   
  1632.   info->drag_enabled = False;
  1633.   info->drag_icon = NULL;
  1634.   info->drag_type = FE_DND_NONE;
  1635. }
  1636.