/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2007-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ /* A window listing all objects in the game */ #include "C4Include.h" #include "editor/C4ObjectListDlg.h" #include "editor/C4Console.h" #include "object/C4Object.h" #include "object/C4GameObjects.h" #ifdef USE_GTK #include /* Some boilerplate GObject defines. 'klass' is used instead of 'class', because 'class' is a C++ keyword */ #define C4_TYPE_LIST (c4_list_get_type ()) #define C4_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), C4_TYPE_LIST, C4List)) #define C4_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), C4_TYPE_LIST, C4ListClass)) #define C4_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), C4_TYPE_LIST)) #define C4_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), C4_TYPE_LIST)) #define C4_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), C4_TYPE_LIST, C4ListClass)) /* The data columns that we export via the tree model interface */ enum { C4_LIST_COL_OBJECT, C4_LIST_N_COLUMNS }; typedef struct _C4List C4List; typedef struct _C4ListClass C4ListClass; struct _C4List { GObject parent; /* this MUST be the first member */ C4ObjectList * data; gint stamp; /* A random integer to check if an iter belongs to this model */ }; /* more boilerplate GObject stuff */ struct _C4ListClass { GObjectClass parent_class; }; static GObjectClass *parent_class = NULL; GType c4_list_get_type (void); // Initialise an instance static void c4_list_init (C4List *c4_list) { c4_list->data = &::Objects; c4_list->stamp = g_random_int(); /* Random int to check whether iters belong to this model */ } // destructor static void c4_list_finalize (GObject *object) { /* must chain up - finalize parent */ (* parent_class->finalize) (object); } static GtkTreeModelFlags c4_list_get_flags (GtkTreeModel *tree_model) { g_return_val_if_fail (C4_IS_LIST(tree_model), (GtkTreeModelFlags)0); // neither is this a flat list nor do the iters persist changes in the model //return GtkTreeModelFlags(GTK_TREE_MODEL_ITERS_PERSIST); return GtkTreeModelFlags(0); } // converts 'path' into an iterator and stores that in 'iter' static gboolean c4_list_get_iter (GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreePath * path) { gint *indices, depth; g_assert(C4_IS_LIST(tree_model)); g_assert(path!=NULL); C4List * c4_list = C4_LIST(tree_model); indices = gtk_tree_path_get_indices(path); depth = gtk_tree_path_get_depth(path); iter->stamp = c4_list->stamp; C4ObjectLink * pLnk = C4_LIST(tree_model)->data->First; // Skip Contained Objects in the main list while (pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next; for (int i = 0; i < depth; ++i) { if (!pLnk) return false; if (indices[i] < 0) return false; for (int j = 0; j < indices[i]; ++j) { pLnk = pLnk->Next; // Skip Contained Objects in the main list while (i == 0 && pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next; if (!pLnk) return false; } iter->user_data = pLnk; iter->user_data2 = pLnk->Obj->Contained; pLnk = pLnk->Obj->Contents.First; } return true; } // converts 'iter' into a new tree path and returns that. // Note: This is called by OnObjectRemove with an iter which is not in the // list anymore, but with the object and prev still usable. static GtkTreePath * c4_list_get_path (GtkTreeModel * tree_model, GtkTreeIter * iter) { g_return_val_if_fail (C4_IS_LIST(tree_model), NULL); g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (iter->user_data != NULL, NULL); GtkTreePath *path = gtk_tree_path_new(); C4List * c4_list = C4_LIST(tree_model); C4Object * pObj = ((C4ObjectLink *) iter->user_data)->Obj; int i = 0; for (C4ObjectLink * pLnk = ((C4ObjectLink *) iter->user_data)->Prev; pLnk; pLnk = pLnk->Prev) { // Skip Contained Objects in the main list if (pObj->Contained != pLnk->Obj->Contained) continue; ++i; } gtk_tree_path_prepend_index(path, i); pObj = (C4Object *) iter->user_data2; while (pObj) { i = 0; C4ObjectList * pList = c4_list->data; if (pObj->Contained) pList = &pObj->Contained->Contents; for (C4ObjectLink * pLnk = pList->First; pLnk && pLnk->Obj != pObj; pLnk = pLnk->Next) { // Skip Contained Objects in the main list if (pObj->Contained != pLnk->Obj->Contained) continue; ++i; } gtk_tree_path_prepend_index(path, i); pObj = pObj->Contained; } return path; } // ++iter static gboolean c4_list_iter_next (GtkTreeModel * tree_model, GtkTreeIter * iter) { g_return_val_if_fail (C4_IS_LIST (tree_model), false); if (iter == NULL || iter->user_data == NULL) return false; C4ObjectLink * pLnk = (C4ObjectLink *) iter->user_data; pLnk = pLnk->Next; // Skip Contained Objects in the main list if (!(C4Object *)iter->user_data2) while (pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next; if (!pLnk) return false; iter->user_data = pLnk; iter->user_data2 = pLnk->Obj->Contained; return true; } // Set 'iter' to the first child of 'parent', or the first top-level row if // 'parent' is 0, or return false if there aren't any children. static gboolean c4_list_iter_children (GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent) { g_return_val_if_fail (parent == NULL || parent->user_data != NULL, false); g_return_val_if_fail (C4_IS_LIST (tree_model), false); C4List *c4_list = C4_LIST(tree_model); C4ObjectLink * pLnk; if (parent) { C4ObjectList * pList = &((C4ObjectLink *)parent->user_data)->Obj->Contents; pLnk = pList->First; } else { pLnk = c4_list->data->First; // Skip... while (pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next; } if (!pLnk) return false; /* Set iter to first item in list */ iter->stamp = c4_list->stamp; iter->user_data = pLnk; iter->user_data2 = pLnk->Obj->Contained; return true; } // Return true if 'parent' has children. static gboolean c4_list_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *parent) { g_return_val_if_fail (parent == NULL || parent->user_data != NULL, false); g_return_val_if_fail (C4_IS_LIST (tree_model), false); C4List *c4_list = C4_LIST(tree_model); C4ObjectList * pList = c4_list->data; if (parent) pList = &((C4ObjectLink *)parent->user_data)->Obj->Contents; return pList->First != 0; } // Counts the children 'parent' has. static gint c4_list_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *parent) { g_return_val_if_fail (C4_IS_LIST (tree_model), -1); g_return_val_if_fail (parent == NULL || parent->user_data != NULL, -1); C4List *c4_list = C4_LIST(tree_model); int i = 0; if (parent) { C4ObjectList * pList = &((C4ObjectLink *)parent->user_data)->Obj->Contents; C4ObjectLink * pLnk = pList->First; while (pLnk) { ++i; pLnk = pLnk->Next; } } else { C4ObjectLink * pLnk = c4_list->data->First; while (pLnk) { if (!pLnk->Obj->Contained) ++i; pLnk = pLnk->Next; } } return i; } // Sets 'iter' to the 'n'-th child of 'parent'. static gboolean c4_list_iter_nth_child (GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent, gint n) { g_return_val_if_fail (C4_IS_LIST (tree_model), false); g_return_val_if_fail (parent == NULL || parent->user_data != NULL, false); C4List *c4_list = C4_LIST(tree_model); C4ObjectLink * pLnk; if (parent) { C4ObjectList * pList = &((C4ObjectLink *)parent->user_data)->Obj->Contents; pLnk = pList->First; for (int i = 0; i < n; ++i) { if (!pLnk) return false; pLnk = pLnk->Next; } } else { pLnk = c4_list->data->First; for (int i = 0; i < n; ++i) { if (!pLnk) return false; pLnk = pLnk->Next; // Skip... while (pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next; } } if (!pLnk) return false; iter->stamp = c4_list->stamp; iter->user_data = pLnk; iter->user_data2 = pLnk->Obj->Contained; return true; } // Helper function. static gboolean c4_list_iter_for_C4Object (GtkTreeModel * tree_model, GtkTreeIter * iter, C4ObjectList * pList, C4Object * pObj) { if (!pObj) return false; C4List * c4_list = C4_LIST(tree_model); for (C4ObjectLink * pLnk = pList->First; pLnk; pLnk = pLnk->Next) { if (pLnk->Obj == pObj) { iter->stamp = c4_list->stamp; iter->user_data = pLnk; iter->user_data2 = pLnk->Obj->Contained; return true; } } g_return_val_if_reached(false); } // Sets 'iter' to the parent row of 'child'. static gboolean c4_list_iter_parent (GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * child) { g_return_val_if_fail (C4_IS_LIST (tree_model), false); g_return_val_if_fail (child == NULL || child->user_data != NULL, false); C4List * c4_list = C4_LIST(tree_model); C4Object * pObj = (C4Object *) child->user_data2; C4ObjectList * pList = c4_list->data; if (pObj->Contained) pList = &pObj->Contained->Contents; return c4_list_iter_for_C4Object(tree_model, iter, pList, pObj); } static C4Object * c4_list_iter_get_C4Object(GtkTreeModel * tree_model, GtkTreeIter * iter) { g_return_val_if_fail (C4_IS_LIST (tree_model), NULL); g_return_val_if_fail (iter != NULL && iter->user_data != NULL, NULL); return ((C4ObjectLink *) iter->user_data)->Obj; } // How many columns does this model have? static gint c4_list_get_n_columns (GtkTreeModel * tree_model) { g_return_val_if_fail (C4_IS_LIST(tree_model), 0); return 1; } // What sort of data is in the column? static GType c4_list_get_column_type (GtkTreeModel * tree_model, gint index) { g_return_val_if_fail (C4_IS_LIST(tree_model), G_TYPE_INVALID); g_return_val_if_fail (index < 1 && index >= 0, G_TYPE_INVALID); return G_TYPE_POINTER; } // gets the data for the 'column' in the row at 'iter' and stores that in 'value'. static void c4_list_get_value (GtkTreeModel * tree_model, GtkTreeIter * iter, gint column, GValue * value) { g_return_if_fail (C4_IS_LIST (tree_model)); g_return_if_fail (iter != NULL); g_return_if_fail (column == 0); C4Object * pObj = ((C4ObjectLink *) iter->user_data)->Obj; g_return_if_fail (pObj != NULL); g_value_init (value, G_TYPE_POINTER); g_value_set_pointer(value, pObj); // g_value_set_string(value, pObj->GetName()); } // Wrapper around g_object_new. static C4List * c4_list_new (void) { C4List * list; list = (C4List *) g_object_new (C4_TYPE_LIST, NULL); g_assert(list != NULL); return list; } // Called once for the class. static void c4_list_class_init (C4ListClass * klass) { GObjectClass * object_class; parent_class = (GObjectClass*) g_type_class_peek_parent (klass); object_class = (GObjectClass*) klass; object_class->finalize = c4_list_finalize; } // fill in the GtkTreeModel interface with the functions above. static void c4_list_tree_model_init (GtkTreeModelIface *iface) { iface->get_flags = c4_list_get_flags; iface->get_n_columns = c4_list_get_n_columns; iface->get_column_type = c4_list_get_column_type; iface->get_iter = c4_list_get_iter; iface->get_path = c4_list_get_path; iface->get_value = c4_list_get_value; iface->iter_next = c4_list_iter_next; iface->iter_children = c4_list_iter_children; iface->iter_has_child = c4_list_iter_has_child; iface->iter_n_children = c4_list_iter_n_children; iface->iter_nth_child = c4_list_iter_nth_child; iface->iter_parent = c4_list_iter_parent; } // Return the type, registering it on first call. GType c4_list_get_type (void) { static GType c4_list_type = 0; if (c4_list_type == 0) { // Some boilerplate type registration stuff static const GTypeInfo c4_list_info = { sizeof (C4ListClass), /* class_size */ NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) c4_list_class_init, /* class_init */ NULL, /* class finalize */ NULL, /* class_data */ sizeof (C4List), /* instance_size */ 0, /* n_preallocs */ (GInstanceInitFunc) c4_list_init, /* instance_init */ NULL /* value_table */ }; c4_list_type = g_type_register_static (G_TYPE_OBJECT, "C4List", &c4_list_info, (GTypeFlags)0); /* register the GtkTreeModel interface with the type system */ static const GInterfaceInfo tree_model_info = { (GInterfaceInitFunc) c4_list_tree_model_init, NULL, NULL }; g_type_add_interface_static (c4_list_type, GTK_TYPE_TREE_MODEL, &tree_model_info); } return c4_list_type; } void C4ObjectListDlg::OnObjectRemove(C4ObjectList * pList, C4ObjectLink * pLnk) { if (!model) return; C4List * c4_list = C4_LIST(model); C4Object * Contained = pLnk->Obj->Contained; GtkTreeIter iter; iter.stamp = c4_list->stamp; iter.user_data = pLnk; iter.user_data2 = Contained; // While pLnk is not in the list anymore, with pLnk->Prev and Contained a path can still be made GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &iter); gtk_tree_model_row_deleted(GTK_TREE_MODEL(model), path); gtk_tree_path_free(path); // Removed from a now empty container? if (Contained && !Contained->Contents.First) { printf("Removed from a now empty container\n"); GtkTreeIter parent; C4ObjectList * pList = c4_list->data; if (Contained->Contained) pList = &Contained->Contained->Contents; c4_list_iter_for_C4Object(GTK_TREE_MODEL(model), &parent, pList, Contained); GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &parent); gtk_tree_model_row_has_child_toggled(GTK_TREE_MODEL(model), path, &parent); gtk_tree_path_free(path); } // Cheat: For the signals it must look as if the object had it's parent removed already pLnk->Obj->Contained = 0; // if removed from contents, it get's added to main list if (pList != c4_list->data) { printf("Removed from a container\n"); GtkTreeIter iter; C4ObjectList * pList = c4_list->data; c4_list_iter_for_C4Object(GTK_TREE_MODEL(model), &iter, pList, pLnk->Obj); GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &iter); gtk_tree_model_row_inserted(GTK_TREE_MODEL(model), path, &iter); gtk_tree_path_free(path); } // End-of-cheat pLnk->Obj->Contained = Contained; } void C4ObjectListDlg::OnObjectAdded(C4ObjectList * pList, C4ObjectLink * pLnk) { if (!model) return; C4List * c4_list = C4_LIST(model); // Inserted into a container? Remove from main list if (pList != c4_list->data) { printf("Inserted into a container\n"); GtkTreePath *path = gtk_tree_path_new(); int i = 0; C4ObjectList * pList = c4_list->data; C4Object * pObj = pLnk->Obj; for (C4ObjectLink * pLnk2 = pList->First; pLnk2 && pLnk2->Obj != pObj; pLnk2 = pLnk2->Next) { // Skip Contained Objects in the main list if (pLnk2->Obj->Contained) continue; ++i; } gtk_tree_path_prepend_index(path, i); gtk_tree_model_row_deleted(GTK_TREE_MODEL(model), path); gtk_tree_path_free(path); } GtkTreeIter iter; iter.stamp = c4_list->stamp; iter.user_data = pLnk; iter.user_data2 = pLnk->Obj->Contained; GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &iter); gtk_tree_model_row_inserted(GTK_TREE_MODEL(model), path, &iter); gtk_tree_path_free(path); // Inserted into a previously empty container? if (pLnk->Obj->Contained && pLnk->Obj->Contained->Contents.First == pLnk->Obj->Contained->Contents.Last) { printf("Inserted into a previously empty container\n"); GtkTreeIter parent; c4_list_iter_parent(GTK_TREE_MODEL(model), &parent, &iter); GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &parent); gtk_tree_model_row_has_child_toggled(GTK_TREE_MODEL(model), path, &parent); gtk_tree_path_free(path); } } void C4ObjectListDlg::OnObjectRename(C4ObjectList * pList, C4ObjectLink * pLnk) { } void C4ObjectListDlg::OnDestroy(GtkWidget* widget, C4ObjectListDlg* dlg) { dlg->window = 0; dlg->model = 0; dlg->treeview = 0; } void C4ObjectListDlg::OnRowActivated(GtkTreeView * tree_view, GtkTreePath * path, GtkTreeViewColumn * column, C4ObjectListDlg * dlg) { Console.EditCursor.SetMode(C4CNS_ModeEdit); Console.EditCursor.OpenPropTools(); } void C4ObjectListDlg::OnSelectionChanged(GtkTreeSelection* selection, C4ObjectListDlg* dlg) { if (dlg->updating_selection) return; dlg->updating_selection = true; GList* list = gtk_tree_selection_get_selected_rows(selection, NULL); Console.EditCursor.GetSelection().Clear(); for (GList * i = list; i; i = i->next) { GtkTreePath * path = (GtkTreePath *)i->data; GtkTreeIter iter; c4_list_get_iter(GTK_TREE_MODEL(dlg->model), &iter, path); Console.EditCursor.GetSelection().Add(((C4ObjectLink *)iter.user_data)->Obj, C4ObjectList::stNone); } g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL); g_list_free (list); Console.EditCursor.OnSelectionChanged(); dlg->updating_selection = false; } void C4ObjectListDlg::Update(C4ObjectList &rSelection) { if (updating_selection) return; if (!window) return; updating_selection = true; GtkTreeSelection *selection; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); gtk_tree_selection_unselect_all(selection); for (C4ObjectLink * pLnk = rSelection.First; pLnk; pLnk = pLnk->Next) { GtkTreeIter iter; C4List * c4_list = C4_LIST(model); C4ObjectList * pList = c4_list->data; if (pLnk->Obj->Contained) pList = &pLnk->Obj->Contained->Contents; c4_list_iter_for_C4Object(GTK_TREE_MODEL(model), &iter, pList, pLnk->Obj); gtk_tree_selection_select_iter(selection, &iter); } updating_selection = false; } C4ObjectListDlg::C4ObjectListDlg(): window(0), treeview(0), model(0), updating_selection(false) { } C4ObjectListDlg::~C4ObjectListDlg() { } void C4ObjectListDlg::Execute() { } static void name_cell_data_func(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, gpointer data) { C4Object* object = c4_list_iter_get_C4Object(model, iter); g_object_set(G_OBJECT(renderer), "text", object->GetName(), (gpointer)NULL); } enum { ICON_SIZE = 24 }; #ifdef _UNUSED_ static void icon_cell_data_func(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, gpointer data) { C4Object* object = c4_list_iter_get_C4Object(model, iter); // Icons for objects with ColorByOwner are cached by object, others by Def // FIXME: Invalidate cache when objects change color, and redraw. gpointer key = object->Def; if (object->Def->ColorByOwner) key = object; GHashTable* table = static_cast(data); GdkPixbuf* pixbuf = GDK_PIXBUF(g_hash_table_lookup(table, key)); if (pixbuf == NULL) { /* Not yet cached, create from Graphics */ C4Surface* surface = object->Def->Graphics.Bmp.Bitmap; if (object->Def->Graphics.Bmp.BitmapClr) surface = object->Def->Graphics.Bmp.BitmapClr; const C4Rect& picture = object->Def->PictureRect; pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, picture.Wdt, picture.Hgt); guchar* pixels = gdk_pixbuf_get_pixels(pixbuf); surface->Lock(); for (int y = 0; y < picture.Hgt; ++ y) for (int x = 0; x < picture.Wdt; ++ x) { DWORD dw = surface->GetPixDw(picture.x + x, picture.y + y, true); *pixels = (dw >> 16) & 0xff; ++ pixels; *pixels = (dw >> 8 ) & 0xff; ++ pixels; *pixels = (dw ) & 0xff; ++ pixels; *pixels = 0xff - ((dw >> 24) & 0xff); ++ pixels; } surface->Unlock(); // Scale down to ICON_SIZE, keeping aspect ratio guint dest_width, dest_height; if (picture.Wdt >= picture.Hgt) { double factor = static_cast(picture.Hgt) / static_cast(picture.Wdt); dest_width = ICON_SIZE; dest_height = dest_width * factor; } else { double factor = static_cast(picture.Wdt) / static_cast(picture.Hgt); dest_height = ICON_SIZE; dest_width = dest_height * factor; } GdkPixbuf* scaled = gdk_pixbuf_scale_simple(pixbuf, dest_width, dest_height, GDK_INTERP_HYPER); g_object_unref(G_OBJECT(pixbuf)); pixbuf = scaled; g_hash_table_insert(table, key, pixbuf); } g_object_set(G_OBJECT(renderer), "pixbuf", pixbuf, NULL); } #endif // _UNUSED_ void C4ObjectListDlg::Open() { // Create Window if necessary if (window == NULL) { // The Windows window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_resizable(GTK_WINDOW(window), true); gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_UTILITY); gtk_window_set_role(GTK_WINDOW(window), "objectlist"); gtk_window_set_title(GTK_WINDOW(window), "Objects"); gtk_window_set_default_size(GTK_WINDOW(window), 180, 300); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(Console.window)); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(OnDestroy), this); // The Tree GtkWidget* scrolled_wnd = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_wnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_wnd), GTK_SHADOW_IN); model = G_OBJECT(c4_list_new()); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); g_object_unref(model); /* destroy store automatically with view */ g_signal_connect(G_OBJECT(treeview), "row-activated", G_CALLBACK(OnRowActivated), this); GtkTreeViewColumn * col = gtk_tree_view_column_new(); GtkCellRenderer * renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(col, renderer, true); gtk_tree_view_column_set_cell_data_func(col, renderer, name_cell_data_func, NULL, NULL); gtk_tree_view_column_set_title(col, "Name"); gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview),col); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), false); gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), true); GtkTreeSelection * selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(OnSelectionChanged), this); gtk_container_add(GTK_CONTAINER(scrolled_wnd), treeview); gtk_widget_set_vexpand(scrolled_wnd, true); gtk_widget_set_hexpand(scrolled_wnd, true); gtk_container_add(GTK_CONTAINER(window), scrolled_wnd); gtk_widget_show_all(window); } else { gtk_window_present_with_time(GTK_WINDOW(window), gtk_get_current_event_time()); } } #else C4ObjectListDlg::C4ObjectListDlg() { } C4ObjectListDlg::~C4ObjectListDlg() { } void C4ObjectListDlg::Execute() { } void C4ObjectListDlg::Open() { } void C4ObjectListDlg::Update(C4ObjectList &rSelection) { } void C4ObjectListDlg::OnObjectRemove(C4ObjectList * pList, C4ObjectLink * pLnk) { } void C4ObjectListDlg::OnObjectAdded(C4ObjectList * pList, C4ObjectLink * pLnk) { } void C4ObjectListDlg::OnObjectRename(C4ObjectList * pList, C4ObjectLink * pLnk) { } #endif C4ObjectListChangeListener & ObjectListChangeListener = Console.ObjectListDlg;