[dia] [gtk-deprecated] obsolete TableDialog



commit 84630526edb8fc915809da07887b761dd3526983
Author: Hans Breuer <hans breuer org>
Date:   Sun Aug 19 19:00:42 2012 +0200

    [gtk-deprecated] obsolete TableDialog
    
    With the newly developed ArrayProp editor there is no
    need to have a custom made dialog anymore. The new one
    is based completely on StdProps and only looses one
    feature: constraints for Primary Key. But that's
    requested to be removed by bug 668484 anyway.
    
    The new dialog appearance could use some polishing
    but that might come when turning UmlClassDialog ...

 lib/Makefile.am                 |    5 +
 lib/diacellrendererenum.c       |   46 ++
 lib/diacellrendererenum.h       |    9 +
 lib/makefile.msc                |    2 +
 lib/prop_sdarray.c              |   22 +-
 lib/prop_sdarray_widget.c       |  605 ++++++++++++++++
 lib/prop_sdarray_widget.h       |    1 +
 objects/Database/Makefile.am    |    3 +-
 objects/Database/database.h     |   19 -
 objects/Database/table.c        |  234 ++++++-
 objects/Database/table_dialog.c | 1445 ---------------------------------------
 objects/makefile.msc            |    5 +-
 12 files changed, 882 insertions(+), 1514 deletions(-)
---
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 838c538..6761891 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -30,6 +30,8 @@ stdprop_files = \
 	prop_widgets.h \
 	prop_sdarray.c \
 	prop_sdarray.h \
+	prop_sdarray_widget.c \
+	prop_sdarray_widget.h \
 	propdialogs.c  \
 	propregistry.c
 
@@ -185,6 +187,9 @@ libdia_la_SOURCES =  \
 		diacontext.c \
 		diacontext.h \
 		\
+		diacellrendererenum.c \
+		diacellrendererenum.h \
+		\
 		diacellrendererproperty.c \
 		diacellrendererproperty.h \
 		\
diff --git a/lib/diacellrendererenum.c b/lib/diacellrendererenum.c
new file mode 100644
index 0000000..4d3111e
--- /dev/null
+++ b/lib/diacellrendererenum.c
@@ -0,0 +1,46 @@
+#include <config.h>
+
+#include "diacellrendererenum.h"
+#include "properties.h"
+
+enum
+{
+  COLUMN_ENUM_NAME,
+  COLUMN_ENUM_VALUE,
+  NUM_ENUM_COLUMNS
+};
+
+GtkCellRenderer *
+dia_cell_renderer_enum_new (const PropEnumData *enum_data)
+{
+  /* The combo-renderer should be customized for better rendering,
+   * e.g. using a shorter name like visible_char[]={ '+', '-', '#', ' ' } instead of
+   * the full of _uml_visibilities[]; likewise for line style there or arrows there
+   * should be a pixbuf preview instead of strings ...
+   */
+  GtkCellRenderer *cell_renderer = gtk_cell_renderer_combo_new ();
+  /* create the model from enum_data */
+  GtkListStore *model;
+  GtkTreeIter iter;
+  int i;
+  
+  model = gtk_list_store_new (NUM_ENUM_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
+  for (i = 0; enum_data[i].name != NULL; ++i) {
+  
+    gtk_list_store_append (model, &iter);
+
+    gtk_list_store_set (model, &iter,
+                        COLUMN_ENUM_NAME, enum_data[i].name,
+                        COLUMN_ENUM_VALUE, enum_data[i].enumv,
+                        -1);
+  }
+
+  g_object_set (cell_renderer,
+                "model", model,
+                "text-column", COLUMN_ENUM_NAME,
+                "has-entry", FALSE,
+                "editable", TRUE,
+                NULL);
+
+  return cell_renderer;
+}
diff --git a/lib/diacellrendererenum.h b/lib/diacellrendererenum.h
new file mode 100644
index 0000000..cffcb52
--- /dev/null
+++ b/lib/diacellrendererenum.h
@@ -0,0 +1,9 @@
+#ifndef DIA_CELLRENDERERENUM_H
+#define DIA_CELLRENDERERENUM_H
+
+#include <gtk/gtk.h>
+#include "diatypes.h"
+
+GtkCellRenderer *dia_cell_renderer_enum_new (const PropEnumData *enum_data);
+
+#endif
diff --git a/lib/makefile.msc b/lib/makefile.msc
index d5e76a7..f1ceb66 100644
--- a/lib/makefile.msc
+++ b/lib/makefile.msc
@@ -54,6 +54,7 @@ OBJECTS = \
 	debug.obj \
 	diaarrowchooser.obj \
 	diaarrowselector.obj \
+	diacellrendererenum.obj \
 	diacolorselector.obj \
 	diacontext.obj \
 	dialinechooser.obj \
@@ -101,6 +102,7 @@ OBJECTS = \
 	prop_matrix.obj \
 	prop_pixbuf.obj \
 	prop_sdarray.obj \
+	prop_sdarray_widget.obj \
 	prop_text.obj \
 	prop_widgets.obj \
 	propdesc.obj \
diff --git a/lib/prop_sdarray.c b/lib/prop_sdarray.c
index 3c47aab..4000f09 100644
--- a/lib/prop_sdarray.c
+++ b/lib/prop_sdarray.c
@@ -33,6 +33,7 @@
 #include "widgets.h"
 #include "properties.h"
 #include "propinternals.h"
+#include "prop_sdarray_widget.h"
 
 /******************************************/
 /* The SARRAY and DARRAY property types.  */
@@ -239,31 +240,24 @@ darrayprop_set_from_offset(ArrayProperty *prop,
 /*!
  * Create a dialog containing the list of array properties
  */
-static void
-darray_prop_edit (GtkWidget *widget, gpointer data)
-{
-  ArrayProperty *prop G_GNUC_UNUSED = data;
-  
-}
-
 static WIDGET *
 arrayprop_get_widget(ArrayProperty *prop, PropDialog *dialog) 
-{ 
-  GtkWidget *ret = gtk_button_new_with_label (prop->common.descr->tooltip);
-  g_signal_connect (G_OBJECT (ret), "clicked",
-                    G_CALLBACK (darray_prop_edit), prop);
-  
+{
+  GtkWidget *ret = _arrayprop_get_widget (prop, dialog);
+
   return ret;  
 }
 
 static void 
-arrayprop_reset_widget(NoopProperty *prop, WIDGET *widget)
+arrayprop_reset_widget(ArrayProperty *prop, WIDGET *widget)
 {
+  _arrayprop_reset_widget (prop, widget);
 }
 
 static void 
-arrayprop_set_from_widget(NoopProperty *prop, WIDGET *widget) 
+arrayprop_set_from_widget(ArrayProperty *prop, WIDGET *widget) 
 {
+  _arrayprop_set_from_widget (prop, widget);
 }
 
 static gboolean 
diff --git a/lib/prop_sdarray_widget.c b/lib/prop_sdarray_widget.c
new file mode 100644
index 0000000..f3496e9
--- /dev/null
+++ b/lib/prop_sdarray_widget.c
@@ -0,0 +1,605 @@
+/* Dia -- a diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * Property system for dia objects/shapes.
+ * Copyright (C) 2000 James Henstridge
+ * Copyright (C) 2001 Cyrille Chepelov
+ *
+ * Properties List Widget
+ * Copyright (C) 2007  Hans Breuer
+ *
+ * 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 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ The basic idea is to register *every* Dia property within the GType system. 
+ Thus we could just put these into e.g. a list-store, but some range checking
+ may also be possible for load/bindings/etc.
+ 
+ The registered type could replace the type quark as it would give a fast 
+ unique number as well.
+ 
+ Prop names with namespces like UML? No. they would by nothing new, a simple 
+ 'dia__' should be enough to make them unique. Also the basic idea of Dia's 
+ property type system always was: same name, same type. 
+
+ Stuff like 'line_width' should mean the same everywhere ...
+ *BUT* does the same assumption hold for 'attributes (UML::Class|Database::Table)' ??
+ 
+ First iteration:
+   - dia prop type (PROP_TYPE_*) plus member name ('line_width') give a unique GType
+   + PROP_TYPE_INT also may include a range
+   + PROP_TYPE_ENUM
+   - 
+   
+ WHEN TO REGISTER?
+  - during prop_desc_list_calculate_quarks() that is on firts access of *_describe_props()
+  - (eralier?) during object_register_type() - 
+
+ SO MUCH FOR THE ORIGINAL IDEA.
+
+ what's been implemented below is something a lot simpler, just enough to
+ support exisiting ArrayProp usage ;)
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "properties.h"
+#include "propinternals.h"
+#include "diacellrendererenum.h"
+
+/** A small wrapper to connect to the model */
+static GtkCellRenderer *
+_cell_renderer_enum_new (const Property *p)
+{
+  const EnumProperty *prop = (const EnumProperty *)p;
+  PropEnumData *enumdata = prop->common.descr->extra_data;
+  GtkCellRenderer *cren = dia_cell_renderer_enum_new (enumdata);
+
+  return cren;
+}
+/** Wrapper to setup ranges */
+static GtkCellRenderer *
+_cell_renderer_real_new (const Property *p)
+{
+  const RealProperty *prop = (RealProperty *)p;
+  GtkCellRenderer *cren = gtk_cell_renderer_spin_new ();
+  PropNumData *numdata = prop->common.descr->extra_data;
+  GtkAdjustment *adj;
+
+  /* must be non NULL to make it editable */
+  adj = gtk_adjustment_new (prop->real_data,
+			    numdata->min, numdata->max,
+			    numdata->step, 
+			    10.0 * numdata->step, 0);
+
+  g_object_set (G_OBJECT (cren), "adjustment", adj, NULL);
+
+  return cren;
+}
+static void
+_toggle_data_func (GtkTreeViewColumn *tree_column,
+		   GtkCellRenderer   *cell,
+		   GtkTreeModel      *model,
+		   GtkTreeIter       *iter,
+		   gpointer           user_data)
+{
+  gint column = GPOINTER_TO_INT (user_data);
+  gboolean value;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (model), iter, 
+                      column, &value, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), iter, 
+                      column, value, -1);
+}
+/* Found no built-in way to get to the column in the callback ... */
+#define COLUMN_KEY "column-key"
+/** Make it editable, connect signals */
+static void
+_toggle_callback (GtkCellRendererToggle *renderer,
+                  gchar                 *path_string,
+                  GtkTreeView           *tree_view)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+  GtkTreePath *path;
+  GtkTreeIter iter;
+  gboolean value;
+  int column;
+
+  path = gtk_tree_path_new_from_string (path_string);
+  if (!gtk_tree_model_get_iter (model, &iter, path))
+    {
+      g_warning ("%s: bad path?", G_STRLOC);
+      return;
+    }
+  gtk_tree_path_free (path);
+
+  column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (renderer), COLUMN_KEY));
+
+  gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 
+                      column, &value, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 
+                      column, !value, -1);
+  g_object_set_data (G_OBJECT (model), "modified", GINT_TO_POINTER (1));
+}
+static GtkCellRenderer *
+_cell_renderer_toggle_new (const Property *p, GtkTreeView *view)
+{
+  GtkCellRenderer *cren = gtk_cell_renderer_toggle_new ();
+
+  g_object_set (G_OBJECT (cren), 
+		"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+		"activatable", TRUE,
+		NULL);
+  g_signal_connect(G_OBJECT (cren), "toggled",
+		   G_CALLBACK (_toggle_callback), view);
+
+  return cren;
+}
+static void
+_text_edited (GtkCellRenderer *renderer,
+	      gchar           *path_string,
+	      gchar           *new_text,
+	      GtkTreeView     *tree_view)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+  GtkTreePath *path;
+  GtkTreeIter iter;
+  gchar *value;
+  int column;
+
+  path = gtk_tree_path_new_from_string (path_string);
+  if (!gtk_tree_model_get_iter (model, &iter, path))
+    {
+      g_warning ("%s: bad path?", G_STRLOC);
+      return;
+    }
+  gtk_tree_path_free (path);
+
+  column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (renderer), COLUMN_KEY));
+
+  gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 
+                      column, &value, -1);
+  g_free (value);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 
+                      column, g_strdup (new_text), -1);
+  g_object_set_data (G_OBJECT (model), "modified", GINT_TO_POINTER (1));
+}
+static GtkCellRenderer *
+_cell_renderer_text_new (const Property *p, GtkTreeView *tree_view)
+{
+  GtkCellRenderer *cren = gtk_cell_renderer_text_new ();
+
+  g_signal_connect (G_OBJECT (cren), "edited",
+		    G_CALLBACK (_text_edited), tree_view);
+  g_object_set (G_OBJECT (cren), "editable", TRUE, NULL);
+
+  return cren;
+}
+
+typedef void (*DataFunc) (GtkTreeViewColumn *tree_column,
+			  GtkCellRenderer   *cell,
+			  GtkTreeModel      *model,
+			  GtkTreeIter       *iter,
+			  gpointer           user_data);
+
+static struct {
+  const char *type;  /* the type sting */
+  GQuark      type_quark; /* it's calculated quark */
+  GType       gtype;
+  GtkCellRenderer *(*create_renderer) ();
+  const char *bind;
+  DataFunc    data_func;
+} _dia_gtk_type_map[] = {
+  { PROP_TYPE_DARRAY, 0, G_TYPE_POINTER, /* just child data */ },
+  { PROP_TYPE_BOOL, 0, G_TYPE_BOOLEAN, _cell_renderer_toggle_new, "active" },
+  { PROP_TYPE_INT, 0, G_TYPE_INT, gtk_cell_renderer_spin_new },
+  { PROP_TYPE_ENUM, 0, G_TYPE_INT, _cell_renderer_enum_new, "text" },
+  { PROP_TYPE_REAL, 0, G_TYPE_DOUBLE, _cell_renderer_real_new },
+  { PROP_TYPE_STRING, 0, G_TYPE_STRING, _cell_renderer_text_new, "text" },
+  { NULL, 0 }
+};
+
+static int
+_find_type (const Property *prop)
+{
+  int i;
+
+  /* calculate quarks first time called */
+  if (_dia_gtk_type_map[0].type_quark == 0) {
+    for (i = 0; _dia_gtk_type_map[i].type != NULL; ++i) {
+      _dia_gtk_type_map[i].type_quark = g_quark_from_static_string (_dia_gtk_type_map[i].type);
+    }
+  }
+
+  for (i = 0; _dia_gtk_type_map[i].type != NULL; ++i) {
+    if (prop->type_quark == _dia_gtk_type_map[i].type_quark)
+      return i;
+  }
+  return -1;
+}
+
+/**
+ * Create an empty model (list store) with Dia types mapped to GType
+ */
+static GtkTreeStore *
+create_sdarray_model (ArrayProperty *prop)
+{
+  int idx, i, columns = prop->ex_props->len;
+  GtkTreeStore *model;
+  GType *types = g_alloca (sizeof(GType) * columns);
+
+  for (i = 0; i < columns; i++) {
+    Property *p = g_ptr_array_index(prop->ex_props, i);
+
+    /* map Dia's property types to gtk-tree_model */
+    idx = _find_type (p);
+    if (idx >= 0) {
+      types[i] = _dia_gtk_type_map[idx].gtype;
+    } else {
+      types[i] = G_TYPE_POINTER;
+      g_warning (G_STRLOC "No model type for '%s'\n", p->descr->name);
+    }
+  }
+  model = gtk_tree_store_newv (columns, types);
+
+  return model;
+}
+
+static gboolean
+_get_active_iter (GtkTreeView *tree_view,
+	          GtkTreeIter *iter)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+
+  /* will not work with GTK_SELECTION_MULTIPLE */
+  if (!gtk_tree_selection_get_selected (selection, NULL, iter)) {
+    /* nothing selected yet, just use the start */
+    return gtk_tree_model_get_iter_first (model, iter);
+  } else {
+    /* done with it */
+    return TRUE;
+  }
+}
+/* Working on the model, not the properties */
+static void
+_insert_row_callback (GtkWidget   *button,
+		      GtkTreeView *tree_view)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+  GtkTreeIter iter;
+
+  if (!_get_active_iter (tree_view, &iter))
+    gtk_tree_store_insert_after (GTK_TREE_STORE (model), &iter, NULL, NULL);
+  else
+    gtk_tree_store_insert_after (GTK_TREE_STORE (model), &iter, NULL, &iter);
+  gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tree_view), &iter);
+
+}
+static void
+_remove_row_callback (GtkWidget   *button,
+		      GtkTreeView *tree_view)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+  GtkTreeIter iter;
+
+  if (_get_active_iter (tree_view, &iter)) {
+    GtkTreeIter next = iter;
+    if (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &next))
+      gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tree_view), &next);
+    gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
+  }
+}
+static void
+_upper_row_callback (GtkWidget   *button,
+		     GtkTreeView *tree_view)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+  GtkTreeIter iter;
+
+  if (_get_active_iter (tree_view, &iter)) {
+    /* There is no gtk_tree_model_iter_prev, so we have to resort to pathes */
+    GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
+    GtkTreeIter prev;
+
+    if (   path != NULL
+        && gtk_tree_path_prev (path)
+        && gtk_tree_model_get_iter (model, &prev, path))
+      gtk_tree_store_move_before (GTK_TREE_STORE (model), &iter, &prev);
+    else
+      gtk_tree_store_move_before (GTK_TREE_STORE (model), &iter, NULL);
+    gtk_tree_path_free (path);
+  }
+}
+static void
+_lower_row_callback (GtkWidget   *button,
+		     GtkTreeView *tree_view)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+  GtkTreeIter iter;
+
+  if (_get_active_iter (tree_view, &iter)) {
+    GtkTreeIter pos = iter;
+    if (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &pos))
+      gtk_tree_store_move_after (GTK_TREE_STORE (model), &iter, &pos);
+    else
+      gtk_tree_store_move_after (GTK_TREE_STORE (model), &iter, NULL);
+  }
+}
+
+/*! 
+ * PropertyType_GetWidget: create a widget capable of editing the property
+ */
+WIDGET *
+_arrayprop_get_widget (ArrayProperty *prop, PropDialog *dialog) 
+{
+  GtkTreeStore *model;
+  GtkWidget *view;
+  int idx, i, cols, rows;
+  const PropDescCommonArrayExtra *extra = prop->common.descr->extra_data;
+
+  /* create */
+  cols = prop->ex_props->len;
+  model = create_sdarray_model (prop);
+
+  /* visualize */
+  view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+  /* to keep add/remove simple */
+  gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)),
+			       GTK_SELECTION_SINGLE);
+
+  for (i = 0; i < cols; i++) {
+    /* for every property type we need a cell renderer and view */
+    Property *p = g_ptr_array_index(prop->ex_props, i);
+
+    idx = _find_type (p);
+    if (idx >= 0) {
+      GtkCellRenderer *renderer;
+      GtkTreeViewColumn *col;
+
+      if (!_dia_gtk_type_map[idx].create_renderer)
+	continue;
+
+      renderer = (_dia_gtk_type_map[idx].create_renderer) (p, view);
+      g_object_set_data (G_OBJECT (renderer), COLUMN_KEY, GINT_TO_POINTER (i));
+      col = gtk_tree_view_column_new_with_attributes (
+		p->descr->description, renderer,
+		_dia_gtk_type_map[idx].bind, i,
+		NULL);
+      gtk_tree_view_column_set_sort_column_id (col, i);
+      gtk_tree_view_column_set_cell_data_func (col, renderer,
+					       _dia_gtk_type_map[idx].data_func,
+					       GINT_TO_POINTER(i), NULL);
+      gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
+
+      if (p->descr->tooltip) {
+	/* FIXME: does not work, probably due to immediately done size calculation */
+	GtkTooltip *tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
+
+	gtk_tooltip_set_text (tooltip, p->descr->tooltip);
+
+	gtk_tree_view_set_tooltip_cell (GTK_TREE_VIEW (view), tooltip, NULL, col, NULL);
+      }
+    } else {
+      g_print ("No model type for '%s'\n", p->descr->name);
+    }
+  }
+
+  /* setup additional controls ... */
+  {
+    static struct {
+      const gchar *stock;
+      GCallback    callback;
+    } _button_data[] = {
+      { GTK_STOCK_ADD,     G_CALLBACK (_insert_row_callback) },
+      { GTK_STOCK_REMOVE,  G_CALLBACK (_remove_row_callback) },
+      { GTK_STOCK_GO_UP,   G_CALLBACK (_upper_row_callback) },
+      { GTK_STOCK_GO_DOWN, G_CALLBACK (_lower_row_callback) },
+      { NULL, NULL }
+    };
+    GtkWidget *button;
+    int i;
+    GtkWidget *hbox = gtk_hbox_new (FALSE /* less size for vutton column */, 0);
+    GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
+
+    gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE /* no expand */, FALSE, 0);
+
+    for (i = 0; _button_data[i].stock != NULL; ++i) {
+      button = gtk_button_new_from_stock (_button_data[i].stock);
+      g_signal_connect (G_OBJECT (button), "clicked",
+			_button_data[i].callback, view);
+      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+    }
+    gtk_widget_show_all (vbox);
+    gtk_widget_show (view);
+    gtk_box_pack_start (GTK_BOX (hbox), view, TRUE /* expand */, TRUE /* fill */, 0);
+    g_object_set_data (G_OBJECT (hbox), "tree-view", view);
+    return hbox;
+  }
+
+  g_object_set_data (G_OBJECT (view), "tree-view", view);
+  return view;
+}
+
+static void
+_write_store (GtkTreeStore *store, GtkTreeIter *parent_iter, ArrayProperty *prop)
+{
+  int idx, i, j, cols, rows;
+
+  cols = prop->ex_props->len;
+  rows = prop->records->len;
+
+  for (j = 0; j < rows; ++j) {
+    GtkTreeIter iter;
+    GPtrArray *r = g_ptr_array_index(prop->records, j);
+ 
+    gtk_tree_store_append (store, &iter, parent_iter);
+
+    for (i = 0; i < cols; ++i) {
+      Property *p = g_ptr_array_index(r,i);
+
+      idx = _find_type (p);
+      if (idx < 0)
+	continue;
+
+      if (p->type_quark == g_quark_from_static_string (PROP_TYPE_DARRAY)) /* recurse */
+	_write_store (store, &iter, (ArrayProperty *)p);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_BOOL))
+	gtk_tree_store_set (store, &iter, i, ((BoolProperty *)p)->bool_data, -1);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_INT))
+	gtk_tree_store_set (store, &iter, i, ((IntProperty *)p)->int_data, -1);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_ENUM))
+	gtk_tree_store_set (store, &iter, i, ((EnumProperty *)p)->enum_data, -1);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_REAL))
+	gtk_tree_store_set (store, &iter, i, ((RealProperty *)p)->real_data, -1);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_STRING))
+	gtk_tree_store_set (store, &iter, i, ((StringProperty *)p)->string_data, -1);
+      else {
+	/* only complain if we have a visible widget */
+	if (_dia_gtk_type_map[idx].create_renderer != NULL)
+	  g_warning (G_STRLOC " Missing getter for '%s'", p->descr->type);
+      }
+    }
+  }
+}
+
+/*! 
+ * PropertyType_ResetWidget: get the value of the property into the widget
+ */
+void
+_arrayprop_reset_widget(ArrayProperty *prop, WIDGET *widget)
+{
+  GtkWidget *view = widget;
+  GtkTreeView *tree_view = g_object_get_data (G_OBJECT (view), "tree-view");
+  GtkTreeStore *store = GTK_TREE_STORE (gtk_tree_view_get_model (tree_view));
+
+  gtk_tree_store_clear (store);
+
+  _write_store (store, NULL, prop);
+  g_object_set_data (G_OBJECT (store), "modified", GINT_TO_POINTER (0));
+}
+
+static gboolean
+_array_prop_adjust_len (ArrayProperty *prop, guint len)
+{
+  guint i, j;
+  guint num_props = prop->ex_props->len;
+
+  if (prop->records->len == len)
+    return FALSE;
+  /* see also: pydia-property.c */
+  for (i = len; i < prop->records->len; ++i) {
+    GPtrArray *record = g_ptr_array_index(prop->records, i);
+    guint j;
+    for (j = 0; j < num_props; j++) {
+      Property *inner =g_ptr_array_index(record, j);
+      inner->ops->free(inner);
+    }
+    g_ptr_array_free(record, TRUE);
+  }
+  for (i = prop->records->len; i < len; ++i) {
+    GPtrArray *record = g_ptr_array_new();
+
+    for (j = 0; j < num_props; j++) {
+      Property *ex = g_ptr_array_index(prop->ex_props, j);
+      Property *inner = ex->ops->copy(ex);
+
+      g_ptr_array_add(record, inner);
+    }
+    g_ptr_array_add(prop->records, record);
+  }
+  g_ptr_array_set_size(prop->records, len);
+  return TRUE;
+}
+static void
+_read_store (GtkTreeStore *store, GtkTreeIter *iter, ArrayProperty *prop)
+{
+  GtkTreeModel *model = GTK_TREE_MODEL (store);
+  int idx, i, j, cols, rows;
+  GtkTreeIter parent_iter;
+  gboolean modified;
+
+  cols = prop->ex_props->len;
+  
+  if (gtk_tree_model_iter_parent (model, &parent_iter, iter))
+    modified = _array_prop_adjust_len (prop, gtk_tree_model_iter_n_children (model, &parent_iter));
+  else
+    modified = _array_prop_adjust_len (prop, gtk_tree_model_iter_n_children (model, NULL));
+  /* Length adjustment might be the only thing done ... */
+  if (modified)
+    g_object_set_data (G_OBJECT (store), "modified", GINT_TO_POINTER (1));
+  rows = prop->records->len;
+
+  for (j = 0; j < rows; ++j) {
+    GPtrArray *r = g_ptr_array_index(prop->records, j);
+
+    for (i = 0; i < cols; ++i) {
+      Property *p = g_ptr_array_index(r,i);
+
+      idx = _find_type (p);
+      if (idx < 0)
+	continue;
+
+      if (p->type_quark == g_quark_from_static_string (PROP_TYPE_DARRAY)) { /* recurse */
+	GtkTreeIter child_iter;
+
+	if (gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &child_iter, iter))
+	  _read_store (store, &child_iter, (ArrayProperty *)p);
+      } else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_BOOL))
+	gtk_tree_model_get (model, iter, i, &((BoolProperty *)p)->bool_data, -1);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_INT))
+	gtk_tree_model_get (model, iter, i, &((IntProperty *)p)->int_data, -1);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_ENUM))
+	gtk_tree_model_get (model, iter, i, &((EnumProperty *)p)->enum_data, -1);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_REAL))
+	gtk_tree_model_get (model, iter, i, &((RealProperty *)p)->real_data, -1);
+      else if (p->type_quark == g_quark_from_static_string (PROP_TYPE_STRING)) {
+	StringProperty *pst = (StringProperty *)p;
+	gchar *value;
+	gtk_tree_model_get (model, iter, i, &value, -1);
+	g_free (pst->string_data);
+	pst->string_data = g_strdup (value);
+      } else {
+	/* only complain if we have a visible widget */
+	if (_dia_gtk_type_map[idx].create_renderer != NULL)
+	  g_warning (G_STRLOC " Missing setter for '%s'", p->descr->type);
+      }
+    }
+
+    gtk_tree_model_iter_next (model, iter);
+  }
+}
+
+/*! 
+ * PropertyType_SetFromWidget: set the value of the property from the 
+ * current value of the widget
+ */
+void 
+_arrayprop_set_from_widget(ArrayProperty *prop, WIDGET *widget) 
+{
+  GtkWidget *view = widget;
+  GtkTreeView *tree_view = g_object_get_data (G_OBJECT (view), "tree-view");
+  GtkTreeStore *store = GTK_TREE_STORE (gtk_tree_view_get_model (tree_view));
+  GtkTreeIter iter;
+
+  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
+    _read_store (store, &iter, prop);
+
+  /* enough to replace prophandler_connect() ? */
+  if (g_object_get_data (G_OBJECT (store), "modified"))
+    prop->common.experience &= ~PXP_NOTSET;
+}
diff --git a/lib/prop_sdarray_widget.h b/lib/prop_sdarray_widget.h
new file mode 100644
index 0000000..8613d55
--- /dev/null
+++ b/lib/prop_sdarray_widget.h
@@ -0,0 +1 @@
+/* internal */WIDGET *_arrayprop_get_widget(ArrayProperty *prop, PropDialog *dialog);void    _arrayprop_reset_widget(ArrayProperty *prop, WIDGET *widget);void    _arrayprop_set_from_widget(ArrayProperty *prop, WIDGET *widget);
\ No newline at end of file
diff --git a/objects/Database/Makefile.am b/objects/Database/Makefile.am
index f4dbb03..5e69071 100644
--- a/objects/Database/Makefile.am
+++ b/objects/Database/Makefile.am
@@ -5,7 +5,6 @@ libdb_objects_la_SOURCES = \
 	database.c \
 	database.h \
 	table.c \
-	table_dialog.c \
 	reference.c \
 	compound.c
 
@@ -14,7 +13,7 @@ libdb_objects_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED
 libdb_objects_la_LIBADD = $(top_builddir)/lib/libdia.la
 
 INCLUDES = -I$(top_srcdir)/intl -I$(srcdir)/../../lib \
-	$(DEBUG_FLAGS) $(GTK_CFLAGS) $(GNOME_CFLAGS) $(PANGOFT2_CFLAGS) $(UNICODE_CFLAGS)
+	$(DEBUG_FLAGS) $(GTK_CFLAGS) $(UNICODE_CFLAGS)
 
 EXTRA_DIST = \
 	pixmaps/table.xpm \
diff --git a/objects/Database/database.h b/objects/Database/database.h
index f7fda38..8379de8 100644
--- a/objects/Database/database.h
+++ b/objects/Database/database.h
@@ -19,8 +19,6 @@
 #ifndef DATABASE_H
 #define DATABASE_H
 
-#include <gtk/gtk.h>
-#include "widgets.h"
 #include "element.h"
 #include "connectionpoint.h"
 #include "orth_conn.h"
@@ -31,7 +29,6 @@
 
 typedef struct _Table Table;
 typedef struct _TableAttribute TableAttribute;
-typedef struct _TablePropDialog TablePropDialog;
 typedef struct _TableReference TableReference;
 typedef struct _TableState TableState;
 typedef struct _TableChange TableChange;
@@ -54,7 +51,6 @@ struct _Table {
   GList * attributes; /**< the attributes of this database table */
 
   /* fonts */
-
   real normal_font_height;
   DiaFont * normal_font;
 
@@ -68,7 +64,6 @@ struct _Table {
   DiaFont * comment_font;
 
   /* colors */
-
   Color line_color;
   Color fill_color;
   Color text_color;
@@ -76,20 +71,13 @@ struct _Table {
   real border_width;
 
   /* computed variables */
-
   gboolean destroyed;
 
   real namebox_height;
   real attributesbox_height;
   real maxwidth_attr_name;
-
-  /* the property dialog pointer */
-
-  TablePropDialog * prop_dialog;
 };
 
-void table_dialog_free (TablePropDialog *dialog);
-
 struct _TableAttribute {
   gchar * name;          /* column name */
   gchar * type;          /* the type of the values in this column */
@@ -162,11 +150,6 @@ struct _TableReference {
   Alignment ep_desc_text_align; /* end-point */
 };
 
-/* in table_dialog.c */
-extern GtkWidget * table_get_properties_dialog (Table *, gboolean);
-/* in table_dialog.c */
-extern ObjectChange * table_dialog_apply_changes (Table *, GtkWidget *);
-
 /* in table.c */
 extern TableAttribute * table_attribute_new (void);
 /* in table.c */
@@ -182,9 +165,7 @@ extern void table_update_connectionpoints (Table *);
 extern void table_update_positions (Table *);
 /* in table.c */
 extern void table_compute_width_height (Table *);
-/* in table_dialog.c */
 extern TableState * table_state_new (Table *);
-/* in table_dialog.c */
 extern TableChange * table_change_new (Table *, TableState *,
                                        GList *, GList *, GList *);
 /* in table.c */
diff --git a/objects/Database/table.c b/objects/Database/table.c
index 66a541b..b981e01 100644
--- a/objects/Database/table.c
+++ b/objects/Database/table.c
@@ -68,7 +68,6 @@ static ObjectChange * table_move_handle (Table *, Handle *,
 static PropDescription * table_describe_props (Table *);
 static void         table_get_props (Table *, GPtrArray *);
 static void         table_set_props (Table *, GPtrArray *);
-ObjectChange *_table_dialog_apply_changes (Table * table, GtkWidget * widget);
 
 static void         table_draw (Table *, DiaRenderer *);
 static real         table_draw_namebox (Table *, DiaRenderer *, Element *);
@@ -123,8 +122,8 @@ static ObjectOps table_ops =
     (CopyFunc)                  table_copy,
     (MoveFunc)                  table_move,
     (MoveHandleFunc)            table_move_handle,
-    (GetPropertiesFunc)         table_get_properties_dialog,
-    (ApplyPropertiesDialogFunc) _table_dialog_apply_changes,
+    (GetPropertiesFunc) object_create_props_dialog,
+    (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
     (ObjectMenuFunc)            table_object_menu,
     (DescribePropsFunc)         table_describe_props,
     (GetPropsFunc)              table_get_props,
@@ -142,13 +141,13 @@ static PropDescription table_attribute_props[] =
     { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
       N_("Comment"), NULL, NULL },
     { "primary_key", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
-      N_("Primary key"), NULL, NULL },
+      N_("Primary"), NULL, N_("Primary key") },
     { "nullable", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
       N_("Nullable"), NULL, NULL },
     { "unique", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
       N_("Unique"), NULL, NULL },
     { "default_value", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
-      N_("Default value"), NULL, NULL },
+      N_("Default"), NULL, N_("Default value") },
 
     PROP_DESC_END
   };
@@ -176,24 +175,32 @@ static PropDescription table_props[] =
   {
     ELEMENT_COMMON_PROPERTIES,
 
-    PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE | PROP_FLAG_STANDARD | PROP_FLAG_OPTIONAL),
-    PROP_STD_LINE_COLOUR_OPTIONAL,
-    PROP_STD_FILL_COLOUR_OPTIONAL,
-    PROP_STD_LINE_WIDTH_OPTIONAL,
-
+    PROP_STD_NOTEBOOK_BEGIN,
+    PROP_NOTEBOOK_PAGE("table", PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS, N_("Table")),
     { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
       N_("Name"), NULL, NULL },
     { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
       N_("Comment"), NULL, NULL },
+
+    PROP_MULTICOL_BEGIN("visibilities"),
+    PROP_MULTICOL_COLUMN("first"),
     { "visible_comment", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
       N_("Visible comments"), NULL, NULL },
-    { "tagging_comment", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
-      N_("Comment tagging"), NULL, NULL },
     { "underline_primary_key", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
       N_("Underline primary keys"), NULL, NULL },
+    PROP_MULTICOL_COLUMN("second"),
+    { "tagging_comment", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
+      N_("Comment tagging"), NULL, NULL },
     { "bold_primary_keys", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
       N_("Use bold font for primary keys"), NULL, NULL },
+    PROP_MULTICOL_END("visibilities"),
 
+    PROP_NOTEBOOK_PAGE("attribute", PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS, N_("Attributes")),
+    { "attributes", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
+      "", NULL, &table_attribute_extra },
+
+    PROP_NOTEBOOK_PAGE("style", PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS, N_("Style")),
+    PROP_FRAME_BEGIN("fonts", 0, N_("Fonts")),
     PROP_MULTICOL_BEGIN("table"),
     PROP_MULTICOL_COLUMN("font"),
     { "normal_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
@@ -210,9 +217,14 @@ static PropDescription table_props[] =
     { "comment_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
       N_(" "), NULL, NULL },
     PROP_MULTICOL_END("table"),
+    PROP_FRAME_END("fonts", 0),
 
-    { "attributes", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
-      N_("Attributes"), NULL, &table_attribute_extra },
+    PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE | PROP_FLAG_STANDARD | PROP_FLAG_OPTIONAL),
+    PROP_STD_LINE_COLOUR_OPTIONAL,
+    PROP_STD_FILL_COLOUR_OPTIONAL,
+    PROP_STD_LINE_WIDTH_OPTIONAL,
+
+    PROP_STD_NOTEBOOK_END,
 
     PROP_DESC_END
   };
@@ -369,9 +381,6 @@ table_create (Point * startpoint,
   table->bold_primary_key = FALSE;
   table->attributes = NULL;
 
-  /* init property dialog pointer */
-  table->prop_dialog = NULL;
-
   /* init colors */
   table->text_color = attributes_get_foreground ();
   table->line_color = attributes_get_foreground ();
@@ -498,10 +507,6 @@ table_destroy (Table * table)
   dia_font_unref (table->primary_key_font);
   dia_font_unref (table->name_font);
   dia_font_unref (table->comment_font);
-
-  if (table->prop_dialog != NULL) {
-    table_dialog_free (table->prop_dialog);
-  }
 }
 
 static void
@@ -909,16 +914,6 @@ table_set_props (Table *table, GPtrArray *props)
     }
 }
 
-ObjectChange *
-_table_dialog_apply_changes (Table * table, GtkWidget * widget)
-{
-  /* fallback, if it isn't our dialog, e.g. during multiple selection change */
-  if (!table->prop_dialog)
-    return object_apply_props_from_dialog(&table->element.object, widget);
-  else
-    return table_dialog_apply_changes(table, widget);
-}
-
 /**
  * Init the height and width of the underlying element. This routine
  * uses `table_calculate_namebox_data' and
@@ -1406,3 +1401,180 @@ table_init_fonts (Table * table)
         dia_font_new_from_style (DIA_FONT_SANS | DIA_FONT_ITALIC, 0.7);
     }
 }
+
+/* TableState & TableChange functions ------------------------------------- */
+
+TableState *
+table_state_new (Table * table)
+{
+  TableState * state = g_new0 (TableState, 1);
+  GList * list;
+
+  state->name = g_strdup (table->name);
+  state->comment = g_strdup (table->comment);
+  state->visible_comment = table->visible_comment;
+  state->tagging_comment = table->tagging_comment;
+  state->underline_primary_key = table->underline_primary_key;
+  state->bold_primary_key = table->bold_primary_key;
+  state->border_width = table->border_width;
+
+  list = table->attributes;
+  while (list != NULL)
+    {
+      TableAttribute * attr = (TableAttribute *) list->data;
+      TableAttribute * copy = table_attribute_copy (attr);
+
+      copy->left_connection = attr->left_connection;
+      copy->right_connection = attr->right_connection;
+
+      state->attributes = g_list_append (state->attributes, copy);
+      list = g_list_next (list);
+    }
+
+  return state;
+}
+
+/**
+ * Set the values stored in state to the passed table and reinit the table
+ * object. The table-state object will be free.
+ */
+static void
+table_state_set (TableState * state, Table * table)
+{
+  table->name = state->name;
+  table->comment = state->comment;
+  table->visible_comment = state->visible_comment;
+  table->tagging_comment = state->tagging_comment;
+  table->border_width = state->border_width;
+  table->underline_primary_key = state->underline_primary_key;
+  table->bold_primary_key = state->bold_primary_key;
+  table->border_width = state->border_width;
+  table->attributes = state->attributes;
+
+  g_free (state);
+
+  table_update_connectionpoints (table);
+  table_update_primary_key_font (table);
+  table_compute_width_height (table);
+  table_update_positions (table);
+}
+
+static void
+table_state_free (TableState * state)
+{
+  GList * list;
+
+  g_free (state->name);
+  g_free (state->comment);
+
+  list = state->attributes;
+  while (list != NULL)
+    {
+      TableAttribute * attr = (TableAttribute *) list->data;
+      table_attribute_free (attr);
+      list = g_list_next (list);
+    }
+  g_list_free (state->attributes);
+
+  g_free (state);
+}
+
+/**
+ * Called to UNDO a change on the table object.
+ */
+static void
+table_change_revert (TableChange *change, DiaObject *obj)
+{
+  TableState *old_state;
+  GList *list;
+
+  old_state = table_state_new(change->obj);
+
+  table_state_set(change->saved_state, change->obj);
+  
+  list = change->disconnected;
+  while (list) {
+    Disconnect *dis = (Disconnect *)list->data;
+
+    object_connect(dis->other_object, dis->other_handle, dis->cp);
+
+    list = g_list_next(list);
+  }
+  
+  change->saved_state = old_state;
+  change->applied = FALSE;
+}
+
+static void
+table_change_free (TableChange *change)
+{
+  GList * free_list, * lst;
+
+  table_state_free (change->saved_state);
+
+  free_list = (change->applied == TRUE)
+    ? change->deleted_cp
+    : change->added_cp;
+
+  lst = free_list;
+  while (lst)
+    {
+      ConnectionPoint * cp = (ConnectionPoint *) lst->data;
+      g_assert (cp->connected == NULL);
+      object_remove_connections_to (cp);
+      g_free (cp);
+
+      lst = g_list_next (lst);
+    }
+  g_list_free (free_list);
+}
+
+/**
+ * Called to REDO a change on the table object.
+ */
+static void
+table_change_apply (TableChange * change, DiaObject * obj)
+{
+  TableState * old_state;
+  GList * lst;
+
+  g_print ("apply (o: 0x%08x) (c: 0x%08x)\n", GPOINTER_TO_UINT(obj), GPOINTER_TO_UINT(change));
+
+  /* first the get the current state for later use */
+  old_state = table_state_new (change->obj);
+  /* now apply the change */
+  table_state_set (change->saved_state, change->obj);
+
+  lst = change->disconnected;
+  while (lst)
+    {
+      Disconnect * dis = (Disconnect *) lst->data;
+      object_unconnect (dis->other_object, dis->other_handle);
+      lst = g_list_next (lst);
+    }
+  change->saved_state = old_state;
+  change->applied = TRUE;
+}
+
+TableChange * 
+table_change_new (Table * table, TableState * saved_state,
+                  GList * added, GList * deleted,
+                  GList * disconnects)
+{
+  TableChange * change;
+
+  change = g_new (TableChange, 1);
+
+  change->obj_change.apply = (ObjectChangeApplyFunc) table_change_apply;
+  change->obj_change.revert = (ObjectChangeRevertFunc) table_change_revert;
+  change->obj_change.free = (ObjectChangeFreeFunc) table_change_free;
+
+  change->obj = table;
+  change->added_cp = added;
+  change->deleted_cp = deleted;
+  change->disconnected = disconnects;
+  change->applied = TRUE;
+  change->saved_state = saved_state;
+
+  return change;
+}
diff --git a/objects/makefile.msc b/objects/makefile.msc
index 95b4eb3..88722ed 100644
--- a/objects/makefile.msc
+++ b/objects/makefile.msc
@@ -111,9 +111,8 @@ OBJECTS = \
 	compound.obj \
 	database.obj \
 	reference.obj \
-	table.obj \
-	table_dialog.obj	
-PKG_LINK = $(PKG_LINK) $(GTK2_LIBS) 
+	table.obj
+PKG_LINK = $(PKG_LINK) 
 !ENDIF
 
 !IFDEF OBJ_eml



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