2fbf67cb29
* NEW: Update FSF address in every source file. git-svn-id: svn://localhost/gambas/trunk@3870 867c0c6c-44f3-4631-809d-bfa615b0a4ec
429 lines
8.6 KiB
C++
429 lines
8.6 KiB
C++
/***************************************************************************
|
|
|
|
gsplitter.cpp
|
|
|
|
(c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "widgets.h"
|
|
#include "widgets_private.h"
|
|
#include "gapplication.h"
|
|
#include "gsplitter.h"
|
|
|
|
static void cb_notify(GtkPaned *paned, GParamSpec *arg1, gSplitter *data)
|
|
{
|
|
if (!strcmp(arg1->name, "position"))
|
|
data->emit(SIGNAL(data->onResize));
|
|
}
|
|
|
|
static void cb_size_allocate(GtkPaned *widget, GtkAllocation *allocation, gSplitter *data)
|
|
{
|
|
data->updateChild(gtk_paned_get_child1(widget));
|
|
}
|
|
|
|
static void cb_child_visibility(GtkWidget *widget, gSplitter *data)
|
|
{
|
|
data->updateVisibility();
|
|
}
|
|
|
|
GtkPaned *gSplitter::next(GtkPaned *iter)
|
|
{
|
|
GtkWidget *child;
|
|
|
|
for(;;)
|
|
{
|
|
if (!iter)
|
|
iter = GTK_PANED(border);
|
|
else
|
|
iter = (GtkPaned*)gtk_paned_get_child2(iter);
|
|
|
|
if (!iter)
|
|
return NULL;
|
|
|
|
child = gtk_paned_get_child1(iter);
|
|
if (child) // && gApplication::controlItem(child)->isVisible())
|
|
return iter;
|
|
}
|
|
}
|
|
|
|
void gSplitter::updateVisibility()
|
|
{
|
|
int i, n;
|
|
GtkPaned *iter;
|
|
|
|
// Hide internal GTK_PANED
|
|
|
|
n = childCount() - 1;
|
|
while (n >= 0)
|
|
{
|
|
//fprintf(stderr, "child(%d)->isVisible() = %d\n", n, child(n)->isVisible());
|
|
if (child(n)->isVisible())
|
|
break;
|
|
n--;
|
|
}
|
|
|
|
//fprintf(stderr, "updateVisibility: last visible is child #%d\n", n);
|
|
|
|
for (i = 0; i <= n; i++)
|
|
{
|
|
iter = GTK_PANED(gtk_widget_get_parent(child(i)->border));
|
|
gtk_widget_show(GTK_WIDGET(iter));
|
|
}
|
|
|
|
n++;
|
|
|
|
if (n < childCount())
|
|
{
|
|
iter = GTK_PANED(gtk_widget_get_parent(child(n)->border));
|
|
gtk_widget_hide(GTK_WIDGET(iter));
|
|
}
|
|
|
|
if (n >= (childCount() - 1))
|
|
refresh(); // Workaround frame drawing bug when the last one is shown or hidden
|
|
emit(SIGNAL(onResize));
|
|
}
|
|
|
|
static int get_child_width(GtkPaned *iter)
|
|
{
|
|
GtkWidget *child = gtk_paned_get_child1(iter);
|
|
if (gApplication::controlItem(child)->isVisible())
|
|
return gtk_paned_get_position(iter);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
gSplitter::gSplitter(gContainer *parent, bool vert) : gContainer(parent)
|
|
{
|
|
g_typ=Type_gSplitter;
|
|
vertical = vert;
|
|
|
|
border = vertical ? gtk_vpaned_new() : gtk_hpaned_new();
|
|
curr = GTK_PANED(border);
|
|
widget = border;
|
|
|
|
onResize = NULL;
|
|
_notify = false;
|
|
|
|
realize();
|
|
|
|
g_signal_connect_after(G_OBJECT(curr), "notify", G_CALLBACK(cb_notify), (gpointer)this);
|
|
g_signal_connect_after(G_OBJECT(curr), "size-allocate", G_CALLBACK(cb_size_allocate), (gpointer)this);
|
|
}
|
|
|
|
void gSplitter::insert(gControl *child, bool realize)
|
|
{
|
|
GtkWidget *w = child->border;
|
|
GtkWidget *tmp;
|
|
int layout_count, *layout;
|
|
|
|
lock();
|
|
|
|
layout_count = layoutCount();
|
|
layout = (int *)malloc(sizeof(int) * (layout_count + 1));
|
|
getLayout(layout);
|
|
if (layout_count == 0)
|
|
layout[0] = 1;
|
|
else if (layout_count == 1)
|
|
layout[1] = layout[0] = 1;
|
|
else
|
|
{
|
|
int s = 0;
|
|
for (int i = 0; i < layout_count; i++)
|
|
s += layout[i];
|
|
layout[layout_count] = s / layout_count;
|
|
}
|
|
|
|
if (!gtk_paned_get_child1(curr))
|
|
{
|
|
gtk_paned_pack1(curr, w, TRUE, TRUE);
|
|
gtk_paned_set_position(curr, child->width());
|
|
}
|
|
else
|
|
{
|
|
if (!vertical)
|
|
tmp=gtk_hpaned_new();
|
|
else
|
|
tmp=gtk_vpaned_new();
|
|
|
|
//gtk_widget_show_all(tmp);
|
|
gtk_paned_pack2(curr, tmp, TRUE, TRUE);
|
|
curr=GTK_PANED(tmp);
|
|
gtk_paned_pack1(curr, w, TRUE, TRUE);
|
|
gtk_paned_set_position(curr, child->width());
|
|
//g_signal_connect_after(G_OBJECT(curr),"notify",G_CALLBACK(slt_notify),(gpointer)this);
|
|
g_signal_connect_after(G_OBJECT(curr), "size-allocate", G_CALLBACK(cb_size_allocate), (gpointer)this);
|
|
g_signal_connect_after(G_OBJECT(curr), "notify", G_CALLBACK(cb_notify), (gpointer)this);
|
|
}
|
|
|
|
g_signal_connect_after(G_OBJECT(w), "show", G_CALLBACK(cb_child_visibility), (gpointer)this);
|
|
g_signal_connect_after(G_OBJECT(w), "hide", G_CALLBACK(cb_child_visibility), (gpointer)this);
|
|
|
|
gContainer::insert(child, realize);
|
|
//updateVisibility();
|
|
|
|
setLayout(layout, layout_count + 1);
|
|
free(layout);
|
|
|
|
unlock();
|
|
}
|
|
|
|
void gSplitter::remove(gControl *child)
|
|
{
|
|
gContainer::remove(child);
|
|
|
|
g_signal_handlers_disconnect_matched(G_OBJECT(child->border), G_SIGNAL_MATCH_DATA, 0, (GQuark)0, NULL, NULL, (gpointer)this);
|
|
}
|
|
|
|
int gSplitter::handleCount()
|
|
{
|
|
int n, nh = -1;
|
|
|
|
for (n = 0; n < childCount(); n++)
|
|
if (child(n)->isVisible())
|
|
nh++;
|
|
|
|
return nh;
|
|
}
|
|
|
|
void gSplitter::setLayout(int *array, int count)
|
|
{
|
|
int i;
|
|
int num;
|
|
double factor;
|
|
GtkPaned *iter;
|
|
int shandle;
|
|
|
|
if (!array)
|
|
return;
|
|
|
|
if (count > childCount())
|
|
count = childCount();
|
|
|
|
if (count <= 0)
|
|
return;
|
|
|
|
#if 0
|
|
fprintf(stderr, "setLayout: ");
|
|
for (i = 0; i < count; i++)
|
|
fprintf(stderr, "%d ", array[i]);
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
gtk_widget_style_get (border,
|
|
"handle-size", &shandle,
|
|
(void *)NULL);
|
|
|
|
factor = 0;
|
|
for (i = 0;; i++)
|
|
{
|
|
if (i >= count)
|
|
break;
|
|
if (child(i)->isVisible())
|
|
{
|
|
num = array[i];
|
|
factor += num;
|
|
}
|
|
}
|
|
|
|
if (factor == 0)
|
|
return;
|
|
|
|
factor = ((vertical ? height() : width()) - handleCount() * shandle) / factor;
|
|
|
|
lock();
|
|
|
|
iter = next(NULL);
|
|
|
|
for (i = 0;; i++)
|
|
{
|
|
if (!iter) break;
|
|
if (i >= count)
|
|
break;
|
|
|
|
if (child(i)->isVisible())
|
|
{
|
|
num = array[i];
|
|
gtk_paned_set_position(iter, (gint)(num * factor + 0.5));
|
|
}
|
|
|
|
iter = next(iter);
|
|
}
|
|
|
|
unlock();
|
|
|
|
emit(SIGNAL(onResize));
|
|
|
|
//fprintf(stderr, "setLayout: layout = %s\n", layout());
|
|
}
|
|
|
|
int gSplitter::layoutCount()
|
|
{
|
|
GtkPaned *iter;
|
|
int n = 0;
|
|
|
|
iter = next(NULL);
|
|
if (iter)
|
|
{
|
|
n++;
|
|
for(;;)
|
|
{
|
|
iter = next(iter);
|
|
if (!iter)
|
|
break;
|
|
n++;
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
void gSplitter::getLayout(int *array)
|
|
{
|
|
GtkPaned *iter;
|
|
int vl, sum, nhandle, shandle;
|
|
int n;
|
|
|
|
gtk_widget_style_get (border,
|
|
"handle-size", &shandle,
|
|
(void *)NULL);
|
|
|
|
iter = next(NULL);
|
|
if (iter)
|
|
{
|
|
sum = 0;
|
|
nhandle = 0;
|
|
n = 0;
|
|
for(;;)
|
|
{
|
|
vl = get_child_width(iter); //gtk_paned_get_position(iter);
|
|
iter = next(iter);
|
|
if (!iter)
|
|
break;
|
|
if (vl)
|
|
nhandle++;
|
|
sum += vl;
|
|
array[n++] = vl;
|
|
}
|
|
|
|
if (childCount() > 1 && child(childCount() - 1)->isVisible())
|
|
array[n++] = (vertical ? height() : width()) - sum - nhandle * shandle;
|
|
else
|
|
array[n++] = 0;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
int gSplitter::childCount()
|
|
{
|
|
GtkPaned *iter;
|
|
int ret=0;
|
|
|
|
if ( !gtk_paned_get_child1(GTK_PANED(border)) ) return 0;
|
|
iter=GTK_PANED(border);
|
|
|
|
while (iter)
|
|
{
|
|
ret++;
|
|
iter=(GtkPaned*)gtk_paned_get_child2(iter);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
gControl* gSplitter::child(int index)
|
|
{
|
|
GtkPaned *iter;
|
|
int ret=0;
|
|
GtkWidget *element=NULL;
|
|
|
|
if (index<0) return NULL;
|
|
|
|
if ( !gtk_paned_get_child1(GTK_PANED(border)) ) return NULL;
|
|
iter=GTK_PANED(border);
|
|
|
|
while (iter)
|
|
{
|
|
if (ret==index)
|
|
{
|
|
element=gtk_paned_get_child1(iter);
|
|
break;
|
|
}
|
|
ret++;
|
|
iter=(GtkPaned*)gtk_paned_get_child2(iter);
|
|
}
|
|
|
|
return gApplication::controlItem(element);
|
|
}
|
|
#endif
|
|
|
|
void gSplitter::updateChild(GtkWidget *w)
|
|
{
|
|
int bucle, nchd;
|
|
gControl *chd;
|
|
|
|
//g_debug("updateChild: %p", w);
|
|
|
|
nchd = childCount();
|
|
for (bucle = 0; bucle < nchd; bucle++)
|
|
{
|
|
chd=child(bucle);
|
|
|
|
if (w && chd->border != w)
|
|
continue;
|
|
|
|
chd->_dirty_pos = false;
|
|
chd->_dirty_size = false;
|
|
|
|
if (chd->bufX == chd->border->allocation.x
|
|
&& chd->bufY == chd->border->allocation.y
|
|
&& chd->bufW == chd->border->allocation.width
|
|
&& chd->bufH == chd->border->allocation.height)
|
|
continue;
|
|
|
|
chd->bufX = chd->border->allocation.x;
|
|
chd->bufY = chd->border->allocation.y;
|
|
chd->bufW = chd->border->allocation.width;
|
|
chd->bufH = chd->border->allocation.height;
|
|
//chd->resize(chd->border->allocation.width, chd->border->allocation.height);
|
|
//gApplication::setDirty();
|
|
//g_debug("gSplitter::updateChild: %s -> (%d %d %d %d)", chd->name(), chd->x(), chd->y(), chd->width(), chd->height());
|
|
if (chd->isContainer())
|
|
((gContainer*)chd)->performArrange();
|
|
}
|
|
}
|
|
|
|
void gSplitter::performArrange()
|
|
{
|
|
//g_debug("performArrange");
|
|
updateChild();
|
|
updateVisibility();
|
|
}
|
|
|
|
void gSplitter::resize(int w, int h)
|
|
{
|
|
if (w == width() && h == height())
|
|
return;
|
|
|
|
//l = layout();
|
|
gContainer::resize(w, h);
|
|
//setLayout(l);
|
|
}
|
|
|
|
|