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

vbi-gui.c

/*
 * vbi-gui  --  motif videotext browser
 *
 *   (c) 2002 Gerd Knorr <kraxel@bytesex.org>
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <iconv.h>
#include <langinfo.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/keysym.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/RowColumn.h>
#include <Xm/CascadeB.h>
#include <Xm/PushB.h>
#include <Xm/DrawingA.h>
#include <Xm/Protocols.h>
#include <Xm/Separator.h>
#include <Xm/SelectioB.h>
#include <Xm/TransferP.h>
#include <Xm/DragIcon.h>
#include <Xm/FileSB.h>

#include "RegEdit.h"
#include "atoms.h"
#include "list.h"
#include "vbi-data.h"
#include "vbi-x11.h"
#include "vbi-gui.h"

#include "channel.h"

static int tt_debug = 1;
static int tt_windows = 0;

struct vbi_selection {
    struct list_head  list;
    Atom              atom;
    struct vbi_page   pg;
    struct vbi_rect   rect;
    Pixmap            pix;
};

static void vbi_new_cb(Widget, XtPointer, XtPointer);
static void vbi_goto_cb(Widget, XtPointer, XtPointer);
static void vbi_subpage_menu(struct vbi_window *vw);
static void selection_pri(struct vbi_window *vw);
static void selection_dnd_start(struct vbi_window *vw, XEvent *event);

/* --------------------------------------------------------------------- */

static void
vbi_fix_head(struct vbi_window *vw, struct vbi_char *ch)
{
    int showno,showsub,red,i;

    showno  = vw->pg.pgno;
    showsub = vw->pg.subno;
    red     = 0;
    if (0 == showno) {
      showno  = vw->pgno;
      showsub = 0;
      red     = 1;
    }
    if (vw->newpage) {
      showno  = vw->newpage;
      showsub = 0;
      red     = 1;
    }

    for (i = 1; i <= 6; i++)
      ch[i].unicode = ' ';
    if (showno >= 0x100)
      ch[1].unicode = '0' + ((showno >> 8) & 0xf);
    if (showno >= 0x10)
      ch[2].unicode = '0' + ((showno >> 4) & 0xf);
    if (showno >= 0x1)
      ch[3].unicode = '0' + ((showno >> 0) & 0xf);
    if (showsub) {
      ch[4].unicode = '/';
      ch[5].unicode = '0' + ((showsub >> 4) & 0xf);
      ch[6].unicode = '0' + ((showsub >> 0) & 0xf);
    }
    if (red) {
      ch[1].foreground = VBI_RED;
      ch[2].foreground = VBI_RED;
      ch[3].foreground = VBI_RED;
    }
}

static void
vbi_check_rectangle(struct vbi_rect *rect)
{
    int h;

    if (rect->x1 > rect->x2)
      h = rect->x1, rect->x1 = rect->x2, rect->x2 = h;
    if (rect->y1 > rect->y2)
      h = rect->y1, rect->y1 = rect->y2, rect->y2 = h;
    
    if (rect->x1 < 0) rect->x1 = 0;
    if (rect->x2 < 0) rect->x2 = 0;
    if (rect->y1 < 0) rect->y1 = 0;
    if (rect->y2 < 0) rect->y2 = 0;

    if (rect->x1 > 41) rect->x1 = 41;
    if (rect->x2 > 41) rect->x2 = 41;
    if (rect->y1 > 25) rect->y1 = 25;
    if (rect->y2 > 25) rect->y2 = 25;
}

static void
vbi_mark_rectangle(struct vbi_window *vw)
{
    struct vbi_rect rect;
    XGCValues values;
    int x,y,w,h;

    rect = vw->s;
    vbi_check_rectangle(&rect);
    x = vw->w * (rect.x1);
    w = vw->w * (rect.x2 - rect.x1);
    y = vw->h * (rect.y1);
    h = vw->h * (rect.y2 - rect.y1);
    values.function   = GXxor;
    values.foreground = ~0;
    XChangeGC(XtDisplay(vw->tt),vw->gc,GCFunction|GCForeground,&values);
    XFillRectangle(XtDisplay(vw->tt),XtWindow(vw->tt),vw->gc,
               x,y,w,h);
}

static void
vbi_render_page(struct vbi_window *vw)
{
    struct vbi_char *ch;
    int y;

    vbi_fix_head(vw,vw->pg.text);
    for (y = 0; y < 25; y++) {
      ch = vw->pg.text + 41*y;
      vbi_render_line(vw,XtWindow(vw->tt),ch,y,0,0,41);
    }
    if ((vw->s.x1 || vw->s.x2) &&
      (vw->s.y1 || vw->s.y2))
      vbi_mark_rectangle(vw);
}

static void
vbi_render_head(struct vbi_window *vw, int pgno, int subno)
{
    vbi_page pg;
    
    memset(&pg,0,sizeof(pg));
    vbi_fetch_vt_page(vw->vbi->dec,&pg,pgno,subno,
                  VBI_WST_LEVEL_1p5,1,0);
    vbi_fix_head(vw,pg.text);
    vbi_render_line(vw,XtWindow(vw->tt),pg.text,0,0,0,41);
}

static void
vbi_newdata(struct vbi_event *ev, void *user)
{
    struct vbi_window *vw = user;
    
    switch (ev->type) {
    case VBI_EVENT_TTX_PAGE:
      if (vw->pgno  == ev->ev.ttx_page.pgno) {
          if (vw->subno == ev->ev.ttx_page.subno ||
            vw->subno == VBI_ANY_SUBNO) {
            vbi_fetch_vt_page(vw->vbi->dec,&vw->pg,vw->pgno,vw->subno,
                          VBI_WST_LEVEL_1p5,25,1);
            vbi_render_page(vw);
          }
          vbi_subpage_menu(vw);
      } else {
          vbi_render_head(vw,
                      ev->ev.ttx_page.pgno,
                      ev->ev.ttx_page.subno);
      }
      break;
    case VBI_EVENT_NETWORK:
      XtVaSetValues(vw->shell,XtNtitle,ev->ev.network.name,NULL);
      break;
    }
}

/* --------------------------------------------------------------------- */
/* GUI handling                                                          */

static void
vbi_subpage_menu(struct vbi_window *vw)
{
    WidgetList children,list;
    Cardinal nchildren;
    Widget push;
    vbi_page pg;
    char page[8];
    unsigned int i;

    /* delete children */
    XtVaGetValues(vw->submenu,XtNchildren,&children,
              XtNnumChildren,&nchildren,NULL);
    if (0 != nchildren) {
      list = malloc(sizeof(Widget*)*nchildren);
      memcpy(list,children,sizeof(Widget*)*nchildren);
      for (i = 0; i < nchildren; i++)
          XtDestroyWidget(list[i]);
      free(list);
    }

    /* rebuild menu */
    push = XtVaCreateManagedWidget("s00",xmPushButtonWidgetClass,
                           vw->submenu,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_goto_cb,vw);
    XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,
                      vw->submenu,NULL);
    if (vw->pg.pgno && vw->pg.subno) {
      XtVaSetValues(vw->subbtn,XtNsensitive,True,NULL);
      for (i = 0; i < VBI_MAX_SUBPAGES; i++) {
          if (!vbi_fetch_vt_page(vw->vbi->dec,&pg,vw->pg.pgno,i,
                           VBI_WST_LEVEL_1,0,0))
            continue;
          sprintf(page,"s%02x",i);
          push = XtVaCreateManagedWidget(page,xmPushButtonWidgetClass,
                                 vw->submenu,NULL);
          XtAddCallback(push,XmNactivateCallback,vbi_goto_cb,vw);
      }
    } else {
      XtVaSetValues(vw->subbtn,XtNsensitive,False,NULL);
    }
}

static void
vbi_setpage(struct vbi_window *vw, int pgno, int subno)
{
    vw->pgno = pgno;
    vw->subno = subno;
    vw->newpage = 0;
    memset(&vw->pg,0,sizeof(struct vbi_page));
    vbi_fetch_vt_page(vw->vbi->dec,&vw->pg,vw->pgno,vw->subno,
                  VBI_WST_LEVEL_1p5,25,1);
    if (XtWindow(vw->tt))
      vbi_render_page(vw);
    vbi_subpage_menu(vw);
}

static void
vbi_expose_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    XmDrawingAreaCallbackStruct *cd = call_data;
    struct vbi_window *vw = clientdata;

    if (cd->event->xexpose.count > 0)
      return;
    vbi_render_page(vw);
}

static void
vbi_destroy_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;
    
    vbi_event_handler_unregister(vw->vbi->dec,vbi_newdata,vw);
    vbi_render_free_font(widget,vw);
    XFreeGC(XtDisplay(widget),vw->gc);
    free(vw);
    tt_windows--;
    if (0 == tt_windows)
      exit(0);
}

static void
vbi_close_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;
    XtDestroyWidget(vw->shell);
}

static void
vbi_new_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;
    Widget shell;

    shell = XtVaAppCreateShell("mtt","mtt",applicationShellWidgetClass,
                         XtDisplay(widget),NULL);
    vbi_create_widgets(shell,vw->vbi);
    XtRealizeWidget(shell);
}

static void
vbi_font_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;
    char *name = XtName(widget);
    vbi_render_set_font(widget, vw, name);
    XtVaSetValues(vw->tt, XmNwidth,vw->w*41, XmNheight,vw->h*25, NULL);
    XClearWindow(XtDisplay(vw->tt),XtWindow(vw->tt));
}

static void
vbi_goto_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;
    int pgno,subno;
    char *name;

    pgno  = vw->pg.pgno;
    subno = vw->pg.subno;
    name = XtName(widget);
    if (0 == strcmp(name,"prev")) {
      pgno  = vbi_calc_page(vw->pgno,-1);
      subno = VBI_ANY_SUBNO;
    } else if (0 == strcmp(name,"next")) {
      pgno  = vbi_calc_page(vw->pgno,+1);
      subno = VBI_ANY_SUBNO;
    } else if (1 == sscanf(name,"s%x",&subno)) {
      /* nothing */
    } else {
      sscanf(name,"%x",&pgno);
      subno = VBI_ANY_SUBNO;
    }
    if (0 == subno)
      subno = VBI_ANY_SUBNO;
    vbi_setpage(vw,pgno,subno);
}

static int
vbi_findpage(struct vbi_page *pg, int px, int py)
{
    int newpage = 0;
    int x;

    if (py == 24) {
      /* navigation line */
      int i = (pg->text[py*41+px].foreground & 7) -1;
      if (i >= 6)
          i = 0;
      newpage = pg->nav_link[i].pgno;
    } else if (px <= 40 && py <= 23) {
      if (pg->text[py*41+px].unicode >= '0' &&
          pg->text[py*41+px].unicode <= '9') {
          /* look for a 3-digit string ... */
          x = px; newpage = 0;
          while (pg->text[py*41+x].unicode >= '0' &&
               pg->text[py*41+x].unicode <= '9' &&
               x > 0) {
            x--;
          }
          x++;
          while (pg->text[py*41+x].unicode >= '0' &&
               pg->text[py*41+x].unicode <= '9' &&
               x < 40) {
            newpage = newpage*16 + pg->text[py*41+x].unicode - '0';
            x++;
          }

      } else if (pg->text[py*41+px].unicode == '>') {
          /* next page */
          newpage = vbi_calc_page(pg->pgno,+1);

      } else if (pg->text[py*41+px].unicode == '<') {
          /* prev page */
          newpage = vbi_calc_page(pg->pgno,-1);
      }
    }
    
    if (newpage < 0x100 || newpage >= 0x999)
      return 0;
    return newpage;
}

static void
vbi_kbd_eh(Widget widget, XtPointer clientdata, XEvent *event, Boolean *cont)
{
    struct vbi_window *vw = clientdata;
    KeySym sym;
    int digit,subno;

    switch (event->type) {
    case KeyPress:
      sym = XKeycodeToKeysym(XtDisplay(widget),event->xkey.keycode,0);
      digit = -1;
      switch (sym) {
      case XK_0:
      case XK_KP_Insert:
          digit = 0;
          break;
      case XK_1:
      case XK_KP_End:
          digit = 1;
          break;
      case XK_2:
      case XK_KP_Down:
          digit = 2;
          break;
      case XK_3:
      case XK_KP_Next:
          digit = 3;
          break;
      case XK_4:
      case XK_KP_Left:
          digit = 4;
          break;
      case XK_5:
      case XK_KP_Begin:
          digit = 5;
          break;
      case XK_6:
      case XK_KP_Right:
          digit = 6;
          break;
      case XK_7:
      case XK_KP_Home:
          digit = 7;
          break;
      case XK_8:
      case XK_KP_Up:
          digit = 8;
          break;
      case XK_9:
      case XK_KP_Prior:
          digit = 9;
          break;
      case XK_space:
      case XK_l:
          vbi_setpage(vw,vbi_calc_page(vw->pgno,+1),VBI_ANY_SUBNO);
          break;
      case XK_BackSpace:
      case XK_h:
          vbi_setpage(vw,vbi_calc_page(vw->pgno,-1),VBI_ANY_SUBNO);
          break;
      case XK_k:
          subno = (vw->subno != VBI_ANY_SUBNO) ? vw->subno : vw->pg.subno;
          subno = vbi_calc_subpage(vw->vbi->dec,vw->pgno,subno,+1);
          vbi_setpage(vw,vw->pgno,subno);
          break;
      case XK_j:
          subno = (vw->subno != VBI_ANY_SUBNO) ? vw->subno : vw->pg.subno;
          subno = vbi_calc_subpage(vw->vbi->dec,vw->pgno,subno,-1);
          vbi_setpage(vw,vw->pgno,subno);
          break;
      }
      if (-1 != digit) {
          vw->newpage *= 16;
          vw->newpage += digit;
          if (vw->newpage >= 0x100)
            vbi_setpage(vw,vw->newpage,VBI_ANY_SUBNO);
      }
      break;
    }
}

static void
vbi_mouse_eh(Widget widget, XtPointer clientdata, XEvent *event, Boolean *cont)
{
    struct vbi_window *vw = clientdata;
    int px,py,newpage;

    switch (event->type) {
    case ButtonPress:
      switch (event->xbutton.button) {
      case 1: /* left mouse button */
          px = event->xbutton.x / vw->w;
          py = event->xbutton.y / vw->h;
          vw->s.x1 = vw->s.x2 = px;
          vw->s.y1 = vw->s.y2 = py;
          vw->down = event->xbutton.time;
          break;
      case 2: /* middle button */
          selection_dnd_start(vw,event);
          break;
      }
      break;
    case MotionNotify:
      if (event->xmotion.state & Button1Mask) {
          vw->s.x2 = event->xbutton.x / vw->w +1;
          vw->s.y2 = event->xbutton.y / vw->h +1;
          vbi_render_page(vw);
      }
      break;
    case ButtonRelease:
      switch (event->xbutton.button) {
      case 1: /* left mouse button */
          px = event->xbutton.x / vw->w;
          py = event->xbutton.y / vw->h;
          if (abs(vw->s.x1 - px) < 2  &&  abs(vw->s.y1 - py) < 2  &&
            event->xbutton.time - vw->down < 500) {
            /* mouse click */
            vw->s.x1 = vw->s.x2 = 0;
            vw->s.y1 = vw->s.y2 = 0;
            newpage = vbi_findpage(&vw->pg,px,py);
            if (0 != newpage)
                vbi_setpage(vw,newpage,VBI_ANY_SUBNO);
            else
                vbi_render_page(vw);
          } else {
            /* marked region */
            vw->s.x2 = px +1;
            vw->s.y2 = py +1;
            vbi_render_page(vw);
            selection_pri(vw);
          }
          break;
      case 4: /* wheel up */
          newpage = vbi_calc_page(vw->pgno,-1);
          vbi_setpage(vw,newpage,VBI_ANY_SUBNO);
          break;
      case 5: /* wheel down */
          newpage = vbi_calc_page(vw->pgno,+1);
          vbi_setpage(vw,newpage,VBI_ANY_SUBNO);
          break;
      }
      break;
    }
}

/* --------------------------------------------------------------------- */
/* export data                                                           */

static void
export_do_save_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    static struct vbi_rect rect = {
      x1:  0,
      x2: 41,
      y1:  0,
      y2: 25,
    };
    XmFileSelectionBoxCallbackStruct *cb = call_data;
    struct vbi_window *vw = clientdata;
    char *filename, *data;
    int len,fh;

    if (cb->reason == XmCR_OK) {
      filename = XmStringUnparse(cb->value,NULL,
                           XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
                           NULL,0,0);
      data = malloc(25*41*8);
      len = vbi_export_txt(data,vw->charset,25*41*8,&vw->pg,&rect,
                       VBI_NOCOLOR);
      fh = open(filename,O_WRONLY | O_CREAT, 0666);
      if (-1 == fh) {
          fprintf(stderr,"open %s: %s\n",filename,strerror(errno));
      } else {
          ftruncate(fh,0);
          write(fh,data,len);
          close(fh);
      }
      free(data);
    }
    XtUnmanageChild(widget);
}

static void
export_charset_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;
    vw->charset = XtName(widget);
}

static void
export_save_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;
    Widget help,text,menu,option,push;
    Arg args[2];

    if (NULL == vw->savebox) {
        vw->savebox = XmCreateFileSelectionDialog(vw->shell,"save",NULL,0);
        help = XmFileSelectionBoxGetChild(vw->savebox,XmDIALOG_HELP_BUTTON);
        text = XmFileSelectionBoxGetChild(vw->savebox,XmDIALOG_TEXT);
        XtUnmanageChild(help);

        menu = XmCreatePulldownMenu(vw->savebox,"formatM",NULL,0);
        XtSetArg(args[0],XmNsubMenuId,menu);
        option = XmCreateOptionMenu(vw->savebox,"format",args,1);
        XtManageChild(option);

      vw->charset = nl_langinfo(CODESET);
      push = XtVaCreateManagedWidget(vw->charset,xmPushButtonWidgetClass,
                               menu, NULL);
      XtAddCallback(push,XmNactivateCallback,export_charset_cb,vw);
      if (0 != strcasecmp(vw->charset,"UTF-8")) {
          push = XtVaCreateManagedWidget("UTF-8",
                                 xmPushButtonWidgetClass,menu,NULL);
          XtAddCallback(push,XmNactivateCallback,export_charset_cb,vw);
      }
      if (0 != strcasecmp(vw->charset,"ISO-8859-1")) {
          push = XtVaCreateManagedWidget("ISO-8859-1",
                                 xmPushButtonWidgetClass,menu,NULL);
          XtAddCallback(push,XmNactivateCallback,export_charset_cb,vw);
      }
      if (0 != strcasecmp(vw->charset,"US-ASCII")) {
          push = XtVaCreateManagedWidget("US-ASCII",
                                 xmPushButtonWidgetClass,menu,NULL);
          XtAddCallback(push,XmNactivateCallback,export_charset_cb,vw);
      }
      XtAddCallback(vw->savebox,XmNokCallback,export_do_save_cb,vw);
        XtAddCallback(vw->savebox,XmNcancelCallback,export_do_save_cb,vw);
    }
    XtManageChild(vw->savebox);
}

/* --------------------------------------------------------------------- */
/* selection handling (cut+paste, drag'n'drop)                           */

static struct vbi_selection*
selection_find(struct vbi_window *vw, Atom selection)
{
    struct list_head      *item;
    struct vbi_selection  *sel;
    
    list_for_each(item,&vw->selections) {
      sel = list_entry(item, struct vbi_selection, list);
      if (sel->atom == selection)
          return sel;
    }
    return NULL;
}

static void
selection_fini(struct vbi_window *vw, Atom selection)
{
    struct vbi_selection  *sel;

    sel = selection_find(vw,selection);
    if (NULL == sel)
      return;
    if (sel->pix)
      XFreePixmap(XtDisplay(vw->tt),sel->pix);

    list_del(&sel->list);
    free(sel);
}

static void
selection_init(struct vbi_window *vw, Atom selection)
{
    struct vbi_selection  *sel;

    selection_fini(vw,selection);
    sel = malloc(sizeof(*sel));
    memset(sel,0,sizeof(*sel));
    list_add_tail(&sel->list,&vw->selections);
    sel->atom = selection;
    sel->pg   = vw->pg;
    sel->rect = vw->s;
    vbi_check_rectangle(&sel->rect);
    if (0 == sel->rect.x2  &&  0 == sel->rect.y2) {
      sel->rect.x2 = 41;
      sel->rect.y2 = 25;
    }
}

static Atom selection_unique_atom(struct vbi_window *vw)
{
    char id_name[32];
    Atom id;
    int i;

    for (i = 0;; i++) {
      sprintf(id_name,"_VBI_DATA_%lX_%d",XtWindow(vw->tt),i);
      id = XInternAtom(XtDisplay(vw->tt),id_name,False);
      if (NULL == selection_find(vw,id))
          break;
    }
    return id;
}

static void
selection_convert_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    XmConvertCallbackStruct *ccs = call_data;
    struct vbi_window *vw = clientdata;
    struct vbi_selection *sel;
    Display *dpy = XtDisplay(widget);
    unsigned long *ldata;
    unsigned char *cdata;
    Atom *targs;
    int n;

    if (tt_debug) {
      char *y = !ccs->type      ? NULL : XGetAtomName(dpy,ccs->type);
      char *t = !ccs->target    ? NULL : XGetAtomName(dpy,ccs->target);
      char *s = !ccs->selection ? NULL : XGetAtomName(dpy,ccs->selection);
      fprintf(stderr,"tt: target=%s type=%s selection=%s\n",t,y,s);
      if (y) XFree(y);
      if (t) XFree(t);
      if (s) XFree(s);
    }

    if ((ccs->target == XA_TARGETS) ||
      (ccs->target == _MOTIF_CLIPBOARD_TARGETS) ||
      (ccs->target == _MOTIF_DEFERRED_CLIPBOARD_TARGETS) ||
      (ccs->target == _MOTIF_EXPORT_TARGETS)) {
      /* query convert capabilities */
      n = 0;
      targs = (Atom*)XtMalloc(sizeof(Atom)*32);
      if (ccs->target != _MOTIF_CLIPBOARD_TARGETS) {
          targs[n++]  = XA_TARGETS;
          targs[n++]  = XA_PIXMAP;
          targs[n++]  = XA_COLORMAP;
          targs[n++]  = XA_FOREGROUND;
          targs[n++]  = XA_BACKGROUND;
          targs[n++]  = MIME_TEXT_UTF_8;
          targs[n++]  = XA_UTF8_STRING;
          targs[n++]  = MIME_TEXT_ISO8859_1;
          targs[n++]  = XA_STRING;
      }
      if (ccs->target == _MOTIF_EXPORT_TARGETS) {
          /* save away drag'n'drop data */
          selection_init(vw,ccs->selection);
      }
      ccs->value  = targs;
      ccs->length = n;
      ccs->type   = XA_ATOM;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;
      return;

    } else if (ccs->target == _MOTIF_SNAPSHOT) {
      /* save away clipboard data */
      n = 0;
      targs = (Atom*)XtMalloc(sizeof(Atom));
      targs[n++] = selection_unique_atom(vw);
      selection_init(vw,targs[0]);
      ccs->value  = targs;
      ccs->length = n;
      ccs->type   = XA_ATOM;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;
      return;
    }

    /* find data */
    sel = selection_find(vw,ccs->selection);
    if (NULL == sel) {
      /* shouldn't happen */
      fprintf(stderr,"tt: oops: selection data not found\n");
      ccs->status = XmCONVERT_REFUSE;
      return;
    }

    if (ccs->target == _MOTIF_LOSE_SELECTION ||
      ccs->target == XA_DONE) {
      /* cleanup */
      selection_fini(vw,ccs->selection);
      if (XA_PRIMARY == ccs->selection) {
          /* unmark selection */
          vw->s.x1 = vw->s.x2 = 0;
          vw->s.y1 = vw->s.y2 = 0;
          vbi_render_page(vw);
      }
      ccs->value  = NULL;
      ccs->length = 0;
      ccs->type   = XA_INTEGER;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;
      return;
    }
      
    /* convert data */
    if (ccs->target == XA_STRING ||
      ccs->target == MIME_TEXT_ISO8859_1) {
      cdata = XtMalloc(25*41*8);
      n = vbi_export_txt(cdata,"ISO8859-1",25*41*8,&sel->pg,&sel->rect,
                     VBI_NOCOLOR);
      ccs->value  = cdata;
      ccs->length = n;
      ccs->type   = XA_STRING;
      ccs->format = 8;
      ccs->status = XmCONVERT_DONE;

    } else if (ccs->target == XA_UTF8_STRING ||
             ccs->target == MIME_TEXT_UTF_8) {
      cdata = XtMalloc(25*41*8);
      n = vbi_export_txt(cdata,"UTF-8",25*41*8,&sel->pg,&sel->rect,
                     VBI_NOCOLOR);
      ccs->value  = cdata;
      ccs->length = n;
      ccs->type   = XA_STRING;
      ccs->format = 8;
      ccs->status = XmCONVERT_DONE;

    } else if (ccs->target == XA_BACKGROUND ||
             ccs->target == XA_FOREGROUND ||
             ccs->target == XA_COLORMAP) {
      n = 0;
      ldata = (Atom*)XtMalloc(sizeof(Atom)*8);
      if (ccs->target == XA_BACKGROUND) {
          ldata[n++] = WhitePixelOfScreen(XtScreen(widget));
          ccs->type  = XA_PIXEL;
      }
      if (ccs->target == XA_FOREGROUND) {
          ldata[n++] = BlackPixelOfScreen(XtScreen(widget));
          ccs->type  = XA_PIXEL;
      }
      if (ccs->target == XA_COLORMAP) {
          ldata[n++] = DefaultColormapOfScreen(XtScreen(widget));
          ccs->type  = XA_COLORMAP;
      }
      ccs->value  = ldata;
      ccs->length = n;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;

    } else if (ccs->target == XA_PIXMAP) {
      /* xfer pixmap */
      if (!sel->pix)
          sel->pix = vbi_export_pixmap(vw,&sel->pg,&sel->rect);
      if (tt_debug)
          fprintf(stderr,"tt: pixmap id is 0x%lx\n",sel->pix);
      ldata = (Pixmap*)XtMalloc(sizeof(Pixmap));
      ldata[0] = sel->pix;
      ccs->value  = ldata;
      ccs->length = 1;
      ccs->type   = XA_DRAWABLE;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;

    } else {
      /* shouldn't happen */
      fprintf(stderr,"tt: oops: target not found\n");
      ccs->status = XmCONVERT_REFUSE;
    }
}

static void
selection_pri(struct vbi_window *vw)
{
    if (tt_debug)
      fprintf(stderr,"tt: primary\n");

    selection_init(vw,XA_PRIMARY);
    XmePrimarySource(vw->tt,XtLastTimestampProcessed(XtDisplay(vw->tt)));
}

static void
selection_clip_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;

    if (tt_debug)
      fprintf(stderr,"tt: clipboard [copy]\n");
      
    XmeClipboardSource(vw->tt,XmCOPY,
                   XtLastTimestampProcessed(XtDisplay(vw->tt)));
}

static void
selection_dnd_done(Widget widget, XtPointer clientdata, XtPointer call_data)
{
    struct vbi_window *vw = clientdata;

    if (tt_debug)
      fprintf(stderr,"tt: dnd done\n");
    selection_fini(vw,_MOTIF_DROP);
}

static void
selection_dnd_start(struct vbi_window *vw, XEvent *event)
{
    Widget    drag;
    Arg       args[4];
    Cardinal  n=0;

    if (tt_debug)
      fprintf(stderr,"tt: dnd start\n");
    n = 0;
    XtSetArg(args[n], XmNdragOperations, XmDROP_COPY); n++;
    drag = XmeDragSource(vw->tt, NULL, event, args, n);
    XtAddCallback(drag, XmNdragDropFinishCallback, selection_dnd_done, vw);
}

/* --------------------------------------------------------------------- */

static void vbi_station_cb(Widget widget, XtPointer client, XtPointer call)
{
    struct vbi_state *vbi = client;
    char *name = XtName(widget);
    int i;

    for (i = 0; i < count; i++)
      if (0 == strcmp(channels[i]->name,name))
          break;
    if (i == count)
      return;
#if 0
    fprintf(stderr,"tune: %.3f MHz [channel %s, station %s]\n",
          channels[i]->freq / 16.0,
          channels[i]->cname,
          channels[i]->name);
#endif

#ifdef linux
#include "videodev.h"
    if (-1 == ioctl(vbi->fd,VIDIOCSFREQ,&channels[i]->freq))
      perror("ioctl VIDIOCSFREQ");
#endif
    /* FIXME: should add some BSD code once libzvbi is ported ... */
}

static void vbi_station_menu(Widget menubar, struct vbi_state *vbi)
{
    struct {
      char *name;
      Widget menu;
    } *sub = NULL;
    int subs = 0;

    Widget m,menu,push;
    XmString label;
    int i,j;

    if (0 == count)
      return;

    menu = XmCreatePulldownMenu(menubar,"stationM",NULL,0);
    XtVaCreateManagedWidget("station",xmCascadeButtonWidgetClass,menubar,
                      XmNsubMenuId,menu,NULL);

    for (i = 0; i < count; i++) {
#if 0
      if (channels[i]->key) {
          if (2 == sscanf(channels[i]->key,
                      "%15[A-Za-z0-9_]+%31[A-Za-z0-9_]",
                      ctrl,key)) {
            sprintf(accel,"%s<Key>%s",ctrl,key);
          } else {
            sprintf(accel,"<Key>%s",channels[i]->key);
          }
      } else {
          accel[0] = 0;
      }
#endif

      m = menu;
      if (0 != strcmp(channels[i]->group,"main")) {
          for (j = 0; j < subs; j++)
            if (0 == strcmp(channels[i]->group,sub[j].name))
                break;
          if (j == subs) {
            subs++;
            sub = realloc(sub, subs * sizeof(*sub));
            sub[j].name = channels[i]->group;
            sub[j].menu = XmCreatePulldownMenu(menu,
                                       channels[i]->group,
                                       NULL,0);
            XtVaCreateManagedWidget(channels[i]->group,
                              xmCascadeButtonWidgetClass, menu,
                              XmNsubMenuId, sub[j].menu,
                              NULL);
          }
          m = sub[j].menu;
      }

      label = XmStringGenerate(channels[i]->name, NULL, XmMULTIBYTE_TEXT, NULL);
      push = XtVaCreateManagedWidget(channels[i]->name,
                               xmPushButtonWidgetClass,m,
                               XmNlabelString,label,
                               NULL);
      XtAddCallback(push,XmNactivateCallback,vbi_station_cb,vbi);
      XmStringFree(label);
    }
}

static int fntcmp(const void *a, const void *b)
{
    char const * const *aa = a;
    char const * const *bb = b;

    return strcmp(*aa,*bb);
}

static void vbi_xft_font_menu(Widget menu, struct vbi_window *vw)
{
#ifdef HAVE_XFT
    FcPattern   *pattern;
    FcObjectSet *oset;
    FcFontSet   *fset;
    Widget      push;
    XmString    label;
    char        **fonts, *h;
    int         i;
    
    pattern = FcNameParse(":style=Regular:spacing=100:slant=0:weight=100");
    oset = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_SPACING, FC_SLANT,
                      FC_WEIGHT, NULL);
    fset = FcFontList(NULL, pattern, oset);
    FcPatternDestroy(pattern);
    if (fset) {
      XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
      fonts = malloc(sizeof(char*) * fset->nfont);
      for (i = 0; i < fset->nfont; i++)
          fonts[i] = FcNameUnparse (fset->fonts[i]);
      qsort(fonts,fset->nfont,sizeof(char*),fntcmp);

      for (i = 0; i < fset->nfont; i++) {
          push = XtVaCreateManagedWidget(fonts[i],xmPushButtonWidgetClass,menu,NULL);
          h = strchr(fonts[i],':');
          if (h)
            *h = 0;
          label = XmStringGenerate(fonts[i], NULL, XmMULTIBYTE_TEXT, NULL);
          XtVaSetValues(push, XmNlabelString, label, NULL);
          XmStringFree(label);
          
          XtAddCallback(push, XmNactivateCallback, vbi_font_cb, vw);
      }

      for (i = 0; i < fset->nfont; i++)
          free(fonts[i]);
      free(fonts);
    }
#endif
}

/* --------------------------------------------------------------------- */

void vbi_create_widgets(Widget shell, struct vbi_state *vbi)
{
    Widget form,menubar,tool,menu,push,tt;
    struct vbi_window *vw;
    int i;

    /* form container */
    XtVaSetValues(shell, XmNallowShellResize, True, NULL);
    form = XtVaCreateManagedWidget("form", xmFormWidgetClass, shell,
                           NULL);

    /* menu- & toolbar */
    menubar = XmCreateMenuBar(form,"bar",NULL,0);
    XtManageChild(menubar);
    tool = XtVaCreateManagedWidget("tool",xmRowColumnWidgetClass,form,NULL);

    /* main view */
    tt = XtVaCreateManagedWidget("tt", xmDrawingAreaWidgetClass,form, NULL);
    vw = vbi_render_init(shell,tt,vbi);
    XtVaSetValues(tt,XmNwidth,vw->w*41,XmNheight,vw->h*25,NULL);
    XtAddEventHandler(tt,KeyPressMask,
                  False,vbi_kbd_eh,vw);
    XtAddEventHandler(tt,ButtonPressMask|ButtonReleaseMask|Button1MotionMask,
                  False,vbi_mouse_eh,vw);
    XtAddCallback(tt,XmNexposeCallback,vbi_expose_cb,vw);
    XtAddCallback(tt,XmNdestroyCallback,vbi_destroy_cb,vw);
    XtAddCallback(tt,XmNconvertCallback,selection_convert_cb,vw);
    vbi_event_handler_register(vw->vbi->dec,~0,vbi_newdata,vw);

    /* menu -- file */
    menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0);
    XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar,
                      XmNsubMenuId,menu,NULL);
    push = XtVaCreateManagedWidget("new",xmPushButtonWidgetClass,menu,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_new_cb,vw);
    push = XtVaCreateManagedWidget("save",xmPushButtonWidgetClass,menu,NULL);
    XtAddCallback(push,XmNactivateCallback,export_save_cb,vw);
    XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
    push = XtVaCreateManagedWidget("quit",xmPushButtonWidgetClass,menu,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_close_cb,vw);

    /* menu -- edit */
    menu = XmCreatePulldownMenu(menubar,"editM",NULL,0);
    XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar,
                      XmNsubMenuId,menu,NULL);
    push = XtVaCreateManagedWidget("copy",xmPushButtonWidgetClass,menu,NULL);
    XtAddCallback(push,XmNactivateCallback,selection_clip_cb,vw);
    
    /* menu -- go (navigation) */
    menu = XmCreatePulldownMenu(menubar,"goM",NULL,0);
    XtVaCreateManagedWidget("go",xmCascadeButtonWidgetClass,menubar,
                      XmNsubMenuId,menu,NULL);
    push = XtVaCreateManagedWidget("100",xmPushButtonWidgetClass,menu,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_goto_cb,vw);
    push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,menu,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_goto_cb,vw);
    push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,menu,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_goto_cb,vw);

    /* menu -- subpage */
    vw->submenu = XmCreatePulldownMenu(menubar,"subpageM",NULL,0);
    vw->subbtn  = XtVaCreateManagedWidget("subpage",xmCascadeButtonWidgetClass,
                                menubar,
                                XmNsubMenuId,vw->submenu,NULL);

    /* menu -- stations */
    vbi_station_menu(menubar,vbi);

    /* menu -- fonts */
    menu = XmCreatePulldownMenu(menubar,"fontM",NULL,0);
    XtVaCreateManagedWidget("font",xmCascadeButtonWidgetClass,menubar,
                      XmNsubMenuId,menu,NULL);
    for (i = 0; vbi_fonts[i].label != NULL; i++) {
      push = XtVaCreateManagedWidget(vbi_fonts[i].label,
                               xmPushButtonWidgetClass,menu,NULL);
      XtAddCallback(push,XmNactivateCallback,vbi_font_cb,vw);
    }
    vbi_xft_font_menu(menu,vw);

    /* toolbar */
    push = XtVaCreateManagedWidget("100",xmPushButtonWidgetClass,tool,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_goto_cb,vw);
    XtAddEventHandler(push,KeyPressMask,False,vbi_kbd_eh,vw);
    push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,tool,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_goto_cb,vw);
    XtAddEventHandler(push,KeyPressMask,False,vbi_kbd_eh,vw);
    push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,tool,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_goto_cb,vw);
    XtAddEventHandler(push,KeyPressMask,False,vbi_kbd_eh,vw);
    XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL);
    push = XtVaCreateManagedWidget("exit",xmPushButtonWidgetClass,tool,NULL);
    XtAddCallback(push,XmNactivateCallback,vbi_close_cb,vw);
    XtAddEventHandler(push,KeyPressMask,False,vbi_kbd_eh,vw);

    /* shell stuff */
    XmdRegisterEditres(shell);
    XmAddWMProtocolCallback(shell,WM_DELETE_WINDOW,vbi_close_cb,vw);

    /* set start page */
    vbi_setpage(vw,0x100,VBI_ANY_SUBNO);
    tt_windows++;
}

Generated by  Doxygen 1.6.0   Back to index