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

viewer.c

#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "../include/disk.h"
#include "../include/string.h"

#include "guiutils.h"
#include "fb.h"

#include "viewer.h"
#include "viewercb.h"
#include "viewerdnd.h"
#include "editordnd.h"
#include "manedit.h"
#include "maneditcb.h"
#include "maneditop.h"
#include "messages.h"
#include "config.h"


#include "images/manedit_16x16.xpm"
#include "images/mp_viewer_16x16.xpm"
#include "images/manedit_20x20.xpm"
#include "images/mp_viewer_20x20.xpm"
#include "images/mp_viewer_48x48.xpm"

#include "images/icon_manual_opened_20x20.xpm"
#include "images/icon_search_20x20.xpm"
#include "images/icon_open_20x20.xpm"
#include "images/icon_close_20x20.xpm"
#include "images/icon_clear_20x20.xpm"
#include "images/icon_stop_20x20.xpm"
#include "images/icon_exit_20x20.xpm"
#include "images/icon_goto_20x20.xpm"
#include "images/icon_folder_parent_20x20.xpm"
#include "images/icon_reload_20x20.xpm"
#include "images/icon_options_20x20.xpm"


viewer_index_item_struct *ViewerIndexItemNew(
      viewer_index_item_type type, const gchar *full_path
);
void ViewerIndexItemDelete(viewer_index_item_struct *item);
void ViewerBranchSelect(
      viewer_struct *v, GtkCTreeNode *branch
);

void ViewerTextInsertPosition(viewer_struct *v, gint start_pos);
void ViewerTextInsert(
      viewer_struct *v, const gchar *buf, gint buf_len,
      gpointer client_data, gint (*func_cb)(glong, glong, gpointer)
);
void ViewerTextDelete(viewer_struct *v, gint start_pos, gint end_pos);

static GtkCTreeNode *ViewerIndexDoLoadIteration(
      viewer_struct *v, GtkCTree *ctree,
      GtkCTreeNode *parent, GtkCTreeNode *sibling,
      const gchar *full_path, const gchar *name
);
void ViewerIndexDoLoad(  
      viewer_struct *v,
      gint total_paths, gchar **path, gchar **name
);

gboolean ViewerDoFind(
      viewer_struct *v, GtkText *text,
      gchar *haystack, gchar *needle,
      gint haystack_len, gint start_pos,
      gboolean case_sensitive,
      gboolean move_to,
      gboolean *search_wrapped
);

void ViewerViewTextRecordScrollPositions(viewer_struct *v);

void ViewerSetBusy(viewer_struct *v);
void ViewerSetReady(viewer_struct *v);

static void ViewerCreateMenuBar(viewer_struct *v, GtkWidget *parent);
static void ViewerCreateToolBar(viewer_struct *v, GtkWidget *parent);
static void ViewerCreateStatusBar(viewer_struct *v, GtkWidget *parent);

viewer_struct *ViewerNew(gpointer core_ptr, gint editor_num);
void ViewerUpdateMenus(viewer_struct *v);
void ViewerSetStatusPosition(viewer_struct *v, gint row, gint column);
void ViewerSetStatusMessage(viewer_struct *v, const gchar *mesg);
void ViewerSetStatusProgress(viewer_struct *v, gfloat percent);
void ViewerRecordPositions(viewer_struct *v);
void ViewerReset(viewer_struct *v, gboolean need_unmap);
void ViewerMap(viewer_struct *v);
void ViewerUnmap(viewer_struct *v);
void ViewerDelete(viewer_struct *v);


#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 VIEWER_BTN_WIDTH      (100 + (2 * 3))
#define VIEWER_BTN_HEIGHT     (30 + (2 * 3))


/*
 *    Creates a new Viewer Index Item.
 */
extern viewer_index_item_struct *ViewerIndexItemNew(
      viewer_index_item_type type, const gchar *full_path
)
{
      viewer_index_item_struct *item = VIEWER_INDEX_ITEM(g_malloc0(
          sizeof(viewer_index_item_struct)
      ));
      if(item == NULL)
          return(NULL);

      item->type = type;
      item->full_path = STRDUP(full_path);

      return(item);
}

/*
 *    Deletes a viewer index item structure.
 */
void ViewerIndexItemDelete(viewer_index_item_struct *item)
{
      if(item == NULL)
          return;

      g_free(item->full_path);
      g_free(item);
}


/*
 *    Selects the given branch on the viewer's index ctree.
 *
 *      Expands the parent branches if any and as needed.
 *
 *      Will call expand, select, and unselect callbacks.
 */
void ViewerBranchSelect(
      viewer_struct *v, GtkCTreeNode *branch
)
{
      GtkCTreeNode *parent;
      GtkCTreeRow *branch_row;
      GtkCTree *ctree;


      if((v == NULL) || (branch == NULL))
          return;

      if(!v->initialized)
          return;

      ctree = (GtkCTree *)v->index_ctree;
      if(ctree == NULL)
          return;

      /* Get parent branch of given branch */
      branch_row = GTK_CTREE_ROW(branch);
      parent = ((branch_row == NULL) ?
          NULL : branch_row->parent
      );
      /* Get row of parent branch (if it is not NULL) */
      branch_row = ((parent == NULL) ?
          NULL : GTK_CTREE_ROW(parent)
      );
      /* Is parent branch expanded? */
      if((branch_row == NULL) ? 0 : !(branch_row->expanded))
      {
          /* Expand all parent branches */
          while(parent != NULL)
          {
            /* Expand parent branch (this will generate an expand
             * signal which will be handled).
             */
            gtk_ctree_expand(ctree, parent);

            /* Get parent of parent branch */
            branch_row = GTK_CTREE_ROW(parent);
            parent = ((branch_row == NULL) ?
                NULL : branch_row->parent   
            ); 
          }
      }

/*      gtk_ctree_node_moveto(ctree, branch, 0, 0, 0); */

      /* Select the branch, this will call the unselect and then
       * select callbacks.
       */
      gtk_ctree_select(ctree, branch);

      return;
}


/*
 *    Sets the starting text insert position for the given viewer's
 *    text widget. If start_pos is -1 then it will be set to the end
 *    of the text widget's text.
 */
void ViewerTextInsertPosition(viewer_struct *v, gint start_pos)
{
      GtkWidget *w;
      GtkText *text;
      gint end_pos;


      if(v == NULL)
          return;

      /* Get view text widget pointer */
      w = v->view_text;
      if(w == NULL)
          return;
      else
          text = GTK_TEXT(w);

      /* Get length of text */
      end_pos = gtk_text_get_length(text);

      if(start_pos < 0)
          start_pos = end_pos;
      if(start_pos > end_pos)
          start_pos = end_pos;

      gtk_text_set_point(text, start_pos);

      return;
}

/*
 *    Inserts the given buffer to the viewer's view text parsed.
 *
 *    If buf_len is -1 then the entire given buf will be inserted,
 *    and in which case the given buf needs to be null terminated.
 *
 *    Calling function is responsible for setting the starting
 *    text insert position.
 *
 *    If the callback function is not NULL and returns non-zero then
 *    the insert will be aborted.
 */
void ViewerTextInsert(
      viewer_struct *v, const gchar *buf, gint buf_len,
      gpointer client_data, gint (*func_cb)(glong, glong, gpointer)
)
{
      GtkWidget *w;
      GtkText *text;
      gchar last_char;
      const gchar *buf_ptr, *buf_next, *buf_end;
      medit_core_struct *core_ptr;
      medit_styles_list_struct *styles_list;
      GdkFont *cur_font = NULL;
      GtkStyle *style_ptr;


      if((v == NULL) || (buf == NULL))
          return;

      /* Get pointer to core structure */
      core_ptr = MEDIT_CORE(v->core_ptr);
      if(core_ptr == NULL)
          return;

      /* Get pointer to styles list structure on core */
      styles_list = &core_ptr->styles_list;

      /* Sanitize buffer length */
      if(buf_len < 0)
          buf_len = strlen(buf);

      if(buf_len == 0)
          return;

      /* Get view text widget pointer */
      w = v->view_text;
      if(w == NULL)
          return;
      else
          text = GTK_TEXT(w);


      /* Set current standard font */
      style_ptr = styles_list->manpage_text_standard;
      cur_font = (style_ptr != NULL) ? style_ptr->font : NULL;

#define IS_CTL_CHAR(c)  (((c) == 0x08))

      gtk_text_freeze(text);

      /* Iterate through buffer */
      last_char = '\0'; /* Reset last character to be printed */
      buf_ptr = buf;
      buf_end = (const gchar *)(buf + buf_len);
      while(buf_ptr < buf_end)
      {
          if(func_cb != NULL)
          {
#if 0
/* Do not call function callback while GtkText widget is frozen */
            status = func_cb(
                (long)MIN(buf_ptr - buf, buf_len), (long)buf_len,
                client_data
            );
            if(status)
                break;
#endif
          }

          /* Seek to next control charater if any, setting buf_next
           * to point to the next control character or end of buffer
           */
          buf_next = buf_ptr;
          while(buf_next < buf_end)
          {
            if(IS_CTL_CHAR(*buf_next))
                break;
            else
                buf_next++;
          }
          /* Current character a control character? */
          if(IS_CTL_CHAR(*buf_ptr))
          {
            /* Increment buf_ptr past control character */
            buf_ptr++;

            /* Change style */

            /* Last print character an underline? */
            if(last_char == '_')
            {
                style_ptr = styles_list->manpage_text_underline;
            }
            /* Last print character same as this one? */
            else if((buf_ptr < buf_end) ?
                (last_char == (*buf_ptr)) : 0
            )
            {
                style_ptr = styles_list->manpage_text_bold;
            }
            else
            {
                style_ptr = styles_list->manpage_text_standard;
            }
            cur_font = (style_ptr != NULL) ? style_ptr->font : NULL;


            /* Delete one character backwards */
            gtk_text_backward_delete(text, 1);

            /* Increment and insert next character */
            if(buf_ptr < buf_end)
            {
                gtk_text_insert(
                  text,
                  cur_font,
                  (style_ptr == NULL) ?
                      NULL : &style_ptr->fg[GTK_STATE_NORMAL],
                  NULL,
                  buf_ptr,
                  1
                );
/*
printf("Insert one: %.8x %.8x (%.8x)\n",
 (guint)buf_ptr, (guint)(buf_ptr + 1), (guint)buf_end);
 */
            }

            /* Increment to next character */
            buf_ptr++;
          }
          else
          {
            /* Store to next control character or end of buffer */

            /* Calculate this segment length */
            gint seg_len = (gint)(buf_next - buf_ptr);

            /* Use ManPage standard text style */
            style_ptr = styles_list->manpage_text_standard;
            cur_font = (style_ptr != NULL) ? style_ptr->font : NULL;

            /* Any characters to store? */
            if(seg_len > 0)
            {
                gtk_text_insert(
                  text,
                  cur_font,
                  (style_ptr == NULL) ?
                      NULL : &style_ptr->fg[GTK_STATE_NORMAL],
                  NULL,
                  buf_ptr,
                  seg_len
                );
/*
printf("Insert %i: %.8x %.8x (%.8x)\n", seg_len,
 (guint)buf_ptr, (guint)buf_next, (guint)buf_end);
 */
            }

            /* Seek past this segment */
            buf_ptr = (const gchar *)(buf_ptr + MAX(seg_len, 1));

            /* Record last character to be printed */
            if((buf_ptr <= buf_end) && (buf_ptr > buf))
                last_char = (*(buf_ptr - 1));
          }
      }

#undef IS_CTL_CHAR

      gtk_text_thaw(text);
}

/*
 *    Deletes the segment of text.
 *
 *    If end_pos is -1 then all text will be deleted from start_pos.
 */
void ViewerTextDelete(viewer_struct *v, gint start_pos, gint end_pos)
{
      GtkWidget *w;


      if(v == NULL)
          return;

      /* Get view text widget pointer */
      w = v->view_text;
      if(w == NULL)
          return;

      gtk_text_freeze(GTK_TEXT(w));
      gtk_editable_delete_text(
          GTK_EDITABLE(w),
          start_pos, end_pos
      );
      gtk_text_thaw(GTK_TEXT(w));

      return;
}

/*
 *    Loads the given manual page or directory specified
 *    by full_path. If full_path is a directory then each file or
 *    directory within it will be loaded as well.
 *
 *    Inputs assumed valid.
 */
static GtkCTreeNode *ViewerIndexDoLoadIteration(
      viewer_struct *v, GtkCTree *ctree,
      GtkCTreeNode *parent, GtkCTreeNode *sibling,
      const gchar *full_path, const gchar *name
)
{
      struct stat stat_buf;
      gint i, text_total;
      gchar **text;
      GtkCTreeNode *new_branch = NULL;
      viewer_index_item_struct *item;     
      medit_core_struct *core_ptr;
      medit_pixmaps_list_struct *pixmaps_list;

      /* Check for stop */
      if(v->stop_count > 0)
          return(new_branch);

      core_ptr = MEDIT_CORE(v->core_ptr);
      if(core_ptr == NULL)
          return(new_branch);

      pixmaps_list = &core_ptr->pixmaps_list;

      if(full_path == NULL)
          return(new_branch);

      if(stat(full_path, &stat_buf))
          return(new_branch);

      if(S_ISDIR(stat_buf.st_mode))
      {
          text_total = 4;
          text = (gchar **)g_malloc0(text_total * sizeof(gchar *));
          if(text != NULL)
          {
            /* Allocate name cell value */
            if(name != NULL)
            {
                text[0] = STRDUP(name);
            }
            else
            {
                gchar *s = strrchr(full_path, G_DIR_SEPARATOR);
                text[0] = STRDUP((s != NULL) ? (s + 1) : full_path);
            }

            /* Substitute directory name with verbose catagory
             * name if possible
             */
            if(!strcmp(text[0], "man1"))
            {
                g_free(text[0]);
                text[0] = STRDUP(MEDIT_SECT_NAME_1);
            }
            else if(!strcmp(text[0], "man2"))
            {
                g_free(text[0]);
                text[0] = STRDUP(MEDIT_SECT_NAME_2);
            }
            else if(!strcmp(text[0], "man3"))
            {
                g_free(text[0]);
                text[0] = STRDUP(MEDIT_SECT_NAME_3);
            }
            else if(!strcmp(text[0], "man4"))
            {
                g_free(text[0]);
                text[0] = STRDUP(MEDIT_SECT_NAME_4);
            }
            else if(!strcmp(text[0], "man5"))
            {
                g_free(text[0]);
                text[0] = STRDUP(MEDIT_SECT_NAME_5);
            }
            else if(!strcmp(text[0], "man6"))
            {       
                g_free(text[0]);
                text[0] = STRDUP(MEDIT_SECT_NAME_6);
            }
            else if(!strcmp(text[0], "man7"))
            {
                g_free(text[0]);
                text[0] = STRDUP(MEDIT_SECT_NAME_7);
            }
            else if(!strcmp(text[0], "man8"))
            { 
                g_free(text[0]);
                text[0] = STRDUP(MEDIT_SECT_NAME_8);
            }
            /* Now strip .extension from name */
            if(text[0] != NULL)
            {
                gchar *s = strrchr(text[0], '.');
                if(s != NULL)
                  *s = '\0';
            }

            /* Allocate lines and read state cell values */
            text[1] = STRDUP("");
            text[2] = STRDUP("");

            /* Allocate description value */
            text[3] = STRDUP("*Directory*");

            /* Create new branch */
            new_branch = gtk_ctree_insert_node(
                ctree, parent, sibling,
                text,
                MEDIT_LIST_ICON_TEXT_SPACING,
                pixmaps_list->folder_closed_20x20,
                pixmaps_list->folder_closed_20x20_mask,
                pixmaps_list->folder_opened_20x20,
                pixmaps_list->folder_opened_20x20_mask,
                FALSE,        /* Is leaf */
                FALSE         /* Expanded */
            );

            /* Allocate new index item structure */
            item = ViewerIndexItemNew(
                ViewerIndexItemTypeDirectory,   /* Type */
                full_path                       /* Full path */
            );
            /* Set index item structure pointer to branch row data */
            gtk_ctree_node_set_row_data_full(
                ctree, new_branch,
                item, ViewerIndexBranchDestroyCB
            );

            /* Delete cell values */
            strlistfree(text, text_total);
          }
      }
      else
      {
          text_total = 4;
          text = (gchar **)g_malloc0(text_total * sizeof(gchar *));
          if(text != NULL)
          {
            /* Get name cell value */
            gchar *s = strrchr(full_path, G_DIR_SEPARATOR);
            text[0] = STRDUP((s != NULL) ? (s + 1) : full_path);
            /* Now strip .extension from name */
            if(text[0] != NULL)
            {
                gchar *s = strrchr(text[0], '.');
                if(s != NULL)
                  *s = '\0';
            }

            /* Get lines and read state cell values */
            text[1] = STRDUP("");
            text[2] = STRDUP("");

            /* Get description cell value */
            text[3] = STRDUP("Manual Page File");

            /* Create new branch */
            new_branch = gtk_ctree_insert_node(
                ctree, parent, sibling,
                text,
                MEDIT_LIST_ICON_TEXT_SPACING,
                pixmaps_list->manual_closed_20x20,
                pixmaps_list->manual_closed_20x20_mask,
                pixmaps_list->manual_opened_20x20,
                pixmaps_list->manual_opened_20x20_mask,
                TRUE,         /* Is leaf */
                FALSE         /* Expanded */
            );

            /* Allocate new index item structure */
            item = ViewerIndexItemNew(
                ViewerIndexItemTypeManualPage,  /* Type */
                full_path                       /* Full path */
            );
            /* Set index item structure pointer to branch row data */
            gtk_ctree_node_set_row_data_full(
                ctree, new_branch,
                item, ViewerIndexBranchDestroyCB
            );

            /* Delete cell values */
            strlistfree(text, text_total);
          }
      }


      /* If given path is a directory, recurse into it */
      if(S_ISDIR(stat_buf.st_mode))
      {
          gchar **path;

          /* Print status message */
          gchar *s = g_strdup_printf(
            "Scanning %s...",
            full_path
          );
          ViewerSetStatusMessage(v, s);
          g_free(s);

          while(gtk_events_pending() > 0)
            gtk_main_iteration();

          /* Check for stop */
          if(v->stop_count > 0)
            return(new_branch);


          /* Get dir entries */
          path = GetDirEntNames(full_path);

          for(i = 0; path[i] != NULL; i++);
          path = StringQSort(path, i);

          /* Iterate through each path */
          for(i = 0; path[i] != NULL; i++)
          {
            /* Valid directory or file name? */
            if(strcmp(path[i], ".") &&
               strcmp(path[i], "..") &&
               strcmp(path[i], "whatis")
            )
            {
                /* Make into full path as strptr */
                s = STRDUP(PrefixPaths(full_path, path[i]));
                if(s != NULL)
                {
                  ViewerIndexDoLoadIteration(
                      v, ctree,
                      new_branch,         /* Parent */
                      NULL,         /* Sibling */
                      s, NULL
                  );
                  g_free(s);
                }
            }

            /* Delete this path */
            g_free(path[i]);
            path[i] = NULL;
          }

          /* Delete paths pointer array (note each pointed to path
           * was deleted in the above loop)
           */
          g_free(path);
      }

      return(new_branch);
}

/*
 *    Loads recursivly, manual pages in the given paths array.
 *
 *    The given paths must be full paths. The given paths and names
 *    list will not be deleted. The names will be used as the
 *    directory names placed into the index clist, each name corresponds
 *    to the path.
 */
void ViewerIndexDoLoad(
      viewer_struct *v,
      gint total_paths, gchar **path, gchar **name
)
{
      gint i;
      gchar *s;
      GtkCTree *ctree;

      if((v == NULL) || (path == NULL) || (total_paths < 1))
          return;

      if(!v->initialized || v->processing)
          return;

      ctree = (GtkCTree *)v->index_ctree;
      if(ctree == NULL)
          return;


      /* Mark as processing */
      v->processing = TRUE;

      /* Reset stop count */
      v->stop_count = 0;

      gtk_clist_freeze(GTK_CLIST(ctree));

      ViewerSetBusy(v);
      ViewerUpdateMenus(v);


      /* Iterate through each path */
      for(i = 0; i < total_paths; i++)
      {
          if(path[i] == NULL)
            continue;

          /* Manage GTK events */
          while(gtk_events_pending() > 0)
            gtk_main_iteration();

          /* Check for stop */
          if(v->stop_count > 0)
            break;

          /* Print status message */
          s = g_strdup_printf(
            "Scanning %s...",
            path[i]
          );
          ViewerSetStatusMessage(v, s);
          g_free(s);

          /* Load this path and any subdirs */
          ViewerIndexDoLoadIteration(
            v, ctree,
            NULL,       /* Parent */
            NULL,       /* Sibling */
            path[i], name[i]
          );
      }

      ViewerSetStatusMessage(v, "Index scan done");

      gtk_clist_thaw(GTK_CLIST(ctree));

      /* Mark done processing */
      v->processing = FALSE;
      ViewerSetReady(v);
      ViewerUpdateMenus(v);
}


/*
 *      Procedure to find needle from haystack with respect to
 *      the given text widget.
 *
 *      Both needle and haystack may be modified by this function,
 *      but never deleted.
 *
 *      Given GtkText widget will be updated and instructed to select
 *      the matched text if a match was made. If case_sensitive is TRUE
 *      then the search will be made case sensitive, and if move_to is
 *      set to TRUE then gtk_editable_set_position() will be called to
 *      scroll to the new matched position.
 *
 *      If haystack_len is -1 then strlen() will be called on haystack
 *      to get the actual length. Given start_pos will be sanitized,
 *      if start_pos is >= haystack_len then start_pos will be set to 0.
 *
 *      Returns TRUE if a match is made.
 *
 *      Pointer to search_wrapped will be set to TRUE if a match
 *      was made after a search wrapped through the entire buffer.
 */
gboolean ViewerDoFind(
      viewer_struct *v, GtkText *text,
      gchar *haystack, gchar *needle,
      gint haystack_len, gint start_pos,
      gboolean case_sensitive,
      gboolean move_to,
      gboolean *search_wrapped
)
{
      gchar *bp, *match_ptr;


      /* Reset search wrapped */
      if(search_wrapped != NULL)
          (*search_wrapped) = FALSE;

      /* Inputs valid? */
      if((v == NULL) || (text == NULL) ||
         (haystack == NULL) || (needle == NULL)
      )
          return(FALSE);

      /* Need to get haystack length ourself? */
      if(haystack_len < 0)
          haystack_len = strlen(haystack);

      /* Empty haystack? */
      if(haystack_len < 1)
          return(FALSE);

      /* Empty needle? */
      if((*needle) == '\0')
          return(FALSE);

      ViewerSetStatusMessage(v, "Finding...");
      
      /* Sanitize starting position */
      if(start_pos >= haystack_len)
          start_pos = 0;
      else if(start_pos < 0)
          start_pos = 0;
 
      
      /* Case insensitive search? */
      if(!case_sensitive)
      {
          /* Need to modify buffers to upper case */
          
          /* Modify ending portion of haystack */
          bp = (gchar *)(haystack + start_pos);
          while((*bp) != '\0')
          {
            (*bp) = toupper(*bp);
            bp++;
          }
      
          /* Modify needle */
          bp = needle;
          while((*bp) != '\0')
          {
            (*bp) = toupper(*bp);
            bp++;
          }
      }
      
          
      /* Begin search from starting position */
      match_ptr = strstr(
          (gchar *)(haystack + start_pos),
          needle
      );
      if(match_ptr == NULL)
      {
          /* Did not get match, so start from beginning of haystack
           * and search again. First check if this is case insensitive.
           */
          if(!case_sensitive)
          {
            /* Toupper the beginning portion of the buffer */
            bp = haystack;
            while(bp < (gchar *)(haystack + start_pos))
            {
                (*bp) = toupper(*bp);
                bp++;
            }
          }
          /* Now try match again for the second time starting form the
           * beginning.
           */
          match_ptr = strstr( 
            (gchar *)(haystack + 0),
            needle
          );
          if(match_ptr != NULL)
          {
            /* Got match the second time, now mark search wrapped
             * as true.
             */
            if(search_wrapped != NULL)
                (*search_wrapped) = TRUE;
          }
      }
          
      ViewerSetStatusMessage(v, "Find done");
      
      /* Did we get a match? */
      if(match_ptr == NULL)
      {
          /* Sorry, no match made */
          return(FALSE);
      }
      else
      {
          /* Got match */
          gint match_start_pos = (gint)(match_ptr - haystack);
          gint needle_len = strlen(needle);
                
          if(match_start_pos < 0)
            match_start_pos = 0;
          
          /* Now modify the text widget, set new cursor position and
           * select the matched string.
           */
          if(1)
          {
            GtkEditable *editable = (GtkEditable *)text;
          
            /* Scroll to new matched position? */
            if(move_to)
            {
                gtk_text_set_point(text, match_start_pos);
                gtk_editable_set_position(
                  editable, match_start_pos
                );
            }

            if(needle_len > 0)
            {
                gtk_editable_select_region(
                  editable,
                  match_start_pos,
                  match_start_pos + needle_len
                );
            }
          }

          return(TRUE);
      }
}


/*
 *    Records the viewer's view text scroll bar position values.
 */
void ViewerViewTextRecordScrollPositions(viewer_struct *v)
{
      GtkText *text;
      GtkAdjustment *adj;


      if(v == NULL)
          return;

      text = (GtkText *)v->view_text;
      if(text == NULL)
          return;

      adj = text->hadj;
      if(adj != NULL)
          v->last_scroll_hpos = adj->value;

      adj = text->vadj;
      if(adj != NULL)
          v->last_scroll_vpos = adj->value;

      return;
}


/*
 *      Blocks input and sets viewer as marked busy.
 */
void ViewerSetBusy(viewer_struct *v)
{ 
      GtkWidget *w;
      GdkCursor *cur;
      medit_core_struct *core_ptr;


      if(v == NULL)
          return;

      core_ptr = MEDIT_CORE(v->core_ptr);
      if(core_ptr == NULL)
          return;

      w = v->toplevel;
      if(w == NULL)
          return; 
  
      cur = core_ptr->cursors_list.busy;
      if(cur == NULL)
          return;

      if(GTK_WIDGET_NO_WINDOW(w))
          return;

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

/*
 *      Allows input and sets viewer as ready for input.
 */
void ViewerSetReady(viewer_struct *v)
{           
      GtkWidget *w;
  
      if(v == NULL)
          return;
   
      w = v->toplevel;
      if(w == NULL)
          return;
 
      if(GTK_WIDGET_NO_WINDOW(w))
          return;

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

      return;
}


/* 
 *      Creates the menu bar for the viewer, this function should be
 *      called by ViewerNew().
 *
 *      Given parent is assumed to be a vbox.
 */  
static void ViewerCreateMenuBar(viewer_struct *v, GtkWidget *parent)
{
      GtkWidget *menu_bar, *parent2, *menu, *w, *fw;
      gint accel_key;
      GtkAccelGroup *accel_group;
      guint accel_mods;
      gpointer client_data = (gpointer)v;
      gpointer old_client_data;
      guint8 **icon;
      gchar *label = NULL;
      void (*func_cb)(GtkWidget *w, gpointer);
 
 
      /* Create menu bar */
      menu_bar = GUIMenuBarCreate(&accel_group);
      v->menu_bar = 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, (gpointer *)&fw, \
  client_data, func_cb \
 ); \
}

#define DO_ADD_MENU_ITEM_SUBMENU        \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_SUBMENU, accel_group, \
  icon, label, accel_key, accel_mods, (gpointer *)&fw, \
  client_data, func_cb \
 ); \
 if(w != NULL) \
  GUIMenuItemSetSubMenu(w, submenu); \
}
      
#define DO_ADD_MENU_ITEM_CHECK          \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_CHECK, accel_group, \
  icon, label, accel_key, accel_mods, (gpointer *)&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 = (guint8 **)icon_open_20x20_xpm;
          label = "Open...";
          accel_key = 'o';
          accel_mods = GDK_CONTROL_MASK;
          func_cb = ViewerOpenCB;
          DO_ADD_MENU_ITEM_LABEL
          v->open_mi = w;

          icon = (guint8 **)icon_clear_20x20_xpm;
          label = "Clear";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerClearCB;
          DO_ADD_MENU_ITEM_LABEL
          v->clear_mi = w;

          DO_ADD_MENU_SEP

          icon = (guint8 **)icon_close_20x20_xpm;
          label = "Close";
/*          accel_key = GDK_F4; */
          accel_key = 'w';
          accel_mods = GDK_CONTROL_MASK;
          func_cb = ViewerCloseMCB;
          DO_ADD_MENU_ITEM_LABEL
          v->close_mi = w;

          DO_ADD_MENU_SEP

          icon = (guint8 **)icon_exit_20x20_xpm;
          label = "Exit";
          accel_key = 'q';
          accel_mods = GDK_CONTROL_MASK;
          func_cb = ViewerCloseAllCB;
          DO_ADD_MENU_ITEM_LABEL 
          v->exit_mi = w;
      }
      v->file_mh = GUIMenuAddToMenuBar(
          menu_bar, menu,
          "File",
          GUI_MENU_BAR_ALIGN_LEFT
      );

      /* Create edit menu */
      menu = GUIMenuCreate();
      if(menu != NULL)
      {
          icon = (guint8 **)icon_options_20x20_xpm;
          label = "Preferences...";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerPreferencesCB;
          DO_ADD_MENU_ITEM_LABEL
          v->edit_preferences = w;
      }
      v->edit_mh = GUIMenuAddToMenuBar(
          menu_bar, menu,
          "Edit",
          GUI_MENU_BAR_ALIGN_LEFT
      );

      /* Create view menu */
      menu = GUIMenuCreate();
      if(menu != NULL)
      {
          icon = NULL;
          label = "Manual Page";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerPageToggleCB;
          DO_ADD_MENU_ITEM_CHECK
          v->view_view_mi = w;

          icon = NULL;
          label = "Index";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerPageToggleCB;
          DO_ADD_MENU_ITEM_CHECK
          v->view_index_mi = w;
      }
      v->view_mh = GUIMenuAddToMenuBar(
          menu_bar, menu,
          "View",
          GUI_MENU_BAR_ALIGN_LEFT
      );

      /* Create windows menu */
      menu = GUIMenuCreate();
      if(menu != NULL)
      {
          old_client_data = client_data;
          client_data = v->core_ptr;

          icon = (guint8 **)manedit_20x20_xpm;
          label = "New Editor";
          accel_key = 0;
          accel_mods = 0;
          func_cb = MEditEditorNewCB;
          DO_ADD_MENU_ITEM_LABEL
          v->windows_new_editor = w;

          icon = (guint8 **)mp_viewer_20x20_xpm;
          label = "New Viewer";
          accel_key = 0;
          accel_mods = 0;
          func_cb = MEditViewerNewCB;
          DO_ADD_MENU_ITEM_LABEL
          v->windows_new_viewer = w;

          client_data = old_client_data;
      }
      v->windows_mh = GUIMenuAddToMenuBar(
          menu_bar, menu,
          "Windows",
          GUI_MENU_BAR_ALIGN_LEFT
      );

      /* Help menu */
      menu = MEditCreateHelpMenu(
          MEDIT_CORE(v->core_ptr), accel_group
      );
      GUIMenuAddToMenuBar(
          menu_bar, menu,
          "Help",   
          GUI_MENU_BAR_ALIGN_RIGHT
      );

#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_ITEM_SUBMENU
#undef DO_ADD_MENU_ITEM_CHECK 
#undef DO_ADD_MENU_SEP


      /* Handle for menu bar */
      w = gtk_handle_box_new();
      v->menu_bar_dock = w;
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent2 = w;

      gtk_container_add(GTK_CONTAINER(parent2), menu_bar);

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

      return;
}

/*
 *      Creates the tool bar for the viewer, this function should be
 *      called by ViewerNew(). 
 *
 *      Given parent is assumed to be a vbox.
 */
static void ViewerCreateToolBar(viewer_struct *v, GtkWidget *parent)
{
      gboolean show_tooltips = TRUE;
      GtkWidget *w, *parent2, *parent3;
      gint bw = 25, bh = 25;
      gint sw = 5, sh = -1;


      /* Handle for tool bar hbox */
      w = gtk_handle_box_new();
      v->tool_bar_dock = w;
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent2 = w;

      /* Tool bar hbox inside handle */
      w = gtk_hbox_new(FALSE, 0);
      v->tool_bar = w;
      gtk_container_border_width(GTK_CONTAINER(w), 2);
      gtk_container_add(GTK_CONTAINER(parent2), w);
      gtk_widget_show(w);
      parent3 = w;

      /* Open */
      w = (GtkWidget *)GUIButtonPixmapLabelV(
          (guint8 **)icon_open_20x20_xpm,
          "Open", NULL
      );
      v->open_btn = w;
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_set_usize(w, bw, bh);
      gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
      gtk_signal_connect(
          GTK_OBJECT(w), "clicked",
          GTK_SIGNAL_FUNC(ViewerOpenCB),
          (gpointer)v
      );
      if(show_tooltips)
          GUISetWidgetTip(w, VIEWER_TT_OPEN);
      gtk_widget_show(w);

      /* Clear */
      w = (GtkWidget *)GUIButtonPixmapLabelV(
          (guint8 **)icon_clear_20x20_xpm,
          "Clear", NULL
      );
      v->clear_btn = w;
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_set_usize(w, bw, bh);
      gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
      gtk_signal_connect(
          GTK_OBJECT(w), "clicked",
          GTK_SIGNAL_FUNC(ViewerClearCB),
          (gpointer)v
      );
      if(show_tooltips)
          GUISetWidgetTip(w, VIEWER_TT_CLEAR);
      gtk_widget_show(w);

      /* Separator */
      w = gtk_label_new("");
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_set_usize(w, sw, sh);
      gtk_widget_show(w);

      /* Stop */
      w = (GtkWidget *)GUIButtonPixmapLabelV(
          (guint8 **)icon_stop_20x20_xpm,
          "Stop", NULL
      );
      v->stop_btn = w;
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_set_usize(w, bw, bh);
      gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
      gtk_signal_connect(
          GTK_OBJECT(w), "clicked",
          GTK_SIGNAL_FUNC(ViewerStopCB),
          (gpointer)v
      );
      if(show_tooltips)
          GUISetWidgetTip(w, VIEWER_TT_STOP);
      gtk_widget_show(w);

      return;
}

/*
 *    Creates the status bar for the viewer, this function should be
 *    called by ViewerNew().
 *
 *    Given parent is assumed to be a vbox.
 */
static void ViewerCreateStatusBar(
      viewer_struct *v, GtkWidget *parent
)
{
      gboolean show_tooltips = TRUE;
      GtkWidget *w, *parent2, *parent3;
      GtkAdjustment *adj;


      /* Main hbox for status bar and window map buttons */
      w = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent = w;

      /* Handle for status bar */
      w = gtk_handle_box_new();
      v->status_bar_dock = w;
      gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
      gtk_widget_show(w);
      parent2 = w;

      /* Toplevel hbox inside dock */
      w = gtk_hbox_new(FALSE, 0);
      v->status_bar_toplevel = w;
      gtk_container_add(GTK_CONTAINER(parent2), w);
      gtk_widget_show(w);
      parent2 = w;

      /* Main frame in toplevel */
      w = gtk_frame_new(NULL);
      gtk_widget_set_usize(w, -1, 25);
      gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
      gtk_container_border_width(GTK_CONTAINER(w), 0);
      gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
      gtk_widget_show(w);
      parent2 = w;

      /* Table, put into main frame */
      w = gtk_table_new(1, 3, FALSE);
      gtk_container_add(GTK_CONTAINER(parent2), w);
      gtk_widget_show(w);
      parent2 = w;


      /* Progress bar */
      adj = (GtkAdjustment *)gtk_adjustment_new(0, 1, 100, 0, 0, 0);
      w = gtk_progress_bar_new_with_adjustment(adj);
      v->status_bar_progress = w;
      gtk_widget_set_usize(w, 100, -1);
      gtk_progress_bar_set_orientation(
          GTK_PROGRESS_BAR(w), GTK_PROGRESS_LEFT_TO_RIGHT
      );
      gtk_progress_bar_set_bar_style(
          GTK_PROGRESS_BAR(w), GTK_PROGRESS_CONTINUOUS
      );
/*
      gtk_progress_bar_set_discrete_blocks(
          GTK_PROGRESS_BAR(w), 10
      );
 */
      gtk_progress_set_activity_mode(
          GTK_PROGRESS(w), FALSE
      );
      gtk_table_attach(
          GTK_TABLE(parent2), w,
          0, 1, 0, 1,
          0,
          GTK_SHRINK | GTK_FILL,
          0, 0
      );
      gtk_widget_show(w);


      /* Label */
      w = gtk_frame_new(NULL);
      gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
      gtk_container_border_width(GTK_CONTAINER(w), 1);
      gtk_table_attach(
          GTK_TABLE(parent2), w,
          1, 2, 0, 1,
          GTK_SHRINK | GTK_EXPAND | GTK_FILL,
          GTK_SHRINK | GTK_FILL,
          0, 0
      );
      gtk_widget_show(w);
      parent3 = w;

      w = gtk_hbox_new(FALSE, 0);
      gtk_container_add(GTK_CONTAINER(parent3), w);
      gtk_widget_show(w);
      parent3 = w;

      w = gtk_label_new("");
      v->status_bar_label = w;
      gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 2);
      gtk_widget_show(w);


      /* Cursor position label */
      w = gtk_frame_new(NULL);
      gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
      gtk_container_border_width(GTK_CONTAINER(w), 1);
      gtk_table_attach(
          GTK_TABLE(parent2), w,
          2, 3, 0, 1,
          GTK_SHRINK | GTK_FILL,
          GTK_SHRINK | GTK_FILL,
          0, 0
      );
      gtk_widget_show(w);
      parent3 = w;

      w = gtk_hbox_new(FALSE, 0);
      gtk_container_add(GTK_CONTAINER(parent3), w);
      gtk_widget_show(w);
      parent3 = w;

      w = gtk_label_new("");
      v->status_bar_cursor_label = w;
      gtk_widget_set_usize(w, 100, -1);
      gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 2);
      gtk_widget_show(w);



      /* Handle bar for window map buttons */
      w = gtk_handle_box_new();
/*      v->window_map_dock = w; */
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent2 = w;

      /* Main frame in toplevel */
      w = gtk_frame_new(NULL);
      gtk_widget_set_usize(w, -1, 25);
      gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
      gtk_container_border_width(GTK_CONTAINER(w), 0);
      gtk_container_add(GTK_CONTAINER(parent2), w);
      gtk_widget_show(w);
      parent2 = w;

      /* Hbox for window map buttons */
      w = gtk_hbox_new(FALSE, 0);
      gtk_container_add(GTK_CONTAINER(parent2), w);
      gtk_widget_show(w);
      parent2 = w;

      /* Window map buttons */
      w = (GtkWidget *)GUIButtonPixmap(
          (guint8 **)manedit_16x16_xpm
      );
      gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
      gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
      gtk_widget_set_usize(w, 25, -1);
      gtk_signal_connect(
          GTK_OBJECT(w), "clicked",
          GTK_SIGNAL_FUNC(MEditEditorNewCB),
          (gpointer)v->core_ptr
      );
      if(show_tooltips)
          GUISetWidgetTip(w, MEDIT_SB_TT_NEW_EDITOR);
      gtk_widget_show(w);

      w = (GtkWidget *)GUIButtonPixmap(
          (guint8 **)mp_viewer_16x16_xpm
      );
      gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
      gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
      gtk_widget_set_usize(w, 25, -1);
      gtk_signal_connect(
          GTK_OBJECT(w), "clicked",
          GTK_SIGNAL_FUNC(MEditViewerNewCB),
          (gpointer)v->core_ptr
      );
      if(show_tooltips)
          GUISetWidgetTip(w, MEDIT_SB_TT_NEW_VIEWER);
      gtk_widget_show(w);


      return;
}

/*
 *    Creates a new viewer structure with its values reset and widgets
 *    built or NULL on error.
 */
viewer_struct *ViewerNew(gpointer core_ptr, gint editor_num)
{
      gboolean show_tooltips = TRUE;
      GtkWidget *w, *fw, *menu;
      GtkWidget *parent, *parent2, *parent3, *parent4;
      GtkWidget *scroll_parent;
      GtkCList *clist;
      GdkColormap *colormap;
      gpointer combo_rtn;
      GList *glist;
      GtkStyle *style_ptr;
      gint accel_key;
      gpointer accel_group;
      unsigned int accel_mods;
      const gchar *label;
      guint8 **icon;
      gpointer mclient_data;
      void (*func_cb)(GtkWidget *w, gpointer);
      gchar *title[4];
      medit_cursors_list_struct *cursors_list = NULL;
      medit_styles_list_struct *styles_list = NULL;
      pref_struct *pref = NULL;
      const GtkTargetEntry dnd_tar_types[] = {
{"text/plain",                          0,      MEDIT_DND_TYPE_INFO_TEXT_PLAIN},
{"text/uri-list",                       0,      MEDIT_DND_TYPE_INFO_TEXT_URI_LIST},
{"STRING",                              0,      MEDIT_DND_TYPE_INFO_STRING},
{MEDIT_DND_TYPE_NAME_EDITOR_BRANCH_CMD, GTK_TARGET_SAME_APP,
                                    MEDIT_DND_TYPE_INFO_EDITOR_BRANCH_CMD},
{MEDIT_DND_TYPE_NAME_VIEWER_BRANCH_CMD, GTK_TARGET_SAME_APP,
                                    MEDIT_DND_TYPE_INFO_VIEWER_BRANCH_CMD}
      };
      const GtkTargetEntry dnd_src_types[] = {
{MEDIT_DND_TYPE_NAME_VIEWER_BRANCH_CMD, GTK_TARGET_SAME_APP,
                                    MEDIT_DND_TYPE_INFO_VIEWER_BRANCH_CMD}
      };
      viewer_struct *v = VIEWER(g_malloc0(
          sizeof(viewer_struct)
      ));
      if(v == NULL)
          return(NULL);


      /* Get pointers to resources on core structure */
      if(core_ptr != NULL)
      {
          cursors_list = &(MEDIT_CORE(core_ptr)->cursors_list);
          styles_list = &(MEDIT_CORE(core_ptr)->styles_list);
          pref = MEDIT_CORE(core_ptr)->pref;
      }

      /* Reset values */
      v->initialized = TRUE;
      v->map_state = FALSE;
      v->processing = FALSE;
      v->stop_count = 0;

      v->core_ptr = core_ptr;
      v->editor_num = editor_num;

      v->current_page = ViewerPageNumView;
      v->selected_index_branch = NULL;

      v->cur_manpage_path = NULL;
      v->cur_manpage_name = NULL;
      v->last_open_path = NULL;


#define DO_ADD_MENU_ITEM_LABEL      \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_LABEL, accel_group, \
  icon, label, accel_key, accel_mods, (gpointer *)&fw, \
  mclient_data, func_cb \
 ); \
}
#define DO_ADD_MENU_ITEM_SUBMENU    \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_SUBMENU, accel_group, \
  icon, label, accel_key, accel_mods, (gpointer *)&fw, \
  mclient_data, func_cb \
 ); \
 if(w != NULL) \
  GUIMenuItemSetSubMenu(w, submenu); \
}
#define DO_ADD_MENU_ITEM_CHECK  \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_CHECK, accel_group, \
  icon, label, accel_key, accel_mods, (gpointer *)&fw, \
  mclient_data, func_cb \
 ); \
}
#define DO_ADD_MENU_SEP \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_SEPARATOR, NULL, \
  NULL, NULL, 0, 0, NULL, \
  NULL, NULL \
 ); \
}

      /* Toplevel */
      v->toplevel = w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_widget_realize(w);
      GUISetWMIcon(w->window, (guint8 **)mp_viewer_48x48_xpm);
      if(PrefParmGetValueB(pref, MEDIT_PREF_PARM_RECORD_WIN_POS))
      {
          /* TRUE implies pref is also valid */
          gtk_widget_set_usize(
            w,
            MAX(pref->last_viewer_width, 256),
            MAX(pref->last_viewer_height, 256)
          );
      }
      else
      {
          gtk_widget_set_usize(
             w,
              MEDIT_VIEWER_DEF_WIDTH, MEDIT_VIEWER_DEF_HEIGHT
          );
      }
      gtk_window_set_policy(
          GTK_WINDOW(w), 
          TRUE, TRUE, FALSE
      );
      if((pref != NULL) && !GTK_WIDGET_NO_WINDOW(w))
      {
          GdkGeometry geometry;

          geometry.min_width = 256;
          geometry.min_height = 256;

          geometry.base_width = 0;
          geometry.base_height = 0;

          geometry.width_inc = 1;
          geometry.height_inc = 1;
/*
          geometry.min_aspect = 1.3;
          geometry.max_aspect = 1.3;
 */
          gdk_window_set_geometry_hints(
            w->window,
            &geometry,
            GDK_HINT_MIN_SIZE |
            GDK_HINT_BASE_SIZE |
            /* GDK_HINT_ASPECT | */
            GDK_HINT_RESIZE_INC
          );
      }
      gtk_signal_connect(
          GTK_OBJECT(w), "destroy",
          GTK_SIGNAL_FUNC(ViewerDestroyCB),
          (gpointer)v
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "delete_event",
          GTK_SIGNAL_FUNC(ViewerCloseCB),
          (gpointer)v
      );
      gtk_window_set_title(GTK_WINDOW(w), MEDIT_VIEWER_TITLE);
      parent = w;

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


      /* Create menu bar */
      ViewerCreateMenuBar(v, v->main_vbox);

      /* Create tool bar */
      ViewerCreateToolBar(v, v->main_vbox);


      /* Main notebook */
      w = gtk_notebook_new();
      v->main_notebook = w;
      gtk_notebook_set_tab_pos(GTK_NOTEBOOK(w), GTK_POS_TOP);
      gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 2);
      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(ViewerSwitchPageCB),
          (gpointer)v
      );
      gtk_widget_show(w);
      parent2 = w;


      /* Begin creating page 0 on main_notebook */

      w = gtk_vbox_new(FALSE, 0);
      gtk_notebook_append_page( 
          GTK_NOTEBOOK(parent2),
          w,
          gtk_label_new("Manual Page")
      );
      gtk_widget_show(w);
      parent3 = w;

      /* Hbox to align things */
      w = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 5);
      gtk_widget_show(w);
      parent3 = w;

      /* Vbox to hold combos and view text */
      w = gtk_vbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 5);
      gtk_widget_show(w);
      parent3 = w;

      /* Hbox for category and manpage bar */
      w = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent4 = w;

      /* Category combo */
      glist = NULL;
      glist = g_list_append(glist, MEDIT_SECT_NAME_ANY);
      glist = g_list_append(glist, MEDIT_SECT_NAME_EXACT);
      glist = g_list_append(glist, MEDIT_SECT_NAME_1);
      glist = g_list_append(glist, MEDIT_SECT_NAME_2);
      glist = g_list_append(glist, MEDIT_SECT_NAME_3);
      glist = g_list_append(glist, MEDIT_SECT_NAME_4);
      glist = g_list_append(glist, MEDIT_SECT_NAME_5);
      glist = g_list_append(glist, MEDIT_SECT_NAME_6);
      glist = g_list_append(glist, MEDIT_SECT_NAME_7);
      glist = g_list_append(glist, MEDIT_SECT_NAME_8);

      w = GUIComboCreate(
          "Section:",
          MEDIT_SECT_NAME_ANY,      /* Initial value */
          glist,              /* Initial glist of items */
          20,                 /* Max items, give more for user added items */
          &combo_rtn,
          (gpointer)v,
          NULL,
          NULL
      );

      g_list_free(glist);
      glist = NULL;

      v->section_combo = (GtkWidget *)combo_rtn;
      gtk_widget_set_usize((GtkWidget *)combo_rtn, 70, -1);
      gtk_combo_set_case_sensitive(
          (GtkCombo *)combo_rtn,
          FALSE
      );
      if(show_tooltips)
          GUISetWidgetTip(GTK_COMBO(combo_rtn)->entry, VIEWER_TT_SECTION_COMBO);
      gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0);
      gtk_widget_show(w);


      /* Manpage combo */
      w = GUICreateMenuItemIcon(
          (guint8 **)icon_manual_opened_20x20_xpm
      );
      gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
      gtk_widget_show(w);

      w = GUIComboCreate(
          "ManPage:",
          "",                 /* Initial value */
          NULL,         /* Initial glist of items */
          20,                 /* Max items */
          &combo_rtn,
          (gpointer)v,
          ViewerManPageActivateCB,
          NULL
      );
      v->manpage_combo = (GtkWidget *)combo_rtn;
      gtk_combo_set_case_sensitive(
          (GtkCombo *)combo_rtn,
          TRUE
      );
      if(show_tooltips)  
          GUISetWidgetTip(GTK_COMBO(combo_rtn)->entry, VIEWER_TT_SEARCH_MANPAGE);
      gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0);
      gtk_widget_show(w);


      /* Hbox for find in current manpage combo */
      w = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent4 = w;

      w = GUICreateMenuItemIcon(
          (guint8 **)icon_search_20x20_xpm
      );
      gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
      gtk_widget_show(w);

      w = GUIComboCreate(
          "Find:",
          "",                 /* Initial value */
          NULL,               /* Initial glist of items */
          20,                 /* Max items */
          &combo_rtn,
          (gpointer)v,
          ViewerFindActivateCB,
          NULL
      );
      v->find_combo = (GtkWidget *)combo_rtn;
      gtk_combo_set_case_sensitive(
          (GtkCombo *)combo_rtn,
          TRUE
      );
      gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0);
      gtk_widget_show(w);



      /* View text table parent to hold view text and scroll bars */
      w = gtk_table_new(2, 2, FALSE);
      gtk_table_set_row_spacing(GTK_TABLE(w), 0, 2);
      gtk_table_set_col_spacing(GTK_TABLE(w), 0, 2);
      gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0);
      gtk_widget_show(w);
      parent4 = w;

      /* View text */
      v->view_text = w = gtk_text_new(NULL, NULL);
      gtk_widget_set_events(
          w,
          GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK |
          GDK_KEY_RELEASE_MASK
      );
      /* Need to connect key event signals to change editable
       * state. Reason explained in ViewerViewKeyEventCB().
       */
      gtk_signal_connect_after(
          GTK_OBJECT(w), "key_press_event",
          GTK_SIGNAL_FUNC(ViewerViewKeyEventCB),
          (gpointer)v
      );
      gtk_signal_connect_after(
          GTK_OBJECT(w), "key_release_event",
          GTK_SIGNAL_FUNC(ViewerViewKeyEventCB),
          (gpointer)v
      );
      /* Need to connect button_press_event signal_after just
       * after text widget is created, this is so that the right
       * click menu can be mapped properly.
       */
      gtk_signal_connect_after(
          GTK_OBJECT(w), "button_press_event",
          GTK_SIGNAL_FUNC(ViewerMenuMapCB),
          (gpointer)v
      );
      gtk_editable_set_editable(GTK_EDITABLE(w), FALSE);
      gtk_text_set_word_wrap(GTK_TEXT(w), TRUE);
      gtk_table_attach(
          GTK_TABLE(parent4), w,
          0, 1, 0, 1,
          GTK_EXPAND | GTK_SHRINK | GTK_FILL,
          GTK_EXPAND | GTK_SHRINK | GTK_FILL,
          0, 0
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "changed",
          GTK_SIGNAL_FUNC(ViewerTextChangeCB),
          (gpointer)v
      );
      gtk_widget_realize(w);
      if(cursors_list != NULL)  
          gdk_window_set_cursor(w->window, cursors_list->text);
      /* Set up DND target for the view text */
      GUIDNDSetTar(
          w,
          dnd_tar_types,
          sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
          GDK_ACTION_COPY | GDK_ACTION_MOVE,  /* Actions */
          GDK_ACTION_MOVE,                    /* Default action if same */
          GDK_ACTION_COPY,                    /* Default action */
          ViewerViewTextDNDDataRecievedCB,
          (gpointer)v
      );

      /* Create a new style for the GtkText, because we may want to
       * adjust the style later on.
       */
      if(GTK_WIDGET_NO_WINDOW(w))
          colormap = NULL;
      else
          colormap = gdk_window_get_colormap(w->window);
      style_ptr = gtk_widget_get_style(w);
      style_ptr = ((style_ptr == NULL) ?
          NULL : gtk_style_copy(style_ptr)
      );
      if(style_ptr != NULL)
      {
          GtkStyle *style_src_ptr;

          /* Set base color */
          style_src_ptr = styles_list->manpage_text_background;
          if((style_src_ptr != NULL) && (colormap != NULL))
          {
            style_ptr->base[GTK_STATE_NORMAL] =
                style_src_ptr->base[GTK_STATE_NORMAL];
            gdk_color_alloc(
                colormap,
                &style_ptr->base[GTK_STATE_NORMAL]
            );
          }

          style_src_ptr = styles_list->manpage_text_standard;
          if(style_src_ptr != NULL)
          {
            if(style_ptr->font != NULL)
                gdk_font_unref(style_ptr->font);
            style_ptr->font = style_src_ptr->font;
            if(style_ptr->font != NULL)
                gdk_font_ref(style_ptr->font);
          }

          gtk_widget_set_style(w, style_ptr);
      }
      gtk_widget_show(w);

      /* Vertical scroll bar for text widget */
      scroll_parent = gtk_vscrollbar_new(GTK_TEXT(w)->vadj);
      gtk_table_attach(
          GTK_TABLE(parent4), scroll_parent,
          1, 2, 0, 1,
          GTK_FILL,
          GTK_EXPAND | GTK_SHRINK | GTK_FILL,
          0, 0
      );
      gtk_widget_show(scroll_parent);


      /* View text right click menu */
      menu = (GtkWidget *)GUIMenuCreate();
      v->view_menu = menu;
      accel_group = NULL;
      mclient_data = v;

      if(menu != NULL)
      {
          icon = (guint8 **)icon_open_20x20_xpm;
          label = "Open...";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerOpenCB;
          DO_ADD_MENU_ITEM_LABEL
          v->view_open_mi = w;

          icon = (guint8 **)icon_close_20x20_xpm;
          label = "Clear";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerClearCB;
          DO_ADD_MENU_ITEM_LABEL
          v->view_clear_mi = w;
      }


      /* Begin creating page 1 on main_notebook */

      /* Index ctree vbox parent to hold index ctree widgets */
      w = gtk_vbox_new(FALSE, 5);
      gtk_container_border_width(GTK_CONTAINER(w), 5);
      gtk_notebook_append_page(
          GTK_NOTEBOOK(parent2),
          w,
          gtk_label_new("Index")
      );
      gtk_widget_show(w);
      parent3 = w;


      /* Hbox for find in pages combo */
      w = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent4 = w;

      w = GUICreateMenuItemIcon(
          (guint8 **)icon_search_20x20_xpm
      );
      gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
      gtk_widget_show(w);

      w = GUIComboCreate(
          "Find Content:",
          "",                 /* Initial value */
          NULL,               /* Initial glist of items */
          20,                 /* Max items */
          &combo_rtn,
          v,
          ViewerFindInPagesActivateCB,
          NULL
      );
      v->index_find_in_pages_combo = (GtkWidget *)combo_rtn;
      gtk_combo_set_case_sensitive(
          (GtkCombo *)combo_rtn,
          TRUE
      );
      gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0);
      gtk_widget_show(w);


      /* Scrolled window for index ctree */
      w = gtk_scrolled_window_new(NULL, NULL);
      gtk_scrolled_window_set_policy(
          GTK_SCROLLED_WINDOW(w),
          GTK_POLICY_AUTOMATIC,
          GTK_POLICY_AUTOMATIC
      );
      gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0);
      gtk_widget_show(w);
      scroll_parent = w;

      /* Index ctree */
      title[0] = "Name";
      title[1] = "Lines";
      title[2] = "Read";
      title[3] = "Description";
      v->index_ctree = w = gtk_ctree_new_with_titles(
          4, 0, title
      );
      clist = GTK_CLIST(w);
      if(!GTK_WIDGET_NO_WINDOW(w))
      {
          gtk_signal_connect_after(
            GTK_OBJECT(w), "button_press_event",
            GTK_SIGNAL_FUNC(ViewerMenuMapCB),
            (gpointer)v
          );
      }
      gtk_clist_column_titles_passive(clist);
      gtk_clist_column_titles_show(clist);
      gtk_clist_set_row_height(clist, MEDIT_LIST_ROW_SPACING);
      gtk_clist_set_shadow_type(clist, GTK_SHADOW_IN);
      gtk_clist_set_column_width(clist, 0, 200);
      gtk_clist_set_column_width(clist, 1, 60);
      gtk_clist_set_column_width(clist, 2, 40);
      gtk_clist_set_column_width(clist, 3, 250);
      gtk_container_add(GTK_CONTAINER(scroll_parent), w);
      gtk_signal_connect(
          GTK_OBJECT(w), "tree_select_row",
          GTK_SIGNAL_FUNC(ViewerIndexCTreeSelectCB),
          (gpointer)v
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "tree_unselect_row",
          GTK_SIGNAL_FUNC(ViewerIndexCTreeUnselectCB),
          (gpointer)v
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "tree_expand",
          GTK_SIGNAL_FUNC(ViewerIndexCTreeExpandCB),
          (gpointer)v
      );
      gtk_signal_connect(
          GTK_OBJECT(w), "tree_collapse",
          GTK_SIGNAL_FUNC(ViewerIndexCTreeExpandCB), 
          (gpointer)v
      );
      /* Set up DND for the index ctree */
      GUIDNDSetSrc(
          w,
          dnd_src_types,
          sizeof(dnd_src_types) / sizeof(GtkTargetEntry),
          GDK_ACTION_COPY,                /* Actions */
          GDK_BUTTON1_MASK,               /* Buttons */
          NULL,
          ViewerIndexCTreeDNDDataRequestCB,
          ViewerDNDDataDeleteCB,
          NULL,
          v
      );
      /* No drag target for this index ctree */
      gtk_widget_show(w);

      /* Index ctree right click menu */
      menu = (GtkWidget *)GUIMenuCreate();
      v->index_menu = menu;
      accel_group = NULL;
      mclient_data = v;

      if(menu != NULL)
      {
          icon = NULL;
          label = "Expand/Collapse";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerIndexBranchExpandCB;
          DO_ADD_MENU_ITEM_LABEL
          v->index_expand_mi = w;

          DO_ADD_MENU_SEP

          icon = (guint8 **)icon_folder_parent_20x20_xpm;
          label = "Parent";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerIndexGotoParentCB;
          DO_ADD_MENU_ITEM_LABEL
          v->index_parent_mi = w;

          icon = (guint8 **)icon_goto_20x20_xpm;
          label = "Goto";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerGotoCB;
          DO_ADD_MENU_ITEM_LABEL
          v->index_goto_mi = w;

          icon = (guint8 **)icon_reload_20x20_xpm;
          label = "Refresh";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerIndexRefreshCB;
          DO_ADD_MENU_ITEM_LABEL
          v->index_refresh_mi = w;

          DO_ADD_MENU_SEP

          icon = (guint8 **)icon_stop_20x20_xpm;
          label = "Stop";
          accel_key = 0;
          accel_mods = 0;
          func_cb = ViewerStopCB; 
          DO_ADD_MENU_ITEM_LABEL
          v->index_stop_mi = w;  
      }

      /* Separator */
      w = gtk_hseparator_new();
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_show(w);

      /* Hbox for index ctree buttons */
      w = gtk_hbox_new(TRUE, 0);
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 5);
      gtk_widget_show(w);
      parent4 = w;

      /* Parent button */
      w = GUIButtonPixmapLabelH(
          (guint8 **)icon_folder_parent_20x20_xpm, "Parent", NULL
      );
      v->index_parent_btn = w;
      gtk_widget_set_usize(w, VIEWER_BTN_WIDTH, VIEWER_BTN_HEIGHT);
      GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
      gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
      gtk_signal_connect(
          GTK_OBJECT(w), "clicked",
          GTK_SIGNAL_FUNC(ViewerIndexGotoParentCB), v
      );
      gtk_widget_show(w);

      /* Goto button */
      w = GUIButtonPixmapLabelH(
          (guint8 **)icon_goto_20x20_xpm, "Goto", NULL
      );
      v->index_goto_btn = w;
      gtk_widget_set_usize(w, VIEWER_BTN_WIDTH, VIEWER_BTN_HEIGHT);
      GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
      gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
      gtk_signal_connect(
          GTK_OBJECT(w), "clicked",
          GTK_SIGNAL_FUNC(ViewerGotoCB), v
      );
      gtk_widget_show(w);

      /* Refresh button */
      w = GUIButtonPixmapLabelH(
          (guint8 **)icon_reload_20x20_xpm, "Refresh", NULL
      );
      v->index_refresh_btn = w;
      gtk_widget_set_usize(w, VIEWER_BTN_WIDTH, VIEWER_BTN_HEIGHT);
      GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
      gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
      gtk_signal_connect(
          GTK_OBJECT(w), "clicked",
          GTK_SIGNAL_FUNC(ViewerIndexRefreshCB), v
      );
      gtk_widget_show(w);






      /* Create status bar */
      ViewerCreateStatusBar(v, v->main_vbox);




#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_ITEM_SUBMENU
#undef DO_ADD_MENU_ITEM_CHECK
#undef DO_ADD_MENU_SEP

      /* Reset values to defaults */
      ViewerReset(v, FALSE);

      /* Update menus */
      ViewerUpdateMenus(v);

      return(v);
}

/*
 *    Updates all menus on viewer.
 */
void ViewerUpdateMenus(viewer_struct *v)
{
      static gboolean reenterant = FALSE;
      GtkWidget *w, *menu;
      medit_core_struct *core_ptr;
      GtkCTreeRow *branch_row;
      gboolean sensitivity, state;
      viewer_index_item_struct *item;
      gint toolbar_btn_layout = 2;

      if(v == NULL)
          return;

      if(!v->initialized)
          return;

      core_ptr = MEDIT_CORE(v->core_ptr);
      if(core_ptr == NULL)
          return;

      if(reenterant)
          return;
      else
          reenterant = TRUE;


#define SET_WIDGET_SENSITIVITY            {     \
 if(w != NULL)                            \
  gtk_widget_set_sensitive(w, sensitivity);     \
}
#define SET_CHECK_MENU_ITEM_STATE   {     \
 GUIMenuItemSetCheck(w, state, TRUE);           \
}
#define SET_BUTTON_LAYOUT           {     \
 if(w != NULL) {                    \
  switch(toolbar_btn_layout) {                  \
   case 2:                          \
    GUIButtonChangeLayout(w, 1, 1);       \
    gtk_widget_set_usize(w, 60, 50);            \
    break;                          \
   case 1:                          \
    GUIButtonChangeLayout(w, 1, 0);       \
    gtk_widget_set_usize(w, 30, 30);            \
    break;                          \
   default:                         \
    GUIButtonChangeLayout(w, 0, 1);       \
    gtk_widget_set_usize(w, -1, 30);            \
    break;                          \
} } }

      /* Get tool bar button display type */
      if(PrefParmGetValueB(
          core_ptr->pref, MEDIT_PREF_PARM_TOOLBAR_PT
      ))
          toolbar_btn_layout = 2;
      else if(PrefParmGetValueB(
          core_ptr->pref, MEDIT_PREF_PARM_TOOLBAR_P
      ))          
          toolbar_btn_layout = 1;
      else
          toolbar_btn_layout = 0;


/* TODO: Update title */


      /* File menu */
      menu = v->file_mh;
      if(menu != NULL)
      {
          w = v->open_mi;
          sensitivity = !FileBrowserIsQuery();
          SET_WIDGET_SENSITIVITY
      }

      /* View menu */
      menu = v->view_mh;
      {
          w = v->view_view_mi;
          state = (v->current_page == ViewerPageNumView) ? TRUE : FALSE;
          SET_CHECK_MENU_ITEM_STATE

          w = v->view_index_mi;
          state = (v->current_page == ViewerPageNumIndex) ? TRUE : FALSE;
          SET_CHECK_MENU_ITEM_STATE
      }


      /* View right click menu */
      menu = v->view_menu;
      if(menu != NULL)
      {
          w = v->view_open_mi;
          sensitivity = !FileBrowserIsQuery();
          SET_WIDGET_SENSITIVITY



      }

      /* Index right click menu */
      menu = v->index_menu;
      if(menu != NULL)
      {
          if(v->selected_index_branch == NULL)
            branch_row = NULL;
          else
            branch_row = GTK_CTREE_ROW(v->selected_index_branch);
          w = v->index_parent_mi;
          sensitivity = (branch_row != NULL) ?
            (branch_row->parent != NULL) : FALSE;
          SET_WIDGET_SENSITIVITY  

          if((v->index_ctree != NULL) &&
             (v->selected_index_branch != NULL)
          )
            item = VIEWER_INDEX_ITEM(gtk_ctree_node_get_row_data(
                GTK_CTREE(v->index_ctree), v->selected_index_branch
            ));
          else
            item = NULL;
          w = v->index_expand_mi;
          if(item == NULL)
            sensitivity = FALSE;
          else if(item->type == ViewerIndexItemTypeDirectory)
            sensitivity = TRUE;
          else
            sensitivity = FALSE;
          SET_WIDGET_SENSITIVITY

          w = v->index_goto_mi;
          if(item == NULL)
            sensitivity = FALSE;
          else if((item->type == ViewerIndexItemTypeManualPage) ||
                (item->type == ViewerIndexItemTypeOtherFile)
          )
            sensitivity = TRUE;
          else
            sensitivity = FALSE;
          SET_WIDGET_SENSITIVITY

          w = v->index_refresh_mi;
          sensitivity = TRUE;
          SET_WIDGET_SENSITIVITY

          w = v->index_stop_mi;
          sensitivity = v->processing;
          SET_WIDGET_SENSITIVITY
      }

      /* Buttons on tool bar */
      w = v->tool_bar;
      if(w != NULL)
      {
          w = v->open_btn;
          sensitivity = !FileBrowserIsQuery();
          SET_WIDGET_SENSITIVITY
          SET_BUTTON_LAYOUT

          w = v->clear_btn;
          SET_BUTTON_LAYOUT

          w = v->stop_btn;
          sensitivity = v->processing;
          SET_WIDGET_SENSITIVITY
          SET_BUTTON_LAYOUT
      }


      /* Buttons on index ctree page */
      if(v->selected_index_branch == NULL)
          branch_row = NULL;
      else
          branch_row = GTK_CTREE_ROW(v->selected_index_branch);
      w = v->index_parent_btn;
      sensitivity = ((branch_row == NULL) ?
          FALSE : (branch_row->parent != NULL)
      );
      SET_WIDGET_SENSITIVITY

      if((v->index_ctree != NULL) &&
         (v->selected_index_branch != NULL)
      )
          item = VIEWER_INDEX_ITEM(gtk_ctree_node_get_row_data(
            GTK_CTREE(v->index_ctree), v->selected_index_branch
          ));
      else
          item = NULL;
      w = v->index_goto_btn;
      if(item == NULL)
          sensitivity = FALSE;
      else if((item->type == ViewerIndexItemTypeManualPage) ||
            (item->type == ViewerIndexItemTypeOtherFile)
      )
          sensitivity = TRUE;
      else
          sensitivity = FALSE;
      SET_WIDGET_SENSITIVITY 

      w = v->index_refresh_btn;
      sensitivity = TRUE;
      SET_WIDGET_SENSITIVITY


#undef SET_WIDGET_SENSITIVITY
#undef SET_CHECK_MENU_ITEM_STATE
#undef SET_BUTTON_LAYOUT

      reenterant = FALSE;
}



/*
 *    Updates the cursor position value on the status bar.
 */
void ViewerSetStatusPosition(viewer_struct *v, gint row, gint column)
{
      gchar *s;
      GtkWidget *w;

      if(v == NULL)
          return;

      w = v->status_bar_cursor_label;
      if(w == NULL)
          return;

      s = g_strdup_printf(
          "Row: %i / Col: %i",
          row, column
      );
      gtk_label_set_text(GTK_LABEL(w), s);
      g_free(s);

      while(gtk_events_pending() > 0)
          gtk_main_iteration();
}

/*
 *    Sets the status message for the viewer.
 *
 *    Passing NULL clears the message.
 *
 *      Note that gtk_main_iteration() will be called when this function
 *      is called to update the label properly.
 */
void ViewerSetStatusMessage(viewer_struct *v, const gchar *mesg)
{
      GtkWidget *w;

      if(v == NULL)
          return;

      w = v->status_bar_label;
      if(w == NULL)
          return;

      gtk_label_set_text(
          GTK_LABEL(w),
          (mesg != NULL) ? mesg : ""
      );

      while(gtk_events_pending() > 0)
          gtk_main_iteration();
}

/*
 *    Sets the status of the progress bar on the viewer.
 *
 *    Value must be from 0.0 to 1.0 inclusive.
 *
 *    Note that gtk_main_iteration() will be called when this function
 *    is called to update the progress bar properly.
 */
void ViewerSetStatusProgress(viewer_struct *v, gfloat percent)
{
      gfloat p;
      GtkWidget *w;

      if(v == NULL)
          return;

      w = v->status_bar_progress;
      if(w == NULL)
          return;

      /* Negative? Implies just do activity */
      if(percent < 0.0)
      {
          GtkAdjustment *adj = GTK_PROGRESS(w)->adjustment;
      
          percent = gtk_progress_get_value(GTK_PROGRESS(w)) + 1;
          if(percent > adj->upper)
            percent = adj->lower;

          gtk_progress_set_activity_mode(
            GTK_PROGRESS(w), TRUE
          );
          gtk_progress_set_value(GTK_PROGRESS(w), percent);

          while(gtk_events_pending() > 0)
            gtk_main_iteration();

          return;
      }

      if(percent > 1.0f)
          percent = 1.0f;

      gtk_progress_set_format_string(
          GTK_PROGRESS(w), "%p%%"
      );
      gtk_progress_set_show_text(
          GTK_PROGRESS(w), (percent > 0.0) ? TRUE : FALSE
      );
      gtk_progress_set_activity_mode(
          GTK_PROGRESS(w), FALSE
      );

      p = v->status_bar_progress_pos_last;
      if(p > percent)
          p = percent;

      while(TRUE)
      {
          if(p > percent)
            p = percent;

          gtk_progress_bar_update(
            GTK_PROGRESS_BAR(w), p
          );

          while(gtk_events_pending() > 0)
            gtk_main_iteration();

          if(p < percent)
            p += 0.05f;
          else
            break;
      }

      v->status_bar_progress_pos_last = percent;
}

/*
 *      Records positions and sizes of viewer's widgets onto its
 *      core structure's preferences structure.
 */
void ViewerRecordPositions(viewer_struct *v)
{
      medit_core_struct *core_ptr;
      pref_struct *pref;
      GtkWidget *w;


      if(v == NULL)
          return;

      if(!v->initialized)
          return;

      if(v->processing)
          return;

      core_ptr = MEDIT_CORE(v->core_ptr);
      if(core_ptr == NULL)
          return;

      pref = core_ptr->pref;
      if(pref == NULL)
          return;

      w = v->toplevel;
      if((w == NULL) ? 0 : !GTK_WIDGET_NO_WINDOW(w))
      {
          GdkWindow *window = w->window;
          if(window != NULL)
          {
            gint x, y, width, height, depth;

            gdk_window_get_geometry(
                window, &x, &y, &width, &height, &depth
            );

            pref->last_viewer_width = MAX(width, 0);
            pref->last_viewer_height = MAX(height, 0);
          }

          if(v->last_open_path != NULL)
          {
            g_free(pref->last_opened_manage_view_path);
            pref->last_opened_manage_view_path = STRDUP(
                v->last_open_path
            );
          }
      }
}

/*
 *    Resets all values to defaults on viewer and deletes
 *    undo/redo list and all layout branches.
 */
void ViewerReset(viewer_struct *v, gboolean need_unmap)
{
      static gboolean reenterant = FALSE;
      GtkCTree *ctree;
      GtkCTreeNode *branch, *prev_branch;
      GtkCTreeRow *branch_row;
      pref_struct *pref;
      GtkWidget *w;
      medit_core_struct *core_ptr;


      if(v == NULL)
          return;

      if(!v->initialized)
          return;

      /* Still processing? */
      if(v->processing)
          return;

      core_ptr = MEDIT_CORE(v->core_ptr);
      if(core_ptr == NULL)
          return;

      pref = core_ptr->pref;

      if(reenterant)
          return;
      else
          reenterant = TRUE;


      /* Unmap as needed */
      if(need_unmap)
      {
          w = v->toplevel;
          if(w != NULL)
            gtk_widget_hide(w);
          v->map_state = FALSE;
      }

      /* Reset values */
      v->processing = FALSE;
      v->stop_count = 0;

      v->status_bar_progress_pos_last = 0;

      v->last_scroll_hpos = 0.0;
      v->last_scroll_vpos = 0.0;

      /* Resize toplevel */
      w = v->toplevel;
      if((w == NULL) ? 0 : !GTK_WIDGET_NO_WINDOW(w))
      {
        if(PrefParmGetValueB(pref, MEDIT_PREF_PARM_RECORD_WIN_POS))
        {
          /* TRUE implies pref is also valid */
          gtk_widget_set_usize(
            w,
            MAX(pref->last_viewer_width, 256),
            MAX(pref->last_viewer_height, 256)
          );
        }
        else
        {
          gtk_widget_set_usize(
             w,
            MEDIT_VIEWER_DEF_WIDTH, MEDIT_VIEWER_DEF_HEIGHT
          );
        }
      }


      /* Clear search combo */
      GUIComboClearAll(v->manpage_combo);


      /* Remove all branches on index ctree */
      ctree = (GtkCTree *)v->index_ctree;
      if(ctree != NULL)
      {
        branch = gtk_ctree_node_nth(ctree, 0);
        while(branch != NULL)  
        {
          prev_branch = branch;       /* Record current branch */

          /* Get branch row data */
          branch_row = GTK_CTREE_ROW(branch);
          /* Get next sibling branch */
          branch = ((branch_row == NULL) ? NULL : branch_row->sibling);

          /* Remove current branch */
          gtk_ctree_remove_node(ctree, prev_branch);
        }
      }

      /* Switch page */
      w = v->main_notebook;
      if(w != NULL)
      {
          gtk_notebook_set_page(
            GTK_NOTEBOOK(w),
            ViewerPageNumView
          );
      }
      v->current_page = ViewerPageNumView;


      /* Free current manual page file path */
      g_free(v->cur_manpage_path);
      v->cur_manpage_path = NULL;

      /* Free current manual page name */
      g_free(v->cur_manpage_name);
      v->cur_manpage_name = NULL;

      /* Get paths */
      if(pref != NULL)
      {
          g_free(v->last_open_path);
          v->last_open_path = STRDUP(pref->last_opened_manage_view_path);
      }

      /* Reset status messages */
      ViewerSetStatusPosition(v, 0, 0);
      ViewerSetStatusMessage(v, NULL);
      ViewerSetStatusProgress(v, 0.0);

      /* Clear view text */
      ViewerTextDelete(v, 0, -1);

      reenterant = FALSE;
}

/*
 *    Maps viewer as needed.
 */
void ViewerMap(viewer_struct *v)
{
      GtkWidget *w;

      if(v == NULL)
          return;

      if(!v->initialized)
          return;

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

      if(!v->map_state)
      {
          v->map_state = TRUE;
          gtk_widget_show(w);
      }

      return;
}

/*
 *      Unmaps viewer as needed.
 */
void ViewerUnmap(viewer_struct *v)
{       
      GtkWidget *w;
      
      if(v == NULL)
          return;
      
      if(!v->initialized)
          return;
   
      w = v->toplevel;
      if(w == NULL)
          return;
      
      if(v->map_state)
      {
          v->map_state = FALSE;
          gtk_widget_hide(w);
      }

      return;
}


/*
 *    Destroys the viewer and all its widgets and allocated resources.
 *
 *    The viewer structure itself will also be deleted.
 */
void ViewerDelete(viewer_struct *v)
{
      GtkWidget *w;


      if(v == NULL)
          return;

      if(v->initialized)
      {
          /* Still processing? */
          if(v->processing)
            return;

          /* Reset values to defaults, this will cause any loaded data
           * to be deleted.
           */
          ViewerReset(v, TRUE);

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


          v->file_mh = NULL;
          v->open_mi = NULL;
          v->clear_mi = NULL;
          v->close_mi = NULL;
          v->exit_mi = NULL;

          v->edit_mh = NULL;
          v->edit_preferences = NULL;

          v->view_index_mi = NULL;
          v->view_view_mi = NULL;
          v->view_index_mi = NULL;

          v->windows_mh = NULL;
          v->windows_new_editor = NULL;
          v->windows_new_viewer = NULL;

          w = v->menu_bar;
          v->menu_bar = NULL;
          DO_DESTROY_WIDGET

          w = v->menu_bar_dock;
          v->menu_bar_dock = NULL;
          DO_DESTROY_WIDGET

          w = v->tool_bar;
          v->tool_bar = NULL;
          v->open_btn = NULL;
          v->clear_btn = NULL;
          v->stop_btn = NULL;
          DO_DESTROY_WIDGET

          w = v->tool_bar_dock;
          v->tool_bar_dock = NULL;
          DO_DESTROY_WIDGET


          w = v->view_text;
          v->view_text = NULL;
          DO_DESTROY_WIDGET

          w = v->view_menu;
          v->view_menu = NULL;
          v->view_open_mi = NULL;
          DO_DESTROY_WIDGET

          w = v->index_menu;
          v->index_menu = NULL;
          v->index_expand_mi = NULL;
          v->index_parent_mi = NULL;
          v->index_goto_mi = NULL;
          v->index_refresh_mi = NULL;
          v->index_stop_mi = NULL;
          DO_DESTROY_WIDGET


          w = v->main_notebook;
          v->main_notebook = NULL;
          v->view_text = NULL;
          v->index_ctree = NULL;
          v->index_parent_btn = NULL;
          v->index_goto_btn = NULL;
          v->index_refresh_btn = NULL;
          v->manpage_combo = NULL;
          v->section_combo = NULL;
          v->find_combo = NULL;
          DO_DESTROY_WIDGET


          w = v->status_bar_toplevel;
          v->status_bar_toplevel = NULL;
          v->status_bar_progress = NULL;
          v->status_bar_label = NULL;
          v->status_bar_cursor_label = NULL;
          DO_DESTROY_WIDGET

          w = v->status_bar_dock;
          v->status_bar_dock = NULL;
          DO_DESTROY_WIDGET


          w = v->main_vbox;
          v->main_vbox = NULL;
          DO_DESTROY_WIDGET

          w = v->toplevel;
          v->toplevel = NULL;
          DO_DESTROY_WIDGET

#undef DO_DESTROY_WIDGET
      }

      /* Delete last opened file path */
      g_free(v->last_open_path);
      v->last_open_path = NULL;

      g_free(v);
}


Generated by  Doxygen 1.6.0   Back to index