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

clipboard.c

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.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:
 */
typedef struct {

      gboolean    initialized,
                  map_state;

      gint        busy_count;

      GdkCursor   *busy_cur,
                  *pan_cur;

      GtkAccelGroup     *accelgrp;

      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;

      gboolean    own_buffer,       /* Own selection buffer */
                  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;

      gboolean    monitor_changes;
      guint       monitor_changes_toid;

} clipboard_browser_struct;
#define CLIPBOARD_BROWSER(p)  ((clipboard_browser_struct *)(p))


/* Utilities */
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
);
                
/* 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, GdkEvent *event, gpointer data
);
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, gpointer data
);
static gint ClipboardNotifyCB(
      GtkWidget *widget, GdkEventSelection *event, gpointer data
);
static void ClipboardBufferRequestCB(
      GtkWidget *widget, GtkSelectionData *selection_data,
      guint info, guint time_stamp,
      gpointer data
);

/* Fetch & Put Data */
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, gboolean need_unmap
);
void ClipboardBrowserUpdateMenus(clipboard_browser_struct *cb);
void ClipboardBrowserMap(void);
void ClipboardBrowserUnmap(void);
void ClipboardBrowserShutdown(void);


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)


#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 the clipboard browser as busy.
 */
static void ClipboardBrowserSetBusy(clipboard_browser_struct *cb)
{
      GtkWidget *w = (cb != NULL) ? cb->toplevel : NULL;
      if(w == NULL)
          return;

      cb->busy_count++;

      if((w->window != NULL) && (cb->busy_count == 1))
      {
          gdk_window_set_cursor(w->window, cb->busy_cur);
          gdk_flush();
      }
}

/*
 *    Marks clipboard browser as ready.
 */
static void ClipboardBrowserSetReady(clipboard_browser_struct *cb)
{
      GtkWidget *w = (cb != NULL) ? cb->toplevel : NULL;
      if(w == NULL)
          return;

      cb->busy_count--;
      if(cb->busy_count < 0)
          cb->busy_count = 0;

      if((w->window != NULL) && (cb->busy_count == 0))
      {
          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 = (cb != NULL) ? cb->status_mesg_label : NULL;
      if(w == NULL)
          return;

      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)
{
      GdkWindow *window;
      GtkStyle *style;
      GtkWidget *w, *parent;


      if(cb == NULL)
          return;

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

      window = w->window;
      style = gtk_widget_get_style(w);

      /* Set toplevel WM icon depending on if the clipboard has
       * data or not
       */
      GUISetWMIcon(
          window,
          (guint8 **)((cb->data != NULL) ?
            icon_clipboard_48x48_xpm :
            icon_clipboard_empty_48x48_xpm
          )
      );

      /* Update status content icon */
      parent = cb->status_content_icon_fixed;
      if(parent != NULL)
      {
          GdkBitmap *mask;
          GdkPixmap *pixmap;
          guint8 **data = (guint8 **)((cb->data != NULL) ?
            icon_clipboard_16x16_xpm :
            icon_clipboard_empty_16x16_xpm
          );

          /* Load pixmap */
          pixmap = gdk_pixmap_create_from_xpm_d(
            window, &mask,
            &style->bg[GTK_STATE_NORMAL],
            (gchar **)data
          );
          if(pixmap != NULL)
          {
            gint width, height;

            gdk_window_get_size(pixmap, &width, &height);

            /* Update pixmap widget, create it as needed */
            w = cb->status_content_icon_pm;
            if(w != NULL)
            {
                gtk_pixmap_set(GTK_PIXMAP(w), pixmap, mask);
            }
            else
            {
                cb->status_content_icon_pm = w = gtk_pixmap_new(
                  pixmap, mask
                );
                gtk_fixed_put(GTK_FIXED(parent), w, 0, 0);
                gtk_widget_show(w);
            }

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

            gtk_widget_shape_combine_mask(parent, mask, 0, 0);

            /* Unref loaded pixmap and mask */
            gdk_pixmap_unref(pixmap);
            if(mask != NULL)
                gdk_bitmap_unref(mask);
          }
      }

      /* Update status owned icon */
      parent = cb->status_owned_icon_fixed;
      if(parent != NULL)
      {
          GdkBitmap *mask;
          GdkPixmap *pixmap;
          guint8 **data = (guint8 **)((cb->own_buffer) ?
            icon_owned_16x16_xpm :
            icon_unowned_16x16_xpm
          );

          /* Load pixmap */
          pixmap = gdk_pixmap_create_from_xpm_d(
            window, &mask,
            &style->bg[GTK_STATE_NORMAL],
            (gchar **)data
          );
          if(pixmap != NULL)
          {
            gint width, height;

            gdk_window_get_size(pixmap, &width, &height);

            /* Update pixmap widget, create it as needed */
            w = cb->status_owned_icon_pm;
            if(w != NULL)
            {
                gtk_pixmap_set(GTK_PIXMAP(w), pixmap, mask);
            }
            else
            {
                cb->status_owned_icon_pm = w = gtk_pixmap_new(
                  pixmap, mask
                );
                gtk_fixed_put(GTK_FIXED(parent), w, 0, 0);
                gtk_widget_show(w);
            }

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

            gtk_widget_shape_combine_mask(parent, mask, 0, 0);

            /* Unref loaded pixmap and mask */
            gdk_pixmap_unref(pixmap);
            if(mask != NULL)
                gdk_bitmap_unref(mask);
          }
      }
}

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

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

      cb->data_type = CLIPBOARD_DATA_TYPE_ASCII;

      cb->own_buffer = FALSE;
}

/*
 *    Records the widget by appending it to the given list.
 */
static void ClipboardBrowserRecordWidget(
      GtkWidget ***list, int *total, GtkWidget *w
)
{
      gint i;

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

      i = MAX(*total, 0);
      (*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)
{
      gboolean we_owned_buffer;
      clipboard_browser_struct *cb = CLIPBOARD_BROWSER(data);
      if(cb == NULL)
          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(data);
      if(cb == NULL)
          return;

      ClipboardBrowserSetStatusMesg(cb, "Requesting buffer...");
      while(gtk_events_pending() > 0)
          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 
)
{
      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(data)
      );
}

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

/*
 *    Clipboard browser view port event callback.
 */
static gint ClipboardBrowserViewEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
)
{
      gboolean status = FALSE;
      GtkAdjustment *adj = NULL;
      GdkCursor *cur = NULL;
      GtkWidget *w;
      gint etype;
      gint xs, ys;
      static gboolean button1, button2;
      static gint xs_last, ys_last;
      gint xsw_min, xsw_max, ysw_min, ysw_max;
      GdkModifierType mask;
      GdkEventConfigure *configure;
      GdkEventKey *key;
      GdkEventButton *button;
      GdkEventMotion *motion;
      clipboard_browser_struct *cb = CLIPBOARD_BROWSER(data);
      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 sets 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 calculates 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->type;
      switch(etype)
      {
        case GDK_EXPOSE:
          break;

        case GDK_CONFIGURE:
          configure = (GdkEventConfigure *)event;
          break;

        case GDK_KEY_PRESS:
        case GDK_KEY_RELEASE:
          key = (GdkEventKey *)event;
          break;

        case GDK_BUTTON_PRESS:
          button = (GdkEventButton *)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 = (GdkEventButton *)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 = (GdkEventMotion *)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)
          )
          {
            gfloat      dxsw = xs_last - xs,
                  dysw = ys_last - ys;

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

            /* 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;
      }

      return(status);
#undef DO_GET_VIEWPORT_WIDGET
#undef DO_GET_SCROLL_WINDOW_BOUNDS
}

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

      if(reenterant)
          return;
      else
          reenterant = TRUE;

      /* Main notebook? */
      if(cb->notebook == GTK_WIDGET(notebook))
      {
          w = cb->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 gboolean reenterant = FALSE;
      GtkWidget *w;
      GtkNotebook *notebook;
      clipboard_browser_struct *cb = CLIPBOARD_BROWSER(data);
      if((widget == NULL) || (cb == NULL))
          return;

      w = cb->notebook;
      if(w != NULL)
          notebook = GTK_NOTEBOOK(w);
      else
          return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;

      /* Text? */
      if(cb->view_text_micheck == widget)
      {
          gtk_notebook_set_page(notebook, 0);
      }
      /* Binary? */
      if(cb->view_binary_micheck == 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(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
)
{
      gboolean 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 */
      g_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 *)g_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;
}

/*
 *    `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, gpointer data
)
{
      clipboard_browser_struct *cb = CLIPBOARD_BROWSER(data);
      if((widget == NULL) || (event == NULL) || (cb == NULL))
          return(FALSE);

      /* Delete 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, gpointer data
)
{
      clipboard_browser_struct *cb = CLIPBOARD_BROWSER(data);
      if((widget == NULL) || (event == NULL) || (cb == NULL))
          return(FALSE);

#if 0
g_print("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
);
g_print("Selection: %s\n", gdk_atom_name(event->selection));
g_print("Requestor: %i\n", event->requestor);

      /* Update menus */
      ClipboardBrowserUpdateMenus(cb);
#endif

      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,
      gpointer data
)
{
      clipboard_browser_struct *cb = CLIPBOARD_BROWSER(data);
      if((widget == NULL) || (selection_data == NULL) || (cb == NULL))
          return;

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

      ClipboardBrowserSetStatusMesg(cb, "Processing buffer request...");
      while(gtk_events_pending() > 0)
          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");
}


/*
 *    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;

      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 *)g_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;

      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);
}

/*
 *    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;

      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);


      /* Warn if previous buffer was not disowned and deleted */
      if(cb->data != NULL)
          g_printerr(
"ClipboardPutBinary(): Warning: Previous buffer not disowned.\n"
          );

      /* Create new buffer and copy new data to it */
      cb->data = (guchar *)g_malloc(length * sizeof(guchar));
      if(cb->data != NULL)
      {
          cb->data_type = CLIPBOARD_DATA_TYPE_BINARY;
          memcpy(cb->data, data, length * sizeof(guchar));
          cb->data_length = length;
      }
      else
      {
          cb->data_type = CLIPBOARD_DATA_TYPE_BINARY;
          cb->data_length = 0;
      }

      ClipboardBrowserUpdateMenus(cb);

      return(cb->data_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
)
{
        guint8 **icon;
        const gchar *label;
      guint accel_key, accel_mods;
      GtkAccelGroup *accelgrp = cb->accelgrp;
      GtkWidget *menu_bar, *menu, *w;
      gpointer client_data = cb;
      void (*func_cb)(GtkWidget *w, gpointer);


      /* Create menu bar */
      cb->menu_bar = menu_bar = GUIMenuBarCreate(NULL);
      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, accelgrp,           \
  icon, label, accel_key, accel_mods, NULL,           \
  client_data, func_cb                          \
 );                                       \
}
#define DO_ADD_MENU_ITEM_CHECK      {                 \
 w = GUIMenuItemCreate(                         \
  menu, GUI_MENU_ITEM_TYPE_CHECK, accelgrp,           \
  icon, label, accel_key, accel_mods, NULL,           \
  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 =
#if defined(PROG_LANGUAGE_SPANISH)
"Claro"
#elif defined(PROG_LANGUAGE_FRENCH)
"Clair"
#elif defined(PROG_LANGUAGE_GERMAN)
"Klar"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Chiaro"
#elif defined(PROG_LANGUAGE_DUTCH)
"Helder"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Claro"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Klar"
#else
"Clear"
#endif
          ;
          accel_key = 0;
          accel_mods = 0;
          func_cb = ClipboardBrowserClearMCB;
          DO_ADD_MENU_ITEM_LABEL
          cb->clear_mi = w;

          DO_ADD_MENU_SEP

          icon = (guint8 **)icon_close_20x20_xpm;
          label =
#if defined(PROG_LANGUAGE_SPANISH)
"Cierre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Fin"
#elif defined(PROG_LANGUAGE_GERMAN)
"Nah"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Vicino"
#elif defined(PROG_LANGUAGE_DUTCH)
"Einde"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Próximo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Nær"
#else
"Close"
#endif
          ;
          accel_key = 0;
          accel_mods = 0;
          func_cb = ClipboardBrowserCloseMCB;
          DO_ADD_MENU_ITEM_LABEL
      }
      GUIMenuAddToMenuBar(
          menu_bar, menu,
#if defined(PROG_LANGUAGE_SPANISH)
"Archivo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Fichier"
#elif defined(PROG_LANGUAGE_GERMAN)
"Akte"
#elif defined(PROG_LANGUAGE_ITALIAN)
"File"
#elif defined(PROG_LANGUAGE_DUTCH)
"Dossier"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Arquivo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Arkiv"
#else
"File"
#endif
          , 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 = (guint8 **)icon_reload_20x20_xpm;
          label =
#if defined(PROG_LANGUAGE_SPANISH)
"Refresque"
#elif defined(PROG_LANGUAGE_FRENCH)
"Rafraîchir"
#elif defined(PROG_LANGUAGE_GERMAN)
"Erfrischen Sie"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Rinfrescare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verfris"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Refresque-se"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Forfrisk"
#else
"Refresh"
#endif
          ;
          accel_key = GDK_F5;
          accel_mods = 0;
          func_cb = ClipboardBrowserRefreshMCB;
          DO_ADD_MENU_ITEM_LABEL
      }
      GUIMenuAddToMenuBar(
          menu_bar, menu,
#if defined(PROG_LANGUAGE_SPANISH)
"Vista"
#elif defined(PROG_LANGUAGE_FRENCH)
"Vue"
#elif defined(PROG_LANGUAGE_GERMAN)
"Blick"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Veduta"
#elif defined(PROG_LANGUAGE_DUTCH)
"Overzicht"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Vista"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Sikt"
#else
"View"
#endif
          , GUI_MENU_BAR_ALIGN_LEFT
      );

#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_ITEM_CHECK
#undef DO_ADD_MENU_SEP

}


/*
 *    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(
          (guint8 **)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(
          (guint8 **)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)
{
      const gint border_minor = 2;
      GtkAccelGroup *accelgrp;
      GtkWidget *w, *parent, *parent2, *parent3, *parent4;
      GtkWidget *handle_box, *menu;
      guint accel_key, accel_mods;
      gpointer client_data;
      guint8 **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->busy_count = 0;
      cb->accelgrp = accelgrp = gtk_accel_group_new();
      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);
      gtk_window_set_title(GTK_WINDOW(w), "ClipBoard Browser");
      GUISetWMIcon(w->window, (guint8 **)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_accel_group_attach(accelgrp, GTK_OBJECT(w));
      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 */

#define DO_ADD_MENU_ITEM_LABEL      {                 \
 w = GUIMenuItemCreate(                         \
  menu, GUI_MENU_ITEM_TYPE_LABEL, accelgrp,           \
  icon, label, accel_key, accel_mods, NULL,           \
  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 = (guint8 **)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, gboolean need_unmap
)
{
      GtkWidget *w;

      if(cb == NULL)
          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;
      }
}

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

      if(cb == NULL)
          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      \
{ GUIMenuItemSetCheck(w, state, TRUE); }


      /* Menu items */
      w = cb->clear_mi;
      sensitivity = (cb->data != NULL) ? TRUE : FALSE;
      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) ? TRUE : FALSE;
      SET_WIDGET_SENSITIVITY



      /* Buttons on tool ribbon */
      w = cb->clear_btn;  
      sensitivity = (cb->data != NULL) ? TRUE : FALSE;
      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)
      {
          gchar text[80];

          if(cb->data_length <= 0)
            strcpy(text, "");
          else
            g_snprintf(
                text, sizeof(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
       */
      if(TRUE)
      {
          GtkWidget **w;

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

          /* First destroy view childs */
          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;

#undef DO_DESTROY_WIDGET
      }

      /* 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((gint)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);

            gtk_rc_style_unref(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);

            gtk_rc_style_unref(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);

            gtk_rc_style_unref(rcstyle);
          }


          /* Delete texts */
          g_free(text_hex);
          g_free(text_address);
          g_free(text_ascii);
      }

      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;

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

      ClipboardBrowserUpdateMenus(cb);
}

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

      if(cb == NULL)
          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) { gdk_cursor_destroy(*cur); *cur = NULL; } }

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

          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

          if(cb->accelgrp != NULL)
          {
            gtk_accel_group_unref(cb->accelgrp);
            cb->accelgrp = NULL;
          }

          /* 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