Logo Search packages:      
Sourcecode: vertex version File versions

clipboard.c

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>

#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>

#include "guiutils.h"
#include "clipboard.h"

#ifdef MEMWATCH
# include "memwatch.h"
#endif


#include "images/icon_close_20x20.xpm"
#include "images/icon_clear_20x20.xpm"
#include "images/icon_reload_20x20.xpm"
#include "images/icon_clipboard_16x16.xpm"
#include "images/icon_clipboard_empty_16x16.xpm"
#include "images/icon_clipboard_48x48.xpm"
#include "images/icon_clipboard_empty_48x48.xpm"
#include "images/icon_owned_16x16.xpm"
#include "images/icon_unowned_16x16.xpm"


/*
 *      Clipboard data types:
 */
#define CLIPBOARD_DATA_TYPE_ASCII   0
#define CLIPBOARD_DATA_TYPE_BINARY  1


/*
 *      Clipboard browser structure:
 */
typedef struct {

      gbool initialized;
      gbool map_state;
      GdkCursor   *busy_cur,
                  *pan_cur;

      GtkWidget   *toplevel,
                  *main_vbox,
                  *menu_bar,
                  *tool_ribbon,
                  *notebook,
                  *status_bar_dock,
                  *status_bar_frame,
                        *status_content_icon_fixed,
                        *status_content_icon_pm,
                        *status_owned_icon_fixed,
                        *status_owned_icon_pm,
                        *status_size_label,
                        *status_mesg_label;

        GtkWidget       *owner_btn;     /* Button widget to own `selections'
                                         * it's never shown to the user.
                                         */
        /* Buttons on tool ribbon. */
        GtkWidget       *clear_btn;

        /* Important menu items. */
        GtkWidget       *clear_mi,
                        *monitor_changes_micheck,
                        *view_text_micheck,
                        *view_binary_micheck;

        /* Right click menu. */
        GtkWidget       *view_menu,
                        *view_clear_mi,
                        *view_refresh_mi;

        /* Containers and childs on notebook. */
        GtkWidget       *view_text_viewport,
                        *view_text_event_box,
                        *view_text_vbox,        /* Holds childs. */

                        *view_binary_viewport,
                        *view_binary_event_box,
                        *view_binary_vbox;      /* Holds childs. */

        gint notebook_cur_page;



        /* Childs in text viewport. */
        GtkWidget **view_text_widget;
        gint total_view_text_widgets;

        /* Childs in binary viewport. */
        GtkWidget **view_binary_widget;
        gint total_view_binary_widgets;

        gbool own_buffer;       /* We have (own) selection (buffer). */
        gbool waiting_response; /* TRUE when waiting for a buffer request
                                 * response.
                                 */
        gint data_type;         /* One of CLIPBOARD_DATA_TYPE_*. */

        guchar *data;           /* Local and dynamically allocated. */
        gint data_length;

        gbool monitor_changes;
        guint monitor_changes_toid;

} clipboard_browser_struct;


/* Utils (private). */
static void ClipboardBrowserSetBusy(clipboard_browser_struct *cb);
static void ClipboardBrowserSetReady(clipboard_browser_struct *cb);
static void ClipboardBrowserSetStatusMesg(
      clipboard_browser_struct *cb,
      const char *mesg
);
static void ClipboardBrowserSetStatusIcon(clipboard_browser_struct *cb);
static void ClipboardBrowserDDEClear(clipboard_browser_struct *cb);
static void ClipboardBrowserRecordWidget(
      GtkWidget ***list, gint *total, GtkWidget *w
);
                    
/* General callbacks. */
static void ClipboardBrowserClearMCB(GtkWidget *widget, gpointer data);
static void ClipboardBrowserRefreshMCB(GtkWidget *widget, gpointer data);
static gint ClipboardBrowserCloseCB(
      GtkWidget *widget, GdkEvent *event,
      clipboard_browser_struct *cb
);
static void ClipboardBrowserCloseMCB(GtkWidget *widget, gpointer data);
static void ClipboardBrowserDestroyCB(
      GtkObject *object, clipboard_browser_struct *cb
);
static gint ClipboardBrowserViewEventCB(
      GtkWidget *widget, gpointer event, clipboard_browser_struct *cb
);
static void ClipboardBrowserSwitchPageCB(
      GtkNotebook *notebook, GtkNotebookPage *page, guint page_num,
      gpointer data
);

static void ClipboardBrowserViewCheckMCB(GtkWidget *widget, gpointer data);
static gint ClipboardMonitorChangesTOCB(gpointer data);

/* Fetching callbacks. */
static void ClipboardBufferBufferRecievedCB(
      GtkWidget *widget, GtkSelectionData *selection_data,
      gpointer null_data
);

/* Putting callbacks. */
static gint ClipboardClearCB(
        GtkWidget *widget, GdkEventSelection *event,
        clipboard_browser_struct *cb
);
static gint ClipboardNotifyCB(
        GtkWidget *widget, GdkEventSelection *event,
        clipboard_browser_struct *cb
);
static void ClipboardBufferRequestCB(
        GtkWidget *widget, GtkSelectionData *selection_data,
        guint info, guint time_stamp,
        clipboard_browser_struct *cb
);

/* Fetch and put procedures (most are public). */
guint8 *ClipboardFetchBinary(int *length);
static void ClipboardBrowserFetch(clipboard_browser_struct *cb);
gint ClipboardPutBinary(const guint8 *data, gint length);

/* Clipboard browser management. */
static void ClipboardBrowserLoadCursors(clipboard_browser_struct *cb);
static void ClipboardBrowserCreateMenuBar(
        clipboard_browser_struct *cb, GtkWidget *parent
);
static void ClipboardBrowserCreateToolRibbon(
        clipboard_browser_struct *cb, GtkWidget *parent
);
gint ClipboardBrowserInit(void);
void ClipboardBrowserReset(
        clipboard_browser_struct *cb, gbool need_unmap
);
void ClipboardBrowserUpdateMenus(clipboard_browser_struct *cb);
void ClipboardBrowserMap(void);
void ClipboardBrowserUnmap(void);
void ClipboardBrowserShutdown(void);


#define CLIPBOARD_DEF_WIDTH         640
#define CLIPBOARD_DEF_HEIGHT        480

#define CLIPBOARD_STATUSBAR_HEIGHT  26


#ifndef GDK_BUTTON1
# define GDK_BUTTON1    1
#endif

#ifndef GDK_BUTTON2
# define GDK_BUTTON2    2
#endif

#ifndef GDK_BUTTON3
# define GDK_BUTTON3    3
#endif


static clipboard_browser_struct clipboard_browser;



/*
 *    Marks clipboard browser as busy.
 */
static void ClipboardBrowserSetBusy(clipboard_browser_struct *cb)
{
        GtkWidget *w;
        GdkCursor *cur;

        if(cb == NULL)
            return;
      if(!cb->initialized)
          return;

        w = cb->toplevel;
        if(w == NULL)
            return;  

        cur = cb->busy_cur;
        if(cur == NULL)   
            return;

        if(GTK_WIDGET_NO_WINDOW(w))
            return;

        gdk_window_set_cursor(w->window, cur);
        gdk_flush();
}

/*
 *    Marks clipboard browser as ready.
 */
static void ClipboardBrowserSetReady(clipboard_browser_struct *cb)
{
        GtkWidget *w;

        if(cb == NULL)
            return;
        if(!cb->initialized)
            return;

        w = cb->toplevel;
        if(w == NULL)
            return;

        if(GTK_WIDGET_NO_WINDOW(w))
            return;

        gdk_window_set_cursor(w->window, NULL);
        gdk_flush();  
}

/*
 *    Updates the status message.
 */
static void ClipboardBrowserSetStatusMesg(
        clipboard_browser_struct *cb,
        const char *mesg
)
{
      GtkWidget *w;

      if(cb == NULL)
          return;

      if(!cb->initialized)
          return;

      w = cb->status_mesg_label;
      if(w != NULL)
      {
          gtk_label_set_text(
            GTK_LABEL(w),
            (mesg == NULL) ? "" : mesg
          );
      }
}

/*
 *    Updates the clipboard browser's status bar icon.
 */
static void ClipboardBrowserSetStatusIcon(clipboard_browser_struct *cb)
{
        GtkWidget *w, *parent, *pixmap;
        GdkWindow *window = NULL;
        GdkPixmap *gdk_pixmap;
        GdkBitmap *mask;
        GtkStyle *style;   
        u_int8_t **icon_data = NULL;
        gint width, height;


      if(cb == NULL)
          return;

        /* Get window and style of toplevel. */
        w = cb->toplevel;
        if(w != NULL)
        {
            if(!GTK_WIDGET_NO_WINDOW(w))
          {
                window = w->window;
            if(cb->data == NULL)
                GUISetWMIcon(window, (u_int8_t **)icon_clipboard_empty_48x48_xpm);
            else
                GUISetWMIcon(window, (u_int8_t **)icon_clipboard_48x48_xpm);
          }
        }
        style = gtk_widget_get_style(w);
        if((style == NULL) || (window == NULL))
            return;


      /* Begin recreating clipboard content icon. */
      parent = cb->status_content_icon_fixed;
      if(parent != NULL)
      {
          /* Set icon_data depending on if the local `selection'
           * buffer contains data.
           */
          if(cb->data == NULL)
            icon_data = (u_int8_t **)icon_clipboard_empty_16x16_xpm;
          else
            icon_data = (u_int8_t **)icon_clipboard_16x16_xpm;

          /* Create icon. */
          gdk_pixmap = gdk_pixmap_create_from_xpm_d(
            window, &mask,
            &style->bg[GTK_STATE_NORMAL],
            (gchar **)icon_data
          );
          pixmap = gtk_pixmap_new(gdk_pixmap, mask);
          gdk_window_get_size((GdkWindow *)gdk_pixmap, &width, &height);

          /* Adjust size of fixed widget to fit pixmap. */
          gtk_widget_set_usize(parent, width, height);

          /* Put pixmap into fixed widget. */
          gtk_fixed_put(GTK_FIXED(parent), pixmap, 0, 0);
          gtk_widget_shape_combine_mask(parent, mask, 0, 0);
          gtk_widget_show(pixmap);

          /* Unref gdk pixmap and bitmap. */
          gdk_pixmap_unref(gdk_pixmap);
          if(mask != NULL)
            gdk_bitmap_unref(mask);

          /* Destroy previous pixmap. */
          w = cb->status_content_icon_pm;
          if(w != NULL)
            gtk_widget_destroy(w);

          /* Set new pixmap. */
          cb->status_content_icon_pm = pixmap;
      }

        /* Begin recreating clipboard owned icon. */
        parent = cb->status_owned_icon_fixed;
        if(parent != NULL)
        {
            /* Set icon_data depending on if we own the `selection'
           * buffer.
           */
            if(cb->own_buffer)
                icon_data = (u_int8_t **)icon_owned_16x16_xpm;
            else
                icon_data = (u_int8_t **)icon_unowned_16x16_xpm;

            /* Create icon. */
            gdk_pixmap = gdk_pixmap_create_from_xpm_d(
                window, &mask,
                &style->bg[GTK_STATE_NORMAL],
                (gchar **)icon_data
            );
            pixmap = gtk_pixmap_new(gdk_pixmap, mask);
            gdk_window_get_size((GdkWindow *)gdk_pixmap, &width, &height);

            /* Adjust size of fixed widget to fit pixmap. */
            gtk_widget_set_usize(parent, width, height);

            /* Put pixmap into fixed widget. */
            gtk_fixed_put(GTK_FIXED(parent), pixmap, 0, 0);
            gtk_widget_shape_combine_mask(parent, mask, 0, 0);
            gtk_widget_show(pixmap);

            /* Unref gdk pixmap and bitmap. */
            gdk_pixmap_unref(gdk_pixmap);
            if(mask != NULL)
                gdk_bitmap_unref(mask);

            /* Destroy previous pixmap. */
            w = cb->status_owned_icon_pm;
            if(w != NULL)
                gtk_widget_destroy(w);

            /* Set new pixmap. */  
            cb->status_owned_icon_pm = pixmap;
        }
}

/*
 *    Forceably deallocates the DDE buffer on clipboard browser and
 *    marks it as not owning the buffer.
 */
static void ClipboardBrowserDDEClear(clipboard_browser_struct *cb)
{
      if(cb == NULL)
          return;

      if(!cb->initialized)
          return;

      free(cb->data);
      cb->data = NULL;
      cb->data_length = 0;

      cb->data_type = CLIPBOARD_DATA_TYPE_ASCII;

      cb->own_buffer = FALSE;
}

/*
 *    Records the widget (appends) to the specified list..
 */
static void ClipboardBrowserRecordWidget(
        GtkWidget ***list, int *total, GtkWidget *w
)
{
      gint i;

      if((list == NULL) || (total == NULL))
          return;

      if((*total) < 0)
          (*total) = 0;

      i = (*total);
      (*total) = i + 1;
      (*list) = (GtkWidget **)g_realloc(
          *list,
          (*total) * sizeof(GtkWidget *)
      );
      if((*list) == NULL)
      {
          (*total) = 0;
          return;
      }

      (*list)[i] = w;
}

/*
 *    Clipboard clear buffer menu (not `selection' buffer) callback.
 */
static void ClipboardBrowserClearMCB(GtkWidget *widget, gpointer data)
{
      gbool we_owned_buffer;
      clipboard_browser_struct *cb = (clipboard_browser_struct *)data;
      if(cb == NULL)
          return;

      if(!cb->initialized)
          return;

      we_owned_buffer = cb->own_buffer;

      /* This will disown the `selection' buffer if we own it and
       * clear the local buffer but not put anything afterwards.
       * Menus will also be updated.
       */
      ClipboardPutBinary(NULL, 0);

      ClipboardBrowserSetStatusMesg(
          cb,
          ((we_owned_buffer) ? "Buffer cleared" : "Display cleared")
      );
}

/*
 *    Refetches any data from the `selection' buffer.
 */
static void ClipboardBrowserRefreshMCB(GtkWidget *widget, gpointer data)
{
        clipboard_browser_struct *cb = (clipboard_browser_struct *)data;
        if(cb == NULL)
            return;

        if(!cb->initialized)
            return;

      ClipboardBrowserSetStatusMesg(cb, "Requesting buffer...");
      gtk_main_iteration();

      /* Get `selection' buffer, copying it to the clipboard
       * structure and updating its menus and views.
       */
      ClipboardBrowserFetch(cb);

      ClipboardBrowserSetStatusMesg(cb, "Buffer refreshed");
}

/*
 *    Clipboard browser close callback.
 */
static gint ClipboardBrowserCloseCB( 
        GtkWidget *widget, GdkEvent *event,
        clipboard_browser_struct *cb 
)
{
      if((widget == NULL) ||
           (cb == NULL)
      )
          return(FALSE);

      if(!cb->initialized)
          return(FALSE);

      ClipboardBrowserUnmap();

      return(TRUE);
}

/*
 *    Clipboard browser close callback (fur menu item and widget
 *    callback inputs).
 */
static void ClipboardBrowserCloseMCB(
      GtkWidget *widget, gpointer data
)
{
      ClipboardBrowserCloseCB(
          widget, NULL,
          (clipboard_browser_struct *)data
      );

      return;
}

/*
 *    Destroy callback.
 */
static void ClipboardBrowserDestroyCB(
        GtkObject *object, clipboard_browser_struct *cb
)
{
      return;
}

/*
 *    Clipboard browser view port event callback.
 */
static gint ClipboardBrowserViewEventCB(
        GtkWidget *widget, gpointer event, clipboard_browser_struct *cb
)
{
      gbool status = FALSE;
      GtkAdjustment *adj = NULL;
        GdkCursor *cur = NULL;
        GtkWidget *w;
      int etype;
      int xs, ys;
      static gbool button1, button2;
      static int xs_last, ys_last;
      int xsw_min, xsw_max, ysw_min, ysw_max;
        GdkModifierType mask;
        GdkEventConfigure *configure;
        GdkEventKey *key;
        GdkEventButton *button;
        GdkEventMotion *motion;


      if((widget == NULL) ||
           (event == NULL) ||
           (cb == NULL)
      )
          return(status);

/* Updates w to contain the viewport widget depending on widget
 * looked up in cb. If matches fail then does w = widget.
 */
#define DO_GET_VIEWPORT_WIDGET      \
{ \
 if(widget == cb->view_text_event_box) \
  w = cb->view_text_viewport; \
 else if(widget == cb->view_binary_event_box) \
  w = cb->view_binary_viewport; \
 else \
  w = widget; \
}

/* Fetches and calcualges bounds; xsw_min, xsw_max, ysw_min, and ysw_max
 * based on w being the scrolled window and uses the adj variable.
 */
#define DO_GET_SCROLL_WINDOW_BOUNDS \
{ \
 if(GTK_IS_SCROLLED_WINDOW(w)) \
 { \
  adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(w)); \
  if(adj != NULL) \
  { \
   xsw_min = adj->value; \
   xsw_max = xsw_min + w->allocation.width; \
  } \
  adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w)); \
  if(adj != NULL) \
  { \
   ysw_min = adj->value; \
   ysw_max = ysw_min + w->allocation.height; \
  } \
/* Need to take into account scroll bars. */ \
 } \
 else \
 { \
  xsw_min = 0; ysw_min = 0; \
  xsw_max = w->allocation.width; ysw_max = w->allocation.height; \
 } \
}


      etype = (*(gint *)event);
      switch(etype)
      {
        case GDK_EXPOSE:
          break;

        case GDK_CONFIGURE:
          configure = event;
          break;

          case GDK_KEY_PRESS: case GDK_KEY_RELEASE:
            key = event;
          break;

          case GDK_BUTTON_PRESS:
            button = event;
          DO_GET_VIEWPORT_WIDGET
            xs = button->x;
            ys = button->y;
          DO_GET_SCROLL_WINDOW_BOUNDS
            switch(button->button)
            {
              case GDK_BUTTON1:
            button1 = TRUE;
            cur = cb->pan_cur;
            break;

              case GDK_BUTTON2:
            button2 = TRUE;
            cur = cb->pan_cur;
                break;

              case GDK_BUTTON3:
            w = cb->view_menu;
            if(w != NULL)
                gtk_menu_popup(
                  GTK_MENU(w),
                  NULL, NULL, NULL, NULL,
                  button->button, button->time
                    );
            status = TRUE;
            return(status);
            break;
            }
          if(!GTK_WIDGET_NO_WINDOW(widget) && (cur != NULL))
            gdk_window_set_cursor(widget->window, cur);
          xs_last = xs;
          ys_last = ys;
          status = TRUE;
          break;

          case GDK_BUTTON_RELEASE:
            button = event;
          DO_GET_VIEWPORT_WIDGET
            xs = button->x;
            ys = button->y;
          DO_GET_SCROLL_WINDOW_BOUNDS
            switch(button->button)
            {
              case GDK_BUTTON1:
            button1 = FALSE;
                break;

              case GDK_BUTTON2:
            button2 = FALSE;
                break;

              default:
                break;
            }
            if(!GTK_WIDGET_NO_WINDOW(widget))
                gdk_window_set_cursor(widget->window, NULL);
            xs_last = xs;
            ys_last = ys;
            status = TRUE;
          break;

          case GDK_MOTION_NOTIFY:
            motion = event;
          DO_GET_VIEWPORT_WIDGET
          /* Fetch pointer positions. */
            if(motion->is_hint)
            {
                gdk_window_get_pointer(
                    motion->window, &xs, &ys, &mask
                );
            }
            else
            {
                xs = motion->x;
                ys = motion->y;
                mask = motion->state;
            }
          /* Scroll adjustments. */
            if(GTK_IS_SCROLLED_WINDOW(w) && (button1 || button2))
            {
            int   dxsw = xs_last - xs,
                  dysw = ys_last - ys;

            /* Adjust scroll adjustments. */
                adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(w));
                if(adj != NULL)
                {
                    adj->value += dxsw;
                if(adj->value < adj->lower)
                  adj->value = adj->lower;
                if(adj->value > (adj->upper - adj->page_size))
                        adj->value = (adj->upper - adj->page_size);
                gtk_adjustment_value_changed(adj);
                }
                adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w));
                if(adj != NULL)
                {
                    adj->value += dysw;
                    if(adj->value < adj->lower)
                        adj->value = adj->lower;
                    if(adj->value > (adj->upper - adj->page_size))
                        adj->value = (adj->upper - adj->page_size);
                    gtk_adjustment_value_changed(adj);
                }

                /* Since whole page moving, need to update current
                 * pointed positions relative to scroll.
                 */
                xs += dxsw;
                ys += dysw;
            }
            xs_last = xs;
            ys_last = ys;
          status = TRUE;
          break;



      }

#undef DO_GET_VIEWPORT_WIDGET
#undef DO_GET_SCROLL_WINDOW_BOUNDS

      return(status);
}

/*
 *    Notebook switch page callback.
 */
static void ClipboardBrowserSwitchPageCB(
        GtkNotebook *notebook, GtkNotebookPage *page, guint page_num,  
        gpointer data
)
{
      static gbool reenterant = FALSE;
      GtkWidget *w;
        clipboard_browser_struct *cb = (clipboard_browser_struct *)data;
        if((notebook == NULL) ||
           (cb == NULL)
      )
            return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

      if(!cb->initialized)
          return;

      /* Main notebook? */
      w = cb->notebook;
      if(w == (GtkWidget *)notebook)
      {
          /* gtk_notebook_set_page(notebook, page_num); */
          cb->notebook_cur_page = page_num;
          ClipboardBrowserUpdateMenus(cb);
      }

      reenterant = FALSE;

      return;
}

/*
 *    Clipboard browser view menu check item callback.
 */
static void ClipboardBrowserViewCheckMCB(GtkWidget *widget, gpointer data)
{
      static gbool reenterant = FALSE;
      GtkWidget *w;
      GtkNotebook *notebook;
      clipboard_browser_struct *cb = (clipboard_browser_struct *)data;
      if((widget == NULL) ||
           (cb == NULL)
      )
          return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

      /* Get pointer to notebook widget. */
      notebook = (GtkNotebook *)cb->notebook;
      if(notebook == NULL)
      {
          reenterant = FALSE;
          return;
      }

      /* Checked on text? */
      w = cb->view_text_micheck;
      if(w == widget)
      {
          gtk_notebook_set_page(notebook, 0);
      }
      /* Checked on binary? */
        w = cb->view_binary_micheck;
        if(w == widget)
        {
            gtk_notebook_set_page(notebook, 1);
        }

        /* Update menus. */
        ClipboardBrowserUpdateMenus(cb);

      reenterant = FALSE;

      return;
}

/*
 *    Checks if we own the `selection' buffer, if not then
 *    it will check for changes.
 */
static gint ClipboardMonitorChangesTOCB(gpointer data)
{
        static GdkAtom targets_atom = GDK_NONE;
      guint8 *old_buf_ptr;
      GtkWidget *owner;
      clipboard_browser_struct *cb = (clipboard_browser_struct *)data;
        if(cb == NULL)
            return(FALSE);

/* Let's not do this, since it's all async and this can mess things up
 * with too many requests and if one request occures during another
 * request it can throw things off.
 */
return(TRUE);

        /* Set type (target) atom. */
        if(targets_atom == GDK_NONE)
            targets_atom = gdk_atom_intern("STRING", FALSE);

      if(!cb->initialized)
          return(FALSE);

        owner = cb->owner_btn;
        if(owner == NULL) 
            return(FALSE);
        if(GTK_WIDGET_NO_WINDOW(owner))
            return(FALSE);  


      /* Skip if we own the selection buffer. */
      if(cb->own_buffer)
          return(TRUE);

      /* Is mapped? */
      if(!cb->map_state)
          return(TRUE);

      /* Record old pointer. */
      old_buf_ptr = cb->data;

        /* Request selection */
        gtk_selection_convert(
            owner,
            GDK_SELECTION_PRIMARY,
            targets_atom,
            GDK_CURRENT_TIME
        );

        /* Enter gui blocking loop, keep looping till we recieve
         * a response for our requested `selection' buffer.
         * This loop will be broken when
         * ClipboardBufferBufferRecievedCB() is called.
         */
        gtk_main();

      /* Any changes? */
      if(old_buf_ptr != cb->data)
      {
            /* Update menus. */
            ClipboardBrowserUpdateMenus(cb);
      }

      return(TRUE);
}

/*
 *    A `selection' buffer we requested has arrived, fetch
 *    it and break out of the blocking loop that we entered
 *    when we first made the request and copy arrived buffer
 *    to clipboard browser's local data.
 */
static void ClipboardBufferBufferRecievedCB(
        GtkWidget *widget, GtkSelectionData *selection_data,
        gpointer null_data
)
{
      gbool data_is_different = FALSE;
      clipboard_browser_struct *cb = &clipboard_browser;


        if((widget == NULL) ||
           (selection_data == NULL)
        )
      {
          cb->waiting_response = FALSE;
            return;
      }

      /* Compare if data is different. */
      if((selection_data->length > 0) &&
           (selection_data->length == cb->data_length) &&
         (selection_data->data != NULL) &&
         (selection_data->data != cb->data) &&
           (cb->data != NULL)
      )
      {
          /* Length is the same, check contents. */
          gint i, total;
          guint8 *src_ptr, *tar_ptr;

          for(i = 0, total = selection_data->length,
            src_ptr = cb->data,
            tar_ptr = (guint8 *)selection_data->data;
            i < total;
            i++, src_ptr++, tar_ptr++
          )
          {
            if((*src_ptr) != (*tar_ptr))
            {
                data_is_different = TRUE;
                break;
            }
          }
      }
      else
      {
          /* Lengths are different, definatly changed. */
          data_is_different = TRUE;
      }
      /* Was data different? */
      if(!data_is_different)
      {
          cb->waiting_response = FALSE;
            return;
      }


      /* Free current local buffer if any. */
      free(cb->data);
      cb->data = NULL;
      cb->data_length = 0;

      /* Did we recieve any data? */
        if(selection_data->length <= 0)
      {
          cb->waiting_response = FALSE;
          return;
      }

      /* Handle by selection type. */
        if(selection_data->type == GDK_SELECTION_TYPE_STRING)
        {
          /* Set new data length (exclude null terminating byte)
           * and allocate new local buffer data.
           */

          cb->data_length = selection_data->length;
          cb->data = (guchar *)malloc(
            (selection_data->length + 1) * sizeof(guchar)
          );
          if(cb->data == NULL)
          {
            cb->data_length = 0;
            cb->waiting_response = FALSE;
            return;
          }

          memcpy(
            cb->data, selection_data->data,
            selection_data->length * sizeof(guchar)
          );
          cb->data[cb->data_length] = '\0';

          ClipboardBrowserSetStatusMesg(cb, "Buffer received");
        }


      /* Data type (cb->data_type) will be updated outside of the
       * blocking loop.
       */

      /* Break out of blocked gui loop. */
      cb->waiting_response = FALSE;

      return;
}

/*
 *    `Selection' buffer clear callback, this function is called
 *    whenever another client has taken ownership of the primary
 *    `selection' buffer or our buffer has been disowned.
 *
 *    The clipboard browser is to mark itself as no longer owning
 *    the primary `selection' buffer and deallocate its local
 *    buffer since it won't be asked for anymore.
 */
static gint ClipboardClearCB(
        GtkWidget *widget, GdkEventSelection *event,
        clipboard_browser_struct *cb
)
{
        if((widget == NULL) ||
           (event == NULL) ||
           (cb == NULL)
        )
            return(FALSE);

      /* Deallocate local buffers and mark as not owning buffer. */
      ClipboardBrowserDDEClear(cb);

        /* Update menus. */
        ClipboardBrowserUpdateMenus(cb);

        return(TRUE);
}

/*
 *      Not sure what this is for.
 */
static gint ClipboardNotifyCB( 
        GtkWidget *widget, GdkEventSelection *event,
        clipboard_browser_struct *cb
)
{
        if((widget == NULL) ||
           (event == NULL) ||
           (cb == NULL)
        )
            return(FALSE);

/*
printf("Window: 0x%.8x (0x%.8x) | Widgets 0x%.8x 0x%.8x\n",
 (guint32)event->window,
 (guint32)((cb->owner_btn == NULL) ? 0 : cb->owner_btn->window),
 (guint32)widget,
 (guint32)cb->owner_btn
);
printf("Selection: %s\n", gdk_atom_name(event->selection));
printf("Requestor: %i\n", event->requestor);
 */

        /* Update menus. */
/*
        ClipboardBrowserUpdateMenus(cb);
 */
        return(TRUE);
}

/*
 *    Handles the request when this or another application requests
 *    our clipboard `selection' buffer.
 */
static void ClipboardBufferRequestCB(
        GtkWidget *widget, GtkSelectionData *selection_data,
        guint info, guint time_stamp,
      clipboard_browser_struct *cb
)
{
      if((widget == NULL) ||
           (selection_data == NULL) ||
           (cb == NULL)
      )
          return;

      if(!cb->initialized)
          return;

      /* Got a request for our data even though we don't own
       * the primary `selection' buffer?
       */
      if(!cb->own_buffer)
      {
          fprintf(stderr,
"ClipboardBufferRequestCB(): Got request for buffer contents while not owning primary buffer.\n"
          );
      }

      ClipboardBrowserSetStatusMesg(cb, "Processing buffer request...");
      gtk_main_iteration();

        /* When we return a single string, it should not be null terminated.
         * That will be done for us
         */
        gtk_selection_data_set(
          selection_data, GDK_SELECTION_TYPE_STRING,
          8,            /* 8 bits per character. */
          cb->data, cb->data_length
        );

      ClipboardBrowserSetStatusMesg(cb, "Buffer sent");

        return;
}


/*
 *    Fetches the primary `selection' buffer (if any) and returns
 *    a dynamically allocated coppied data which can be NULL.
 */
guint8 *ClipboardFetchBinary(gint *length)
{
      guint8 *buf = NULL;
      gint buf_len = 0;
        GtkWidget *owner;
        static GdkAtom targets_atom = GDK_NONE;
        clipboard_browser_struct *cb = &clipboard_browser;


        /* Set type (target) atom. */
        if(targets_atom == GDK_NONE)
          targets_atom = gdk_atom_intern("STRING", FALSE);

      /* Reset returned buffer length. */
      if(length != NULL)
          (*length) = buf_len;

        if(!cb->initialized)
            return(buf);

        owner = cb->owner_btn;
        if(owner == NULL)
            return(buf);
        if(GTK_WIDGET_NO_WINDOW(owner))
            return(buf);

        /* Do we own the `selection' buffer? */
        if(!cb->own_buffer)
      {
          /* No we do not, request `selection' buffer. */
          ClipboardBrowserSetBusy(cb);

            /* Request selection */
            cb->waiting_response = TRUE;
          gtk_selection_convert(
            owner,
            GDK_SELECTION_PRIMARY,
            targets_atom,
            GDK_CURRENT_TIME
          );

          /* Enter gui blocking loop, keep looping till we recieve
           * a response for our requested `selection' buffer.
           * This loop will be broken when
           * ClipboardBufferBufferRecievedCB() is called.
           */
            while(cb->waiting_response)
                gtk_main_iteration();
      }

      /* Check if we got anything. */
      if((cb->data != NULL) && (cb->data_length > 0) && (buf == NULL))
      {
          /* Allocate and set up return data. */
          buf = (guint8 *)malloc(cb->data_length * sizeof(guint8));
          if(buf != NULL)
          {
            buf_len = cb->data_length;
            memcpy(buf, cb->data, cb->data_length * sizeof(guint8));

                /* Update returned buffer length. */
                if(length != NULL)
                (*length) = buf_len;
          }
      }

      /* Update type. */
      cb->data_type = CLIPBOARD_DATA_TYPE_BINARY;

        /* Update menus. */
        ClipboardBrowserUpdateMenus(cb);

      ClipboardBrowserSetReady(cb);

      return(buf);
}

/*
 *    Same as ClipboardFetchBinary() except it only updates the data
 *    on the clipboard browser structure and does not allocate and
 *    copy that for return.
 */
static void ClipboardBrowserFetch(clipboard_browser_struct *cb)
{
        GtkWidget *owner;
        static GdkAtom targets_atom = GDK_NONE;


        /* Set type (target) atom. */
        if(targets_atom == GDK_NONE)
            targets_atom = gdk_atom_intern("STRING", FALSE);

      if(cb == NULL)
          return;
        if(!cb->initialized)
            return;

        owner = cb->owner_btn;
        if(owner == NULL)
            return;
        if(GTK_WIDGET_NO_WINDOW(owner))
            return;

        /* Do we own the `selection' buffer? */
      if(!cb->own_buffer)
      {
            /* No we do not, request `selection' buffer. */
            ClipboardBrowserSetBusy(cb);

            /* Request selection */
          cb->waiting_response = TRUE;
            gtk_selection_convert(
                owner,
                GDK_SELECTION_PRIMARY,     
                targets_atom,
                GDK_CURRENT_TIME
            );

            /* Enter gui blocking loop, keep looping till we recieve
             * a response for our requested `selection' buffer.
             * This loop will be broken when
             * ClipboardBufferBufferRecievedCB() is called.
             */
          while(cb->waiting_response)
            gtk_main_iteration();
      }

        /* Update type. */   
        cb->data_type = CLIPBOARD_DATA_TYPE_BINARY;

        /* Update menus. */
        ClipboardBrowserUpdateMenus(cb);

        ClipboardBrowserSetReady(cb);

      return;
}

/*
 *    Coppies the specified binary data (if any) to the clipboard.
 *    If data is NULL or length is <= 0 then the current (last) buffer
 *    (if any) owned by this application will be disowned and cleared.
 *
 *    Returns the actual number of bytes put, can return
 *    -1 on error.
 */
gint ClipboardPutBinary(const guint8 *data, gint length)
{
      GtkWidget *owner;
      clipboard_browser_struct *cb = &clipboard_browser;


      if(!cb->initialized)
          return(-1);

      owner = cb->owner_btn;
      if(owner == NULL)
          return(-1);
      if(GTK_WIDGET_NO_WINDOW(owner))
          return(-1);

      /* Do we own the primary `selection' buffer? If so then
       * disown it first.
       */
        if(cb->own_buffer)
      {
          /* Double check if we really own the prumary
           * `selection' buffer.
           */
            if(gdk_selection_owner_get(GDK_SELECTION_PRIMARY) ==
            owner->window
          )
          {
            /* Disown it. */
                gtk_selection_owner_set(
                    NULL,
                GDK_SELECTION_PRIMARY,
                    GDK_CURRENT_TIME
                );

            /* Wait untill disowned, we're waiting for
             * ClipboardClearCB() to be called when a buffer we own
             * has been disowned.
             */
            while(cb->own_buffer)
                gtk_main_iteration();
          }
          else
          {
            cb->own_buffer = FALSE;
          }
      }


      /* Forceably deallocate current buffer on clipboard browser,
       * incase disowning it above did not call the clear
       * `selection' buffer callback.
       */
      ClipboardBrowserDDEClear(cb);


      /* Do not go any further if we were given NULL or no length. */
      if((data == NULL) ||
           (length <= 0)
      )
      {
          ClipboardBrowserUpdateMenus(cb);
          return(0);
      }

      /* Chown ownership of primary `selection' buffer. */
      cb->own_buffer = gtk_selection_owner_set(
          owner,
            GDK_SELECTION_PRIMARY,
            GDK_CURRENT_TIME
        );
        /* If claiming the selection failed, we return error. */
        if(!cb->own_buffer)
          return(-1);


      /* Copy new data to buffer. */
      cb->data = (guchar *)malloc(length * sizeof(guchar));
      if(cb->data == NULL)
          return(-1);

      cb->data_type = CLIPBOARD_DATA_TYPE_BINARY;
      memcpy(cb->data, data, length * sizeof(guchar));

      cb->data_length = length;

      /* Update menus. */
      ClipboardBrowserUpdateMenus(cb);

      return(length);
}


/*
 *    Loads cursors used by the clipboard browser.
 */
static void ClipboardBrowserLoadCursors(clipboard_browser_struct *cb)
{
        cb->busy_cur = gdk_cursor_new(GDK_WATCH);
      cb->pan_cur = gdk_cursor_new(GDK_FLEUR);
}

/*
 *    Build menu bar for clipboard browser.
 */
static void ClipboardBrowserCreateMenuBar(
      clipboard_browser_struct *cb, GtkWidget *parent
)
{
        GtkWidget *menu_bar, *menu, *w, *fw;
        gint accel_key;
        gpointer accel_group;
      guint accel_mods;
      gpointer client_data = cb;
        u_int8_t **icon;
      gchar *label = NULL;
        void (*func_cb)(GtkWidget *w, gpointer);


        /* Create menu bar. */
      cb->menu_bar = menu_bar = GUIMenuBarCreate(&accel_group);
        gtk_container_add(GTK_CONTAINER(parent), menu_bar);
        gtk_widget_show(menu_bar);

#define DO_ADD_MENU_ITEM_LABEL  \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_LABEL, accel_group, \
  icon, label, accel_key, accel_mods, (void **)&fw, \
  client_data, func_cb \
 ); \
}

#define DO_ADD_MENU_ITEM_CHECK  \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_CHECK, accel_group, \
  icon, label, accel_key, accel_mods, (void **)&fw, \
  client_data, func_cb \
 ); \
}

#define DO_ADD_MENU_SEP \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_SEPARATOR, NULL, \
  NULL, NULL, 0, 0, NULL, \
  NULL, NULL \
 ); \
}

        /* Create file menu. */
        menu = GUIMenuCreate();
        if(menu != NULL)
        {
          icon = NULL;
            label = "Clear";
            accel_key = 0;
            accel_mods = 0;
            func_cb = ClipboardBrowserClearMCB;
            DO_ADD_MENU_ITEM_LABEL
          cb->clear_mi = w;

          DO_ADD_MENU_SEP

            icon = (u_int8_t **)icon_close_20x20_xpm;
            label = "Close";
            accel_key = 0;
            accel_mods = 0;
            func_cb = ClipboardBrowserCloseMCB;
            DO_ADD_MENU_ITEM_LABEL
        }
        GUIMenuAddToMenuBar(
            menu_bar, menu,
            "File",
            GUI_MENU_BAR_ALIGN_LEFT
        );

        /* Create view menu. */
        menu = GUIMenuCreate();
        if(menu != NULL)
        {
            icon = NULL;
            label = "as Text";
            accel_key = 0; 
            accel_mods = 0;
            func_cb = ClipboardBrowserViewCheckMCB;
          DO_ADD_MENU_ITEM_CHECK
          cb->view_text_micheck = w;

            icon = NULL;
            label = "as Binary";
            accel_key = 0;
            accel_mods = 0;
            func_cb = ClipboardBrowserViewCheckMCB;
            DO_ADD_MENU_ITEM_CHECK
            cb->view_binary_micheck = w;

            DO_ADD_MENU_SEP

            icon = (u_int8_t **)icon_reload_20x20_xpm;
            label = "Refresh";
            accel_key = GDK_F5;
            accel_mods = 0;
            func_cb = ClipboardBrowserRefreshMCB;
            DO_ADD_MENU_ITEM_LABEL
        }
        GUIMenuAddToMenuBar(
            menu_bar, menu,
            "View",
            GUI_MENU_BAR_ALIGN_LEFT
        );


#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_ITEM_CHECK
#undef DO_ADD_MENU_SEP

        /* Attach accel group to toplevel window. */
        if((cb->toplevel != NULL) && (accel_group != NULL))
        {
            gtk_window_add_accel_group(
                GTK_WINDOW(cb->toplevel),
                (GtkAccelGroup *)accel_group
            );
        }
}


/*
 *    Creates tool ribbon.
 */
static void ClipboardBrowserCreateToolRibbon(
        clipboard_browser_struct *cb, GtkWidget *parent
)
{
        GtkWidget *w, *parent2, *parent3;
      gint border_major = 5;
        gint bw = 60, bh = 50;
        gint sw = 5, sh = -1;


      /* Hbox tool ribbon to hold buttons. */
      cb->tool_ribbon = w = gtk_hbox_new(FALSE, 0);
      gtk_container_border_width(GTK_CONTAINER(w), 2);
        gtk_container_add(GTK_CONTAINER(parent), w);
        gtk_widget_show(w);
        parent2 = w;

#define DO_ADD_SEPARATOR        \
{ \
 w = gtk_vbox_new(TRUE, 0); \
 gtk_widget_set_usize(w, sw, sh); \
 gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0); \
 gtk_widget_show(w); \
 parent3 = w; \
 w = gtk_vseparator_new(); \
 gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, border_major); \
 gtk_widget_show(w); \
}

        /* Refresh. */
        w = GUIButtonPixmapLabelV(
            (u_int8_t **)icon_reload_20x20_xpm, "Refresh", NULL
        );
      gtk_widget_set_usize(w, bw, bh);
      gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(ClipboardBrowserRefreshMCB),
            cb
        );
        gtk_widget_show(w);

      DO_ADD_SEPARATOR

        /* Clear. */
        cb->clear_btn = w = GUIButtonPixmapLabelV(
            (u_int8_t **)icon_clear_20x20_xpm, "Clear", NULL
        );
        gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
      gtk_widget_set_usize(w, bw, bh);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(ClipboardBrowserClearMCB),
            cb
        );
        gtk_widget_show(w);

#undef DO_ADD_SEPARATOR
}

/*
 *    Initializes the clipboard browser.
 */
gint ClipboardBrowserInit(void)
{
      gint  /* border_major = 5 , */
            border_minor = 2;
      GtkWidget *w, *fw, *parent, *parent2, *parent3, *parent4;
      GtkWidget *handle_box, *menu;
        gint accel_key;
        gpointer accel_group;
        guint accel_mods;
        gpointer client_data;
        u_int8_t **icon;
        const gchar *label = NULL;
        void (*func_cb)(GtkWidget *w, gpointer);
        clipboard_browser_struct *cb = &clipboard_browser;


      /* Set callback client data. */
      client_data = cb;


        /* Reset values. */
        cb->initialized = TRUE; 
        cb->map_state = FALSE;

      cb->notebook_cur_page = 0;

      cb->waiting_response = FALSE;
      cb->monitor_changes_toid = (guint)(-1);


        /* Load cursors. */
      ClipboardBrowserLoadCursors(cb);


        /* Toplevel. */
      cb->toplevel = w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_widget_set_usize(w, CLIPBOARD_DEF_WIDTH, CLIPBOARD_DEF_HEIGHT);
        gtk_widget_realize(w);
      GUISetWMIcon(w->window, (u_int8_t **)icon_clipboard_48x48_xpm);
        gtk_window_set_policy(GTK_WINDOW(w), TRUE, TRUE, FALSE);
        gtk_signal_connect(
            GTK_OBJECT(w), "delete_event",
            GTK_SIGNAL_FUNC(ClipboardBrowserCloseCB),
            cb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "destroy",
            GTK_SIGNAL_FUNC(ClipboardBrowserDestroyCB),
            cb
        );
        gtk_window_set_title(GTK_WINDOW(w), "ClipBoard Browser");
        gtk_container_border_width(GTK_CONTAINER(w), 0);
        parent = w;


        /* Main vbox. */
      cb->main_vbox = w = gtk_vbox_new(FALSE, 0);
        gtk_container_add(GTK_CONTAINER(parent), w);
        gtk_widget_show(w);
        parent = w;


        /* Handle and menu bar. */
        handle_box = gtk_handle_box_new();
        gtk_box_pack_start(GTK_BOX(parent), handle_box, FALSE, FALSE, 0);
        gtk_widget_show(handle_box);
        ClipboardBrowserCreateMenuBar(cb, handle_box);

        /* Handle and tool ribbon. */
        handle_box = gtk_handle_box_new();
        gtk_box_pack_start(GTK_BOX(parent), handle_box, FALSE, FALSE, 0);
        gtk_widget_show(handle_box);
      ClipboardBrowserCreateToolRibbon(cb, handle_box);


      /* Push button for owning clipboard `selections', this widget
       * is never shown to the user.
       */
      w = gtk_button_new();
      /* Fetch: requested buffer has arrived for fetching signal. */
        gtk_signal_connect(
            GTK_OBJECT(w), "selection_received",
            GTK_SIGNAL_FUNC(ClipboardBufferBufferRecievedCB), cb
        );
      /* Put: `selection' buffer cleared or chowned to other signal. */ 
        gtk_signal_connect(
            GTK_OBJECT(w), "selection_clear_event",
            GTK_SIGNAL_FUNC(ClipboardClearCB), cb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "selection_notify_event",
            GTK_SIGNAL_FUNC(ClipboardNotifyCB), cb
        );
      /* Put: request for our buffer came in signal. */
        gtk_selection_add_target(
            w,
            GDK_SELECTION_PRIMARY,
            GDK_SELECTION_TYPE_STRING,
            1
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "selection_get",
            GTK_SIGNAL_FUNC(ClipboardBufferRequestCB), cb
        );
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
      cb->owner_btn = w;


      /* Note book. */
      cb->notebook = w = gtk_notebook_new();
      gtk_notebook_set_tab_pos(GTK_NOTEBOOK(w), GTK_POS_TOP);
        gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
      gtk_notebook_set_scrollable(GTK_NOTEBOOK(w), TRUE);
      gtk_notebook_set_show_tabs(GTK_NOTEBOOK(w), TRUE);
      gtk_notebook_set_show_border(GTK_NOTEBOOK(w), TRUE);
/*    gtk_notebook_set_page(GTK_NOTEBOOK(w), 0); */
        gtk_signal_connect(
            GTK_OBJECT(w), "switch_page",
            GTK_SIGNAL_FUNC(ClipboardBrowserSwitchPageCB), cb
        );
      gtk_widget_show(w);
      parent2 = w;


      /* View text viewport. */
      w = gtk_scrolled_window_new(NULL, NULL);
      cb->view_text_viewport = w;
      gtk_notebook_append_page(
            GTK_NOTEBOOK(parent2),
          w,
          gtk_label_new("Text")
      );
      gtk_scrolled_window_set_policy(
          GTK_SCROLLED_WINDOW(w),
          GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC 
      );
        gtk_widget_show(w);
      parent3 = w;

        /* Create event box as first child in view port. */
        w = gtk_event_box_new();
        cb->view_text_event_box = w;
        gtk_widget_add_events(
          w,
            GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
            GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
          GDK_LEAVE_NOTIFY_MASK
        );   
        gtk_signal_connect(
            GTK_OBJECT(w), "button_press_event",
            GTK_SIGNAL_FUNC(ClipboardBrowserViewEventCB),
            cb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_release_event",
            GTK_SIGNAL_FUNC(ClipboardBrowserViewEventCB),
            cb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "motion_notify_event",
            GTK_SIGNAL_FUNC(ClipboardBrowserViewEventCB),
            cb
        );
        gtk_scrolled_window_add_with_viewport(
            GTK_SCROLLED_WINDOW(parent3),   
            w
        );
        gtk_widget_show(w);
        
        /* Leave hbox to hold childs NULL. */
        cb->view_text_vbox = NULL;


        /* View binary viewport. */
        w = gtk_scrolled_window_new(NULL, NULL);
        cb->view_binary_viewport = w;
        gtk_notebook_append_page(
            GTK_NOTEBOOK(parent2),
            w,
            gtk_label_new("Binary")
        );
        gtk_scrolled_window_set_policy(
            GTK_SCROLLED_WINDOW(w),
            GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC
        );
        gtk_widget_show(w);
        parent3 = w;

      /* Create event box as first child in view port. */
      cb->view_binary_event_box = w = gtk_event_box_new();
        gtk_widget_add_events(w,
            GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
            GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
            GDK_LEAVE_NOTIFY_MASK
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_press_event",
            GTK_SIGNAL_FUNC(ClipboardBrowserViewEventCB),
            cb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_release_event",
            GTK_SIGNAL_FUNC(ClipboardBrowserViewEventCB),
            cb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "motion_notify_event",
            GTK_SIGNAL_FUNC(ClipboardBrowserViewEventCB),
            cb
        );
        gtk_scrolled_window_add_with_viewport(
            GTK_SCROLLED_WINDOW(parent3),
            w
        );
      gtk_widget_show(w);

        /* Leave hbox to hold childs NULL. */
        cb->view_binary_vbox = NULL;



      /* Main status bar dock. */
      w = gtk_handle_box_new();
        cb->status_bar_dock = w;
        gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
        gtk_widget_show(w);
        parent2 = w;

      /* Main status bar frame. */
      w = gtk_frame_new(NULL);
      cb->status_bar_frame = w;
      gtk_widget_set_usize(w, -1, CLIPBOARD_STATUSBAR_HEIGHT);
      gtk_container_add(GTK_CONTAINER(parent2), w);
      gtk_container_border_width(GTK_CONTAINER(w), 0);
        gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
        gtk_widget_show(w);
      parent2 = w;

      /* Hbox inside main status bar frame. */
      w = gtk_hbox_new(FALSE, 0);
        gtk_container_add(GTK_CONTAINER(parent2), w);
        gtk_widget_show(w);
        parent2 = w;


      /* Frame to hold contents icon. */
        w = gtk_frame_new(NULL);
      gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
/*    gtk_container_border_width(GTK_CONTAINER(w), 1); */
        gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
        gtk_widget_show(w);
        parent3 = w;

        /* Contents icon fixed. */
        w = gtk_fixed_new();
      cb->status_content_icon_fixed = w;
        gtk_container_add(GTK_CONTAINER(parent3), w);
/*    gtk_widget_set_usize(w, 20, 20); */
        gtk_widget_show(w);
        parent4 = w;

      /* Set contents icon pixmap to NULL. */
      cb->status_content_icon_pm = NULL;


        /* Frame to hold owned icon. */
        w = gtk_frame_new(NULL);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
      gtk_container_border_width(GTK_CONTAINER(w), 1);
        gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
        gtk_widget_show(w);
        parent3 = w;

        /* Owned icon fixed. */
        w = gtk_fixed_new();
        cb->status_owned_icon_fixed = w;
        gtk_container_add(GTK_CONTAINER(parent3), w);
/*    gtk_widget_set_usize(w, 20, 20); */
        gtk_widget_show(w);
        parent4 = w;

        /* Set contents icon pixmap to NULL. */
        cb->status_owned_icon_pm = NULL;


        /* Frame to hold status size label. */
        w = gtk_frame_new(NULL);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
      gtk_container_border_width(GTK_CONTAINER(w), 1);
        gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
        gtk_widget_show(w);
        parent3 = w;

        /* Status size hbox, and label. */
        w = gtk_hbox_new(FALSE, 0);
        gtk_container_add(GTK_CONTAINER(parent3), w);
        gtk_widget_show(w);
        parent4 = w;

        w = gtk_label_new("");
        cb->status_size_label = w;
        gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);   
        gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
        gtk_widget_set_usize(w, 100, -1);
        gtk_widget_show(w);


        /* Frame to hold status message label. */
        w = gtk_frame_new(NULL);
        gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
      gtk_container_border_width(GTK_CONTAINER(w), 1);
        gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
        gtk_widget_show(w);
        parent3 = w;

      /* Status message hboxes (two of them), and label. */
      w = gtk_hbox_new(FALSE, 0);
      gtk_container_add(GTK_CONTAINER(parent3), w);
      gtk_widget_show(w);
      parent4 = w;

        w = gtk_hbox_new(FALSE, 0);
        gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, border_minor);
        gtk_widget_show(w);   
        parent4 = w;

      w = gtk_label_new("");
      cb->status_mesg_label = w;
      gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
      gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
      gtk_widget_show(w);



      /* Right click menu. */
      accel_group = NULL;

#define DO_ADD_MENU_ITEM_LABEL  \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_LABEL, accel_group, \
  icon, label, accel_key, accel_mods, (void **)&fw, \
  client_data, func_cb \
 ); \
}
#define DO_ADD_MENU_SEP \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_SEPARATOR, NULL, \
  NULL, NULL, 0, 0, NULL, \
  NULL, NULL \
 ); \
}

        menu = GUIMenuCreate();
        if(menu != NULL)
        {
            icon = NULL;
            label = "Clear";
            accel_key = 0;
            accel_mods = 0;
            func_cb = ClipboardBrowserClearMCB;
            DO_ADD_MENU_ITEM_LABEL
            cb->view_clear_mi = w;

            DO_ADD_MENU_SEP

            icon = (u_int8_t **)icon_reload_20x20_xpm;
            label = "Refresh";
            accel_key = 0;
            accel_mods = 0;
            func_cb = ClipboardBrowserRefreshMCB;
            DO_ADD_MENU_ITEM_LABEL
          cb->view_refresh_mi = w;
        }   
      cb->view_menu = menu;

#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_SEP

      /* Set monitor changes timeout callback. */
      cb->monitor_changes_toid = gtk_timeout_add(
          1000,
          (GtkFunction)ClipboardMonitorChangesTOCB,
          cb
      );



      /* Reset values. */
      ClipboardBrowserReset(cb, FALSE);


      return(0);
}

/*
 *    Resets values on clipboard browser to defaults.
 */
void ClipboardBrowserReset(
      clipboard_browser_struct *cb, gbool need_unmap
)
{
      GtkWidget *w;


      if(cb == NULL)
          return;

      if(!cb->initialized)
          return;

      cb->waiting_response = FALSE;

      ClipboardBrowserDDEClear(cb);

      ClipboardBrowserSetStatusMesg(cb, "");

      cb->monitor_changes = TRUE;

      /* Force unmap as needed. */
      if(need_unmap)
      {
          w = cb->toplevel;
          if(w != NULL)
            gtk_widget_hide(w);

          cb->map_state = FALSE;
      }

      return;
}

/*
 *      Update menu values on the clipboard browser, also updates 
 *      (recreates) the view widgets' childs.
 */
void ClipboardBrowserUpdateMenus(clipboard_browser_struct *cb)
{
        static gbool reenterant = FALSE;
      gint page_num;
        gbool sensitivity, state;
        GtkWidget *w, *parent;


        if(cb == NULL)
            return;

        if(!cb->initialized)
            return;

        if(reenterant)
            return;
        else
            reenterant = TRUE;

#define SET_WIDGET_SENSITIVITY  \
{ \
 if(w != NULL) \
  gtk_widget_set_sensitive(w, sensitivity); \
}

#define UPDATE_CHECK_MENU_ITEM_STATE    \
{ \
 if(w != NULL) \
  gtk_check_menu_item_set_active( \
   GTK_CHECK_MENU_ITEM(w), state \
  ); \
}


      /* Menu items. */
      w = cb->clear_mi;
      sensitivity = ((cb->data == NULL) ?
            FALSE : TRUE
        );
      SET_WIDGET_SENSITIVITY

      /* View page checks. */
      page_num = cb->notebook_cur_page;
      w = cb->view_text_micheck;
      state = ((page_num == 0) ? TRUE : FALSE);
      UPDATE_CHECK_MENU_ITEM_STATE

        w = cb->view_binary_micheck;
        state = ((page_num == 1) ? TRUE : FALSE);
        UPDATE_CHECK_MENU_ITEM_STATE

      /* Right click menu. */
        w = cb->view_clear_mi;
        sensitivity = ((cb->data == NULL) ?
            FALSE : TRUE
        );
        SET_WIDGET_SENSITIVITY



      /* Buttons on tool ribbon. */
        w = cb->clear_btn;  
        sensitivity = ((cb->data == NULL) ?
            FALSE : TRUE
        );
        SET_WIDGET_SENSITIVITY




#undef UPDATE_CHECK_MENU_ITEM_STATE
#undef SET_WIDGET_SENSITIVITY 


      /* Update status bar icons. */
      ClipboardBrowserSetStatusIcon(cb);

      /* Update status size label. */
      w = cb->status_size_label;
      if(w != NULL)
      {
          char text[256];

          if(cb->data_length <= 0)
            strcpy(text, "");
          else
            sprintf(text, "%i bytes", cb->data_length);
          gtk_label_set_text(GTK_LABEL(w), text);
      }


      /* Recreate view child widgets and update to contents of
       * local buffer.
       */
#define DO_DESTROY_WIDGET       \
{ \
 if(w != NULL) \
  gtk_widget_destroy(w); \
}
      /* First destroy view childs. */
        w = cb->view_text_vbox; 
      cb->view_text_vbox = NULL;
        DO_DESTROY_WIDGET
        g_free(cb->view_text_widget);
        cb->view_text_widget = NULL;
        cb->total_view_text_widgets = 0;

        w = cb->view_binary_vbox;
      cb->view_binary_vbox = NULL;  
        DO_DESTROY_WIDGET
        g_free(cb->view_binary_widget);
        cb->view_binary_widget = NULL;
        cb->total_view_binary_widgets = 0;

      /* Create view text widgets. */
      parent = cb->view_text_event_box;
      if(parent != NULL)
      {
          w = gtk_vbox_new(FALSE, 0);
          cb->view_text_vbox = w;
          gtk_container_add(GTK_CONTAINER(parent), w);
            gtk_widget_show(w);
          parent = w;

            w = gtk_hbox_new(FALSE, 0);
            ClipboardBrowserRecordWidget(
                &cb->view_text_widget, &cb->total_view_text_widgets, w
            );
          gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 2);
            gtk_widget_show(w);
            parent = w;

          if((cb->data != NULL) && (cb->data_length > 0))
          {
            gchar *tmp_text = (gchar *)g_malloc(
                (cb->data_length + 1) * sizeof(gchar)
            );
            if(tmp_text != NULL)
            {
                strncpy(tmp_text, cb->data, cb->data_length);
                tmp_text[cb->data_length] = '\0';

                w = gtk_label_new(tmp_text);
                ClipboardBrowserRecordWidget(
                  &cb->view_text_widget, &cb->total_view_text_widgets,
                  w
                );
                gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 2);
                gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
                gtk_widget_show(w);
            }
            g_free(tmp_text);
          }
      }

        /* Create view binary widgets. */
        parent = cb->view_binary_event_box;
        if(parent != NULL)
        {
          gint inc;
          gint src_i, src_len = cb->data_length;
          gint tar_i, tar_column, tar_hex_columns = 16;
          gchar *tar_ptr, *tar_text;
          gint tar_text_len;
          gchar *text_address, *text_hex, *text_ascii;


            /* Allocate address column. */
            tar_text = NULL;
          tar_text_len = 0;
            for(src_i = 0, tar_i = 0, inc = 11;
                src_i < src_len;
                src_i += tar_hex_columns
            )
          {
                if((tar_i + inc) >= tar_text_len)
                {
                    tar_text_len = tar_i + inc + 1;
                    tar_text = (gchar *)g_realloc(
                  tar_text, tar_text_len * sizeof(gchar)
                );
                    if(tar_text == NULL)
                    {
                        tar_text_len = 0;
                        break;
                    }
                }
                tar_ptr = &(tar_text[tar_i]);
            if((src_i + tar_hex_columns) < src_len)
                    sprintf(tar_ptr, "0x%.8X\n", (guint)src_i);
            else
                sprintf(tar_ptr, "0x%.8X", (guint)src_i);
                tar_i += inc;
          }
          text_address = tar_text;  /* Transfer tar_text to text_address. */

          /* Allocate target text for hex column. */
          tar_text = NULL;
          tar_text_len = 0;
          for(src_i = 0, tar_i = 0, tar_column = 0;
                src_i < src_len;
              src_i++
          )
          {
            /* Hex value. */
            inc = 4;
                if((tar_i + inc) >= tar_text_len)
                {
                    tar_text_len = tar_i + inc + 1;
                    tar_text = (gchar *)g_realloc(
                  tar_text, tar_text_len * sizeof(gchar)
                );
                    if(tar_text == NULL)
                    {
                        tar_text_len = 0;
                        break;
                    }
                }
                tar_ptr = &(tar_text[tar_i]);
            sprintf(tar_ptr, "0x%.2X", (guint8)cb->data[src_i]);
            tar_i += inc;

            /* Column increment and deliminator. */
            tar_column++;
            if(tar_column == (tar_hex_columns / 2))
            {
                    inc = 2;
                    if((tar_i + inc) >= tar_text_len)
                    {
                        tar_text_len = tar_i + inc + 1;
                        tar_text = (gchar *)g_realloc(
                      tar_text, tar_text_len * sizeof(gchar)
                  );
                        if(tar_text == NULL)
                        {
                            tar_text_len = 0;
                            break;
                        }
                }
                    tar_ptr = &(tar_text[tar_i]);
                strcpy(tar_ptr, "  ");
                    tar_i += inc;
            }
            else if(tar_column >= tar_hex_columns)
            {
                    inc = 1;
                    if((tar_i + inc) >= tar_text_len)
                    {
                        tar_text_len = tar_i + inc + 1; 
                        tar_text = (gchar *)g_realloc(
                      tar_text, tar_text_len * sizeof(gchar)
                  );
                        if(tar_text == NULL)
                        {
                            tar_text_len = 0;
                            break;
                        }
                    }
                    tar_ptr = &(tar_text[tar_i]);
                    strcpy(tar_ptr, "\n");
                    tar_i += inc;

                tar_column = 0;
            }
                else
                {
                    inc = 1;
                    if((tar_i + inc) >= tar_text_len)
                    {
                        tar_text_len = tar_i + inc + 1;
                        tar_text = (gchar *)g_realloc(
                      tar_text, tar_text_len * sizeof(gchar)
                  );
                        if(tar_text == NULL)
                        {
                            tar_text_len = 0;
                            break;
                        }
                    }
                    tar_ptr = &(tar_text[tar_i]);
                    strcpy(tar_ptr, " ");
                    tar_i += inc;
                }
          }
          text_hex = tar_text;      /* Transfer tar_text to text_hex. */

          /* Allocate ascii representation column. */
            tar_text = NULL;
          tar_text_len = 0;
            for(src_i = 0, tar_i = 0, tar_column = 0;
                src_i < src_len;
                src_i++
            )
            {
                /* Hex value. */
                inc = 1;
                if((tar_i + inc) >= tar_text_len)
                {
                    tar_text_len = tar_i + inc + 1;
                    tar_text = (gchar *)g_realloc(
                  tar_text, tar_text_len * sizeof(gchar)
                );
                    if(tar_text == NULL)
                    {
                        tar_text_len = 0;
                        break;
                    }
                }
                tar_ptr = &(tar_text[tar_i]);
            if(isprint((int)cb->data[src_i]))
                sprintf(tar_ptr, "%c", (guint8)cb->data[src_i]);
            else
                strcpy(tar_ptr, " ");
                tar_i += inc;

                /* Column increment and deliminator. */
                tar_column++;
                if(tar_column == (tar_hex_columns / 2))
                {
                    inc = 1;
                    if((tar_i + inc) >= tar_text_len)
                    {
                        tar_text_len = tar_i + inc + 1;
                        tar_text = (gchar *)g_realloc(
                      tar_text, tar_text_len * sizeof(gchar)
                  );
                        if(tar_text == NULL)
                        {
                            tar_text_len = 0;
                            break;
                        }
                    }
                    tar_ptr = &(tar_text[tar_i]);
                    strcpy(tar_ptr, " ");
                    tar_i += inc;
                }
                else if(tar_column >= tar_hex_columns)
                {
                    inc = 1;
                    if((tar_i + inc) >= tar_text_len)
                    {
                        tar_text_len = tar_i + inc + 1;
                        tar_text = (gchar *)g_realloc(
                      tar_text, tar_text_len * sizeof(gchar)
                  );
                        if(tar_text == NULL)
                        {
                            tar_text_len = 0;
                            break;
                        }
                    }
                    tar_ptr = &(tar_text[tar_i]);
                    strcpy(tar_ptr, "\n");
                    tar_i += inc;

                    tar_column = 0;
                }
          }
          text_ascii = tar_text;    /* Transfer tar_text to text_ascii. */


          /* Begin creating widgets for the binary view. */
            w = gtk_vbox_new(FALSE, 0);
            cb->view_binary_vbox = w;
            gtk_container_add(GTK_CONTAINER(parent), w);
            gtk_widget_show(w);
            parent = w;

            w = gtk_hbox_new(FALSE, 0);
            gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 2);
            gtk_widget_show(w);
            parent = w;


          /* Address column label. */
            if(text_address != NULL)
            {
            GtkRcStyle *rcstyle = gtk_rc_style_new();
            g_free(rcstyle->font_name);
            rcstyle->font_name = g_strdup(
                "-misc-fixed-medium-*-*-*-13-*-*-*-*-*-*-*"
            );

                w = gtk_label_new(text_address);
                gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 5);
                gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
            gtk_widget_modify_style(w, rcstyle);
                gtk_widget_show(w);

            GUIRCStyleDeallocUnref(rcstyle);
            }

          /* Hex column label. */
          if(text_hex != NULL)
          {
                GtkRcStyle *rcstyle = gtk_rc_style_new();
                g_free(rcstyle->font_name);
                rcstyle->font_name = g_strdup(
                    "-misc-fixed-medium-*-*-*-13-*-*-*-*-*-*-*"
                );

            w = gtk_label_new(text_hex);
                gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 5);
                gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
                gtk_widget_modify_style(w, rcstyle);
                gtk_widget_show(w);

                GUIRCStyleDeallocUnref(rcstyle);
            }

          /* Ascii representation column label. */
            if(text_ascii != NULL)
            {
                GtkRcStyle *rcstyle = gtk_rc_style_new();
                g_free(rcstyle->font_name);
                rcstyle->font_name = g_strdup(
                    "-misc-fixed-medium-*-*-*-13-*-*-*-*-*-*-*"
                );

                w = gtk_label_new(text_ascii);
                gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 5);
                gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
                gtk_widget_modify_style(w, rcstyle);
                gtk_widget_show(w);

                GUIRCStyleDeallocUnref(rcstyle);
            }


          /* Deallocate texts. */
            g_free(text_hex);
          g_free(text_address);
          g_free(text_ascii);
      }

#undef DO_DESTROY_WIDGET

      reenterant = FALSE;
}

/*
 *    Maps the clipboard browser and updates its menus.
 */
void ClipboardBrowserMap(void)
{
        GtkWidget *w;
        clipboard_browser_struct *cb = &clipboard_browser;


        if(cb == NULL)
            return;

        if(!cb->initialized)
            return;

      w = cb->toplevel;
      gtk_widget_show_raise(w);
      cb->map_state = TRUE;

      /* Update menus. */
      ClipboardBrowserUpdateMenus(cb);
}

/*
 *    Unmaps the clipboard browser.
 */
void ClipboardBrowserUnmap(void)
{
      GtkWidget *w;
        clipboard_browser_struct *cb = &clipboard_browser;


        if(cb == NULL)
            return;

        if(!cb->initialized)
            return;

      if(cb->map_state)
        {   
            w = cb->toplevel;
            if(w != NULL)
                gtk_widget_hide(w);

            cb->map_state = FALSE;
        }
}

/*
 *    Deallocates and destroys the clipboard browser and all
 *    of its allocated resources.
 */
void ClipboardBrowserShutdown(void)
{
      GtkWidget **w;
      GdkCursor **cur;
        clipboard_browser_struct *cb = &clipboard_browser;


      if(cb->initialized)
      {
#define DO_UNREF_CURSOR       \
{ \
 if((*cur) != NULL) \
 { \
  GdkCursor *tc = *cur; \
  (*cur) = NULL; \
  gdk_cursor_destroy(tc); \
 } \
}

#define DO_DESTROY_WIDGET       \
{ \
 if((*w) != NULL) \
 { \
  GtkWidget *tmp_w = *w; \
  (*w) = NULL; \
  gtk_widget_destroy(tmp_w); \
 } \
}

          cb->waiting_response = FALSE;

          if(cb->monitor_changes_toid != (guint)(-1))
          {
            gtk_timeout_remove(cb->monitor_changes_toid);
            cb->monitor_changes_toid = (guint)(-1);
          }

          ClipboardBrowserDDEClear(cb);


          /* Begin destroying widgets. */
          /* View menu (right click). */
          w = &cb->view_menu;
          DO_DESTROY_WIDGET

            /* Icon pixmaps. */
            w = &cb->status_content_icon_pm;
          DO_DESTROY_WIDGET
          w = &cb->status_owned_icon_pm;
          DO_DESTROY_WIDGET

          /* Views. */
          w = &cb->view_text_vbox;
          DO_DESTROY_WIDGET
          g_free(cb->view_text_widget);
          cb->view_text_widget = NULL;
          cb->total_view_text_widgets = 0;

            w = &cb->view_binary_vbox;
          DO_DESTROY_WIDGET
          g_free(cb->view_binary_widget);
          cb->view_binary_widget = NULL;
          cb->total_view_binary_widgets = 0;

          /* Notebook. */
          w = &cb->notebook;
          DO_DESTROY_WIDGET


          /* Toplevel. */
          w = &cb->toplevel;
          cb->clear_mi = NULL;
          cb->monitor_changes_micheck = NULL;
          cb->view_text_micheck = NULL;
          cb->view_binary_micheck = NULL;
          DO_DESTROY_WIDGET


          /* Cursors. */
          cur = &cb->busy_cur;
          DO_UNREF_CURSOR
          cur = &cb->pan_cur;
          DO_UNREF_CURSOR

#undef DO_UNREF_CURSOR
#undef DO_DESTROY_WIDGET
      }

      /* Clear clipboard structure. */
      memset(cb, 0x00, sizeof(clipboard_browser_struct));
}


Generated by  Doxygen 1.6.0   Back to index