Logo Search packages:      
Sourcecode: ddd version File versions  Download package

file.C

// $Id$ -*- C++ -*-
// DDD file functions

// Copyright (C) 1996-1998 Technische Universitaet Braunschweig, Germany.
// Copyright (C) 2000-2001 Universitaet Passau, Germany.
// Written by Andreas Zeller <zeller@gnu.org>.
// 
// This file is part of DDD.
// 
// DDD is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// 
// DDD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, write to the Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page, 
// `http://www.gnu.org/software/ddd/',
// or send a mail to the DDD developers <ddd@gnu.org>.

char file_rcsid[] = 
    "$Id$";

#include "file.h"

#include "AppData.h"
#include "GDBAgent.h"
#include "MString.h"
#include "Delay.h"
#include "DestroyCB.h"
#include "ExitCB.h"
#include "HelpCB.h"
#include "SmartC.h"
#include "SourceView.h"
#include "VarArray.h"
#include "Command.h"
#include "basename.h"
#include "cook.h"
#include "ddd.h"
#include "filetype.h"
#include "glob.h"
#include "history.h"
#include "java.h"
#include "mydialogs.h"
#include "post.h"
#include "question.h"
#include "regexps.h"
#include "shell.h"
#include "strclass.h"
#include "status.h"
#include "string-fun.h"
#include "uniquify.h"
#include "verify.h"
#include "wm.h"

#include <limits.h>
#include <string.h>           // strerror()
#include <errno.h>

#include <Xm/Xm.h>
#include <Xm/FileSB.h>
#include <Xm/List.h>
#include <Xm/SelectioB.h>
#include <Xm/MessageB.h>
#include <Xm/Text.h>
#include <Xm/RowColumn.h>
#include <Xm/TextF.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>

// ANSI C++ doesn't like the XtIsRealized() macro
#ifdef XtIsRealized
#undef XtIsRealized
#endif

#if !HAVE_POPEN_DECL
extern "C" FILE *popen(const char *command, const char *mode);
#endif
#if !HAVE_PCLOSE_DECL
extern "C" int pclose(FILE *stream);
#endif


//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
// Last opened file
string open_file_reply;


//-----------------------------------------------------------------------------
// Opening files
//-----------------------------------------------------------------------------

typedef void (*FileSearchProc)(Widget fs, 
                         XmFileSelectionBoxCallbackStruct *cbs);

static WidgetArray file_filters;
static WidgetArray file_dialogs;

static string current_file_filter = "";

// Make sure that every change in one filter is reflected in all others
static void SyncFiltersCB(Widget dialog, XtPointer, XtPointer)
{
    static bool entered = false;

    if (entered)
      return;

    entered = true;

    // std::clog << "widget = " << longName(text) << "\n";

    while (dialog != 0 && !XmIsFileSelectionBox(dialog))
      dialog = XtParent(dialog);
      
    // std::clog << "dialog = " << longName(dialog) << "\n";

    Widget text = XmFileSelectionBoxGetChild(dialog, XmDIALOG_FILTER_TEXT);
    String _current_file_filter = XmTextGetString(text);
    current_file_filter = _current_file_filter;
    XtFree(_current_file_filter);

    for (int i = 0; i < file_filters.size(); i++)
    {
      if (file_dialogs[i] != dialog)
      {
          // std::clog << "other dialog = " << longName(file_dialogs[i]) << "\n";
          XmTextSetString(file_filters[i], XMST(current_file_filter.chars()));
      }
    }

    entered = false;
}

// Make sure that every new filter call is performed in all other
// dialogs as well
static void FilterAllCB(Widget dialog, XtPointer client_data, 
                  XtPointer call_data)
{
    SyncFiltersCB(dialog, client_data, call_data);

    // std::clog << "widget = " << longName(dialog) << "\n";

    while (dialog != 0 && !XmIsFileSelectionBox(dialog))
      dialog = XtParent(dialog);
      
    // std::clog << "dialog = " << longName(dialog) << "\n";

    for (int i = 0; i < file_dialogs.size(); i++)
    {
      if (file_dialogs[i] != dialog)
      {
          // std::clog << "other dialog = " << longName(file_dialogs[i]) << "\n";
          XmFileSelectionDoSearch(file_dialogs[i], 0);
      }
    }
}

static void ClearStatusCB(Widget, XtPointer, XtPointer)
{
    set_status("");
}

// Create a file dialog NAME with DO_SEARCH_FILES and DO_SEARCH_DIRS
// as search procedures for files and directories, respectively, and
// OK_CALLBACK as the procedure called when a file is selected.
static Widget file_dialog(Widget w, const string& name,
                    FileSearchProc do_search_files = 0,
                    FileSearchProc do_search_dirs  = 0,
                    XtCallbackProc ok_callback     = 0)
{
    Delay delay(w);

    Arg args[10];
    int arg = 0;

    string pwd;

    arg = 0;
    if (do_search_files)
    {
      XtSetArg(args[arg], XmNfileSearchProc, do_search_files); arg++;
    }
    if (do_search_dirs)
    {
      XtSetArg(args[arg], XmNdirSearchProc, do_search_dirs); arg++;
    }

    if (remote_gdb())
    {
      static MString xmpwd;
      xmpwd = source_view->pwd();
      XtSetArg(args[arg], XmNdirectory, xmpwd.xmstring()); arg++;
    }

    Widget dialog = 
      verify(XmCreateFileSelectionDialog(w, XMST(name.chars()), args, arg));
    Delay::register_shell(dialog);

    if (ok_callback != 0)
      XtAddCallback(dialog, XmNokCallback,     ok_callback, 0);

    XtAddCallback(dialog, XmNcancelCallback, UnmanageThisCB, 
              XtPointer(dialog));
    XtAddCallback(dialog, XmNhelpCallback,   ImmediateHelpCB, 0);

    Widget filter = XmFileSelectionBoxGetChild(dialog, XmDIALOG_FILTER_TEXT);
    file_filters += filter;
    if (!current_file_filter.empty())
      XmTextSetString(filter, XMST(current_file_filter.chars()));
    XtAddCallback(filter, XmNvalueChangedCallback, SyncFiltersCB, 0);

    Widget filter_button = 
      XmFileSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON);
    XtAddCallback(filter_button, XmNactivateCallback, FilterAllCB, 0);
    XtAddCallback(dialog, XmNunmapCallback, ClearStatusCB, 0);

    file_dialogs += dialog;

    return dialog;
}

// Create various file dialogs
static Widget create_file_dialog(Widget w, const _XtString name,
                         FileSearchProc searchRemoteFiles       = 0,
                         FileSearchProc searchRemoteDirectories = 0,
                         FileSearchProc searchLocalFiles        = 0,
                         FileSearchProc searchLocalDirectories  = 0,
                         XtCallbackProc openDone = 0)
{                      
    if (remote_gdb())
      return file_dialog(find_shell(w), name,
                     searchRemoteFiles, searchRemoteDirectories, 
                     openDone);
    else if (app_data.filter_files)
      return file_dialog(find_shell(w), name,
                     searchLocalFiles, searchLocalDirectories, 
                     openDone);
    else
      return file_dialog(find_shell(w), name, 0, 0, openDone);
}


// Synchronize file dialogs with current directory
void process_cd(const string& pwd)
{
    current_file_filter = pwd + "/*";

    for (int i = 0; i < file_filters.size(); i++)
    {
      if (file_filters[i] != 0)
      {
          XmTextSetString(file_filters[i], XMST(current_file_filter.chars()));
          break;
      }
    }
}

static const char *delay_message = "Filtering files";

// Search for remote files and directories, using the command CMD
static void searchRemote(Widget fs,
                   XmFileSelectionBoxCallbackStruct *cbs,
                   const _XtString cmd,
                   bool search_dirs)
{
    StatusDelay delay(delay_message);

    int nitems = 0;
    int size = 256;
    XmStringTable items = 
      XmStringTable(XtMalloc(size * sizeof(XmString)));

    String mask;
    if (!XmStringGetLtoR(cbs->mask, MSTRING_DEFAULT_CHARSET, &mask))
    {
      delay.outcome = "failed";
      return;
    }
    String dir;
    if (!XmStringGetLtoR(cbs->dir, MSTRING_DEFAULT_CHARSET, &dir))
    {
      delay.outcome = "failed";
      return;
    }

    if (search_dirs)
    {
      string extra_dir = string(dir) + ".";
      items[nitems++] = 
          XmStringCreateLtoR(XMST(extra_dir.chars()), MSTRING_DEFAULT_CHARSET);
      extra_dir = string(dir) + "..";
      items[nitems++] = 
          XmStringCreateLtoR(XMST(extra_dir.chars()), MSTRING_DEFAULT_CHARSET);
    }

    string command = cmd;
    command.gsub("@MASK@", mask);
    command = sh_command(command);

    Agent search(command);
    search.start();

    FILE *fp = search.inputfp();
    if (fp == 0)
    {
      delay.outcome = strerror(errno);
      return;
    }

    char buf[BUFSIZ];
    while (fgets(buf, sizeof(buf), fp))
    {
      if (buf[0] && buf[strlen(buf) - 1] == '\n')
          buf[strlen(buf) - 1] = '\0';

      if (nitems >= size)
      {
          size += 256;
          items = XmStringTable(XtRealloc((char *)items,
                                  size * sizeof(XmString)));
      }
          
      items[nitems++] = XmStringCreateLtoR(buf, MSTRING_DEFAULT_CHARSET);

      if (nitems == 1 || nitems % 10 == 0)
      {
          std::ostringstream status;
          status << delay_message << "... ("
               << nitems << " processed)";
          string s(status);
          set_status(s, true);
      }
    }

    if (search_dirs)
    {
      XtVaSetValues(fs,
                  XmNdirListItems,     items,
                  XmNdirListItemCount, nitems,
                  XmNdirectoryValid,   True,
                  XmNlistUpdated,      True,
                  XtPointer(0));
    }
    else
    {
      if (nitems > 0)
      {
          XtVaSetValues(fs,
                    XmNfileListItems,     items,
                    XmNfileListItemCount, nitems,
                    XmNdirSpec,           items[0],
                    XmNlistUpdated,       True,
                    XtPointer(0));
      }
      else
      {
          XtVaSetValues(fs,
                    XmNfileListItems,     0,
                    XmNfileListItemCount, 0,
                    XmNlistUpdated,       True,
                    XtPointer(0));
      }
    }
}


static void searchRemoteExecFiles(Widget fs,
                          XmFileSelectionBoxCallbackStruct *cbs)
{
    searchRemote(fs, cbs, app_data.list_exec_command, false);
}

static void searchRemoteCoreFiles(Widget fs,
                          XmFileSelectionBoxCallbackStruct *cbs)
{
    searchRemote(fs, cbs, app_data.list_core_command, false);
}

static void searchRemoteSourceFiles(Widget fs,
                            XmFileSelectionBoxCallbackStruct *cbs)
{
    searchRemote(fs, cbs, app_data.list_source_command, false);
}

static void searchRemoteDirectories(Widget fs,
                            XmFileSelectionBoxCallbackStruct *cbs)
{
    searchRemote(fs, cbs, app_data.list_dir_command, true);
}

// Search for local files and directories, using the predicate IS_OKAY
static void searchLocal(Widget fs,
                  XmFileSelectionBoxCallbackStruct *cbs,
                  bool is_okay(const string& file_name))
{
    String mask;
    if (!XmStringGetLtoR(cbs->mask, MSTRING_DEFAULT_CHARSET, &mask))
      return;

    char **files = glob_filename(mask);
    if (files == (char **)0)
    {
      std::cerr << mask << ": glob failed\n";
    }
    else if (files == (char **)-1)
    {
      post_error(string(mask) + ": " + strerror(errno));
    }
    else
    {
      StatusDelay delay(delay_message);

      int count;
      for (count = 0; files[count] != 0; count++)
          ;
      smart_sort(files, count);

      XmStringTable items = 
          XmStringTable(XtMalloc(count * sizeof(XmString)));

      int nitems = 0;
      int i;
      for (i = 0; files[i] != 0; i++)
      {
          if (is_okay(files[i]))
            items[nitems++] = XmStringCreateLtoR(files[i], 
                                         MSTRING_DEFAULT_CHARSET);
          free(files[i]);

          int percent     = (i * 100) / count;
          int old_percent = ((i - 1) * 100) / count;
          if (percent % 10 == 0 && old_percent % 10 != 0)
          {
            std::ostringstream status;
            status << delay_message << "... ("
                   << percent << "% processed)";
            string s(status);
            set_status(s, true);
          }
      }
      free((char *)files);

      if (nitems > 0)
      {
          XtVaSetValues(fs,
                    XmNfileListItems,     items,
                    XmNfileListItemCount, nitems,
                    XmNdirSpec,           items[0],
                    XmNlistUpdated,       True,
                    XtPointer(0));
      }

      freeXmStringTable(items, nitems);

      if (nitems > 0)
          return;
    }

    // Error or nothing found 
    XtVaSetValues(fs,
              XmNfileListItems,     0,
              XmNfileListItemCount, 0,
              XmNlistUpdated,       True,
              XtPointer(0));
}

static void searchLocalExecFiles(Widget fs,
                         XmFileSelectionBoxCallbackStruct *cbs)
{
    switch (gdb->type())
    {
    case PYDB:
      searchLocal(fs, cbs, is_python_file);
      break;

    case PERL:
      searchLocal(fs, cbs, is_perl_file);
      break;

    case BASH:
      searchLocal(fs, cbs, is_perl_file);
      break;

    case DBG:
      searchLocal(fs, cbs, is_php_file);
      break;
    
    case GDB:
      searchLocal(fs, cbs, is_debuggee_file);
      break;

    default:
      searchLocal(fs, cbs, is_exec_file);
    }
}

static void searchLocalCoreFiles(Widget fs,
                         XmFileSelectionBoxCallbackStruct *cbs)
{
    searchLocal(fs, cbs, is_core_file);
}

static void searchLocalSourceFiles(Widget fs,
                           XmFileSelectionBoxCallbackStruct *cbs)
{
    switch (gdb->type())
    {
    case PYDB:
      searchLocal(fs, cbs, is_python_file);
      break;

    case PERL:
      searchLocal(fs, cbs, is_perl_file);
      break;

    default:
      searchLocal(fs, cbs, is_source_file);
    }
}

// Get the file name from the file selection box W
string get_file(Widget w, XtPointer, XtPointer call_data)
{
    XmFileSelectionBoxCallbackStruct *cbs = 
      (XmFileSelectionBoxCallbackStruct *)call_data;

    String s;
    if (!XmStringGetLtoR(cbs->value, MSTRING_DEFAULT_CHARSET, &s))
      return NO_GDB_ANSWER;

    string filename = s;
    XtFree(s);

    if (filename.empty() || filename[0] != '/')
    {
      String dir;
      if (!XmStringGetLtoR(cbs->dir, MSTRING_DEFAULT_CHARSET, &dir))
          return NO_GDB_ANSWER;

      filename = string(dir) + "/" + filename;
      XtFree(dir);
    }

    if (is_directory(filename))
    {
      MString filter(filename);
      XmFileSelectionDoSearch(w, filter.xmstring());
      return "";
    }

    return filename;
}



//-----------------------------------------------------------------------------
// OK pressed
//-----------------------------------------------------------------------------

static void open_file(const string& filename)
{
    open_file_reply = filename;

    if (gdb->type() == GDB)
    {
      // GDB does not always detach processes upon opening new
      // files, so we do it explicitly
      ProgramInfo info;
      if (info.attached)
          gdb_command("detach");
    }

    if (gdb_initialized)
    {
      string cmd = gdb->debug_command(filename);
      if (gdb->type() == PERL)
          cmd.gsub("perl", string(app_data.debugger_command));

      gdb_command(cmd);
    }
}

// OK pressed in `Open File'
static void openFileDone(Widget w, XtPointer client_data, XtPointer call_data)
{
    string filename = get_file(w, client_data, call_data);
    if (filename.empty())
      return;

    XtUnmanageChild(w);

    if (filename == NO_GDB_ANSWER)
      return;

    open_file(filename);
}


// OK pressed in `Open Core'
static void openCoreDone(Widget w, XtPointer client_data, XtPointer call_data)
{
    string corefile = get_file(w, client_data, call_data);
    if (corefile.empty())
      return;

    ProgramInfo info;

    XtUnmanageChild(w);

    if (corefile != NO_GDB_ANSWER)
    {
      switch(gdb->type())
      {
      case GDB:
          gdb_command("core-file " + gdb->quote_file(corefile));
          break;

      case DBX:
          if (info.file != NO_GDB_ANSWER && !info.file.empty())
            gdb_command(gdb->debug_command(info.file) + " " + 
                      gdb->quote_file(info.core));
          else
            post_error("No program.", "no_program", w);
          break;

      case BASH:
      case DBG:
      case JDB:
      case PERL:
      case PYDB:
      case XDB:
          break;        // FIXME
      }
    }
}

// OK pressed in `Open Source'
static void openSourceDone(Widget w, XtPointer client_data, 
                     XtPointer call_data)
{
    string filename = get_file(w, client_data, call_data);
    if (filename.empty())
      return;

    XtUnmanageChild(w);
    set_status("");

    // For PYDB, issue a 'file filename' command
    if (gdb->type() == PYDB)
      gdb_command(gdb->debug_command(filename));

    if (filename != NO_GDB_ANSWER)
      source_view->read_file(filename);
}


//-----------------------------------------------------------------------------
// Program Info
//-----------------------------------------------------------------------------

// Get information on current debuggee
ProgramInfo::ProgramInfo()
    : file(NO_GDB_ANSWER),
      core(NO_GDB_ANSWER),
      pid(0),
      attached(false),
      running(false),
      state()
{
    if (source_view->have_exec_pos())
    {
      state = "has stopped";
      running = true;
    }
    else
    {
      state = "is not being run";
      running = false;
    }

    switch(gdb->type())
    {
    case GDB:
    case PYDB:
    case DBG:
    {
      string ans;

      if (gdb->is_windriver_gdb())
      {
          // Windriver GDB (VxWorks).

          // VxWorks allows multiple dynamically relocatable
          // programs to be loaded simultaneously. Before a program
          // is run, the 'info source' command can report
          // information from a source file not related to the
          // program just downloaded for debugging. (The WindRiver
          // version of gdb does not support the 'info files'
          // command.)

          // In order to tell DDD that there is indeed a source file
          // available, we need to first see if a program has
          // already been started. If so, then the 'info source'
          // command can be used.

          // Otherwise, the 'info sources' command is used, and the
          // first file in the list is used, assuming it is related
          // to the current program that was downloaded.

          // See if the program has been run first
          ans = gdb_question("info frame");
          if (ans == NO_GDB_ANSWER)
            break;

          file = "";
          if (ans.contains("No stack"))
          {
            // Then try using info sources and use first file listed
            ans = gdb_question("info sources");
            if (ans == NO_GDB_ANSWER)
                break;

            if (ans.contains("Source files for which "
                         "symbols have been read in:"))
            {
                file = ans.after("\n");
                file = file.before(" ");
                file = unquote(file);
            }
          }
          else
          {
            // Try using `info source'.
            ans = gdb_question("info source");
            if (ans == NO_GDB_ANSWER)
                break;

            if (ans.contains("Current source file is "))
            {
                file = ans.after("Current source file is ");
                file = file.before(".\n");
                file = unquote(file);
            }
          }
      }
      else
      {
          // Ordinary GDB.
          ans = gdb_question("info files");
          if (ans == NO_GDB_ANSWER)
            break;

          file = "";
          if (ans.contains("Symbols from "))
          {
            file = ans.after("Symbols from ");
            file = file.before(".\n");
            file = unquote(file);
          }

      }
      core = "";
      if (ans.contains("core dump"))
      {
          core = ans.after("core dump");
          core = core.after('`');
          core = core.before("',");
      }

      if (ans.contains("process "))
      {
          string p = ans.after("process ");
          pid = atoi(p.chars());
      }

      attached = ans.contains("attached process ");

      ans = gdb_question("info program");
      if (ans == NO_GDB_ANSWER)
          break;

      if (ans.contains("not being run"))
          running = false;
      else if (ans.contains("\nIt stopped "))
      {
          state = ans.from("\nIt stopped ");
          state = "has " + state.after("\nIt ");
          state = state.before('.');
          running = true;
      }
      break;
    }

    case DBX:
    {
      if (gdb->is_ladebug())
      {
          string ans = gdb_question("show process");
          // typical answers:
          // 1:no process 
          // There are no processes being debugged.
          // 2: process has been paused.
          // Current Process: localhost:26177 (a.out) paused.
          // 3: process terminated
          // Current Process: localhost:13614 (ddd) terminated.
          // TODO: treat the terminated case 
     
          if (ans != NO_GDB_ANSWER && 
            !ans.contains("no processes being debugged"))
          {
            if (ans.contains("Current Process: "))
            {
                ans = ans.after(": ");
                {
                  string p = ans.after(":");
                  p = p.before(" ");
                  pid = atoi(p.chars());
                }
                ans = ans.after(" (");
                file = ans.before(")");

                ans = ans.after(") ");
                running = ans.contains("paused");
                // ladebug always creates a process.
                // It may be "loaded" or attached.
                attached = true;
            }
          }     
      }

      // AD TODO: shouldn't it be an "else" ?

      string ans = gdb_question(gdb->debug_command());
      if (ans != NO_GDB_ANSWER)
      {
          if (ans.contains("Current givenfile is ", 0))
            ans = ans.after("Current givenfile is ");
          else if (ans.contains("Debugging: ", 0))
            ans = ans.after(": ");

          strip_space(ans);
          if (!ans.contains(' ')) // Sanity check
            file = ans;
      }
      break;
    }

    case XDB:
      break;                  // FIXME

    case PERL:
      // Use the program we were invoked with
      file = gdb->program();
      if (file.matches(rxint))
          file = "";          // Invoked with a constant expression

      if (file.empty())
      {
          // Not invoked with a program?  Use the current file, then.
          file = source_view->file_of_cursor();
          file = file.before(":");
      }
      core = "";
      break;

    case BASH:
      // Use the program we were invoked with
      file = gdb->program();
      if (file.matches(rxint))
          file = "";          // Invoked with a constant expression

      if (file.empty())
      {
          // Not invoked with a program?  Use the current file, then.
          file = source_view->file_of_cursor();
          file = file.before(":");
      }
      core = "";
      break;

    case JDB:
      // Just use the current class.
      file = source_view->line_of_cursor();
      file = file.before(":");
      core = "";

      // Save whether JDB's VM is running
      static int last_jdb_pid = -1;
      static bool jvm_running = false;

      if (gdb->pid() != last_jdb_pid)
      {
          // New JDB: reset info
          jvm_running = false;
          last_jdb_pid = gdb->pid();
      }

      // The VM is running iff the prompt contains a backtrace
      // level ("[n]").
      if (!jvm_running && gdb->prompt().contains("["))
          jvm_running = true;

      running = jvm_running;
      break;
    }

    if (file == NO_GDB_ANSWER)
    {
      // As a fallback, get core file and executable from argument list.
      // Works only on local file system and is more a guess.
      char **argv = saved_argv();
      int argc = 0;
      while (argv[argc] != 0)
          argc++;

      for (int i = argc - 1; i > 0 && file == NO_GDB_ANSWER; i--)
      {
          // All debuggers supported by DDD have [EXEC [CORE]] as
          // their last arguments.
          string arg = argv[i];
          if (is_core_file(arg))
            core = arg;
          else if (is_debuggee_file(arg))
            file = arg;
      }
    }

    if (file != NO_GDB_ANSWER)
      add_to_recent(file);
}


//-----------------------------------------------------------------------------
// Processes
//-----------------------------------------------------------------------------

// Process selection
static int ps_pid_index = 0;

// Retrieve PID from PS output
static int ps_pid(const string& line)
{
    const char *s = line.chars() + ps_pid_index;
    while (s > line.chars() && isdigit(s[-1]))
      --s;

    return atoi(s);
}

// Fill the pids in DISP_NRS
static void getPIDs(Widget selectionList, IntArray& disp_nrs)
{
    static const IntArray empty;
    disp_nrs = empty;

    XmStringTable selected_items;
    int selected_items_count = 0;

    assert(XmIsList(selectionList));

    XtVaGetValues(selectionList,
              XmNselectedItemCount, &selected_items_count,
              XmNselectedItems, &selected_items,
              XtPointer(0));

    for (int i = 0; i < selected_items_count; i++)
    {
      String _item;
      XmStringGetLtoR(selected_items[i], LIST_CHARSET, &_item);
      string item(_item);
      XtFree(_item);

      int p = ps_pid(item);
      if (p > 0)
          disp_nrs += p;
    }
}

// Get the PID from the selection list in CLIENT_DATA
static int get_pid(Widget, XtPointer client_data, XtPointer)
{
    IntArray pids;
    Widget processes = Widget(client_data);
    if (processes != 0)
      getPIDs(processes, pids);

    if (pids.size() == 1)
      return pids[0];
    else
      return 0;
}

// Process selection

static void sortProcesses(StringArray& a)
{
    // Shell sort -- simple and fast
    int h = 1;
    do {
      h = h * 3 + 1;
    } while (h <= a.size());
    do {
      h /= 3;
      for (int i = h; i < a.size(); i++)
      {
          string v = a[i];
          int j;
          for (j = i; j >= h && ps_pid(a[j - h]) > ps_pid(v); j -= h)
            a[j] = a[j - h];
          if (i != j)
            a[j] = v;
      }
    } while (h != 1);
}


inline bool is_separator(char c)
{
    return c == ' ' || c == '\'' || c == '\"';
}

// Check whether LINE is a valid PS line.  Exclude occurrences of PS_COMMAND.
static bool valid_ps_line(const string& line, const string& ps_command)
{
    int pid = ps_pid(line);
    if (pid == 0)
      return false;           // No PID

    // You don't want to debug DDD, don't you?
    if (!remote_gdb() && pid == getpid())
      return false;

    // Neither should you debug GDB by itself.
    if (pid == gdb->pid())
      return false;

    // Don't issue lines containing `ps' (or whatever the first word
    // in PS_COMMAND is).
    string ps = ps_command;
    if (ps.contains(' '))
      ps = ps.before(' ');
    ps = basename(ps.chars());
    int index = line.index(ps);
    if (index > 0
      && (line[index - 1] == '/' || is_separator(line[index - 1]))
      && (line.length() == index + ps.length()
          || is_separator(line[index + ps.length()])))
      return false;

    // Okay, just leave it
    return true;
}


// Create list of processes
static void update_processes(Widget processes, bool keep_selection)
{
    StatusDelay delay("Getting list of processes");

    string cmd = sh_command(app_data.ps_command) + " 2>&1";
    FILE *fp = popen(cmd.chars(), "r");
    if (fp == 0)
    {
      delay.outcome = strerror(errno);
      return;
    }

    StringArray all_process_list;
    int c;
    string line = "";
    bool first_line = true;

    while ((c = getc(fp)) != EOF)
    {
      if (c == '\n')
      {
          if (first_line || valid_ps_line(line, app_data.ps_command))
            all_process_list += line;
#if 0
          else
            std::clog << "Excluded: " << line << "\n";
#endif

          if (first_line)
          {
            // Find first occurrence of `PID' title
            ps_pid_index = line.index(" PID ");
            if (ps_pid_index < 0)
                ps_pid_index = 0;
          }

          line = "";
          first_line = false;
      }
      else
      {
          line += c;
      }
    }

    pclose(fp);
    sortProcesses(all_process_list);
    DynIntArray pids(all_process_list.size());

    // If GDB cannot send a signal to the process, we cannot debug it.
    // Try a `kill -0' (via GDB, as it may be setuid) and filter out
    // all processes in the `kill' diagnostic -- that is, all
    // processes that `kill' could not send a signal.
    string kill = "kill -0";

    if (gdb->has_handler_command())
      kill = "/usr/bin/kill -0"; // Bypass built-in SUN DBX command

    int i;
    for (i = 0; i < all_process_list.size(); i++)
    {
      pids[i] = ps_pid(all_process_list[i]);
      if (pids[i])
          kill += string(" ") + itostring(pids[i]);
    }
#if defined(__sun) 
    // bypass underlying debugger
    // Fix for Sun: use /usr/bin/kill
    string kill_result;
    {
      std::ostringstream os;
      kill += " 2>&1";
      FILE *fp = popen(kill.chars(), "r");
      if (fp != 0)
      {
        int c;
        while ((c = getc(fp)) != EOF)
        {
            os << (char)c;
        }
        pclose(fp);
      }
      kill_result = os;
    }
#else
    string kill_result = gdb_question(gdb->shell_command(kill));
#endif
    i = 0;
    while (i >= 0)
    {
      i = kill_result.index(rxint, i);
      if (i >= 0)
      {
          int bad_pid = atoi(kill_result.chars() + i);
          for (int k = 0; k < all_process_list.size(); k++)
          {
            if (pids[k] != 0 && pids[k] == bad_pid)
            {
#if 0
                std::clog << "Excluded: " << all_process_list[k] << "\n";
#endif
                all_process_list[k] = NO_GDB_ANSWER;
            }
          }
          i++;
      }
    }

    StringArray process_list;
    for (i = 0; i < all_process_list.size(); i++)
      if (all_process_list[i] != NO_GDB_ANSWER)
          process_list += all_process_list[i];

    // Now set the selection.
    bool *selected = new bool[process_list.size()];
    for (i = 0; i < process_list.size(); i++)
      selected[i] = false;

    int pos = -1;
    if (keep_selection)
    {
      // Preserve old selection: each PID selected before will also be
      // selected after.
      IntArray selection;
      getPIDs(processes, selection);

      for (i = 0; i < selection.size(); i++)
      {
          for (int j = 0; j < process_list.size(); j++)
            if (selection[i] == ps_pid(process_list[j]))
            {
                if (pos < 0)
                  pos = j;
                selected[j] = true;
            }
      }
    }

    if (pos < 0)
    {
      // Create new selection from current file and current pid.
      ProgramInfo info;

      // Check for current pid; if found, highlight it.
      for (i = 0; pos < 0 && i < process_list.size(); i++)
      {
          if (info.pid != 0 && ps_pid(process_list[i]) == info.pid)
            pos = i;
      }

      if (pos < 0)
      {
          // Not found? Try leftmost occurrence of process base name.
          string current_base = basename(info.file.chars());
          int leftmost = INT_MAX;
          for (i = 0; i < process_list.size(); i++)
          {
            int occurrence = process_list[i].index(current_base);
            if (occurrence >= 0 && occurrence < leftmost 
                && ps_pid(process_list[i]) > 0)
            {
                leftmost = occurrence;
                pos = i;
            }
          }
      }
    }

    if (pos >= 0)
      selected[pos] = true;

    setLabelList(processes, process_list.values(),
             selected, process_list.size(), true, false);

    if (pos >= 0)
      ListSetAndSelectPos(processes, pos + 1);

    delete[] selected;
}


static void gdbUpdateProcessesCB(Widget, XtPointer client_data, XtPointer)
{
    Widget processes = Widget(client_data);
    update_processes(processes, true);
}

// Select a process
static void SelectProcessCB(Widget w, XtPointer client_data, 
                      XtPointer call_data)
{
    XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
    int pos = cbs->item_position;
    if (pos == 1)
      XmListDeselectAllItems(w); // Title selected
    else
      ListSetAndSelectPos(w, pos);

    int pid = get_pid(w, client_data, call_data);
    if (pid <= 0)
      set_status("");
    else
      set_status("Process " + itostring(pid));
}


// OK pressed in `Open Process'
static void openProcessDone(Widget w, XtPointer client_data, 
                      XtPointer call_data)
{
    int pid = get_pid(w, client_data, call_data);
    if (pid <= 0)
    {
      gdbUpdateProcessesCB(w, client_data, call_data);      
      return;
    }

    XtUnmanageChild(w);

    ProgramInfo info;

    if (pid == info.pid)
    {
      set_status("Already attached to process " + itostring(pid) + ".");
      return;
    }

    if (info.file == NO_GDB_ANSWER || info.file.empty())
    {
      post_error("No program.", "no_program", w);
      return;
    }

    // GDB does not always detach processes upon opening new
    // files, so we do it explicitly
    if (info.attached)
      gdb_command(gdb->detach_command(info.pid));

    // Attach to new process
    gdb_command(gdb->attach_command(pid, info.file));
}

// When W is to be destroyed, remove all references in Widget(CLIENT_DATA)
static void RemoveCallbacksCB(Widget w, XtPointer client_data, XtPointer)
{
    Widget ref = Widget(client_data);
    XtRemoveCallback(ref, XmNokCallback,      UnmanageThisCB, XtPointer(w));
    XtRemoveCallback(ref, XmNcancelCallback,  UnmanageThisCB, XtPointer(w));
    XtRemoveCallback(ref, XmNdestroyCallback, RemoveCallbacksCB, XtPointer(w));
}

// If we don't have a current executable, issue a warning.
static void warn_if_no_program(Widget popdown)
{
    ProgramInfo info;

    if (info.file.empty())
    {
      Widget warning = post_warning("Please open a program first.", 
                              "no_program", popdown);

      if (popdown != 0 && warning != 0)
      {
          // Tie the warning to the dialog - if one is popped down,
          // so is the other.
          XtAddCallback(warning, XmNokCallback, 
                    UnmanageThisCB, XtPointer(popdown));
          XtAddCallback(warning, XmNcancelCallback, 
                    UnmanageThisCB, XtPointer(popdown));
          XtAddCallback(popdown, XmNdestroyCallback,
                    RemoveCallbacksCB, XtPointer(warning));

          XtAddCallback(popdown, XmNokCallback,
                    UnmanageThisCB, XtPointer(warning));
          XtAddCallback(popdown, XmNcancelCallback,
                    UnmanageThisCB, XtPointer(warning));
          XtAddCallback(warning, XmNdestroyCallback,
                    RemoveCallbacksCB, XtPointer(popdown));
      }
    }
}



//-----------------------------------------------------------------------------
// Helpers for class and source selection
//-----------------------------------------------------------------------------

// Get the selected item ids
static void get_items(Widget selectionList, StringArray& itemids)
{
    static const StringArray empty;
    itemids = empty;

    XmStringTable selected_items;
    int selected_items_count = 0;

    assert(XmIsList(selectionList));

    XtVaGetValues(selectionList,
              XmNselectedItemCount, &selected_items_count,
              XmNselectedItems, &selected_items,
              XtPointer(0));

    for (int i = 0; i < selected_items_count; i++)
    {
      String _item;
      XmStringGetLtoR(selected_items[i], LIST_CHARSET, &_item);
      string item(_item);
      XtFree(_item);

      itemids += item;
    }
}

// Get the item from the selection list in CLIENT_DATA
static string get_item(Widget, XtPointer client_data, XtPointer)
{
    StringArray itemids;
    Widget items = Widget(client_data);
    if (items != 0)
      get_items(items, itemids);

    if (itemids.size() == 1)
      return itemids[0];

    return "";
}


//-----------------------------------------------------------------------------
// Classes (JDB only)
//-----------------------------------------------------------------------------

// Select a class
static void SelectClassCB(Widget w, XtPointer client_data, 
                    XtPointer call_data)
{
    XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
    int pos = cbs->item_position;
    ListSetAndSelectPos(w, pos);

    string cls = get_item(w, client_data, call_data);
    if (cls.empty())
      set_status("");
    else
      set_status(source_view->full_path(java_class_file(cls)));
}

static void update_classes(Widget classes)
{
    StatusDelay delay("Getting list of classes");
    StringArray classes_list;
    get_java_classes(classes_list);

    // Now set the selection.
    bool *selected = new bool[classes_list.size()];
    for (int i = 0; i < classes_list.size(); i++)
      selected[i] = false;

    setLabelList(classes, classes_list.values(),
             selected, classes_list.size(), false, false);

    delete[] selected;
}

static void gdbUpdateClassesCB(Widget, XtPointer client_data, XtPointer)
{
    Widget classes = Widget(client_data);
    update_classes(classes);
}

// OK pressed in `Open Class'
static void openClassDone(Widget w, XtPointer client_data, 
                    XtPointer call_data)
{
    string cls = get_item(w, client_data, call_data);
    if (cls.empty())
    {
      gdbUpdateClassesCB(w, client_data, call_data);  
      return;
    }

    XtUnmanageChild(w);

    gdb_command(gdb->debug_command(cls));
}


//-----------------------------------------------------------------------------
// Lookup sources and functions (GDB only)
//-----------------------------------------------------------------------------

static StringArray all_sources;

// Select a source; show the full path name in the status line
static void SelectSourceCB(Widget w, XtPointer, XtPointer call_data)
{
    XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
    int pos = cbs->item_position;
    ListSetAndSelectPos(w, pos);

    pos--;
    if (pos < 0)
      pos = all_sources.size() - 1;
    set_status(all_sources[pos]);
}

// Get list of sources into SOURCES_LIST
void get_gdb_sources(StringArray& sources_list)
{
    static const StringArray empty;
    sources_list = empty;

    string ans = gdb_question("info sources");
    if (ans != NO_GDB_ANSWER)
    {
      // Create a newline-separated list of sources
      string new_ans;
      while (!ans.empty())
      {
          string line = ans.before('\n');
          ans = ans.after('\n');

          if (line.empty() || line.contains(':', -1))
            continue;

          line.gsub(", ", "\n");
          new_ans += line + '\n';
      }

      ans = new_ans;
      while (!ans.empty())
      {
          string line = ans.before('\n');
          ans = ans.after('\n');
          
          sources_list += line;
      }

      smart_sort(sources_list);
      uniq(sources_list);
    }
}

// Remove adjacent duplicates in A1
static void uniq(StringArray& a1, StringArray& a2)
{
    StringArray b1;
    StringArray b2;

    for (int i = 0; i < a1.size(); i++)
    {
      if (i == 0 || a1[i - 1] != a1[i])
      {
          b1 += a1[i];
          b2 += a2[i];
      }
    }
    
    a1 = b1;
    a2 = b2;
}

// Sort A1 and A2 according to the values in A1
static void sort(StringArray& a1, StringArray& a2)
{
    assert(a1.size() == a2.size());

    // Shell sort -- simple and fast
    int h = 1;
    do {
      h = h * 3 + 1;
    } while (h <= a1.size());
    do {
      h /= 3;
      for (int i = h; i < a1.size(); i++)
      {
          string v1 = a1[i];
          string v2 = a2[i];
          int j;
          for (j = i; j >= h && smart_compare(a1[j - h], v1) > 0; j -= h)
          {
            a1[j] = a1[j - h];
            a2[j] = a2[j - h];
          }
          if (i != j)
          {
            a1[j] = v1;
            a2[j] = v2;
          }
      }
    } while (h != 1);
}

static void filter_sources(StringArray& labels, StringArray& sources,
                     const string& pattern)
{
    assert(labels.size() == sources.size());

    StringArray new_labels;
    StringArray new_sources;

    for (int i = 0; i < labels.size(); i++)
    {
      if (glob_match(pattern.chars(), labels[i].chars(), 0) ||
          glob_match(pattern.chars(), sources[i].chars(), 0))
      {
          new_labels  += labels[i];
          new_sources += sources[i];
      }
    }

    labels  = new_labels;
    sources = new_sources;
}

static void update_sources(Widget sources, Widget filter)
{
    StatusDelay delay("Getting sources");
    get_gdb_sources(all_sources);

    String pattern_s = XmTextFieldGetString(filter);
    string pattern = pattern_s;
    XtFree(pattern_s);

    strip_space(pattern);
    if (pattern.empty())
      pattern = "*";
    XmTextFieldSetString(filter, XMST(pattern.chars()));

    StringArray labels;
    uniquify(all_sources, labels);

    // Sort and remove duplicates
    sort(labels, all_sources);
    uniq(labels, all_sources);

    // Filter pattern
    filter_sources(labels, all_sources, pattern);

    // Now set the selection.
    bool *selected = new bool[labels.size()];
    for (int i = 0; i < labels.size(); i++)
      selected[i] = false;

    setLabelList(sources, labels.values(),
             selected, labels.size(), false, false);

    delete[] selected;
}

// OK pressed in `Lookup Source'
static void lookupSourceDone(Widget w,
                       XtPointer client_data, 
                       XtPointer call_data)
{
    Widget sources = Widget(client_data);
    XmSelectionBoxCallbackStruct *cbs = 
      (XmSelectionBoxCallbackStruct *)call_data;

    set_status("");

    string source = get_item(w, client_data, call_data);

    if (source.contains('/'))
    {
      // Expand to full path name
      int *position_list = 0;
      int position_count = 0;
      if (XmListGetSelectedPos(sources, &position_list, &position_count))
      {
          if (position_count == 1)
          {
            int pos = position_list[0];
            pos--;
            if (pos < 0)
                pos = all_sources.size() - 1;
            source = all_sources[pos];
          }

          XtFree((char *)position_list);
      }
    }

    if (!source.empty())
    {
      source_view->lookup(source + ":1");

      if (cbs != 0 && 
          cbs->reason != XmCR_APPLY && 
          cbs->reason != XmCR_ACTIVATE)
      {
          Widget scroll = XtParent(sources);
          Widget dialog = XtParent(scroll);
          XtUnmanageChild(dialog);
      }
    }
}

static void open_source_msg()
{
    set_status("Open Source is an idea whose time has finally come.  "
             "See http://www.opensource.org/.");
}



//-----------------------------------------------------------------------------
// Entry Points
//-----------------------------------------------------------------------------

void gdbOpenFileCB(Widget w, XtPointer, XtPointer)
{
    static Widget dialog = 
      create_file_dialog(w, "exec_files", 
                     searchRemoteExecFiles, 
                     searchRemoteDirectories,
                     searchLocalExecFiles, 0,
                     openFileDone);
    manage_and_raise(dialog);
}

void gdbOpenRecentCB(Widget, XtPointer client_data, XtPointer)
{
    int index = ((int)(long)client_data) - 1;

    StringArray recent_files;
    get_recent(recent_files);

    if (index >= 0 && index < recent_files.size())
    {
      string file = recent_files[index];
      open_file(file);
      // This is a kludge as I don't [yet] understand how to force the
      // reading of the source file automatically, as is done when an
      // compiled executable is opened.
      if (gdb->type() == PYDB)
          source_view->read_file(file);
    }
}

void gdbOpenCoreCB(Widget w, XtPointer, XtPointer)
{
    static Widget dialog = 
      create_file_dialog(w, "core_files", 
                     searchRemoteCoreFiles, searchRemoteDirectories,
                     searchLocalCoreFiles, 0,
                     openCoreDone);
    manage_and_raise(dialog);
    warn_if_no_program(dialog);
}

void gdbOpenSourceCB(Widget w, XtPointer, XtPointer)
{
    static Widget dialog = 
      create_file_dialog(w, "source_files", 
                     searchRemoteSourceFiles, searchRemoteDirectories,
                     searchLocalSourceFiles, 0,
                     openSourceDone);
    manage_and_raise(dialog);

    open_source_msg();

    if ((gdb->type() != JDB) && (gdb->type() != PYDB))
    {
      warn_if_no_program(dialog);
    }
    else
    {
      // JDB works well without executable
      // PYDB doesn't use an executable
    }
}

void gdbOpenProcessCB(Widget w, XtPointer, XtPointer)
{
    static Widget dialog = 0;
    static Widget processes = 0;

    if (dialog == 0)
    {
      Arg args[10];
      int arg = 0;
    
      XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
      dialog = verify(XmCreateSelectionDialog(find_shell(w), 
                                    XMST("processes"), 
                                    args, arg));

      Delay::register_shell(dialog);

      XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
                                     XmDIALOG_SELECTION_LABEL));
      XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
                                     XmDIALOG_TEXT));

      processes = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);

      XtAddCallback(processes, XmNsingleSelectionCallback,
                  SelectProcessCB, XtPointer(processes));
      XtAddCallback(processes, XmNmultipleSelectionCallback,
                  SelectProcessCB, XtPointer(processes));
      XtAddCallback(processes, XmNextendedSelectionCallback,
                  SelectProcessCB, XtPointer(processes));
      XtAddCallback(processes, XmNbrowseSelectionCallback,
                  SelectProcessCB, XtPointer(processes));

      XtAddCallback(dialog, XmNokCallback, 
                  openProcessDone, XtPointer(processes));
      XtAddCallback(dialog, XmNapplyCallback, 
                  gdbUpdateProcessesCB, XtPointer(processes));
      XtAddCallback(dialog, XmNcancelCallback, 
                  UnmanageThisCB, XtPointer(dialog));
      XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);
    }

    update_processes(processes, false);
    manage_and_raise(dialog);
    warn_if_no_program(dialog);
}

void gdbOpenClassCB(Widget w, XtPointer, XtPointer)
{
    static Widget dialog = 0;
    static Widget classes = 0;

    if (dialog == 0)
    {
      Arg args[10];
      int arg = 0;
    
      XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
      dialog = verify(XmCreateSelectionDialog(find_shell(w), 
                                    XMST("classes"), args, arg));

      Delay::register_shell(dialog);

      XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
                                     XmDIALOG_SELECTION_LABEL));
      XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
                                     XmDIALOG_TEXT));

      classes = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);

      XtAddCallback(classes, XmNsingleSelectionCallback,
                  SelectClassCB, XtPointer(classes));
      XtAddCallback(classes, XmNmultipleSelectionCallback,
                  SelectClassCB, XtPointer(classes));
      XtAddCallback(classes, XmNextendedSelectionCallback,
                  SelectClassCB, XtPointer(classes));
      XtAddCallback(classes, XmNbrowseSelectionCallback,
                  SelectClassCB, XtPointer(classes));

      XtAddCallback(dialog, XmNokCallback, 
                  openClassDone, XtPointer(classes));
      XtAddCallback(dialog, XmNapplyCallback, 
                  gdbUpdateClassesCB, XtPointer(classes));
      XtAddCallback(dialog, XmNcancelCallback, 
                  UnmanageThisCB, XtPointer(dialog));
      XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);
    }

    update_classes(classes);
    manage_and_raise(dialog);
}

static Widget source_list   = 0;
static Widget source_filter = 0;

void update_sources()
{
    if (source_list != 0)
      update_sources(source_list, source_filter);
}

static void FilterSourcesCB(Widget, XtPointer, XtPointer)
{
    update_sources();
}

static void LoadSharedLibrariesCB(Widget, XtPointer, XtPointer)
{
    StatusDelay delay("Loading shared object library symbols");
    
    gdb_question("sharedlibrary");
    update_sources();
}

void gdbLookupSourceCB(Widget w, XtPointer client_data, XtPointer call_data)
{
    if (gdb->type() != GDB)
    {
      gdbOpenSourceCB(w, client_data, call_data);
      return;
    }

    static Widget dialog  = 0;

    if (dialog == 0)
    {
      Arg args[10];
      int arg = 0;
    
      XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
#if XmVersion >= 1002
      XtSetArg(args[arg], XmNchildPlacement, XmPLACE_TOP); arg++;
#endif
      dialog = verify(XmCreateSelectionDialog(find_shell(w), 
                                    XMST("sources"), args, arg));

      Delay::register_shell(dialog);

      XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
                                     XmDIALOG_SELECTION_LABEL));
      XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
                                     XmDIALOG_TEXT));

      arg = 0;
      XtSetArg(args[arg], XmNmarginWidth,     0);     arg++;
      XtSetArg(args[arg], XmNmarginHeight,    0);     arg++;
      XtSetArg(args[arg], XmNborderWidth,     0);     arg++;
      XtSetArg(args[arg], XmNadjustMargin,    False); arg++;
      XtSetArg(args[arg], XmNshadowThickness, 0);     arg++;
      XtSetArg(args[arg], XmNspacing,         0);     arg++;
      Widget bigbox = XmCreateRowColumn(dialog, XMST("bigbox"), args, arg);
      XtManageChild(bigbox);

      arg = 0;
      XtSetArg(args[arg], XmNmarginWidth,     0);     arg++;
      XtSetArg(args[arg], XmNmarginHeight,    0);     arg++;
      XtSetArg(args[arg], XmNborderWidth,     0);     arg++;
      XtSetArg(args[arg], XmNadjustMargin,    False); arg++;
      XtSetArg(args[arg], XmNshadowThickness, 0);     arg++;
      XtSetArg(args[arg], XmNspacing,         0);     arg++;
      Widget box = XmCreateRowColumn(bigbox, XMST("box"), args, arg);
      XtManageChild(box);

      arg = 0;
      Widget label = XmCreateLabel(box, XMST("label"), args, arg);
      XtManageChild(label);

      arg = 0;
      source_filter = XmCreateTextField(box, XMST("filter"), args, arg);
      XtManageChild(source_filter);

      arg = 0;
      Widget sharedlibrary = 
          XmCreatePushButton(bigbox, XMST("sharedlibrary"), args, arg);
      XtManageChild(sharedlibrary);

#if XmVersion >= 1002
      arg = 0;
      Widget lookup = XmCreatePushButton(dialog, 
                                 XMST("lookup"), args, arg);
      XtManageChild(lookup);
#endif

      source_list = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);

      XtAddCallback(source_list, XmNsingleSelectionCallback,
                  SelectSourceCB, XtPointer(source_list));
      XtAddCallback(source_list, XmNmultipleSelectionCallback,
                  SelectSourceCB, XtPointer(source_list));
      XtAddCallback(source_list, XmNextendedSelectionCallback,
                  SelectSourceCB, XtPointer(source_list));
      XtAddCallback(source_list, XmNbrowseSelectionCallback,
                  SelectSourceCB, XtPointer(source_list));

      XtAddCallback(dialog, XmNokCallback, 
                  lookupSourceDone, XtPointer(source_list));
      XtAddCallback(dialog, XmNapplyCallback, FilterSourcesCB, 0);
      XtAddCallback(dialog, XmNcancelCallback, 
                  UnmanageThisCB, XtPointer(dialog));
      XtAddCallback(dialog, XmNunmapCallback, ClearStatusCB, 0);
      XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);

      XtAddCallback(source_filter, XmNactivateCallback, 
                  FilterSourcesCB, 0);
      XtAddCallback(sharedlibrary, XmNactivateCallback, 
                  LoadSharedLibrariesCB, 0);

#if XmVersion >= 1002
      XtAddCallback(lookup, XmNactivateCallback, 
                  lookupSourceDone, XtPointer(source_list));
#endif
    }

    update_sources(source_list, source_filter);

    open_source_msg();
    manage_and_raise(dialog);
    warn_if_no_program(dialog);
}

Generated by  Doxygen 1.6.0   Back to index