2023-11-09 06:58:48 +01:00
|
|
|
/*
|
|
|
|
* Gem-graph OpenGL experiments
|
|
|
|
*
|
|
|
|
* Desc: User interface functions
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
|
|
|
|
*
|
|
|
|
* This file is part of Gem-graph.
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <gtk-4.0/gtk/gtk.h>
|
|
|
|
#include <glib-2.0/glib.h>
|
|
|
|
|
|
|
|
#include "../../include/base.h"
|
|
|
|
#include "../../include/ui.h"
|
|
|
|
|
2023-11-25 08:59:48 +01:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
COL_FIRST_NAME = 0,
|
|
|
|
COL_LAST_NAME,
|
|
|
|
NUM_COLS
|
|
|
|
} ;
|
|
|
|
|
2023-11-24 16:34:06 +01:00
|
|
|
GtkListStore *list_store;
|
2023-11-24 18:18:32 +01:00
|
|
|
GtkTreeStore *tree_store;
|
|
|
|
GtkTreeIter iter, child;
|
2023-11-24 16:34:06 +01:00
|
|
|
|
2023-11-25 08:14:39 +01:00
|
|
|
static void tree_c_printing_test(void);
|
|
|
|
|
2023-11-25 08:59:48 +01:00
|
|
|
static GtkTreeModel *
|
|
|
|
create_and_fill_model (void)
|
|
|
|
{
|
|
|
|
GtkTreeStore *treestore;
|
|
|
|
GtkTreeIter toplevel, child;
|
|
|
|
|
|
|
|
treestore = gtk_tree_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
|
|
|
|
|
|
|
|
/* Append a top level row and leave it empty */
|
|
|
|
gtk_tree_store_append(treestore, &toplevel, NULL);
|
|
|
|
|
|
|
|
/* Append a second top level row, and fill it with some data */
|
|
|
|
gtk_tree_store_append(treestore, &toplevel, NULL);
|
|
|
|
gtk_tree_store_set(treestore, &toplevel,
|
|
|
|
COL_FIRST_NAME, "Joe",
|
|
|
|
COL_LAST_NAME, "Dalton",
|
|
|
|
-1);
|
|
|
|
|
|
|
|
/* Append a child to the second top level row, and fill in some data */
|
|
|
|
gtk_tree_store_append(treestore, &child, &toplevel);
|
|
|
|
gtk_tree_store_set(treestore, &child,
|
|
|
|
COL_FIRST_NAME, "Averell",
|
|
|
|
COL_LAST_NAME, "Dalton",
|
|
|
|
-1);
|
|
|
|
|
|
|
|
return GTK_TREE_MODEL(treestore);
|
|
|
|
}
|
|
|
|
|
2023-11-24 17:57:44 +01:00
|
|
|
/***********************************************************
|
|
|
|
* *
|
|
|
|
* Going through every row in a list store *
|
|
|
|
* *
|
|
|
|
***********************************************************/
|
|
|
|
|
|
|
|
void
|
|
|
|
traverse_list_store (GtkListStore *liststore)
|
|
|
|
{
|
|
|
|
gboolean valid;
|
|
|
|
|
|
|
|
g_return_if_fail ( liststore != NULL );
|
|
|
|
|
|
|
|
/* Get first row in list store */
|
|
|
|
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter);
|
|
|
|
|
|
|
|
while (valid)
|
|
|
|
{
|
|
|
|
/* ... do something with that row using the iter ... */
|
|
|
|
/* (Here column 0 of the list store is of type G_TYPE_STRING) */
|
|
|
|
gtk_list_store_set(liststore, &iter, 0, "Joe", -1);
|
|
|
|
|
|
|
|
/* Make iter point to the next row in the list store */
|
|
|
|
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter);
|
|
|
|
}
|
|
|
|
}
|
2023-11-24 16:50:51 +01:00
|
|
|
/*************************************************************
|
|
|
|
* *
|
|
|
|
* Converting a GtkTreePath into a GtkTreeIter *
|
|
|
|
* *
|
|
|
|
*************************************************************/
|
2023-11-24 16:34:06 +01:00
|
|
|
|
2023-11-24 16:50:51 +01:00
|
|
|
/*************************************************************
|
|
|
|
* *
|
|
|
|
* onTreeViewRowActivated: a row has been double-clicked *
|
|
|
|
* *
|
|
|
|
*************************************************************/
|
|
|
|
|
|
|
|
void
|
|
|
|
onTreeViewRowActivated (GtkTreeView *view, GtkTreePath *path,
|
|
|
|
GtkTreeViewColumn *col, gpointer userdata)
|
|
|
|
{
|
|
|
|
GtkTreeModel *model;
|
|
|
|
|
|
|
|
model = gtk_tree_view_get_model(view);
|
|
|
|
|
|
|
|
if (gtk_tree_model_get_iter(model, &iter, path))
|
|
|
|
{
|
|
|
|
gchar *name;
|
|
|
|
|
|
|
|
gtk_tree_model_get(model, &iter, "ANY_COLUMN_NAME", &name, -1);
|
|
|
|
|
|
|
|
g_print ("The row containing the name '%s' has been double-clicked.\n", name);
|
|
|
|
|
|
|
|
g_free(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2023-11-25 08:14:39 +01:00
|
|
|
tree_c_test(void)
|
2023-11-24 16:25:56 +01:00
|
|
|
{
|
2023-11-24 16:34:06 +01:00
|
|
|
list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
|
|
|
|
|
2023-11-24 17:57:44 +01:00
|
|
|
/* Append empty rows to the list store. Iter will point to the new row */
|
|
|
|
gtk_list_store_append(list_store, &iter);
|
|
|
|
gtk_list_store_append(list_store, &iter);
|
|
|
|
gtk_list_store_append(list_store, &iter);
|
|
|
|
|
2023-11-24 18:18:32 +01:00
|
|
|
tree_store = gtk_tree_store_new(1, G_TYPE_STRING);
|
|
|
|
|
|
|
|
/* Append an empty top-level row to the tree store.
|
|
|
|
* Iter will point to the new row */
|
|
|
|
gtk_tree_store_append(tree_store, &iter, NULL);
|
|
|
|
|
|
|
|
/* Append another empty top-level row to the tree store.
|
|
|
|
* Iter will point to the new row */
|
|
|
|
gtk_tree_store_append(tree_store, &iter, NULL);
|
|
|
|
|
|
|
|
/* Append a child to the row we just added.
|
|
|
|
* Child will point to the new row */
|
|
|
|
gtk_tree_store_append(tree_store, &child, &iter);
|
|
|
|
|
|
|
|
/* Get the first row, and add a child to it as well (could have been done
|
|
|
|
* right away earlier of course, this is just for demonstration purposes) */
|
|
|
|
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(tree_store), &iter))
|
|
|
|
{
|
2023-11-25 08:14:39 +01:00
|
|
|
/* Child will point to new row */
|
|
|
|
gtk_tree_store_append(tree_store, &child, &iter);
|
2023-11-24 18:18:32 +01:00
|
|
|
}
|
2023-11-25 08:14:39 +01:00
|
|
|
else g_error("Oops, we should have a first row in the tree store!\n");
|
2023-11-24 18:18:32 +01:00
|
|
|
|
2023-11-25 10:29:08 +01:00
|
|
|
GtkTreeViewColumn *col;
|
|
|
|
GtkCellRenderer *renderer;
|
|
|
|
GtkWidget *view;
|
|
|
|
GtkTreeModel *model;
|
|
|
|
|
|
|
|
GtkWidget *window;
|
|
|
|
|
|
|
|
|
2023-11-25 08:14:39 +01:00
|
|
|
tree_c_printing_test();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tree_c_printing_test(void)
|
|
|
|
{
|
2023-11-24 16:25:56 +01:00
|
|
|
printf("tree_c_printing_test() \
|
2023-11-25 10:29:08 +01:00
|
|
|
------------------------------------------------------------------\n\
|
|
|
|
(https://en.wikibooks.org/wiki/GTK (modulo) 2B_By_Example/Tree_View/Columns_and_Renderer)\n\
|
|
|
|
Trying to adapt the first example of this tuto to display a simple tree view in our window.\n\
|
|
|
|
Can't use :\n\
|
|
|
|
- gtk_init(&argc, &argv);\n\
|
|
|
|
- window = gtk_tree_view_new(GTK_WINDOW_TOPLEVEL);\n\
|
|
|
|
- g_signal_connect(window, 'delete_event', gtk_main_quit, NULL);\n\
|
|
|
|
- tree_c_test(); (or : create_view_and_model() or some equivalent tree description)\n\
|
|
|
|
- gtk_container_add(GTK_CONTAINER(window), view);\n\
|
|
|
|
- gtk_widget_show_all(window);\n\
|
|
|
|
- gtk_main();\n\
|
|
|
|
(see main.c lines 35-44)\n\
|
|
|
|
");
|
2023-11-24 18:18:32 +01:00
|
|
|
}
|
|
|
|
|
2023-11-25 10:29:08 +01:00
|
|
|
// https://en.wikibooks.org/wiki/GTK%2B_By_Example/Tree_View/Tree_Models
|
|
|
|
|
2023-11-24 18:18:32 +01:00
|
|
|
/*
|
|
|
|
https://stackoverflow.com/questions/51002454/how-to-list-all-the-rows-of-a-gtk-treeview
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using Gtk;
|
|
|
|
|
|
|
|
public class Actress
|
|
|
|
{
|
|
|
|
public string Name;
|
|
|
|
public string Place;
|
|
|
|
public int Year;
|
|
|
|
|
|
|
|
public Actress(string name, string place, int year)
|
|
|
|
{
|
|
|
|
Name = name;
|
|
|
|
Place = place;
|
|
|
|
Year = year;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class SharpApp : Window
|
|
|
|
{
|
|
|
|
ListStore store;
|
|
|
|
Statusbar statusbar;
|
|
|
|
|
|
|
|
enum Column
|
|
|
|
{
|
|
|
|
Name,
|
|
|
|
Place,
|
|
|
|
Year
|
|
|
|
}
|
|
|
|
|
|
|
|
Actress[] actresses =
|
|
|
|
{
|
|
|
|
new Actress("Jessica Alba", "Pomona", 1981),
|
|
|
|
new Actress("Sigourney Weaver", "New York", 1949),
|
|
|
|
new Actress("Angelina Jolie", "Los Angeles", 1975),
|
|
|
|
new Actress("Natalie Portman", "Jerusalem", 1981),
|
|
|
|
new Actress("Rachel Weissz", "London", 1971),
|
|
|
|
new Actress("Scarlett Johansson", "New York", 1984)
|
|
|
|
};
|
|
|
|
|
|
|
|
public SharpApp() : base ("ListView")
|
|
|
|
{
|
|
|
|
BorderWidth = 8;
|
|
|
|
|
|
|
|
SetDefaultSize(350, 250);
|
|
|
|
SetPosition(WindowPosition.Center);
|
|
|
|
DeleteEvent += delegate { Application.Quit(); };
|
|
|
|
|
|
|
|
VBox vbox = new VBox(false, 8);
|
|
|
|
|
|
|
|
|
|
|
|
ScrolledWindow sw = new ScrolledWindow();
|
|
|
|
sw.ShadowType = ShadowType.EtchedIn;
|
|
|
|
sw.SetPolicy(PolicyType.Automatic, PolicyType.Automatic);
|
|
|
|
vbox.PackStart(sw, true, true, 0);
|
|
|
|
|
|
|
|
store = CreateModel();
|
|
|
|
|
|
|
|
TreeView treeView = new TreeView(store);
|
|
|
|
treeView.RulesHint = true;
|
|
|
|
treeView.RowActivated += OnRowActivated;
|
|
|
|
sw.Add(treeView);
|
|
|
|
|
|
|
|
AddColumns(treeView);
|
|
|
|
|
|
|
|
statusbar = new Statusbar();
|
|
|
|
|
|
|
|
vbox.PackStart(statusbar, false, false, 0);
|
|
|
|
|
|
|
|
Add(vbox);
|
|
|
|
ShowAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnRowActivated (object sender, RowActivatedArgs args) {
|
|
|
|
|
|
|
|
TreeIter iter;
|
|
|
|
TreeView view = (TreeView) sender;
|
|
|
|
|
|
|
|
if (view.Model.GetIter(out iter, args.Path)) {
|
|
|
|
string row = (string) view.Model.GetValue(iter, (int) Column.Name );
|
|
|
|
row += ", " + (string) view.Model.GetValue(iter, (int) Column.Place );
|
|
|
|
row += ", " + view.Model.GetValue(iter, (int) Column.Year );
|
|
|
|
statusbar.Push(0, row);
|
|
|
|
}
|
|
|
|
// *** if I can dump treeview to the console here I'll be happy ***
|
|
|
|
// *** I'd prefer a foreach or do/while ***
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddColumns(TreeView treeView)
|
|
|
|
{
|
|
|
|
CellRendererText rendererText = new CellRendererText();
|
|
|
|
TreeViewColumn column = new TreeViewColumn("Name", rendererText,
|
|
|
|
"text", Column.Name);
|
|
|
|
column.SortColumnId = (int) Column.Name;
|
|
|
|
treeView.AppendColumn(column);
|
|
|
|
|
|
|
|
rendererText = new CellRendererText();
|
|
|
|
column = new TreeViewColumn("Place", rendererText,
|
|
|
|
"text", Column.Place);
|
|
|
|
column.SortColumnId = (int) Column.Place;
|
|
|
|
treeView.AppendColumn(column);
|
|
|
|
|
|
|
|
rendererText = new CellRendererText();
|
|
|
|
column = new TreeViewColumn("Year", rendererText,
|
|
|
|
"text", Column.Year);
|
|
|
|
column.SortColumnId = (int) Column.Year;
|
|
|
|
treeView.AppendColumn(column);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ListStore CreateModel()
|
|
|
|
{
|
|
|
|
ListStore store = new ListStore( typeof(string),
|
|
|
|
typeof(string), typeof(int) );
|
|
|
|
|
|
|
|
foreach (Actress act in actresses) {
|
|
|
|
store.AppendValues(act.Name, act.Place, act.Year );
|
|
|
|
}
|
|
|
|
|
|
|
|
return store;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void Main()
|
|
|
|
{
|
|
|
|
Application.Init();
|
|
|
|
new SharpApp();
|
|
|
|
Application.Run();
|
|
|
|
}
|
2023-11-24 16:25:56 +01:00
|
|
|
}
|
2023-11-24 18:18:32 +01:00
|
|
|
*/
|
2023-11-09 06:58:48 +01:00
|
|
|
|