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

status.C

// $Id$ -*- C++ -*-
// Show messages in status line

// Copyright (C) 1996-1998 Technische Universitaet Braunschweig, 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 status_rcsid[] = 
    "$Id$";

#include "status.h"

#include "Command.h"
#include "Delay.h"
#include "DestroyCB.h"
#include "GDBAgent.h"
#include "HelpCB.h"
#include "MakeMenu.h"
#include "charsets.h"
#include "cmdtty.h"           // annotate()
#include "ddd.h"
#include "findParent.h"
#include "mydialogs.h"
#include "post.h"
#include "string-fun.h"
#include "verify.h"

#include <ctype.h>
#include <Xm/Xm.h>
#include <Xm/Text.h>
#include <Xm/SelectioB.h>
#include <Xm/RowColumn.h>
#include <Xm/Label.h>
#include <Xm/MenuShell.h>

#include <X11/IntrinsicP.h>   // LessTif hacks
#include "LessTifH.h"

//-----------------------------------------------------------------------------
// Data
//-----------------------------------------------------------------------------

// True if last cmd came from GDB window
bool gdb_keyboard_command = false;

// True if the next line is to be displayed in the status line
bool show_next_line_in_status = false;

// True if GDB is asking `yes or no' right now
bool gdb_asks_yn;

// Current contents of status window
static MString current_status_text;

// Non-zero if status is locked (i.e. unchangeable)
static int status_locked = 0;

// True iff status is to be logged
static bool log_status = true;


//-----------------------------------------------------------------------------
// Status lock
//-----------------------------------------------------------------------------

void lock_status(void)       { status_locked++;   }
void unlock_status(void)     { if (status_locked > 0) status_locked--; }
void reset_status_lock(void) { status_locked = 0; }


//-----------------------------------------------------------------------------
// Prompt recognition
//-----------------------------------------------------------------------------

void set_buttons_from_gdb(Widget buttons, string& text)
{
    bool yn = gdb->ends_with_yn(text);

    if (yn)
    {
      if (!gdb_asks_yn)
          annotate("query");

      gdb_asks_yn = true;
    }
    else if (gdb->isReadyWithPrompt())
    {
      if (gdb_asks_yn)
          annotate("post-query");

      gdb_asks_yn = false;
      unpost_gdb_yn();
    }

    if (yn && !gdb_keyboard_command)
    {
      // Fetch previous output lines, in case this is a multi-line message.
      String s = XmTextGetString(gdb_w);
      string prompt(s);
      XtFree(s);

      // FIXME: Handle JDB
      char prompt_start = (gdb->type() == XDB ? '>' : '(');

      int pos = prompt.index(prompt_start, -1);
      if (pos >= 0)
          pos = prompt.index('\n', pos) + 1;
      if (pos == 0)
          pos = messagePosition;

      XmTextReplace(gdb_w, pos, XmTextGetLastPosition(gdb_w), XMST(""));
      promptPosition = pos;

      prompt = prompt.from(pos);
      if (text.contains('('))
          prompt += text.before('(', -1); // Don't repeat `(y or n)'
      else
          prompt += text;

      post_gdb_yn(prompt);
      text = "";
      return;
    }

    if (buttons == 0)
      return;

    static bool last_yn = false;
    if (yn == last_yn)
      return;

    last_yn = yn;

    if (XtIsComposite(buttons))
    {
      set_sensitive(buttons, false);

      WidgetList children   = 0;
      Cardinal num_children = 0;

      XtVaGetValues(buttons,
                  XmNchildren, &children,
                  XmNnumChildren, &num_children,
                  XtPointer(0));

      int i;
      for (i = 0; i < int(num_children); i++)
          XtManageChild(children[i]);
      for (i = 0; i < int(num_children); i++)
      {
      
          Widget w = children[i];
          string name = XtName(w);

          if (yn == (name == "Yes" || name == "No"))
            XtManageChild(w);
          else
            XtUnmanageChild(w);
      }

      set_sensitive(buttons, true);
    }
}


//-----------------------------------------------------------------------------
// Status history
//-----------------------------------------------------------------------------

int status_history_size = 20;
static MString *history = 0;
static int current_history = 0;

static Widget history_label = 0;
static Widget history_row   = 0;

static Widget create_status_history(Widget parent)
{
    static Widget history_shell = 0;

    if (history_shell != 0)
      return history_shell;

    Arg args[10];
    int arg;

    arg = 0;
    XtSetArg(args[arg], XmNallowShellResize, True); arg++;
    XtSetArg(args[arg], XmNwidth,            10);   arg++;
    XtSetArg(args[arg], XmNheight,           10);   arg++;
    history_shell = verify(XmCreateMenuShell(parent, XMST("status_history"), 
                                   args, arg));

    arg = 0;
    XtSetArg(args[arg], XmNmarginWidth, 0);     arg++;
    XtSetArg(args[arg], XmNmarginHeight, 0);    arg++;
    XtSetArg(args[arg], XmNresizeWidth, True);  arg++;
    XtSetArg(args[arg], XmNresizeHeight, True); arg++;
    XtSetArg(args[arg], XmNborderWidth, 0);     arg++;
    XtSetArg(args[arg], XmNshadowThickness, 0); arg++;
    history_row = verify(XmCreateRowColumn(history_shell, XMST("row"), 
                                 args, arg));
    XtManageChild(history_row);

    arg = 0;
    XtSetArg(args[arg], XmNresizable, True); arg++;
    XtSetArg(args[arg], XmNalignment, XmALIGNMENT_BEGINNING); arg++;
    history_label = verify(XmCreateLabel(history_row, XMST("label"), 
                               args, arg));
    XtManageChild(history_label);

    return history_shell;
}

Widget status_history(Widget parent)
{
    Widget history_shell = create_status_history(parent);

    MString history_msg;

    if (history == 0 || status_history_size == 0)
    {
      history_msg = rm("No history.");
    }
    else
    {
      history_msg = MString("Recent messages");
      history_msg += rm(" (oldest first)");
      history_msg += cr();

      int i = current_history;
      do {
          if (!history[i].isEmpty())
          {
            if (!history_msg.isEmpty())
                history_msg += cr();
            history_msg += history[i];
          }
          i = (i + 1) % status_history_size;
      } while (i != current_history);
    }

    if (lesstif_version <= 87)
    {
      // LessTif 0.87 and earlier fails to resize the shell properly
      // - the border width is zero.  Use this hack instead.
      XmFontList font_list;
      XtVaGetValues(history_label, XmNfontList, &font_list, XtPointer(0));
    
      Dimension history_width  = history_msg.width(font_list)  + 6;
      Dimension history_height = history_msg.height(font_list) + 6;

      XtResizeWidget(history_label, history_width, history_height, 0);
      XtResizeWidget(history_row,   history_width, history_height, 0);

      XtResizeWidget(history_shell, history_width, history_height, 1);
    }

    XtVaSetValues(history_label, XmNlabelString, history_msg.xmstring(), 
              XtPointer(0));

    return history_shell;
}

// Return true iff S1 is a prefix of S2
static bool is_prefix(const MString& m1, const MString& m2)
{
    XmString s1 = m1.xmstring();
    XmString s2 = m2.xmstring();

    XmStringContext c1;
    XmStringContext c2;

    XmStringInitContext(&c1, s1);
    XmStringInitContext(&c2, s2);

    XmStringComponentType t1 = XmSTRING_COMPONENT_UNKNOWN;
    XmStringComponentType t2 = XmSTRING_COMPONENT_UNKNOWN;

    while (t1 != XmSTRING_COMPONENT_END && t2 != XmSTRING_COMPONENT_END)
    {
      char *s_text1            = 0;
      XmStringCharSet s_cs1    = 0;
      XmStringDirection d1     = XmSTRING_DIRECTION_DEFAULT;
      XmStringComponentType u1 = XmSTRING_COMPONENT_UNKNOWN;
      unsigned short ul1       = 0;
      unsigned char *s_uv1     = 0;
      
      t1 = XmStringGetNextComponent(c1, &s_text1, &s_cs1, &d1, 
                              &u1, &ul1, &s_uv1);

      char *s_text2            = 0;
      XmStringCharSet s_cs2    = 0;
      XmStringDirection d2     = XmSTRING_DIRECTION_DEFAULT;
      XmStringComponentType u2 = XmSTRING_COMPONENT_UNKNOWN;
      unsigned short ul2       = 0;
      unsigned char *s_uv2     = 0;

      t2 = XmStringGetNextComponent(c2, &s_text2, &s_cs2, &d2,
                              &u2, &ul2, &s_uv2);

      // Upon EOF in LessTif 0.82, XmStringGetNextComponent()
      // returns XmSTRING_COMPONENT_UNKNOWN instead of
      // XmSTRING_COMPONENT_END.  Work around this.
      if (t1 == XmSTRING_COMPONENT_UNKNOWN && s_uv1 == 0)
          t1 = XmSTRING_COMPONENT_END;
      if (t2 == XmSTRING_COMPONENT_UNKNOWN && s_uv2 == 0)
          t2 = XmSTRING_COMPONENT_END;

      // Place string values in strings
      string text1(s_text1 == 0 ? "" : s_text1);
      string text2(s_text2 == 0 ? "" : s_text2);
      string cs1(s_cs1 == 0 ? "" : s_cs1);
      string cs2(s_cs2 == 0 ? "" : s_cs2);
      string uv1;
      string uv2;
      if (s_uv1 != 0)
          uv1 = string((char *)s_uv1, ul1);
      if (s_uv2 != 0)
          uv2 = string((char *)s_uv2, ul2);

      // Free unused memory
      XtFree(s_text1);
      XtFree(s_text2);
      XtFree(s_cs1);
      XtFree(s_cs2);
      XtFree((char *)s_uv1);
      XtFree((char *)s_uv2);

      if (t1 != t2)
      {
          goto done;          // Differing tags
      }

      switch (t1)
      {
      case XmSTRING_COMPONENT_CHARSET:
      {
          if (cs1.empty())    // In LessTif 0.82, XmStringGetNextComponent()
            cs1 = text1;      // swaps CS and TEXT.  Work around this.
          if (cs2.empty())
            cs2 = text2;

          if (cs1 != cs2)
            goto done;  // Differing character sets
          break;
      }

      case XmSTRING_COMPONENT_TEXT:
#if XmVersion >= 1002
      case XmSTRING_COMPONENT_LOCALE_TEXT:
#endif
#if XmVersion >= 2000
      case XmSTRING_COMPONENT_WIDECHAR_TEXT:
#endif
      {
          if (text1.empty())  // In LessTif 0.82, XmStringGetNextComponent()
            text1 = cs1;      // swaps CS and TEXT.  Work around this.
          if (text2.empty())
            text2 = cs2;

          if (!text2.contains(text1, 0))
            goto done;
          XmStringComponentType next2 = XmStringPeekNextComponent(c2);

          // In LessTif 0.82, XmStringPeekNextComponent() returns
          // XmSTRING_COMPONENT_UNKNOWN instead of
          // XmSTRING_COMPONENT_END.  Work around this.
          if (next2 != XmSTRING_COMPONENT_END && 
            next2 != XmSTRING_COMPONENT_UNKNOWN)
            goto done;
          break;
      }

      case XmSTRING_COMPONENT_DIRECTION:
      {
          if (d1 != d2)
            goto done;
          break;
      }

      case XmSTRING_COMPONENT_SEPARATOR:
      case XmSTRING_COMPONENT_END:
      {
          // These are the same by definition
          break;
      }

      case XmSTRING_COMPONENT_UNKNOWN:
      {
          if (uv1 != uv2)
            goto done;  // Differing unknown tags
          break;
      }

      default:
      {
          break;        // Skip everything else
      }
      }
    }
 done:

    XmStringFreeContext(c2);
    XmStringFreeContext(c1);

    return t1 == XmSTRING_COMPONENT_END;
}

static void add_to_status_history(const MString& message)
{
    static MString empty = rm(" ");

    if (history == 0)
      history = new MString[status_history_size];

    int last_history = 
      (status_history_size + current_history - 1) % status_history_size;

    if (message.isNull() || message.isEmpty() || message == empty)
      return;

    if (is_prefix(history[last_history], message))
    {
      history[last_history] = message;
      return;
    }

    history[current_history] = message;
    current_history = (current_history + 1) % status_history_size;
}

//-----------------------------------------------------------------------------
// Status recognition
//-----------------------------------------------------------------------------

void set_status_from_gdb(const string& text)
{
    if (private_gdb_input)
      return;

    if (!show_next_line_in_status && !text.contains(gdb->prompt(), -1))
      return;

    // Fetch line before prompt in GDB window
    String s = XmTextGetString(gdb_w);
    string message = s + messagePosition;
    XtFree(s);

    if (message.empty() && text.contains('\n'))
      message = text;

    if (show_next_line_in_status && 
      (message.empty() || message[message.length() - 1] != '\n'))
      return;

    // Skip prompt and uncomplete lines
    int idx = message.index('\n', -1);
    if (idx >= 0)
      message = message.before(idx);

    strip_trailing_newlines(message);
    if (message.empty() && text.contains('\n'))
      message = text;

    if (show_next_line_in_status)
    {
      messagePosition = XmTextGetLastPosition(gdb_w) + text.length();
      show_next_line_in_status = false;
      message.gsub('\n', ' ');
    }
    else
    {
      // Show first line only
      while (!message.empty() && message[0] == '\n')
          message = message.after('\n');
      if (message.contains('\n'))
          message = message.before('\n');
    }

    strip_trailing_newlines(message);
    message.gsub('\t', ' ');
    if (message.empty())
      return;

    // Don't log this stuff - it's already logged
    bool old_log_status = log_status;
    log_status = false;
    set_status(message);
    log_status = old_log_status;
}

// Show MESSAGE in status window.
// If TEMPORARY is set, override locks and do not add to status history.
void set_status(const string& message_, bool temporary)
{
    if (status_w == 0)
      return;

    string message(message_);
    if (message.length() > 0
      && !message.contains("=") 
      && isascii(message[0])
      && islower(message[0]))
      message[0] = toupper(message[0]);

    set_status_mstring(rm(message), temporary);
}

// Same, but use an MString.
void set_status_mstring(const MString& message, bool temporary)
{
    if (status_w == 0)
      return;

    if (!temporary)
      add_to_status_history(message);

    if (!status_locked)
    {
      current_status_text = message;

      XtVaSetValues(status_w,
                  XmNlabelString, message.xmstring(),
                  XtPointer(0));
      XFlush(XtDisplay(status_w));
      XmUpdateDisplay(status_w);
    }

    if (log_status && !temporary)
    {
      // Log status message
      string s = message.str();
      if (!s.empty() && s != " ")
      {
          dddlog << "#  " << s << "\n";
          dddlog.flush();
      }
    }
}

const MString& current_status(void)
{
    return current_status_text;
}

Generated by  Doxygen 1.6.0   Back to index