Logo Search packages:      
Sourcecode: vat version File versions

sitebox.cc

/*
 * Copyright (c) 1991-1993 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the Computer Systems
 *    Engineering Group at Lawrence Berkeley Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static const char rcsid[] =
    "@(#) $Header: sitebox.cc,v 1.27 96/05/16 05:21:35 van Exp $ (LBL)";
#endif

#ifndef WIN32
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#if defined(sgi)
#define vfork fork
#endif

#include "Tcl.h"
#include "tkwidget.h"
#include "idlecallback.h"

#include <string.h>

class SiteBox;

class Site : public TclObject {
    public:
      Site(Tk_Window, SiteBox&);
      ~Site();

      void draw() const;
      virtual int command(int argc, const char*const* argv);

      int height();
      void place(int x, int y, int w, int h);

      static int textwidth(const char* s);
      inline const char* text() const { return (text_); }
      inline const char* tag() const { return (tag_); }

      Site* next_;
    protected:
      void text(const char*);
      void tag(const char*);
      void draw_checkbox(Display*, Drawable, GC) const;
      void square(Display* dpy, Drawable window, GC gc,
                int x, int y, int side) const;
      GC raw_gc(Font fid, XColor* fg, XColor* bg, Drawable d) const;
      void free_gc(GC gc) const;

      char* text_;            /* string appearing in display */
      char* tag_;       /* string appearing in display */
      int textlen_;           /* strlen(text_) */
      int text_x_;
      int text_y_;
      int x_;
      int y_;
      int height_;
      int width_;
      int descent_;
      int ascent_;
      int minor_;
      int major_;

#define SF_HIGHLIGHT    0x01
#define SF_DISABLE      0x02
      int state_;
      int mute_;
      int rank_;
      Tk_Window tk_;
      SiteBox& sitebox_;

      static GC copy_gc_;
      static GC fg_[4];
      static GC bg_[4];
      static XColor* fc_;     /* foreground */
      static XColor* bc_;     /* background */
      static XColor* ac_;     /* activeBackground */
      static XColor* dc_;     /* disabled */
      static Drawable pixmap_;
      static int pixw_;
      static int pixh_;
      static Tk_Font fs_;     /* font metrics */
        static Tk_FontMetrics fm_;
};

class SiteBox : public TkWidget, public IdleCallback {
    public:
      SiteBox(const char* path);
      virtual int command(int argc, const char*const* argv);
      void setchan(int channel);
      void LowLightAll();
      inline int Nsites() const { return (nsites_); }
      void idle_callback();
      void change_name();
    private:
      Site* create();
      Site* remove(const char* n);
      void reset();
      void append(Site* s);
      void layout();
      void sort_sites();
      virtual void resize();
      virtual void draw();
      virtual void update();

      Site** sitelist_;
      int nsites_;
      int nsitelist_;
      int empties_;

      int ncol_;        /* number of columns in site display */
      int colitems_;          /* number of items that fit for ncol_ */
      int percol_;            /* number of sites per column */
      int itemht_;            /* vertical dist allocated to a site */

      int place_x_;
      int place_y_;

      GC bg_;
      GC fg_;

      int sorted_;
      int need_sort_;
      int need_layout_;
      int need_redraw_;
};

GC Site::copy_gc_;
GC Site::fg_[4];
GC Site::bg_[4];
Tk_Font Site::fs_;
Tk_FontMetrics Site::fm_;
XColor* Site::fc_;
XColor* Site::bc_;
XColor* Site::ac_;
XColor* Site::dc_;

Drawable Site::pixmap_;
int Site::pixw_;
int Site::pixh_;

const int vpad = 2;

Site::Site(Tk_Window tk, SiteBox& sb) :
      next_(0),
      text_(0),
      tag_(0),
      textlen_(0),
      text_x_(0),
      text_y_(0),
      x_(0),
      y_(0),
      height_(0),
      width_(0),
      state_(0),
      mute_(0),
      rank_(4),
      tk_(tk),
      sitebox_(sb)
{
      Tcl& tcl = Tcl::instance();
      if (fs_ == 0) {
            const char* font = tcl.attr("siteFont");
            fs_ = Tk_GetFont(tcl.interp(), tk_, (char*)font);
            if (fs_ == 0) {
                  fprintf(stderr,
                        "vat: couldn't find font: %s\n", font);
                  fs_ = Tk_GetFont(tcl.interp(), tk_, "screen");
                  if (fs_ == 0)
                        fs_ = Tk_GetFont(tcl.interp(), tk_,
                                           "fixed");
                  if (fs_ == 0) {
                        fprintf(stderr,
                          "vat: couldn't find screen or fixed font\n");
                        exit(1);
                  }
            }
                Tk_GetFontMetrics(fs_, &fm_);
            if (sitebox_.mono()) {
                  fc_ = sitebox_.getcolor("black", "black");
                  bc_ = sitebox_.getcolor("white", "white");
            } else {
                  fc_ = sitebox_.getcolor(tcl.attr("foreground"),
                                    "black");
                  bc_ = sitebox_.getcolor(tcl.attr("background"),
                                    "white");
            }
//          ac_ = sitebox_.getcolor(tcl.attr("highlightColor"), "white");
            ac_ = sitebox_.getcolor("gray95", "white");
            dc_ =  sitebox_.getcolor(tcl.attr("disabledColor"), "gray");

            copy_gc_ = sitebox_.lookup_gc(0, 0, 0);
      }
      descent_ = fm_.descent;
      ascent_ = fm_.ascent;
      major_ = ascent_;
      minor_ = major_ / 2;
}

Site::~Site()
{
}

int Site::command(int argc, const char*const* argv)
{
      Tcl& tcl = Tcl::instance();
      if (argc == 2) {
            if (strcmp(argv[1], "text") == 0) {
                  tcl.result(text_);
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "mute") == 0) {
                  tcl.result(mute_ ? "1" : "0");
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "rank") == 0) {
                  sprintf(tcl.buffer(), "%d", rank_);
                  tcl.result(tcl.buffer());
                  return (TCL_OK);
            }
      } else if (argc == 3) {
            if (strcmp(argv[1], "highlight") == 0) {
                  if (atoi(argv[2]))
                        state_ |= SF_HIGHLIGHT;
                  else
                        state_ &=~ SF_HIGHLIGHT;
                  draw();
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "disable") == 0) {
                  if (atoi(argv[2]))
                        state_ |= SF_DISABLE;
                  else
                        state_ &=~ SF_DISABLE;
                  draw();
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "tag") == 0) {
                  tag(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "text") == 0) {
                  text(argv[2]);
                  sitebox_.change_name();
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "mute") == 0) {
                  mute_ = atoi(argv[2]);
                  draw();
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "rank") == 0) {
                  rank_ = atoi(argv[2]);
                  draw();
                  return (TCL_OK);
            }
      }
      return (TclObject::command(argc, argv));
}

/*
 * Must be called after first site created.
 */
inline int Site::textwidth(const char* s)
{
      return (Tk_TextWidth(fs_, s, strlen(s)));
}

inline void Site::square(Display* dpy, Drawable window, GC gc,
                   int x, int y, int side) const
{
      int r = side >> 1;
      side = (r << 1) + 1;
      XFillRectangle(dpy, window, gc, x - r, y - r, side, side);
}

void Site::draw_checkbox(Display* dpy, Drawable window, GC gc) const
{
      int cy = (height_ - 1) / 2;
      int cx = 2 + cy;
      
#ifdef notdef
      cx += x_;
      cy += y_;
#endif
      /*
       * Draw the rank indicator.
       */
      if (rank_ < 3) {
            int side = minor_;
            switch (rank_) {
            case 0:
                  square(dpy, window, gc, cx, cy, side);
                  break;
            case 2:
                  side -= 2;
                  /* fall through */
            case 1:
                  square(dpy, window,
                         (state_ >= 2) ? gc : fg_[state_ + 2],
                         cx, cy, side);
                  break;
            }
      }
      /*
       * Draw the mute button.
       */
      const int r = major_ >> 1;
      const int s = r << 1;
      XDrawRectangle(dpy, window, gc, cx - r, cy - r, s, s);
      if (mute_) {
            /*
             * Draw an `X' through the mute button.
             */
            XDrawLine(dpy, window, gc, cx - r, cy + r, cx + r, cy - r);
            XDrawLine(dpy, window, gc, cx - r, cy - r, cx + r, cy + r);
      }
}

void Site::draw() const
{
      if (width_ == 0)
            return;

      Display* dpy = Tk_Display(tk_);
      Drawable d = pixmap_;
      if (d == 0)
            return;
      GC gc = fg_[state_];

      XFillRectangle(dpy, d, bg_[state_], 0, 0, width_, height_);
      draw_checkbox(dpy, d, gc);
      XDrawString(dpy, d, gc, text_x_, text_y_, text_, textlen_);
      XDrawLine(dpy, d, fg_[0], 0, height_ - 1, width_ - 1, height_ - 1);
      XCopyArea(dpy, d, Tk_WindowId(tk_), copy_gc_,
              0, 0, width_, height_, x_, y_);
}

void Site::text(const char* newtext)
{
      delete text_;
      textlen_ = strlen(newtext);
      text_ = new char[textlen_ + 1];
      strcpy(text_, newtext);
      draw();
}

void Site::tag(const char* s)
{
      delete tag_;
      tag_ = new char[strlen(s) + 1];
      strcpy(tag_, s);
}

int Site::height()
{
      /*
       * Make sure height allocation is even.  So less one
       * pixel for bottom border is odd, which gives us a 
       * center line.
       */
      int h = ascent_ + descent_ + 2 * vpad;
      h += h & 1;
      return (h);
}

/*
 * XXX Allocate a gc without going through tk.  We cannot use
 * TkWidget::lookup_gc because it allocates the gc using the
 * on-screen window and we need to allocate it using the pixmap
 * to workaround a bug in the Parallax X server.
 */
GC Site::raw_gc(Font fid, XColor* fg, XColor* bg, Drawable d) const
{
      XGCValues v;
      u_long mask;
      sitebox_.set_gcv(v, mask, fid, fg, bg);
#ifdef PARALLAX_BUG
      return (XCreateGC(Tk_Display(tk_), d, mask, &v));
#else
      return (Tk_GetGC(tk_, mask, &v));
#endif
}

void Site::free_gc(GC gc) const
{
#ifdef PARALLAX_BUG
      XFreeGC(Tk_Display(tk_), gc);
#else
      Tk_FreeGC(Tk_Display(tk_), gc);
#endif
}

void Site::place(int x, int y, int w, int h)
{
      width_ = w;
      height_ = h;
      x_ = x;
      y_ = y;

      text_x_ = height_ + 4;
      text_y_ = height_ - (descent_ + vpad + 1);

      if ((pixw_ < w || pixh_ < h) && w > 0 && h > 0) {
            pixw_ = w;
            pixh_ = h;
            Display* dpy = Tk_Display(tk_);
            if (pixmap_ != 0)
                  XFreePixmap(dpy, pixmap_);
            pixmap_ = XCreatePixmap(dpy, Tk_WindowId(tk_), w, h,
                              Tk_Depth(tk_));
            /*
             * Reallocate GCs on this pixmap to work around a bug
             * in the Parallax X server.
             */
            if (fg_[0] != 0) {
                  free_gc(fg_[0]);
                  free_gc(fg_[1]);
                  free_gc(fg_[2]);
                  free_gc(fg_[3]);
                  free_gc(bg_[0]);
                  free_gc(bg_[1]);
            }
            Font fid = Tk_FontId(fs_);
            fg_[0] = raw_gc(fid, fc_, bc_, pixmap_);
            fg_[2] = raw_gc(fid, dc_, bc_, pixmap_);
            bg_[0] = raw_gc(fid, bc_, bc_, pixmap_);
            if (sitebox_.mono()) {
                  fg_[1] = raw_gc(fid, bc_, fc_, pixmap_);
                  fg_[3] = raw_gc(fid, dc_, fc_, pixmap_);
                  bg_[1] = raw_gc(fid, fc_, fc_, pixmap_);
            } else {
                  fg_[1] = raw_gc(fid, fc_, ac_, pixmap_);
                  fg_[3] = raw_gc(fid, dc_, ac_, pixmap_);
                  bg_[1] = raw_gc(fid, ac_, ac_, pixmap_);
            }
            bg_[2] = bg_[0];
            bg_[3] = bg_[1];
      }
}

void SiteBox::change_name()
{
      if (sorted_) {
            need_sort_ = 1;
            idle_sched();
      }
}

void SiteBox::resize()
{
      reset();
      layout();
}

void SiteBox::layout()
{
      need_layout_ = 0;
      int n = nsites_;
      if (n <= 0) {
            place_x_ = 0;
            place_y_ = 0;
            return;
      }
      percol_ = height_ / itemht_;
      if (percol_ <= 0)
            percol_ = 1;
      ncol_ = (n + percol_ - 1) / percol_;
      if (ncol_ <= 0)
            ncol_ = 1;

      colitems_ = ncol_ * percol_;
      int w =  width_ / ncol_;
      int j = 0;
      int x = -w;
      int y = 0;
      int nc = ncol_;
      for (int i = 0; i < n; i++) {
            if (--j <= 0) {
                  j = percol_;
                  y = 0;
                  x += w;
                  if (--nc <= 0)
                        /*
                         * Absorb round off error in
                         * last column
                         */
                        w = width_ - (ncol_ - 1) * w;
            }
            Site* s = sitelist_[i];
            if (s != 0)
                  s->place(x, y, w, itemht_);

            y += itemht_;
      }
      place_x_ = x;
      place_y_ = y;
}

void SiteBox::reset()
{
      ncol_ = 1;
      colitems_ = 0;
      place_x_ = 0;
      place_y_ = 0;
}

/*
 * Place a new site in the next slot.
 * If we run out of room, do a resize
 * to force a new layout computation.
 */
void SiteBox::append(Site* s)
{
      int n = nsites_;
      if (n > colitems_) {
            if (n == 1)
                  itemht_ = s->height();
            need_layout_ = 1;
      } else {
            /*
             * Place this site below the previous one.
             */
            int w = width_ / ncol_;
            w = width_ - (ncol_ - 1) * w;
            s->place(place_x_, place_y_, w, itemht_);
            place_y_ += itemht_;
      }
      need_redraw_ = 1;
      if (sorted_)
            need_sort_ = 1;
      idle_sched();
}

void SiteBox::update()
{
      draw();
}

/*
 * This is a heavyweight redraw.  It gets called only on exposures.
 * Highlighting etc. is generally done directly by the Site objects.
 */
void SiteBox::draw()
{
      need_redraw_ = 0;
      int n = nsites_;
      if (n <= 0 || width_ == 0)
            return;

      /*
       * All sites get redrawn below and they fill in their
       * whole allocation.  So to avoid screen flicker,
       * only erase the blank areas.
       */
      if (ncol_ > 1) {
            XFillRectangle(Tk_Display(tk_), Tk_WindowId(tk_), bg_,
                         place_x_ + 1, place_y_, 
                         width_ - place_x_, height_ - place_y_);
            int h = height_ - percol_ * itemht_;
            if (h > 0)
                  XFillRectangle(Tk_Display(tk_), Tk_WindowId(tk_), bg_,
                               0, height_ - h, width_, h);
      } else {
            XFillRectangle(Tk_Display(tk_), Tk_WindowId(tk_), bg_,
                         0, place_y_, width_, height_ - place_y_);
      }

      Display* const dpy = Tk_Display(tk_);
      const Drawable window = Tk_WindowId(tk_);
      for (int i = 0; i < n; i++) {
            Site* s = sitelist_[i];
            if (s != 0)
                  s->draw();
      }
      if (ncol_ > 1 && colitems_ > n) {
            int x = (ncol_ - 1) * (width_ / ncol_);
            int h = (colitems_ - n) * itemht_;
            int bot = percol_ * itemht_;
            XDrawLine(dpy, window, fg_, x, bot - h, x, bot);
      }
}

static class SiteBoxMatcher : public Matcher {
public:
      SiteBoxMatcher() : Matcher("sitebox") {}
      TclObject* match(const char* path) {
            return (new SiteBox(path));
      }
} sitebox_matcher;
      
SiteBox::SiteBox(const char* path)
      : TkWidget(path, "Vat", 0, 0, 0)
{
      nsitelist_ = 256;
      sitelist_ = (Site**)malloc(nsitelist_ * sizeof(Site*));
      memset((char*)sitelist_, 0, nsitelist_ * sizeof(Site*));
      nsites_ = 0;
      empties_ = 0;

      ncol_ = 1;
      colitems_ = 0;
      itemht_ = 1;
      percol_ = 1;

      sorted_ = 1;
      need_sort_ = 0;

      Tcl& tcl = Tcl::instance();
      Tk_Uid fg = mono()? (char*)"black" : (char*)tcl.attr("foreground");
      Tk_Uid bg = mono()? (char*)"white" : (char*)tcl.attr("background");
      fg_ = lookup_gc(0, fg, bg);
      bg_ = lookup_gc(0, bg, bg);
}

Site* SiteBox::create()
{
      Site* s = new Site(tk_, *this);
      sitelist_[nsites_] = s;
      /* make sure table is null terminated */
      if (++nsites_ >= nsitelist_) {
            int n = nsitelist_ * 2;
            sitelist_ = (Site**)realloc(sitelist_, n * sizeof(*sitelist_));
            memset((char*)&sitelist_[nsitelist_], 0,
                  nsitelist_ * sizeof(*sitelist_));
            nsitelist_ = n;
      }
      return (s);
}

Site* SiteBox::remove(const char* n)
{
      Site* s;
      for (Site **sp = sitelist_; (s = *sp) != 0; ++sp) {
            if (strcmp(s->name(), n) == 0) {
                  --nsites_;
                  Site* target = s;
                  for (;;) {
                        s = sp[1];
                        if ((*sp++ = s) == 0)
                              return (target);
                  }
            }
      }
      return (0);
}

static int
compareSite(const void* p1, const void* p2)
{
      Site* s1 = *(Site**)p1;
      Site* s2 = *(Site**)p2;

      const char* n1 = s1->text();
      while (*n1 && isascii(*n1) && !isalpha(*n1))
            ++n1;
      const char* n2 = s2->text();
      while (*n2 && isascii(*n2) && !isalpha(*n2))
            ++n2;
      return (strcasecmp(n1, n2));
}

void SiteBox::sort_sites()
{
      register int n = nsites_;
      Site** sp = sitelist_;
      qsort(sp, n, sizeof(*sp),
            (int(*)(const void*, const void*))compareSite);
      need_sort_ = 0;
}

void SiteBox::idle_callback()
{
      if (need_sort_) {
            sort_sites();
            need_layout_ = 1;
      }
      if (need_layout_) {
            reset();
            layout();
            need_redraw_ = 1;
      }
      if (need_redraw_)
            draw();
}

int SiteBox::command(int argc, const char*const* argv)
{
      Tcl& tcl = Tcl::instance();
      register int i;
      if (argc == 2) {
            if (strcmp(argv[1], "create") == 0) {
                  Site* s = create();
                  append(s);
                  tcl.result(s->name());
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "sites") == 0) {
                  sprintf(tcl.buffer(), "%d", nsites_);
                  tcl.result(tcl.buffer());
                  return (TCL_OK);
            } 
            if (strcmp(argv[1], "sort") == 0) {
                  need_sort_ = 1;
                  idle_sched();
                  return (TCL_OK);
            }
      } else if (argc == 3) {
            if (strcmp(argv[1], "remove") == 0) {
                  Site* s = remove(argv[2]);
                  delete s;
                  need_layout_ = 1;
                  idle_sched();
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "keep-sorted") == 0) {
                  sorted_ = atoi(argv[2]);
                  if (sorted_) {
                        need_sort_ = 1;
                        idle_sched();
                  }
                  return (TCL_OK);
            }
      } else if (argc == 4) {
            if (strcmp(argv[1], "which") == 0) {
                  int x, y, h, v, dw;
                  x = atoi(argv[2]);
                  y = atoi(argv[3]);
                  v = y / itemht_;
                  dw = width_ / ncol_;
                  h = x / dw;
                  i = h * percol_ + v;
                  if (i >= 0 && i < nsites_) {
                        const char* s = sitelist_[i]->tag();
                        if (s != 0)
                              tcl.result(s);
                  }
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "over-button") == 0) {
                  int x, y, h, v, dw, cb = 0;
                  x = atoi(argv[2]);
                  y = atoi(argv[3]);
                  v = y / itemht_;
                  dw = width_ / ncol_;
                  h = x / dw;
                  i = h * percol_ + v;
                  if (0 <= i && i < nsites_ && x - h * dw < itemht_)
                        cb = 1;
                  sprintf(tcl.buffer(), "%d", cb);
                  tcl.result(tcl.buffer());
                  return (TCL_OK);
            }
      }
      return (TclObject::command(argc, argv));
}

Generated by  Doxygen 1.6.0   Back to index