splitting panes



I want to write an application which allows the user to split the screen into two panes, then to split either or both of the resulting panes again, and possibly again, though I admit this last is unlikely to be needed.

The difficulties I've encountered, plus some of the comments I've read in the archives suggest this is not going to be possible.

Before I modify my design to something much less flexible, can anyone confirm that what I want to do really is impossible?

The summary of the symptoms I see is this:
(GTK 2, Linux Red Hat 9)
Each pane consists (for prototyping's sake) of a vpane and a scrolled window. Each time I split, I add1 a new vpane into the previous one and reparent the scrolled window into it, then add2 a new pane below with its own scrolled window.
I'm having difficulty with my terminology here.
If I split the first pane into two, call them 1 and 2, then if I split 2 into 2 and 3, that's fine and works as I expect
.
However, if I try to split 1 again, or, having split 2 into 2 and 3, I then try to split 2 again, I get various symptoms, depending on how I've left my code hacked. (I've been trying this for days!) Sometimes the pane I'm trying to split just vanishes, leaving everything else where it was, just with a grey background where the old pane was. Sometimes the pane is (correctly) halved in height but the new child isn't added, and then lower panes all move up.

It seems the only pane which can be correctly split is one which hasn't already been split, even though each time I add a new pair of panes.

The code I'm running is embedded below (there's a makefile at the end):

So am I wasting my time or is there a way to do this?
Rob Clack

/*  Last edited: Feb 19 09:57 2004 (rnc) */

#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>

typedef struct SplitWindow splitWindow;
typedef struct SplitPane   splitPane;

struct SplitWindow {
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *vpane;
  GtkWidget *toolbar;
  GList     *pList;
  splitPane *focuspane;
};


struct SplitPane {
  splitWindow *parentWindow;
  GtkWidget *pane;
  GtkWidget *swindow;
};

/* function prototypes */

static void  createMainWindow(splitWindow *window);
static void  addPane         (splitWindow *window, int w, int h );
static void  shrinkPane      (splitWindow *window, int w, int h );
static void recordFocus (GtkWidget *widget, GdkEvent *event, gpointer data);
static int   matchPane       (gconstpointer a, gconstpointer b);
static void  SplitPane       (GtkWidget *widget, gpointer data);
static void  ClosePane       (GtkWidget *widget, gpointer data);

/**************** start of code *************************/

static void Quit(GtkWidget *widget, gpointer data)
{
  splitWindow *window = (splitWindow*)data;

  g_list_free(window->pList);
  gtk_main_quit();
  return;
}

static void recordFocus(GtkWidget *widget, GdkEvent *event, gpointer data)
{
  splitPane *spane = (splitPane*)data;
  /* point the parent window's focuspane pointer at this spane */
  spane->parentWindow->focuspane = spane;
}


static void createMainWindow(splitWindow *window)
{
  window->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window->window), "Paned Windows");
  g_signal_connect (G_OBJECT (window->window), "destroy",
                    G_CALLBACK (Quit), window);

  gtk_container_set_border_width (GTK_CONTAINER (window->window), 10);
  gtk_widget_set_size_request (GTK_WIDGET (window->window), 450, 600);

    /* add a vbox for the toolbar and main pane */
  window->vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window->window), window->vbox);

  window->toolbar = gtk_toolbar_new();

  /* I know gtk_toolbar_append_item is deprecated, but I can't find
   * what I'm supposed to use instead. */

gtk_toolbar_append_item(GTK_TOOLBAR(window->toolbar), "Split","Split Window",
                          NULL, NULL, GTK_SIGNAL_FUNC(SplitPane), window );

gtk_toolbar_append_item(GTK_TOOLBAR(window->toolbar), "Close","Close Pane",
                          NULL, NULL, GTK_SIGNAL_FUNC(ClosePane), window );


gtk_box_pack_start(GTK_BOX(window->vbox), window->toolbar, FALSE, FALSE, 0);

  /* create the first vpane and pack it into the vbox */
  window->vpane = gtk_vpaned_new();
gtk_box_pack_start(GTK_BOX (window->vbox), window->vpane, FALSE, FALSE, 0);

  return;
}

static void addPane(splitWindow *window, int w, int h )
{
  splitPane *spane = (splitPane*)malloc(sizeof(splitPane));
  static int i = 1;
  char text[25];
  GtkWidget *view;
  GtkTextBuffer *buffer;

  /* set up splitPane for this window */
  spane->parentWindow = window;
  spane->pane         = gtk_vpaned_new();
  spane->swindow      = gtk_scrolled_window_new (NULL, NULL);

  /* add splitPane to the linked list */
  window->pList = g_list_append(window->pList, spane);

  /* set up contents of scrolled window */
  view = gtk_text_view_new ();
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
  sprintf(text, "Text for pane %d\n", i++);
  gtk_text_buffer_set_text(buffer, text, -1);

gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (spane->swindow),
                                           view);
  gtk_widget_set_size_request (GTK_WIDGET (spane->swindow), w, h);

  /* when the user clicks a button in the view, call recordFocus() */
  g_signal_connect (GTK_OBJECT (view), "button_press_event",
                    GTK_SIGNAL_FUNC (recordFocus), spane);

  /* First time through, we add the pane to the top vpane in the
   * window. Subsequently it's in the lower half of the current pane. */
  if (window->focuspane)
    gtk_paned_add2(GTK_PANED(window->focuspane->pane), spane->pane);
  else
      gtk_paned_add1(GTK_PANED(window->vpane), spane->pane);

  /* focus on the new pane */
  recordFocus(NULL, NULL, spane);
  gtk_widget_grab_focus(window->focuspane->pane);

  /* add the scolled window to the pane */
  gtk_paned_add1 (GTK_PANED (spane->pane), spane->swindow);

  gtk_widget_show_all (window->window);

    return;
}


static void shrinkPane(splitWindow *window, int w, int h)
{
  splitPane *spane = (splitPane*)malloc(sizeof(splitPane));

  /* create a new vpane and hook the old scrolled window up to it */
  spane->parentWindow = window;
  spane->pane         = gtk_vpaned_new();
  spane->swindow      = window->focuspane->swindow;

  gtk_widget_set_size_request (GTK_WIDGET (spane->pane), w, h);
  gtk_widget_set_size_request (GTK_WIDGET (spane->swindow), w, h);

  /* have tried a reparent here but currently commented out
     gtk_widget_reparent(window->focuspane->swindow, spane->swindow);*/

  gtk_paned_add1 (GTK_PANED(window->focuspane->pane), spane->pane);

/* when the user clicks a button in the scrolled window, call recordFocus() */ g_signal_connect (GTK_OBJECT (window->focuspane->pane), "button_press_event",
                    GTK_SIGNAL_FUNC (recordFocus), spane);
  return;
}


static void SplitPane(GtkWidget *widget, gpointer data)
{
  splitWindow *window = (splitWindow*)data;
  GtkRequisition req;

  /* get size of current pane & halve it */
  gtk_widget_size_request(window->focuspane->swindow, &req);
  req.height /= 2;

  /* shrink the old pane */
  shrinkPane(window, req.width, req.height);
  /* add a new pane below the old one */
  addPane   (window, req.width, req.height);

  return;
}


static void ClosePane(GtkWidget *widget, gpointer data)
{
  splitWindow *window = (splitWindow*)data;
  GList *list;
  GtkRequisition req;

  /* find the pane we're going to give focus to */
  list = g_list_find_custom(window->pList, window->focuspane, matchPane);
  list = g_list_previous(list);

  /* remove the old pane from the linked list */
  g_list_remove(window->pList, window->focuspane);

  /* reset the focus pane */
  window->focuspane = (splitPane*)list->data;

  /* record the new focus pane */
  recordFocus(NULL, NULL, window->focuspane);

  gtk_widget_grab_focus(window->focuspane->pane);
  gtk_widget_size_request(window->focuspane->pane, &req);
  req.height *= 2;
gtk_widget_set_size_request(window->focuspane->pane, req.width, req.height);

  /* destroy the old pane */
  gtk_widget_destroy(GTK_WIDGET(window->focuspane->pane));
  /*gtk_widget_destroy(GTK_WIDGET(window->focuspane->swindow));*/
}


int matchPane(gconstpointer a, gconstpointer b)
{
  splitPane *aptr = (splitPane*)a;
  splitPane *bptr = (splitPane*)b;

  if (aptr->pane == bptr->pane)
    return 0;
  else
    return 1;
}


int main( int   argc,
          char *argv[] )
{
  splitWindow *window = (splitWindow*)malloc(sizeof(splitWindow));

  window->pList = NULL;  /* initialise GList to empty */

  gtk_init (&argc, &argv);

  createMainWindow(window);

  addPane (window, 400, 500);

  gtk_main ();

  return 0;
}
/*********************** end of file **************************/

Makefile:
CFLAGS = -g -Wall `pkg-config --cflags libgnomecanvas-2.0`
LDFLAGS = `pkg-config --libs libgnomecanvas-2.0`

split : split.c
        cc -g -o split $(CFLAGS) $(LDFLAGS) split.c

--
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 It was a good theory that met all valid criteria apart from that of
 actually being right.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Rob Clack                        Acedb Development,  Informatics Group
 email: rnc sanger ac uk                Wellcome Trust Sanger Institute
 Tel: +44 1223 494883                   Wellcome Trust Genome Campus
 Fax: +44 1223 494919                   Hinxton  Cambridge    CB10 1SA



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]