/*
 * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
 *
 * Copyright (C) 2001-2009, William Chia-Wei Cheng.
 *
 * This file may be distributed under the terms of the Q Public License
 * as defined by Trolltech AS of Norway and appearing in the file
 * LICENSE.QPL included in the packaging of this file.
 *
 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * @(#)$Header: /mm2/home/cvs/bc-src/tgif/choose.c,v 1.10 2009/08/13 01:14:20 cvsps Exp $
 */

#define _INCLUDE_FROM_CHOOSE_C_

#include "tgifdefs.h"

#include "auxtext.e"
#include "button.e"
#include "choose.e"
#include "cutpaste.e"
#include "cursor.e"
#include "dialog.e"
#include "drawing.e"
#include "file.e"
#include "font.e"
#include "mainloop.e"
#include "mainmenu.e"
#include "menu.e"
#include "msg.e"
#include "names.e"
#include "raster.e"
#include "rect.e"
#include "remote.e"
#include "ruler.e"
#include "scroll.e"
#include "setup.e"
#include "strtbl.e"
#include "util.e"

GC nameGC=(GC)0, revNameGC=(GC)0;

typedef struct NamesRec {
   Window base_win, dsp_base_win, dsp_win, scroll_win;
   int base_win_w, base_win_h;
   int dsp_base_win_w, dsp_base_win_h;
   int dsp_area_w, dsp_area_h, scroll_area_h;

   char **entries;
   int num_entries;
   int first_index;
   int marked_index;
   int num_btns;
   struct BBRec btn_bbox[MAXBUTTONS], path_bbox;
   char btn_str[MAXBUTTONS][40];
   int btn_id[MAXBUTTONS];

   char inbuf[512];
   int buf_index, just_clicked, scroll_dir;

   DspList *dsp_ptr;
   char *title_str, *formatted_title_str;

   struct CheckArrayRec *p_check_array;

   int title_w, title_h, title_num_lines;
   int a_btn_w, graph_w, btn_start, title_start, graph_start;
   int btn_w, btn_gap, btn_selected, exposed, screen_w, screen_h;
   int edit_style, loop_once, dont_free_entries, leading;

   int change_to_root, pop_from_root, faking_dot_dot;
   int tabbed_from_root, just_tabbed_from_root;
   int def_btn_id, double_click_btn_id;

   GetEntriesFunc *pf_before_loop;
   AfterLoopFunc *pf_after_loop;
   CheckUpdateFunc *pf_check_update; /* called when a checkbox changes state */

   /* used only when edit_style== NAMES_SELECT_FILE */
   char dir_name[MAXPATHLENGTH+1], saved_dir_name[MAXPATHLENGTH+1];
   char name[MAXPATHLENGTH+1], saved_name[MAXPATHLENGTH+1];
   int name_index, remote_file;

   void *userdata;
} *NamesRecPtr;

static struct NamesRec namesInfo;

static XComposeStatus c_stat;

int GetWorkingDirectory(buf, buf_sz)
   char *buf;
   int buf_sz;
{
   char *psz_result=NULL;

#ifdef VMS
   psz_result = getcwd(buf, buf_sz, 0);
#else /* ~VMS */
#ifdef ibm
   psz_result = getwd(buf, buf_sz);
#else /* ~ibm */
#ifdef NeXT
   psz_result = getwd(buf, buf_sz);
#else /* ~NeXT */
#ifdef luna88k
   psz_result = getwd(buf);
#else /* ~luna88k */
#ifdef sequent
   psz_result = getwd(buf);
#else /* ~sequent */
   psz_result = getcwd(buf, buf_sz);
#endif /* sequent */
#endif /* luna88k */
#endif /* NeXT */
#endif /* ibm */
#endif /* VMS */
   if (psz_result == NULL) {
      UtilStrCpyN(buf, buf_sz, ".");
      return FALSE;
   }
   return TRUE;
}

int SetWorkingDirectory(dir)
   char *dir;
{
   return (chdir(dir) == 0);
}

static
void CalcBaseWinWidth(pni)
   struct NamesRec *pni;
{
   int max_w=max(pni->graph_w,pni->btn_w);

   pni->base_win_w = max_w + (defaultFontWidth<<2);
   pni->graph_start = ((pni->base_win_w-pni->graph_w)>>1);
   if (pni->title_num_lines <= 1) {
      pni->title_start = ((pni->base_win_w-pni->title_w)>>1);
   } else {
      pni->title_start = pni->graph_start;
   }
   pni->btn_start = ((pni->base_win_w-pni->btn_w)>>1);

   pni->path_bbox.ltx = pni->graph_start;
   pni->path_bbox.lty = (ROW_HEIGHT<<1)+pni->title_h;
   /* the path_bbox needs 2 pixels around it */
   pni->path_bbox.lty -= 2;
   pni->path_bbox.rbx = pni->path_bbox.ltx + ITEM_LEN*defaultFontWidth+6;
   pni->path_bbox.rby = pni->path_bbox.lty +
         ((msgFontSet==NULL && msgFontPtr==NULL) ? defaultFontHeight :
         msgFontHeight)+1+4;
   if (boldMsgFontSet == NULL && boldMsgFontPtr == NULL) {
      pni->base_win_h = (7+ITEM_DSPED)*ROW_HEIGHT + pni->title_h;
   } else {
      pni->base_win_h = (6+ITEM_DSPED)*ROW_HEIGHT + boldMsgFontHeight + 1 +
            pni->title_h;
   }
}

void CleanUpCheckArray(pCheckArray)
   /* Does not free pCheckArray */
   struct CheckArrayRec *pCheckArray;
{
   int i, num_cols=pCheckArray->num_cols;

   if (pCheckArray->col_name != NULL) {
      /* there's an extra one column here! */
      for (i=0; i <= num_cols; i++) {
         if (pCheckArray->col_name[i] != NULL) {
            UtilFree(pCheckArray->col_name[i]);
         }
      }
      free(pCheckArray->col_name);
   }
   if (pCheckArray->value != NULL) {
      for (i=0; i < num_cols; i++) {
         if (pCheckArray->value[i] != NULL) {
            free(pCheckArray->value[i]);
         }
      }
      free(pCheckArray->value);
   }
   memset(pCheckArray, 0, sizeof(struct CheckArrayRec));
}

int CopyCheckArray(pCheckArrayTo, pCheckArrayFrom)
   struct CheckArrayRec *pCheckArrayTo, *pCheckArrayFrom;
{
   int i;
   int num_cols=pCheckArrayFrom->num_cols, num_rows=pCheckArrayFrom->num_rows;

   memset(pCheckArrayTo, 0, sizeof(struct CheckArrayRec));
   pCheckArrayTo->num_cols = num_cols;
   pCheckArrayTo->num_rows = num_rows;
   if (pCheckArrayFrom->col_name != NULL) {
      /* there's an extra one column here! */
      pCheckArrayTo->col_name = (char**)malloc((num_cols+1)*sizeof(char*));
      if (pCheckArrayTo->col_name == NULL) FailAllocMessage();
      memset(pCheckArrayTo->col_name, 0, (num_cols+1)*sizeof(char*));

      for (i=0; i <= num_cols; i++) {
         if (pCheckArrayFrom->col_name[i] != NULL) {
            pCheckArrayTo->col_name[i] =
                  UtilStrDup(pCheckArrayFrom->col_name[i]);
            if (pCheckArrayTo->col_name[i] == NULL) FailAllocMessage();
         }
      }
   }
   if (pCheckArrayFrom->value != NULL) {
      pCheckArrayTo->value = (int**)malloc(num_cols*sizeof(int*));
      if (pCheckArrayTo->value == NULL) FailAllocMessage();
      memset(pCheckArrayTo->value, 0, num_cols*sizeof(int*));

      for (i=0; i < num_cols; i++) {
         if (pCheckArrayFrom->value[i] != NULL) {
            pCheckArrayTo->value[i] = (int*)malloc(num_rows*sizeof(int));
            if (pCheckArrayTo->value[i] == NULL) FailAllocMessage();
            memcpy(pCheckArrayTo->value[i], pCheckArrayFrom->value[i],
                  num_rows*sizeof(int));
         }
      }
   }
   return TRUE;
}

void NamesSetTitle(title_str)
   char *title_str;
{
   struct NamesRec *pni=(&namesInfo);

   if (pni->title_str != NULL) free(pni->title_str);
   if (pni->formatted_title_str != NULL) free(pni->formatted_title_str);
   pni->formatted_title_str = NULL;
   pni->title_str = UtilStrDup(title_str);
   if (pni->title_str == NULL) FailAllocMessage();

   if (boldMsgFontSet == NULL && boldMsgFontPtr == NULL) {
      CalcFormatStringInBox(pni->title_str, defaultFontPtr, defaultFontHeight,
            pni->graph_w, &pni->title_num_lines, &pni->title_w, &pni->title_h,
            &pni->formatted_title_str);
   } else {
      CalcFormatStringInBox(pni->title_str, boldMsgFontPtr, boldMsgFontHeight,
            pni->graph_w, &pni->title_num_lines, &pni->title_w, &pni->title_h,
            &pni->formatted_title_str);
   }
   CalcBaseWinWidth(pni);
}

void ResetNamesInfo()
{
   struct NamesRec *pni=(&namesInfo);

   if (pni->title_str != NULL) {
      free(pni->title_str);
      pni->title_str = NULL;
   }
   if (pni->formatted_title_str != NULL) {
      free(pni->formatted_title_str);
      pni->formatted_title_str = NULL;
   }
   if (!pni->dont_free_entries) {
      if (pni->dsp_ptr != NULL) {
         free(pni->dsp_ptr);
         pni->dsp_ptr = NULL;
      }
      if (pni->entries != NULL) {
         free(*(pni->entries));
         free(pni->entries);
         pni->entries = NULL;
      }
   }

   memset(&namesInfo, 0, sizeof(struct NamesRec));

   pni->num_entries = INVALID;
   pni->first_index = INVALID;
   pni->marked_index = INVALID;
   /* do not translate -- the string is used to measure things */
   pni->a_btn_w = ButtonWidth("OK", 8, NULL);
   pni->btn_gap = defaultFontWidth;
   pni->screen_w = DisplayWidth(mainDisplay, mainScreen);
   pni->screen_h = DisplayHeight(mainDisplay, mainScreen);
   pni->dsp_area_w = ITEM_LEN * defaultFontWidth;
   pni->dsp_area_h = ITEM_DSPED * ROW_HEIGHT;
   pni->scroll_area_h = pni->dsp_area_h;
   pni->dsp_base_win_w = pni->dsp_area_w + scrollBarW + (windowPadding<<1) +
         (brdrW<<2);
   pni->dsp_base_win_h = pni->dsp_area_h + (windowPadding<<1) + (brdrW<<1);
   pni->graph_w = pni->dsp_area_w + scrollBarW + (brdrW<<2);
   NamesSetTitle("");
   if (boldMsgFontSet == NULL && boldMsgFontPtr == NULL) {
      pni->base_win_h = (8+ITEM_DSPED)*ROW_HEIGHT;
   } else {
      pni->base_win_h = (6+ITEM_DSPED)*ROW_HEIGHT + 2*(boldMsgFontHeight+1);
   }
   pni->def_btn_id = BUTTON_OK;
   pni->double_click_btn_id = BUTTON_OK;
   pni->tabbed_from_root = TRUE;
}

void InitNamesInfo()
{
   XGCValues values;

   memset(&namesInfo, 0, sizeof(struct NamesRec));

   values.foreground = myFgPixel;
   values.background = (threeDLook ? myLtGryPixel : myBgPixel);
   values.fill_style = FillSolid;
   if (msgFontPtr == NULL) {
      values.font = defaultFontPtr->fid;
   } else {
      values.font = msgFontPtr->fid;
   }
   nameGC = XCreateGC(mainDisplay, rootWindow,
         GCForeground | GCBackground | GCFillStyle | GCFont, &values);

   values.foreground = myBgPixel;
   values.background = myFgPixel;
   revNameGC = XCreateGC(mainDisplay, rootWindow,
         GCForeground | GCBackground | GCFillStyle | GCFont, &values);
}

void CleanUpNamesInfo()
{
   struct NamesRec *pni=(&namesInfo);

   ResetNamesInfo();
   if (pni->title_str != NULL) free(pni->title_str);
   pni->title_str = NULL;
   if (pni->formatted_title_str != NULL) free(pni->formatted_title_str);
   pni->formatted_title_str = NULL;
   XFreeGC(mainDisplay, nameGC);
   XFreeGC(mainDisplay, revNameGC);
   nameGC = revNameGC = (GC)0;
}

void NamesAddButton(btn_str, btn_id)
   char *btn_str;
   int btn_id;
{
   struct NamesRec *pni=(&namesInfo);

   pni->btn_id[pni->num_btns] = btn_id;
   strcpy(pni->btn_str[pni->num_btns], btn_str);
   pni->btn_w += ButtonWidth(btn_str, 8, NULL);
   if (pni->num_btns != 0) {
      pni->btn_w += pni->btn_gap;
   }
   pni->num_btns++;
   if (pni->num_btns > MAXBUTTONS) {
#ifdef _TGIF_DBG /* debug, do not translate */
      TgAssert(FALSE,
            "Too many buttons detected in NamesAddButton().", NULL);
#endif /* _TGIF_DBG */
   }
   CalcBaseWinWidth(pni);
}

static
void RedrawNameScrollWin()
{
   struct NamesRec *pni=(&namesInfo);
   double frac, start_frac;
   int block_h, block_start;

   start_frac = (pni->num_entries > 0) ?
         (double)((double)(pni->first_index) /
         (double)(pni->num_entries)) : ((double)0.0);
   /* starting pixel */
   block_start = (int)(pni->scroll_area_h * start_frac);

   if (pni->num_entries > ITEM_DSPED) {
      frac = (double)((double)ITEM_DSPED / (double)(pni->num_entries));
   } else {
      frac = 1.0;
   }
   if (pni->first_index+ITEM_DSPED >= pni->num_entries) {
      block_h = pni->scroll_area_h - block_start;
   } else {
      block_h = (int)(pni->scroll_area_h * frac);
   }
   TgDrawScrollBar(mainDisplay, pni->scroll_win, VERT_SCROLLBAR,
         0, 0, scrollBarW, pni->scroll_area_h, start_frac, ITEM_DSPED,
         pni->num_entries);
}

#include "xbm/check.xbm"

void DrawCheckbox(dpy, win, gc, x, y, w, h, checked)
   Display *dpy;
   Window win;
   GC gc;
   int x, y, w, h, checked;
{
   if (threeDLook) {
      struct BBRec bbox;

      SetBBRec(&bbox, x, y, x+w+1, y+h+1);
      TgDrawThreeDButton(dpy, win, textMenuGC, &bbox, TGBS_LOWRED, 2, FALSE);
   } else {
      XDrawRectangle(dpy, win, gc, x, y, w, h);
   }
   if (checked) {
      if (threeDLook) {
         XGCValues values;

         values.fill_style = FillStippled;
         values.stipple = checkBitmap;
         values.ts_x_origin = x+2;
         values.ts_y_origin = y+2;
         XChangeGC(dpy, gc,
               GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin,
               &values);
         XFillRectangle(dpy, win, gc, values.ts_x_origin,
               values.ts_y_origin, check_width, check_height);
         values.fill_style = FillSolid;
         values.ts_x_origin = 0;
         values.ts_y_origin = 0;
         XChangeGC(dpy, gc,
               GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin, &values);
      } else {
         XDrawLine(dpy, win, gc, x, y, x+w, y+h);
         XDrawLine(dpy, win, gc, x, y+h, x+w, y);
      }
   }
   if (check_bits == NULL) { }
}

static
void RedrawItem(pni, index)
   struct NamesRec *pni;
   int index;
{
   int len=0, top=0, end=0, checkbox_cols=0, text_left=0, box_offset=0, box_w=0;
   int selected=(index==pni->marked_index);

   if ((msgFontSet==NULL && msgFontPtr==NULL)) {
      top = defaultFontAsc+1;
   } else {
      top = msgFontAsc+1;
   }
   if (pni->first_index+ITEM_DSPED > pni->num_entries) {
      end = pni->num_entries;
   } else {
      end = pni->first_index + ITEM_DSPED;
   }
   if (pni->p_check_array != NULL && pni->p_check_array->num_cols > 0) {
      checkbox_cols = pni->p_check_array->num_cols;
      text_left = checkbox_cols*ROW_HEIGHT;
      box_offset = 1;
      box_w = (ROW_HEIGHT-((box_offset+1)<<1));
   }
   len = strlen(pni->entries[index]);
   XFillRectangle(mainDisplay, pni->dsp_win,
         (selected ? nameGC : revNameGC), 0,
         (index-pni->first_index)*ROW_HEIGHT, ITEM_LEN*defaultFontWidth,
         ROW_HEIGHT);
   DrawMsgString(mainDisplay, pni->dsp_win,
         (selected ? revNameGC : nameGC), text_left,
         (index-pni->first_index)*ROW_HEIGHT+top, pni->entries[index], len);
   if (checkbox_cols > 0) {
      int col=0, cur_x=0;

      for (col=0; col < pni->p_check_array->num_cols; col++) {
         DrawCheckbox(mainDisplay, pni->dsp_win,
               (selected ? revNameGC : nameGC), cur_x+box_offset,
               (index-pni->first_index)*ROW_HEIGHT+top-box_w,
               box_w, box_w, pni->p_check_array->value[col][index]);
         cur_x += ROW_HEIGHT;
      }
   }
}

static
void RedrawDspWindow()
{
   struct NamesRec *pni=(&namesInfo);
   int i, end=0;

   if (pni->first_index+ITEM_DSPED > pni->num_entries) {
      end = pni->num_entries;
   } else {
      end = pni->first_index + ITEM_DSPED;
   }
   XFillRectangle(mainDisplay, pni->dsp_win, revNameGC, 0, 0,
         ITEM_LEN*defaultFontWidth, ITEM_DSPED*ROW_HEIGHT);

   for (i=pni->first_index; i < end; i++) {
      RedrawItem(pni, i);
   }
}

static
void RedrawDspBaseWindow()
{
   if (threeDLook) {
      struct NamesRec *pni=(&namesInfo);
      struct BBRec bbox;

      SetBBRec(&bbox, 0, 0, pni->dsp_base_win_w, pni->dsp_base_win_h);
      TgDrawThreeDButton(mainDisplay, pni->dsp_base_win, textMenuGC, &bbox,
            TGBS_LOWRED, 2, FALSE);
   }
}

static
void RedrawNamePath()
{
   struct NamesRec *pni=(&namesInfo);
   int len=strlen(pni->inbuf), cursor_x=0, cursor_y, box_w, box_h, x, y;
   char *c_ptr;
   struct BBRec bbox;

   if (msgFontPtr == NULL) {
      XSetFont(mainDisplay, nameGC, defaultFontPtr->fid);
   } else {
      XSetFont(mainDisplay, nameGC, msgFontPtr->fid);
   }
   x = pni->graph_start;
   /* the path_bbox has 2 pixels around it */
   y = pni->path_bbox.lty+2;

   box_w = ITEM_LEN*defaultFontWidth+6+(windowPadding<<1);
   cursor_y = y;
   if ((msgFontSet==NULL && msgFontPtr==NULL)) {
      y += defaultFontAsc;
   } else {
      y += msgFontAsc;
   }
   box_h = (ROW_HEIGHT+3);
   if (threeDLook) {
      XSetForeground(mainDisplay, textMenuGC, myBgPixel);
      XFillRectangle(mainDisplay, pni->base_win, textMenuGC, x, cursor_y-2,
            box_w+(windowPadding<<1), box_h+(windowPadding<<1));
      SetBBRec(&bbox, x, cursor_y-2, x+box_w+(windowPadding<<1),
            cursor_y-2+box_h+(windowPadding<<1));
      TgDrawThreeDButton(mainDisplay, pni->base_win, textMenuGC, &bbox,
            TGBS_LOWRED, 2, FALSE);
   } else {
      XClearArea(mainDisplay, pni->base_win, x, cursor_y-2, box_w+1, box_h,
            False);
      XDrawRectangle(mainDisplay, pni->base_win, nameGC, x, cursor_y-2,
            box_w+1, box_h);
   }
   if ((msgFontSet==NULL && msgFontPtr==NULL)) {
      if (len > ITEM_LEN) {
         c_ptr = &(pni->inbuf[len-ITEM_LEN]);
         len = ITEM_LEN;
      } else {
         c_ptr = pni->inbuf;
      }
      cursor_x = x+2+len*defaultFontWidth;
   } else {
      int index=0;

      cursor_x = MsgTextWidth(msgFontPtr, pni->inbuf, len);
      while (cursor_x > box_w-4) {
         index++;
         cursor_x = MsgTextWidth(msgFontPtr, &pni->inbuf[index], len-index);
      }
      c_ptr = &pni->inbuf[index];
      len -= index;
      cursor_x += x+2+1;
   }
   DrawMsgString(mainDisplay, pni->base_win, nameGC, x+2+windowPadding,
         y+1+windowPadding, c_ptr, len);
   XDrawLine(mainDisplay, pni->base_win, nameGC, cursor_x+windowPadding,
         cursor_y+1+windowPadding, cursor_x+windowPadding,
         cursor_y+box_h-5+windowPadding);
}

static
void RedrawNameBaseWindow()
{
   struct NamesRec *pni=(&namesInfo);
   int i, left, base_line;

   /* draw a frame around the window */
   if (threeDLook) {
      if (dialogboxUse3DBorder) {
         struct BBRec bbox;

         SetBBRec(&bbox, 0, 0, pni->base_win_w, pni->base_win_h);
         TgDrawThreeDButton(mainDisplay, pni->base_win, textMenuGC, &bbox,
               TGBS_RAISED, 2, FALSE);
      }
   } else {
      XDrawRectangle(mainDisplay, pni->base_win, nameGC, 0, 0,
            pni->base_win_w-1, pni->base_win_h-1);
   }
   if (boldMsgFontSet == NULL && boldMsgFontPtr == NULL) {
      base_line = defaultFontAsc+ROW_HEIGHT;
      XSetFont(mainDisplay, nameGC, defaultFontPtr->fid);
   } else {
      base_line = boldMsgFontAsc+ROW_HEIGHT;
      if (boldMsgFontPtr != NULL) {
         XSetFont(mainDisplay, nameGC, boldMsgFontPtr->fid);
      }
   }
   /* draw the title */
   if (pni->tabbed_from_root) {
      if (pni->title_num_lines <= 1) {
         DrawBoldMsgString(mainDisplay, pni->base_win, nameGC, pni->title_start,
               base_line, pni->title_str, strlen(pni->title_str));
      } else {
         char *psz_line=pni->formatted_title_str;
         char *psz=strchr(pni->formatted_title_str, '\n');

         do {
            if (psz != NULL) *psz = '\0';
            DrawBoldMsgString(mainDisplay, pni->base_win, nameGC,
                  pni->title_start, base_line, psz_line, strlen(psz_line));
            base_line += ((boldMsgFontSet==NULL && boldMsgFontPtr==NULL) ?
                  defaultFontHeight : boldMsgFontHeight);
            if (psz != NULL) {
               *psz++ = '\n';
               psz_line = psz;
               psz = strchr(psz_line, '\n');
            } else {
               break;
            }
         } while (psz_line != NULL);
      }
   } else {
      int tmp_w, len, tmp_start;

      strcpy(gszMsgBox, TgLoadString(STID_TAB_FOR_FILE_COMPLETION));
      len = strlen(gszMsgBox);
      if (boldMsgFontSet == NULL && boldMsgFontPtr == NULL) {
         tmp_w = defaultFontWidth * len;
      } else {
         tmp_w = BoldMsgTextWidth(boldMsgFontPtr, gszMsgBox, len);
      }
      tmp_start = ((pni->base_win_w-tmp_w)>>1);
      DrawBoldMsgString(mainDisplay, pni->base_win, nameGC, tmp_start,
            base_line, gszMsgBox, len);
      if (pni->title_str != NULL && *pni->title_str != '\0') {
         SetStringStatus(pni->title_str);
      }
   }
   if (boldMsgFontSet != NULL || boldMsgFontPtr != NULL) {
      if (msgFontPtr == NULL) {
         XSetFont(mainDisplay, nameGC, defaultFontPtr->fid);
      } else {
         XSetFont(mainDisplay, nameGC, msgFontPtr->fid);
      }
   }
   RedrawNamePath();

   /* draw the buttons */
   left = pni->btn_start;
   for (i=0; i < pni->num_btns; i++) {
      int len=strlen(pni->btn_str[i]);
      int button_w=ButtonWidth(pni->btn_str[i], 8, NULL), button_h=0;

      if (boldMsgFontSet == NULL && boldMsgFontPtr == NULL) {
         button_h = defaultFontHeight + 4;
      } else {
         button_h = boldMsgFontHeight + 4;
      }
      pni->btn_bbox[i].ltx = left;
      pni->btn_bbox[i].lty = (5+ITEM_DSPED)*ROW_HEIGHT + pni->title_h;
      pni->btn_bbox[i].lty -= 1;
      pni->btn_bbox[i].rbx = pni->btn_bbox[i].ltx+button_w;
      pni->btn_bbox[i].rby = pni->btn_bbox[i].lty+button_h;
      DisplayButtonInBBox(pni->base_win, pni->btn_str[i],
            len, &(pni->btn_bbox[i]), BUTTON_NORMAL, FALSE, 0, NULL);
      left = pni->btn_bbox[i].rbx + 1 + defaultFontWidth;
   }
}

static
void RedrawNamesWindow()
{
   RedrawNameBaseWindow();
   RedrawNameScrollWin();
   RedrawDspWindow();
   RedrawDspBaseWindow();
}

static
int ExposeOrMapNames(input)
   XEvent *input;
{
   struct NamesRec *pni=(&namesInfo);
   XEvent ev;

   if ((input->type==MapNotify && input->xany.window==pni->base_win) ||
         (input->type==Expose && (input->xany.window==pni->base_win ||
         input->xany.window==pni->scroll_win ||
         input->xany.window==pni->dsp_win)) || (!pni->exposed &&
         (XCheckWindowEvent(mainDisplay, pni->base_win, ExposureMask,
         &ev) || XCheckWindowEvent(mainDisplay, pni->scroll_win,
         ExposureMask, &ev) || XCheckWindowEvent(mainDisplay, pni->dsp_win,
         ExposureMask, &ev) || XCheckWindowEvent(mainDisplay,
         pni->base_win, StructureNotifyMask, &ev)))) {
      while (XCheckWindowEvent(mainDisplay, pni->base_win,
            ExposureMask, &ev)) ;
      while (XCheckWindowEvent(mainDisplay, pni->scroll_win,
            ExposureMask, &ev)) ;
      while (XCheckWindowEvent(mainDisplay, pni->dsp_win,
            ExposureMask, &ev)) ;
      while (XCheckWindowEvent(mainDisplay, pni->base_win,
            StructureNotifyMask, &ev)) ;

      RedrawNamesWindow();

      pni->exposed = TRUE;
      XSync(mainDisplay, False);

      if ((input->type==MapNotify &&
            input->xany.window==pni->base_win) ||
            (input->type==Expose &&
            (input->xany.window==pni->base_win ||
            input->xany.window==pni->scroll_win ||
            input->xany.window==pni->dsp_win))) {
         return TRUE;
      }
   }
   return FALSE;
}

static
int CreateNamesWindows(win_name)
   char *win_name;
{
   struct NamesRec *pni=(&namesInfo);
   int y, base_win_x, base_win_y;
   int bg_pixel=(threeDLook ? myLtGryPixel : myBgPixel);
   XWMHints wmhints;
   XSizeHints sizehints;
   XSetWindowAttributes win_attrs;

   base_win_x = (pni->base_win_w > pni->screen_w) ? 0 :
         ((pni->screen_w-pni->base_win_w)>>1);
   base_win_y = (pni->base_win_h > pni->screen_h) ? 0 :
         ((pni->screen_h-pni->base_win_h)/3);

   if ((pni->base_win=XCreateSimpleWindow(mainDisplay, rootWindow,
         base_win_x, base_win_y, pni->base_win_w, pni->base_win_h,
         brdrW, myBorderPixel, bg_pixel)) == 0) {
      return FailToCreateWindowMessage("CreateNamesWindows()", NULL, FALSE);
   }
   XDefineCursor(mainDisplay, pni->base_win, defaultCursor);

   y = (ROW_HEIGHT<<2)+pni->title_h;
   if ((pni->dsp_base_win=XCreateSimpleWindow(mainDisplay, pni->base_win,
         pni->graph_start, y, pni->dsp_base_win_w, pni->dsp_base_win_h,
         0, myBorderPixel, bg_pixel)) == 0) {
      XDestroyWindow(mainDisplay, pni->base_win);
      return FailToCreateWindowMessage("CreateNamesWindows()", NULL, FALSE);
   }
   if ((pni->dsp_win=XCreateSimpleWindow(mainDisplay, pni->dsp_base_win,
         windowPadding, windowPadding, pni->dsp_area_w, pni->dsp_area_h,
         brdrW, myBorderPixel, bg_pixel)) == 0) {
      XDestroyWindow(mainDisplay, pni->base_win);
      return FailToCreateWindowMessage("CreateNamesWindows()", NULL, FALSE);
   }
   if ((pni->scroll_win=XCreateSimpleWindow(mainDisplay,
         pni->dsp_base_win, windowPadding+pni->dsp_area_w+(brdrW<<1),
         windowPadding, scrollBarW, pni->dsp_area_h,
         brdrW, myBorderPixel, bg_pixel)) == 0) {
      XDestroyWindow(mainDisplay, pni->base_win);
      return FailToCreateWindowMessage("CreateNamesWindows()", NULL, FALSE);
   }
   win_attrs.save_under = True;
   win_attrs.colormap = mainColormap;
   XChangeWindowAttributes(mainDisplay, pni->base_win,
         CWSaveUnder | CWColormap, &win_attrs);

   wmhints.flags = InputHint | StateHint;
   wmhints.input = True;
   wmhints.initial_state = NormalState;
   XSetWMHints(mainDisplay, pni->base_win, &wmhints);

   sizehints.flags = PPosition | PSize | USPosition | PMinSize | PMaxSize;
   sizehints.x = base_win_x;
   sizehints.y = base_win_y;
   sizehints.width = sizehints.min_width = sizehints.max_width =
         pni->base_win_w;
   sizehints.height = sizehints.min_height = sizehints.max_height =
         pni->base_win_h;
#ifdef NOTR4MODE
   XSetNormalHints(mainDisplay, pni->base_win, &sizehints);
#else
   XSetWMNormalHints(mainDisplay, pni->base_win, &sizehints);
#endif
   RegisterWM_DELETE_WINDOW(pni->base_win);
   XStoreName(mainDisplay, pni->base_win, win_name);

   XSetTransientForHint(mainDisplay, pni->base_win, mainWindow);

#ifdef MAPBEFORESELECT
   XMapWindow(mainDisplay, pni->base_win);
   XSelectInput(mainDisplay, pni->base_win,
         KeyPressMask | ButtonPressMask | ExposureMask | StructureNotifyMask);
   XMapWindow(mainDisplay, pni->dsp_base_win);
   XSelectInput(mainDisplay, pni->dsp_base_win, ExposureMask);
   XMapWindow(mainDisplay, pni->dsp_win);
   XSelectInput(mainDisplay, pni->dsp_win,
         KeyPressMask | ButtonPressMask | ExposureMask);
   XMapWindow(mainDisplay, pni->scroll_win);
   XSelectInput(mainDisplay, pni->scroll_win,
         KeyPressMask | ButtonPressMask | ExposureMask);
#else
   XSelectInput(mainDisplay, pni->base_win,
         KeyPressMask | ButtonPressMask | ExposureMask | StructureNotifyMask);
   XMapWindow(mainDisplay, pni->base_win);
   XSelectInput(mainDisplay, pni->dsp_base_win, ExposureMask);
   XMapWindow(mainDisplay, pni->dsp_base_win);
   XSelectInput(mainDisplay, pni->dsp_win,
         KeyPressMask | ButtonPressMask | ExposureMask);
   XMapWindow(mainDisplay, pni->dsp_win);
   XSelectInput(mainDisplay, pni->scroll_win,
         KeyPressMask | ButtonPressMask | ExposureMask);
   XMapWindow(mainDisplay, pni->scroll_win);
#endif

   if (warpToWinCenter) {
      XWarpPointer(mainDisplay, None, pni->base_win, 0, 0, 0, 0,
            (pni->base_win_w>>1), (pni->base_win_h>>1));
   }
   XSync(mainDisplay, False);

   Msg("");

   return TRUE;
}

static
void NamesUpdateIndices()
{
   struct NamesRec *pni=(&namesInfo);

   pni->first_index = 0;
   if (pni->marked_index == INVALID) {
      if (pni->edit_style != NAMES_SELECT_FILE) {
         *pni->inbuf = '\0';
         pni->buf_index = 0;
      }
   } else {
      if (pni->marked_index >= ITEM_DSPED) {
         if (pni->marked_index < pni->num_entries-ITEM_DSPED) {
            pni->first_index = pni->marked_index;
         } else {
            pni->first_index = pni->num_entries-ITEM_DSPED;
         }
      }
      if (pni->edit_style != NAMES_SELECT_FILE) {
         strcpy(pni->inbuf, &(pni->entries[pni->marked_index])[pni->leading]);
         pni->buf_index = strlen(pni->inbuf);;
      }
   }
}

static
int IsFirstEqChar(s, index)
   char *s;
   int index;
{
   s = (&s[index]);
   if (*s != '=') return FALSE;
   for (--index, --s; index >= 0; index--, s--) {
      if (*s == '=') {
         return FALSE;
      }
   }
   return TRUE;
}

static
int SetMarkedIndex()
{
   struct NamesRec *pni=(&namesInfo);
   int i;

   if (*pni->name == '\0') return FALSE;
   for (i=0; i < pni->num_entries; i++) {
      if (strncmp(&(pni->entries[i])[pni->leading],
            pni->name, pni->name_index) == 0) {
         break;
      }
   }
   if (i < pni->num_entries) {
      if (i < pni->first_index) {
         pni->first_index = i;
      } else if (i >= pni->first_index+ITEM_DSPED) {
         if (i < pni->num_entries-ITEM_DSPED) {
            pni->first_index = i;
         } else {
            pni->first_index = pni->num_entries-ITEM_DSPED;
         }
      }
      pni->marked_index = i;
      return TRUE;
   } else {
      return FALSE;
   }
}

static
void BackSpaceInNames(pn_changing, pn_selected_btn_index)
   int *pn_changing, *pn_selected_btn_index;
{
   struct NamesRec *pni=(&namesInfo);
   int i;

   switch (pni->edit_style) {
   case NAMES_SIMPLE_SELECT_NAME: return;
   case NAMES_COMPLEX_SELECT_NAME:
      if (pni->marked_index == INVALID || pni->buf_index <= 0) return;
      pni->inbuf[--pni->buf_index] = '\0';
      if (pni->buf_index != 0) {
         int i;

         for (i=0; i < pni->num_entries; i++) {
            if (strncmp(&(pni->entries[i])[pni->leading],
                  pni->inbuf, pni->buf_index) == 0) {
               break;
            }
         }
         if (i < pni->num_entries) {
            if (i < pni->first_index) {
               pni->first_index = i;
            } else if (i >= pni->first_index+ITEM_DSPED) {
               if (i < pni->num_entries-ITEM_DSPED) {
                  pni->first_index = i;
               } else {
                  pni->first_index = pni->num_entries-ITEM_DSPED;
               }
            }
            pni->marked_index = i;
         }
      } else {
         pni->first_index = 0;
         pni->marked_index = INVALID;
      }
      break;
   case NAMES_SELECT_FILE:
      if (pni->name_index > 0) {
         pni->name[--pni->name_index] = '\0';
         sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
         pni->buf_index = strlen(pni->inbuf);
         if (pni->tabbed_from_root && pni->name_index != 0) {
            for (i=0; i < pni->num_entries; i++) {
               if (strncmp(&(pni->entries[i])[pni->leading],
                     pni->name, pni->name_index) == 0) {
                  pni->marked_index = i;
                  break;
               }
            }
         } else {
            pni->marked_index = INVALID;
         }
      } else if (pni->change_to_root && *pni->dir_name == '\0' &&
            *pni->saved_dir_name != '\0') {
         pni->pop_from_root = TRUE;
         strcpy(pni->dir_name, pni->saved_dir_name);
         *pni->saved_dir_name = '\0';
         pni->tabbed_from_root = TRUE;
         pni->change_to_root = FALSE;
         sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
         pni->buf_index = strlen(pni->inbuf);
         *pn_changing = FALSE;
         *pn_selected_btn_index = INVALID;
      } else {
         /* fake as if "../" is selected */
         sprintf(pni->name, "..%c", DIR_SEP);
         pni->name_index = strlen(pni->name);
         for (i=0; i < pni->num_entries; i++) {
            if (strncmp(&(pni->entries[i])[pni->leading],
                  pni->name, pni->name_index) == 0) {
               pni->marked_index = i;
               pni->faking_dot_dot = TRUE;
               *pn_changing = FALSE;
               *pn_selected_btn_index = i;
               break;
            }
         }
      }
      break;
   case NAMES_EDIT_ATTR:
      if (pni->marked_index == INVALID || pni->buf_index <= 0) return;
      if (pni->dsp_ptr == NULL ||
            !pni->dsp_ptr[pni->marked_index].directory ||
            !IsFirstEqChar(pni->inbuf, pni->buf_index-1)) {
         pni->inbuf[--pni->buf_index] = '\0';
         strcpy(&(pni->entries[pni->marked_index])[pni->leading],
               pni->inbuf);
      }
      break;
   case NAMES_EDIT_NAME:
      if (pni->marked_index == INVALID || pni->buf_index <= 0) return;
      pni->inbuf[--pni->buf_index] = '\0';
      strcpy(&(pni->entries[pni->marked_index])[pni->leading],
            pni->inbuf);
      break;
   default: break;
   }
   if (pni->exposed) {
      RedrawNamePath();
      RedrawDspWindow();
   }
}

static
void ParseFileName(FullName, DirName, Name)
   char *FullName, *DirName, *Name;
{
   int i, len;

   len = strlen(FullName);
   strcpy(DirName, FullName);
   for (i=len-1; i>=0; i--) {
      if (DirName[i] == DIR_SEP) {
         strcpy(Name, &(DirName[i+1]));
         DirName[i] = '\0';
         return;
      }
   }
   *DirName = *Name = '\0';
}

static
void TabInNames()
{
   struct NamesRec *pni=(&namesInfo);

   if (pni->edit_style == NAMES_SELECT_FILE) {
      pni->tabbed_from_root = TRUE;
      pni->just_tabbed_from_root = TRUE;
      if (!FileIsRemote(pni->name)) {
         /* saved_name is used for just_tabbed_from_root */
         sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
         pni->buf_index = strlen(pni->inbuf);
         strcpy(pni->saved_name, pni->name);
         ParseFileName(pni->inbuf, pni->dir_name, pni->name);
      } else {
         pni->remote_file = TRUE;
      }
   }
}

static
void CharInNames(buf, pn_changing, pn_selected_btn_index)
   char *buf;
   int *pn_changing, *pn_selected_btn_index;
{
   struct NamesRec *pni=(&namesInfo);
   int i=INVALID;

   switch (pni->edit_style) {
   case NAMES_SIMPLE_SELECT_NAME:
      if (buf[0] == ' ' && buf[1] == '\0' &&
            pni->p_check_array != NULL && pni->p_check_array->num_cols == 1) {
         i = pni->marked_index;
         pni->p_check_array->value[0][i] = !(pni->p_check_array->value[0][i]);
         RedrawItem(pni, i);
      }
      return;

   case NAMES_COMPLEX_SELECT_NAME:
      if (buf[0] == '$') {
         i = pni->num_entries-1;
         strcpy(pni->inbuf, &(pni->entries[i])[pni->leading]);
      } else if (buf[0] == '^') {
         i = 0;
         strcpy(pni->inbuf, &(pni->entries[i])[pni->leading]);
      } else {
         pni->inbuf[pni->buf_index++] = buf[0];
         pni->inbuf[pni->buf_index] = '\0';
         for (i=0; i < pni->num_entries; i++) {
            if (strncmp(&(pni->entries[i])[pni->leading],
                  pni->inbuf, pni->buf_index) == 0) {
               break;
            }
         }
      }
      if (i < pni->num_entries) {
         if (i < pni->first_index) {
            pni->first_index = i;
         } else if (i >= pni->first_index+ITEM_DSPED) {
            if (i < pni->num_entries-ITEM_DSPED) {
               pni->first_index = i;
            } else {
               pni->first_index = pni->num_entries-ITEM_DSPED;
            }
         }
         pni->marked_index = i;
      } else {
         pni->inbuf[--pni->buf_index] = '\0';
      }
      break;
   case NAMES_SELECT_FILE:
      if (!pni->tabbed_from_root) {
         pni->name[pni->name_index++] = buf[0];
         pni->name[pni->name_index] = '\0';
         sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
         pni->buf_index = strlen(pni->inbuf);
      } else if (buf[0] == '$') {
         i = pni->num_entries-1;
         strcpy(pni->name, &(pni->entries[i])[pni->leading]);
         pni->name_index = strlen(pni->name);
      } else if (buf[0] == '^') {
         i = 0;
         strcpy(pni->name, &(pni->entries[i])[pni->leading]);
         pni->name_index = strlen(pni->name);
#ifdef apollo
      } else if (pni->name_index == 0 && buf[0] == DIR_SEP &&
            *pni->dir_name == '\0') {
         strcpy(pni->dir_name, DIR_SEP_STR);
         pni->name[pni->name_index++] = buf[0];
         i = pni->num_entries;
         strcpy(pni->inbuf, "//");
#endif /* apollo */
      } else if (pni->name_index == 0 && buf[0] == DIR_SEP) {
         if (*pni->saved_dir_name == '\0') {
            strcpy(pni->saved_dir_name, pni->dir_name);
            pni->num_entries = 0;
         }
         *pn_changing = FALSE;
         pni->change_to_root = TRUE;
         pni->tabbed_from_root = FALSE;
         *pn_selected_btn_index = INVALID;
         *pni->dir_name = *pni->name = '\0';
         pni->name_index = 0;
         sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
         pni->buf_index = strlen(pni->inbuf);
         break;
      } else {
         pni->name[pni->name_index++] = buf[0];
         pni->name[pni->name_index] = '\0';
         for (i=0; i < pni->num_entries; i++) {
            if (strncmp(&(pni->entries[i])[pni->leading],
                  pni->name, pni->name_index) == 0) {
               break;
            }
         }
      }
      if (pni->num_entries > 0) {
         if (i < pni->num_entries) {
            if (i < pni->first_index) {
               pni->first_index = i;
            } else if (i >= pni->first_index+ITEM_DSPED) {
               if (i < pni->num_entries-ITEM_DSPED) {
                  pni->first_index = i;
               } else {
                  pni->first_index = pni->num_entries-ITEM_DSPED;
               }
            }
            pni->marked_index = i;
            sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
            pni->buf_index = strlen(pni->inbuf);
            if (buf[0] == DIR_SEP) {
               *pn_changing = FALSE;
               *pn_selected_btn_index = INVALID;
            }
         } else {
            pni->name[--pni->name_index] = '\0';
         }
      }
      break;
   case NAMES_EDIT_ATTR:
      if (pni->marked_index == INVALID) return;
      pni->inbuf[pni->buf_index++] = buf[0];
      pni->inbuf[pni->buf_index] = '\0';
      strcpy(&(pni->entries[pni->marked_index])[pni->leading],
            pni->inbuf);
      break;
   case NAMES_EDIT_NAME:
      if (pni->marked_index == INVALID) return;
      pni->inbuf[pni->buf_index++] = buf[0];
      pni->inbuf[pni->buf_index] = '\0';
      strcpy(&(pni->entries[pni->marked_index])[pni->leading],
            pni->inbuf);
      break;
   default: break;
   }
   if (pni->exposed) {
      RedrawNamePath();
      RedrawDspWindow();
   }
}

static
int GetNameEntryNum(RowOffset)
   int RowOffset;
{
   struct NamesRec *pni=(&namesInfo);
   int index;

   index = pni->first_index + RowOffset;
   if (index >= pni->num_entries) return INVALID;
   return index;
}

static
int ScrollItemCallback(pv_userdata)
   void *pv_userdata;
   /* returns TRUE to cancel scrolling */
{
   struct NamesRec *pni=((struct NamesRec *)pv_userdata);

   if (pni->scroll_dir == SCRL_UP) {
      if (pni->first_index == 0) return FALSE;
      pni->first_index--;
   } else {
      if (pni->num_entries <= ITEM_DSPED ||
            pni->first_index+ITEM_DSPED == pni->num_entries) {
         return FALSE;
      }
      pni->first_index++;
   }
   RedrawNameScrollWin();
   RedrawDspWindow();
   XSync(mainDisplay, False);

   return FALSE;
}

static
int ScrollPageCallback(pv_userdata)
   void *pv_userdata;
   /* returns TRUE to cancel scrolling */
{
   struct NamesRec *pni=((struct NamesRec *)pv_userdata);

   if (pni->scroll_dir == SCRL_UP) {
      if (pni->first_index == 0) return FALSE;
      pni->first_index -= ITEM_DSPED;
      if (pni->first_index < 0) pni->first_index = 0;
   } else {
      if (pni->num_entries <= ITEM_DSPED ||
            pni->first_index+ITEM_DSPED == pni->num_entries) {
         return FALSE;
      }
      pni->first_index += ITEM_DSPED;
      if (pni->first_index+ITEM_DSPED >= pni->num_entries) {
         pni->first_index = pni->num_entries-ITEM_DSPED;
      }
   }
   RedrawNameScrollWin();
   RedrawDspWindow();
   XSync(mainDisplay, False);

   return FALSE;
}

static
int DoNameBtnScroll(scroll_page, scroll_dir, pbbox, pni)
   int scroll_page, scroll_dir;
   struct BBRec *pbbox;
   struct NamesRec *pni;
   /* returns TRUE if done scrolling */
{
   ScrollBtnCallbackInfo sbci;

   pni->scroll_dir = scroll_dir;
   memset(&sbci, 0, sizeof(ScrollBtnCallbackInfo));

   if (scroll_page) {
      sbci.ms = 200;
      sbci.pv_userdata = pni;
      sbci.pf_scroll_btn_callback = ScrollPageCallback;
      if (TgPressButtonLoop(mainDisplay, pni->scroll_win, NULL,
            &sbci)) {
         if (scroll_dir == SCRL_UP) {
            if (pni->first_index == 0) return TRUE;
            pni->first_index -= ITEM_DSPED;
            if (pni->first_index < 0) pni->first_index = 0;
         } else {
            if (pni->num_entries <= ITEM_DSPED ||
                  pni->first_index+ITEM_DSPED == pni->num_entries) {
               return TRUE;
            }
            pni->first_index += ITEM_DSPED;
            if (pni->first_index+ITEM_DSPED >= pni->num_entries) {
               pni->first_index = pni->num_entries-ITEM_DSPED;
            }
         }
      }
   } else {
      sbci.ms = 50;
      sbci.pv_userdata = pni;
      sbci.pf_scroll_btn_callback = ScrollItemCallback;
      if (TgPressButtonLoop(mainDisplay, pni->scroll_win, pbbox, &sbci)) {
         if (scroll_dir == SCRL_UP) {
            if (pni->first_index == 0) return TRUE;
            pni->first_index--;
         } else {
            if (pni->num_entries <= ITEM_DSPED ||
                  pni->first_index+ITEM_DSPED == pni->num_entries) {
               return TRUE;
            }
            pni->first_index++;
         }
      }
   }
   return FALSE;
}

static
void NameScrollHandler(button_ev)
   XButtonEvent *button_ev;
{
   struct NamesRec *pni=(&namesInfo);
   double frac, start_frac;
   int block_h, block_start, do_drag=FALSE, btn_offset=0;

   if (!threeDLook && button_ev->button == Button3 &&
         button_ev->type == ButtonPress) {
      if (DoNameBtnScroll(((button_ev->state & (ShiftMask|ControlMask)) != 0),
            SCRL_UP, NULL, pni)) {
         return;
      }
   } else if (!threeDLook && button_ev->button == Button1 &&
         button_ev->type == ButtonPress) {
      if (DoNameBtnScroll(((button_ev->state & (ShiftMask|ControlMask)) != 0),
            SCRL_DN, NULL, pni)) {
         return;
      }
   } else if (button_ev->button == Button1 && button_ev->type == ButtonPress) {
      if (button_ev->y < scrollBarW ||
            button_ev->y >= pni->scroll_area_h-scrollBarW) {
         int which=INVALID;
         struct BBRec bbox;

         if (button_ev->y < scrollBarW) {
            which = SCRL_UP;
            SetBBRec(&bbox, 0, 0, scrollBarW, scrollBarW);
         } else {
            which = SCRL_DN;
            SetBBRec(&bbox, 0, pni->scroll_area_h-scrollBarW, scrollBarW,
                  pni->scroll_area_h);
         }
         if (DoNameBtnScroll(FALSE, which, &bbox, pni)) {
            return;
         }
      } else {
         int hit=0;

         start_frac = (pni->num_entries > 0) ?
               (double)((double)(pni->first_index) /
               (double)(pni->num_entries)) : ((double)0.0);
         hit = TgGetScrollHit(button_ev->x, button_ev->y, VERT_SCROLLBAR,
               scrollBarW, pni->scroll_area_h, start_frac, ITEM_DSPED,
               pni->num_entries, &btn_offset);
         if (hit == 0) {
            do_drag = TRUE;
         } else {
            if (DoNameBtnScroll(TRUE, (hit < 0 ? SCRL_UP : SCRL_DN), NULL,
                  pni)) {
               return;
            }
         }
      }
   } else if (!threeDLook && button_ev->button == Button2 &&
         button_ev->type == ButtonPress) {
      do_drag = TRUE;
   }
   if (do_drag) {
      int done=FALSE;
      XEvent ev;

      if (pni->num_entries <= ITEM_DSPED) return;

      frac = (double)((double)ITEM_DSPED / (double)(pni->num_entries));
      block_h = (int)(pni->scroll_area_h * frac);
      if (threeDLook) {
         block_start = button_ev->y+btn_offset;;
         start_frac = (double)((double)(block_start-scrollBarW) /
               (double)(pni->scroll_area_h-(scrollBarW<<1)));
         if (block_start+block_h >= pni->scroll_area_h-scrollBarW) {
            pni->first_index = pni->num_entries - ITEM_DSPED;
         } else {
            pni->first_index = (int)(pni->num_entries * start_frac);
         }
      } else {
         block_start = button_ev->y;
         start_frac = (double)((double)(block_start) /
               (double)(pni->scroll_area_h));
         if (block_start+block_h >= pni->scroll_area_h) {
            pni->first_index = pni->num_entries - ITEM_DSPED;
         } else {
            pni->first_index = (int)(pni->num_entries * start_frac);
         }
      }
      RedrawNameScrollWin();
      RedrawDspWindow();

      XGrabPointer(mainDisplay, pni->scroll_win, False,
            PointerMotionMask | ButtonReleaseMask, GrabModeAsync,
            GrabModeAsync, None, handCursor, CurrentTime);

      while (!done) {
         XNextEvent(mainDisplay, &ev);

         if (ev.type == Expose || ev.type == VisibilityNotify) {
            ExposeEventHandler(&ev, TRUE);
         } else if (ev.type == ButtonRelease) {
            XUngrabPointer(mainDisplay, CurrentTime);
            if (debugNoPointerGrab) XSync(mainDisplay, False);
            done = TRUE;
         } else if (ev.type == MotionNotify) {
            int new_name_first=0, y=ev.xmotion.y;

            if (threeDLook) {
               y += btn_offset;
               start_frac = (double)(((double)(y-scrollBarW)) /
                    ((double)(pni->scroll_area_h-(scrollBarW<<1))));

               if (y <= scrollBarW) {
                  new_name_first = 0;
               } else if (y+block_h >= pni->scroll_area_h-scrollBarW) {
                  new_name_first = pni->num_entries - ITEM_DSPED;
               } else {
                  new_name_first = (int)(pni->num_entries * start_frac);
               }
            } else {
               start_frac = (double)(((double)y) /
                    ((double)pni->scroll_area_h));

               if (y <= 0) {
                  new_name_first = 0;
               } else if (y+block_h >= pni->scroll_area_h) {
                  new_name_first = pni->num_entries - ITEM_DSPED;
               } else {
                  new_name_first = (int)(pni->num_entries * start_frac);
               }
            }
            if (pni->first_index != new_name_first) {
               pni->first_index = new_name_first;
               RedrawNameScrollWin();
               RedrawDspWindow();
            }
            while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
         }
      }
      return;
   }
   RedrawNameScrollWin();
   RedrawDspWindow();
}

static
int NameDspHandler(button_ev)
   XButtonEvent *button_ev;
{
   struct NamesRec *pni=(&namesInfo);
   static Time last_click_time;
   static int last_name_marked;
   Time click_time;
   int checkbox_cols=0, box_offset=0, clicked_col=INVALID, check_updated=FALSE;

   if (enableMouseWheel &&
         (button_ev->button == Button4 || button_ev->button == Button5)) {
      int which=INVALID;
      struct BBRec bbox;

      if (button_ev->button == Button4) {
         which = SCRL_UP;
         SetBBRec(&bbox, 0, 0, scrollBarW, scrollBarW);
      } else if (button_ev->button == Button5) {
         which = SCRL_DN;
         SetBBRec(&bbox, 0, pni->scroll_area_h-scrollBarW, scrollBarW,
               pni->scroll_area_h);
      }
      if (which != INVALID) {
         DoNameBtnScroll(FALSE, which, &bbox, pni);
         RedrawNameScrollWin();
         RedrawDspWindow();
      }
      return INVALID;
   } else if (button_ev->button != Button1) {
      return INVALID;
   }
   if (pni->p_check_array != NULL && pni->p_check_array->num_cols > 0) {
      checkbox_cols = pni->p_check_array->num_cols;
      box_offset = 1;
      if (button_ev->x < checkbox_cols*ROW_HEIGHT) {
         clicked_col = (int)(button_ev->x / ROW_HEIGHT);
      }
   }
   if (pni->marked_index != INVALID &&
         pni->marked_index >= pni->first_index &&
         pni->marked_index < pni->first_index+ITEM_DSPED) {
      int saved_index=pni->marked_index;

      pni->marked_index = INVALID;
      RedrawItem(pni, saved_index);
      pni->marked_index = saved_index;
   }
   pni->marked_index = GetNameEntryNum((int)(button_ev->y / ROW_HEIGHT));
   if (pni->marked_index != INVALID) {
      if (clicked_col != INVALID) {
         int i=pni->marked_index;

         pni->p_check_array->value[clicked_col][i] =
               !(pni->p_check_array->value[clicked_col][i]);
         if (pni->pf_check_update != NULL) {
            /*
             * check_udpated set to TRUE if the callback function calls
             *       SetStringStatus()
             */
            check_updated = (pni->pf_check_update)(&pni->dsp_ptr,
                  &pni->entries, &pni->num_entries,
                  &pni->marked_index, &pni->p_check_array, pni->inbuf,
                  clicked_col, i, pni->userdata);
         }
      }
      RedrawItem(pni, pni->marked_index);
   }
   click_time = button_ev->time;
   if (pni->just_clicked && pni->marked_index != INVALID &&
         last_name_marked == pni->marked_index &&
         (click_time-last_click_time) < doubleClickInterval) {
      return TRUE;
   }
   pni->just_clicked = TRUE;
   last_click_time = click_time;
   last_name_marked = pni->marked_index;

   if (!check_updated) {
      if (pni->dsp_ptr != NULL && pni->marked_index != INVALID) {
         SetStringStatus(pni->dsp_ptr[pni->marked_index].pathstr);
      } else {
         SetStringStatus("");
      }
   }
   return INVALID;
}

static
int ControlChar(key_ev, key_sym)
   XKeyEvent *key_ev;
   KeySym key_sym;
{
   register int i;
   struct NamesRec *pni=(&namesInfo);

   if (key_ev->state & ControlMask) {
      switch (key_sym) {
      case XK_Left: return (BAD);
      case XK_KP_Left: return (BAD);
      case XK_Up: key_sym = ((unsigned long)'b')&0xff; break;
      case XK_KP_Up: key_sym = ((unsigned long)'b')&0xff; break;
      case XK_Right: return (BAD);
      case XK_KP_Right: return (BAD);
      case XK_Down: key_sym = ((unsigned long)'f')&0xff; break;
      case XK_KP_Down: key_sym = ((unsigned long)'f')&0xff; break;
      case XK_Page_Up: key_sym = ((unsigned long)'b')&0xff; break;
      case XK_KP_Page_Up: key_sym = ((unsigned long)'b')&0xff; break;
      case XK_Page_Down: key_sym = ((unsigned long)'f')&0xff; break;
      case XK_KP_Page_Down: key_sym = ((unsigned long)'f')&0xff; break;
      }
   } else {
      switch (key_sym) {
      case XK_Left: return BAD;
      case XK_KP_Left: return BAD;
      case XK_Up: key_sym = ((unsigned long)'k')&0xff; break;
      case XK_KP_Up: key_sym = ((unsigned long)'k')&0xff; break;
      case XK_Right: return BAD;
      case XK_KP_Right: return BAD;
      case XK_Down: key_sym = ((unsigned long)'j')&0xff; break;
      case XK_KP_Down: key_sym = ((unsigned long)'j')&0xff; break;
      case XK_Page_Up: key_sym = ((unsigned long)'b')&0xff; break;
      case XK_KP_Page_Up: key_sym = ((unsigned long)'b')&0xff; break;
      case XK_Page_Down: key_sym = ((unsigned long)'f')&0xff; break;
      case XK_KP_Page_Down: key_sym = ((unsigned long)'f')&0xff; break;
      }
   }
   switch (key_sym&0xff) {
   case 'w': /* erase */
   case 'y':
      return INVALID;
   case 'n': /* down one */
   case 'j':
      i = (pni->marked_index < pni->num_entries-1) ?
            pni->marked_index+1 : pni->num_entries-1;
      break;
   case 'p': /* up one */
   case 'k':
      i = (pni->marked_index>0) ? pni->marked_index-1 : 0;
      break;
   case 'd': /* down one page */
   case 'f':
      if (pni->marked_index==INVALID) {
         i = (pni->num_entries <= ITEM_DSPED) ?
               pni->num_entries-1 : ITEM_DSPED;
      } else if (pni->marked_index < pni->num_entries-ITEM_DSPED) {
         i = pni->marked_index+ITEM_DSPED;
      } else {
         i = pni->num_entries-1;
      }
      break;
   case 'u': /* up one page */
   case 'b':
      i = (pni->marked_index > (ITEM_DSPED-1)) ?
            pni->marked_index-ITEM_DSPED : 0;
      break;
   default: return BAD;
   }
   return i;
}

static
void BackUpOneWord(pn_changing, pn_selected_btn_index)
   int *pn_changing, *pn_selected_btn_index;
{
   struct NamesRec *pni=(&namesInfo);
   int new_dir=FALSE;

   if (pni->buf_index == 0) return;

   if (pni->inbuf[pni->buf_index-1] == DIR_SEP) {
      pni->inbuf[--pni->buf_index] = '\0';
      new_dir = TRUE;
   } else {
      while (pni->buf_index > 0) {
         if (pni->inbuf[(pni->buf_index)-1] == DIR_SEP) {
            pni->inbuf[pni->buf_index] = '\0';
            break;
         } else {
            pni->buf_index--;
         }
      }
      pni->inbuf[pni->buf_index] = '\0';
   }
   ParseFileName(pni->inbuf, pni->dir_name, pni->name);
   if (new_dir) {
      int i;

      sprintf(pni->name, "..%c", DIR_SEP);
      pni->name_index = strlen(pni->name);
      for (i=0; i < pni->num_entries; i++) {
         if (strncmp(&(pni->entries[i])[pni->leading],
               pni->name, pni->name_index) == 0) {
            pni->marked_index = i;
            pni->faking_dot_dot = TRUE;
            *pn_changing = FALSE;
            *pn_selected_btn_index = i;
            break;
         }
      }
   } else {
      pni->first_index = 0;
      pni->marked_index = INVALID;
   }
}

static
void SpecialKeyInNames(key_ev, key_sym, pn_changing, pn_selected_btn_index)
   XKeyEvent *key_ev;
   KeySym key_sym;
   int *pn_changing, *pn_selected_btn_index;
{
   struct NamesRec *pni=(&namesInfo);
   int i=ControlChar(key_ev, key_sym);

   if (i == BAD) return;

   if (i == INVALID) {
      if (pni->edit_style == NAMES_SELECT_FILE) {
         /* back-up one word */
         BackUpOneWord(pn_changing, pn_selected_btn_index);
         sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
         pni->buf_index = strlen(pni->inbuf);
      } else {
         *pni->inbuf = '\0';
         pni->buf_index = 0;
         pni->first_index = 0;
         pni->marked_index = INVALID;
      }
   } else if (i < pni->num_entries) {
      if (pni->edit_style == NAMES_SELECT_FILE) {
         strcpy(pni->name, &(pni->entries[i])[pni->leading]);
         pni->name_index = strlen(pni->name);
         sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
         pni->buf_index = strlen(pni->inbuf);
      } else {
         strcpy(pni->inbuf, &(pni->entries[i])[pni->leading]);
         pni->buf_index = strlen(pni->inbuf);
      }
      if (i < pni->first_index) {
         pni->first_index = i;
      } else if (i >= pni->first_index+ITEM_DSPED) {
         if (i < pni->num_entries-ITEM_DSPED) {
            pni->first_index = i;
         } else {
            pni->first_index = pni->num_entries-ITEM_DSPED;
         }
      }
      pni->marked_index = i;
   }
   if (pni->exposed) {
      RedrawNamePath();
      RedrawNameScrollWin();
      RedrawDspWindow();
      RedrawDspBaseWindow();
   }
   if (pni->dsp_ptr != NULL && pni->marked_index != INVALID) {
      SetStringStatus(pni->dsp_ptr[pni->marked_index].pathstr);
   } else {
      SetStringStatus("");
   }
}

static
int GetBtnIndexFromBtnId(btn_id)
   int btn_id;
{
   struct NamesRec *pni=(&namesInfo);
   int i;

   for (i=0; i < pni->num_btns; i++) {
      if (pni->btn_id[i] == btn_id) {
         return i;
      }
   }
#ifdef _TGIF_DBG /* debug, do not translate */
   sprintf(gszMsgBox, "Programing error!\n\nCannot find a button with id=%1d.",
         btn_id);
   MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
#endif /* _TGIF_DBG */
   return INVALID;
}

static
void KeyPressInNames(key_ev, pn_changing, pn_selected_btn_index)
   XKeyEvent *key_ev;
   int *pn_changing, *pn_selected_btn_index;
{
   struct NamesRec *pni=(&namesInfo);
   char buf[80];
   KeySym key_sym;
   int has_ch;

   has_ch = XLookupString(key_ev, buf, sizeof(buf), &key_sym, &c_stat);
   TranslateKeys(buf, &key_sym);

   if ((key_ev->state & ControlMask) && key_sym == XK_j) {
      SpecialKeyInNames(key_ev, key_sym, pn_changing, pn_selected_btn_index);
   } else if (CharIsCRorLF(key_ev, buf, key_sym, &has_ch)) {
      if (pni->edit_style == NAMES_SELECT_FILE) {
         if (!pni->tabbed_from_root) {
            TabInNames();
         }
         *pn_changing = FALSE;
         *pn_selected_btn_index = GetBtnIndexFromBtnId(pni->def_btn_id);
      } else {
         if (pni->def_btn_id != INVALID) {
            *pn_changing = FALSE;
            *pn_selected_btn_index = GetBtnIndexFromBtnId(pni->def_btn_id);
         }
      }
   } else if (CharIsESC(key_ev, buf, key_sym, &has_ch)) {
      *pn_changing = FALSE;
      *pn_selected_btn_index = GetBtnIndexFromBtnId(BUTTON_CANCEL);
   } else if (CharIsBSorDEL(key_ev, buf, key_sym, &has_ch, FALSE)) {
      if (pni->edit_style == NAMES_SELECT_FILE &&
            (pni->faking_dot_dot || pni->pop_from_root)) {
         *pn_changing = FALSE;
         *pn_selected_btn_index = INVALID;
      } else {
         BackSpaceInNames(pn_changing, pn_selected_btn_index);
      }
   } else if (CharIsTAB(key_ev, buf, key_sym, &has_ch)) {
      if (pni->edit_style == NAMES_SELECT_FILE && pni->change_to_root) {
         TabInNames();
         *pn_changing = FALSE;
         *pn_selected_btn_index = INVALID;
      }
   } else if ((!pni->tabbed_from_root || pni->num_entries != 0) &&
         ((key_sym>'\040' && key_sym<='\177' &&
         (key_ev->state & ControlMask)) || key_sym==XK_Up ||
         key_sym==XK_Down || key_sym==XK_KP_Up || key_sym==XK_KP_Down ||
         key_sym==XK_Page_Up || key_sym==XK_KP_Page_Up ||
         key_sym==XK_Page_Down || key_sym==XK_KP_Page_Down)) {
      SpecialKeyInNames(key_ev, key_sym, pn_changing, pn_selected_btn_index);
   } else if (key_sym>='\040' && key_sym<='\177' &&
         (!pni->tabbed_from_root || pni->num_entries != 0)) {
      CharInNames(buf, pn_changing, pn_selected_btn_index);
   }
}

static
void ButtonPressInPath(button_ev)
   XButtonEvent *button_ev;
{
   struct NamesRec *pni=(&namesInfo);

   if (button_ev->button == Button1) {
   } else if (button_ev->button == Button2) {
      int buf_len=0, from_selection=FALSE;
      char *cut_buffer=NULL;

      switch (pni->edit_style) {
      case NAMES_SIMPLE_SELECT_NAME: return;
      case NAMES_COMPLEX_SELECT_NAME: return;
      case NAMES_SELECT_FILE:
         if (pni->tabbed_from_root) return;
         break;
      case NAMES_EDIT_ATTR: break;
      case NAMES_EDIT_NAME: break;
      }
      cut_buffer = FetchSelectionOrCutBuffer(&buf_len, &from_selection);
      if (cut_buffer != NULL) {
         char *c_ptr=cut_buffer;
         int max_len;

         if (pni->edit_style == NAMES_SELECT_FILE) {
            max_len = sizeof(pni->inbuf)-strlen(pni->dir_name)-3;
            while (pni->name_index < max_len &&
                  *c_ptr >= '\040' && *c_ptr < '\177') {
               pni->name[pni->name_index++] = (*c_ptr++);
            }
            pni->name[pni->name_index] = '\0';
            sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
            pni->buf_index = strlen(pni->inbuf);
         } else {
            max_len = sizeof(pni->inbuf)-strlen(pni->inbuf)-3;
            while (pni->buf_index < max_len &&
                  *c_ptr >= '\040' && *c_ptr < '\177') {
               pni->inbuf[pni->buf_index++] = (*c_ptr++);
            }
            pni->inbuf[pni->buf_index] = '\0';
            strcpy(&(pni->entries[pni->marked_index])[pni->leading],
                  pni->inbuf);
         }
         if (pni->exposed) {
            RedrawNamePath();
            RedrawDspWindow();
         }
         FreeSelectionOrCutBuffer(cut_buffer, from_selection);
      }
   } else if (button_ev->button == Button3) {
   }
}

static
void ButtonPressInNames(button_ev, pn_changing, pn_selected_btn_index)
   XButtonEvent *button_ev;
   int *pn_changing, *pn_selected_btn_index;
{
   struct NamesRec *pni=(&namesInfo);

   if (button_ev->window == pni->base_win) {
      int i;

      if (PointInBBox(button_ev->x, button_ev->y, pni->path_bbox)) {
         ButtonPressInPath(button_ev);
      } else {
         for (i=0; i < pni->num_btns; i++) {
            if (PointInBBox(button_ev->x, button_ev->y,
                  pni->btn_bbox[i])) {
               struct BBRec *bbox=(&(pni->btn_bbox[i]));
               char *btn_str=pni->btn_str[i];
               int looping=TRUE, selected=TRUE, len=strlen(btn_str);
               XEvent ev;

               XGrabPointer(mainDisplay, pni->base_win, False,
                     PointerMotionMask | ButtonReleaseMask, GrabModeAsync,
                     GrabModeAsync, None, handCursor, CurrentTime);
               DisplayButtonInBBox(pni->base_win, btn_str, len,
                     bbox, BUTTON_INVERT, FALSE, 0, NULL);
               while (looping) {
                  XNextEvent(mainDisplay, &ev);

                  if (ev.type == ButtonRelease) {
                     XUngrabPointer(mainDisplay, CurrentTime);
                     if (debugNoPointerGrab) XSync(mainDisplay, False);
                     looping = FALSE;
                  } else if (ev.type == MotionNotify) {
                     if (PointInBBox(ev.xmotion.x, ev.xmotion.y,
                           pni->btn_bbox[i])) {
                        if (!selected) {
                           selected = TRUE;
                           DisplayButtonInBBox(pni->base_win, btn_str, len,
                                 bbox, BUTTON_INVERT, FALSE, 0, NULL);
                        }
                     } else {
                        if (selected) {
                           selected = FALSE;
                           DisplayButtonInBBox(pni->base_win, btn_str, len,
                                 bbox, BUTTON_NORMAL, FALSE, 0, NULL);
                        }
                     }
                  }
               }
               if (selected) {
                  *pn_changing = FALSE;
                  *pn_selected_btn_index = i;
                  DisplayButtonInBBox(pni->base_win, btn_str, len, bbox,
                        BUTTON_NORMAL, FALSE, 0, NULL);
               }
               break;
            }
         }
      }
   } else if (button_ev->window == pni->scroll_win) {
      NameScrollHandler(button_ev);
   } else if (button_ev->window == pni->dsp_win) {
      int double_clicked=(NameDspHandler(button_ev)!=INVALID);

      if (pni->marked_index != INVALID) {
         if (pni->edit_style == NAMES_SELECT_FILE) {
            strcpy(pni->name,
                  &(pni->entries[pni->marked_index])[pni->leading]);
            pni->name_index = strlen(pni->name);
            sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
            pni->buf_index = strlen(pni->inbuf);
         } else {
            strcpy(pni->inbuf,
                  &(pni->entries[pni->marked_index])[pni->leading]);
            pni->buf_index = strlen(pni->inbuf);
         }
         RedrawNamePath();
         if (double_clicked && pni->double_click_btn_id != INVALID) {
            *pn_changing = FALSE;
            *pn_selected_btn_index =
                  GetBtnIndexFromBtnId(pni->double_click_btn_id);
         }
      }
   }
}

void NamesSetDefaultBtnId(def_btn_id, double_click_btn_id)
   int def_btn_id, double_click_btn_id;
{
   struct NamesRec *pni=(&namesInfo);

   pni->def_btn_id = def_btn_id;
   pni->double_click_btn_id = double_click_btn_id;
}

void NamesSetStyle(edit_style, loop_once)
   int edit_style, loop_once;
{
   struct NamesRec *pni=(&namesInfo);

   pni->edit_style = edit_style;
   pni->loop_once = loop_once;
}

void NamesSetEntries(dsp_ptr, entries, num_entries, p_check_array,
      dont_free_entries, marked_index, leading)
   DspList *dsp_ptr;
   char **entries;
   int num_entries, dont_free_entries, marked_index, leading;
   struct CheckArrayRec *p_check_array;
{
   struct NamesRec *pni=(&namesInfo);

   pni->dsp_ptr = dsp_ptr;
   pni->entries = entries;
   pni->num_entries = num_entries;
   pni->p_check_array = p_check_array;
   pni->dont_free_entries = dont_free_entries;
   pni->marked_index = marked_index;
   pni->leading = leading;
}

void NamesSetCallback(pf_before_loop, pf_after_loop, pf_check_update)
   GetEntriesFunc *pf_before_loop;
   AfterLoopFunc *pf_after_loop;
   CheckUpdateFunc *pf_check_update;
{
   struct NamesRec *pni=(&namesInfo);

   if (pni->dsp_ptr != NULL || pni->entries != NULL) {
#ifdef _TGIF_DBG /* debug, do not translate */
      sprintf(gszMsgBox, "%s.",
            "dsp_ptr != NULL || entries != NULL in NamesSetCallback()");
      MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
#endif /* _TGIF_DBG */
      if (pni->dsp_ptr != NULL) free(pni->dsp_ptr);
      if (pni->entries != NULL) {
         free(*(pni->entries));
         free(pni->entries);
      }
   }
   pni->dsp_ptr = NULL;
   pni->entries = NULL;
   pni->num_entries = 0;
   pni->pf_before_loop = pf_before_loop;
   pni->pf_after_loop = pf_after_loop;
   pni->pf_check_update = pf_check_update;
}

void NamesSetDir(dir_name)
   char *dir_name;
{
   struct NamesRec *pni=(&namesInfo);

   if (pni->edit_style != NAMES_SELECT_FILE) {
#ifdef _TGIF_DBG /* debug, do not translate */
      sprintf(gszMsgBox, "%s!\n\nNamesSetDir() called with %s.",
            "Programing error", "pni->edit_style != NAMES_SELECT_FILE.");
      MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
#endif /* _TGIF_DBG */
   }
   UtilStrCpyN(pni->dir_name, sizeof(pni->dir_name), dir_name);
}

static
void BeforeLoopForSelectFile()
{
   struct NamesRec *pni=(&namesInfo);

   if (pni->faking_dot_dot) {
      *pni->name = '\0';
      pni->name_index = 0;
   } else if (pni->tabbed_from_root &&
         !pni->just_tabbed_from_root) {
      pni->name[0] = '\0';
      pni->name_index = 0;
   } else if (pni->tabbed_from_root && pni->just_tabbed_from_root) {
      pni->name_index = strlen(pni->name);
      if (!SetMarkedIndex()) {
         pni->name[0] = '\0';
         pni->name_index = 0;
      }
   }
   sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
   pni->buf_index = strlen(pni->inbuf);

   if (pni->exposed) {
      XClearWindow(mainDisplay, pni->base_win);
      RedrawNameBaseWindow();
   }
   pni->pop_from_root = FALSE;
}

static
int GetParentRealDir(dir, buf, buf_sz)
   char *dir, *buf;
   int buf_sz;
{
   int rc=FALSE;
   char saved_cur_dir[MAXPATHLENGTH];

   if (!GetWorkingDirectory(saved_cur_dir, sizeof(saved_cur_dir))) {
      *saved_cur_dir = '\0';
   }
   if (SetWorkingDirectory(dir) && SetWorkingDirectory("..")) {
      if (GetWorkingDirectory(buf, buf_sz)) {
         rc = TRUE;
      }
   }
   if (*saved_cur_dir != '\0') {
      SetWorkingDirectory(saved_cur_dir);
   }
   return rc;
}

static
int BreakForSelectFileAfterLoop()
{
   struct NamesRec *pni=(&namesInfo);

   if (pni->marked_index == INVALID && !pni->change_to_root &&
         !pni->pop_from_root && !pni->just_tabbed_from_root) {
      return TRUE;
   } else if (FileIsRemote(pni->name)) {
      pni->remote_file = TRUE;
      return TRUE;
   }
   if (pni->inbuf[pni->buf_index-1] != DIR_SEP) {
      if (!pni->just_tabbed_from_root) {
         return TRUE;
      }
   } else {
      if (pni->name[0] == '.' && pni->name[1] == '.' &&
            pni->name[2] == DIR_SEP) {
         /* need to find out what's the real directory */
         char parent_dir[MAXPATHLENGTH];

         if (GetParentRealDir(pni->dir_name, parent_dir, sizeof(parent_dir))) {
            int len=0;

            len = strlen(parent_dir);
            if (len > 0 && parent_dir[len-1] == DIR_SEP) {
               parent_dir[--len] = '\0';
            }
            sprintf(pni->inbuf, "%s%c..", parent_dir, DIR_SEP);
            pni->buf_index = strlen(pni->inbuf);
            ParseFileName(pni->inbuf, pni->dir_name, pni->name);
            return FALSE;
         }
      }
      pni->inbuf[--(pni->buf_index)] = '\0';
      if (pni->name[0] == '.' && pni->name[1] == '.' &&
            pni->name[2] == DIR_SEP) {
         /* saved_name is used for faking_dot_dot */
         int i;

         for (i=strlen(pni->dir_name)-1;
               i >= 0 && pni->dir_name[i] != DIR_SEP; i--) {
         }
         if (i < 0) {
            strcpy(pni->saved_name, pni->dir_name);
            *pni->dir_name = '\0';
         } else {
            strcpy(pni->saved_name, &(pni->dir_name[i+1]));
            pni->dir_name[i] = '\0';
         }
      } else {
         strcpy(&pni->inbuf[pni->buf_index++], DIR_SEP_STR);
         ParseFileName(pni->inbuf, pni->dir_name, pni->name);
      }
   }
   return FALSE;
}

int Names(win_name, pn_selected_index, selected_str, str_sz, p_void)
   char *win_name, *selected_str;
   int *pn_selected_index, str_sz;
   void *p_void;
{
   struct NamesRec *pni=(&namesInfo);
   int i, looping=TRUE, selected_btn_index=INVALID, left=0;

   if (selected_str != NULL) *selected_str = '\0';
   if (pn_selected_index != NULL) *pn_selected_index = INVALID;

   if (!CreateNamesWindows(win_name)) return INVALID;

   EndMeasureTooltip(FALSE);

   pni->userdata = p_void;

   if (pni->edit_style == NAMES_SELECT_FILE) {
      pni->faking_dot_dot = FALSE;
      pni->change_to_root = FALSE;
      pni->just_tabbed_from_root = FALSE;
      *pni->saved_dir_name = '\0';
   }
   left = pni->btn_start;
   for (i=0; i < pni->num_btns; i++) {
      int button_w=ButtonWidth(pni->btn_str[i], 8, NULL), button_h=0;

      if (boldMsgFontSet == NULL && boldMsgFontPtr == NULL) {
         pni->btn_bbox[i].lty = (6+ITEM_DSPED)*ROW_HEIGHT;
         button_h = defaultFontHeight + 4;
      } else {
         pni->btn_bbox[i].lty = (5+ITEM_DSPED)*ROW_HEIGHT +
               (boldMsgFontHeight+1);
         button_h = boldMsgFontHeight + 4;
      }
      pni->btn_bbox[i].ltx = left;
      pni->btn_bbox[i].lty -= 2;
      pni->btn_bbox[i].rbx = pni->btn_bbox[i].ltx+button_w;
      pni->btn_bbox[i].rby = pni->btn_bbox[i].lty+button_h;
      left = pni->btn_bbox[i].rbx + 1 + defaultFontWidth;
   }
   SaveStatusStrings();
   while (looping) {
      int changing=TRUE;

      if (pni->edit_style == NAMES_SELECT_FILE) {
         BeforeLoopForSelectFile();
      }
      if (pni->pf_before_loop != NULL) {
         int rc;
         char saved_ch='\0';

         SetWatchCursor(drawWindow);
         SetWatchCursor(mainWindow);
         SetWatchCursor(pni->base_win);
         if (pni->edit_style == NAMES_SELECT_FILE && !pni->tabbed_from_root &&
               !pni->just_tabbed_from_root) {
            saved_ch = *pni->inbuf;
            *pni->inbuf = '\0';
         }
         rc = (pni->pf_before_loop)(&pni->dsp_ptr,
               &pni->entries, &pni->num_entries,
               &pni->marked_index, &pni->p_check_array, pni->inbuf, p_void);
         if (pni->edit_style == NAMES_SELECT_FILE && !pni->tabbed_from_root &&
               !pni->just_tabbed_from_root) {
            *pni->inbuf = saved_ch;
         }
         if (pni->edit_style == NAMES_SELECT_FILE &&
               pni->just_tabbed_from_root) {
            /* saved_name is used for just_tabbed_from_root */
            *pni->dir_name = '\0';
            strcpy(pni->name, pni->saved_name);
            pni->name_index = strlen(pni->name);
            sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
            pni->buf_index = strlen(pni->inbuf);
            *pni->saved_name = '\0';
            ParseFileName(pni->inbuf, pni->dir_name, pni->name);
            pni->name_index = strlen(pni->name);
            if (!SetMarkedIndex()) {
               pni->name[0] = '\0';
               pni->name_index = 0;
            }
         }
         if (pni->edit_style == NAMES_SELECT_FILE && pni->faking_dot_dot) {
            /* saved_name is used for faking_dot_dot */
            strcpy(pni->name, pni->saved_name);
            pni->name_index = strlen(pni->name);
            sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
            pni->buf_index = strlen(pni->inbuf);
            *pni->saved_name = '\0';
            if (!SetMarkedIndex()) {
               pni->name[0] = '\0';
               pni->name_index = 0;
            }
         }
         SetDefaultCursor(mainWindow);
         ShowCursor();
         SetDrawCursor(pni->base_win);
         if (!rc) break;
      }
      if (pni->edit_style == NAMES_SELECT_FILE) {
         pni->just_tabbed_from_root = FALSE;
         pni->faking_dot_dot = FALSE;
      }
      NamesUpdateIndices();

      if (pni->pf_before_loop != NULL && pni->exposed) {
         RedrawNamePath();
         RedrawNameScrollWin();
         RedrawDspWindow();
         RedrawDspBaseWindow();
      }
      if (pni->dsp_ptr != NULL && pni->marked_index != INVALID) {
         SetStringStatus(pni->dsp_ptr[pni->marked_index].pathstr);
      }
      XSync(mainDisplay, False);

      selected_btn_index = INVALID;
      while (changing) {
         XEvent input, ev;

         XNextEvent(mainDisplay, &input);

         if (ExposeOrMapNames(&input)) {
            continue;
         }
         if (input.type == Expose) {
            ExposeEventHandler(&input, FALSE);
         } else if (input.type==VisibilityNotify &&
               input.xany.window==mainWindow &&
               input.xvisibility.state==VisibilityUnobscured) {
            int i;

            while (XCheckWindowEvent(mainDisplay, mainWindow,
                  VisibilityChangeMask, &ev)) ;
            if (pinnedMainMenu) XMapRaised(mainDisplay, mainMenuWindow);
            for (i=0; i < numExtraWins; i++) {
               if (extraWinInfo[i].mapped && extraWinInfo[i].raise &&
                     extraWinInfo[i].window != None) {
                  XMapRaised(mainDisplay, extraWinInfo[i].window);
               }
            }
            XMapRaised(mainDisplay, pni->base_win);
         } else if (input.type == KeyPress) {
            KeyPressInNames(&input.xkey, &changing, &selected_btn_index);
         } else if (input.type == ButtonPress) {
            ButtonPressInNames(&input.xbutton, &changing, &selected_btn_index);
         } else if ((input.xany.window==pni->base_win ||
               input.xany.window==pni->dsp_base_win ||
               input.xany.window==pni->dsp_win ||
               input.xany.window==pni->scroll_win) &&
               IsWM_DELETE_WINDOW(&input)) {
            KeyPressInNames(&input.xkey, &changing, &selected_btn_index);
            changing = FALSE;
            selected_btn_index = INVALID;
         }
      }
      if (pni->exposed && selected_btn_index != INVALID) {
         DisplayButtonInBBox(pni->base_win, pni->btn_str[selected_btn_index],
               strlen(pni->btn_str[selected_btn_index]),
               &(pni->btn_bbox[selected_btn_index]), BUTTON_INVERT, FALSE, 0,
               NULL);
         XSync(mainDisplay, False);
      }
      if (pni->edit_style == NAMES_SELECT_FILE) {
         if (pni->marked_index != INVALID) {
            strcpy(pni->name,
                  &(pni->entries[pni->marked_index])[pni->leading]);
            pni->name_index = strlen(pni->name);
            sprintf(pni->inbuf, "%s%c%s", pni->dir_name, DIR_SEP, pni->name);
            pni->buf_index = strlen(pni->inbuf);
         }
      }
      if (pni->pf_after_loop != NULL) {
         int btn_id=INVALID;

         if (selected_btn_index != INVALID) {
            btn_id = pni->btn_id[selected_btn_index];
         }
         if (!(pni->pf_after_loop)(&pni->dsp_ptr,
               &pni->entries, &pni->num_entries,
               &pni->marked_index, &pni->p_check_array, pni->inbuf, btn_id,
               pni->marked_index, p_void)) {
            looping = FALSE;
         }
      }
      if (selected_btn_index != INVALID) {
         if (pni->btn_id[selected_btn_index] == BUTTON_CANCEL) {
            looping = FALSE;
         }
      }
      if (pni->edit_style == NAMES_SELECT_FILE) {
         if (BreakForSelectFileAfterLoop()) {
            looping = FALSE;
         }
      }
      if (pni->loop_once == NAMES_LOOP_ONCE) {
         looping = FALSE;
      }
      if (pni->exposed && selected_btn_index != INVALID) {
         DisplayButtonInBBox(pni->base_win, pni->btn_str[selected_btn_index],
               strlen(pni->btn_str[selected_btn_index]),
               &(pni->btn_bbox[selected_btn_index]), BUTTON_NORMAL, FALSE, 0,
               NULL);
         XSync(mainDisplay, False);
      }
   }
   RestoreStatusStrings();
   XDestroyWindow(mainDisplay, pni->base_win);
   if (warpToWinCenter) {
      XWarpPointer(mainDisplay, None, drawWindow, 0, 0, 0, 0,
            (int)(ZOOMED_SIZE(drawWinW)>>1), (int)(ZOOMED_SIZE(drawWinH)>>1));
   }
   if (selected_str != NULL) {
      if (pni->remote_file) {
         UtilStrCpyN(selected_str, str_sz, pni->name);
      } else {
         UtilStrCpyN(selected_str, str_sz, pni->inbuf);
      }
   }
   pni->userdata = NULL;

   if (pn_selected_index != NULL) *pn_selected_index = pni->marked_index;
   if (selected_btn_index != INVALID) {
      return pni->btn_id[selected_btn_index];
   }
   return INVALID;
}
