UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2017 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 32 #include "model.h" 33 #include "image.h" 34 #include "toolkit.h" 35 36 #define IS_UI_LIST_MODEL(obj) \ 37 (G_TYPE_CHECK_INSTANCE_TYPE((obj), list_model_type)) 38 #define UI_LIST_MODEL(obj) \ 39 (G_TYPE_CHECK_INSTANCE_CAST((obj), list_model_type, UiListModel)) 40 41 static void list_model_class_init(GObjectClass *cl, gpointer data); 42 static void list_model_interface_init(GtkTreeModelIface *i, gpointer data); 43 static void list_model_init(UiListModel *instance, GObjectClass *cl); 44 45 static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data); 46 static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data); 47 48 static GObjectClass list_model_class; 49 static const GTypeInfo list_model_info = { 50 sizeof(GObjectClass), 51 NULL, 52 NULL, 53 (GClassInitFunc)list_model_class_init, 54 NULL, 55 NULL, 56 sizeof(UiListModel), 57 0, 58 (GInstanceInitFunc)list_model_init 59 }; 60 static const GInterfaceInfo list_model_interface_info = { 61 (GInterfaceInitFunc)list_model_interface_init, 62 NULL, 63 NULL 64 }; 65 static GType list_model_type; 66 67 static const GInterfaceInfo list_model_dnd_dest_interface_info = { 68 (GInterfaceInitFunc)list_model_dnd_dest_interface_init, 69 NULL, 70 NULL 71 }; 72 static const GInterfaceInfo list_model_dnd_src_interface_info = { 73 (GInterfaceInitFunc)list_model_dnd_src_interface_init, 74 NULL, 75 NULL 76 }; 77 78 void ui_list_init() { 79 list_model_type = g_type_register_static( 80 G_TYPE_OBJECT, 81 "UiListModel", 82 &list_model_info, 83 (GTypeFlags)0); 84 g_type_add_interface_static( 85 list_model_type, 86 GTK_TYPE_TREE_MODEL, 87 &list_model_interface_info); 88 g_type_add_interface_static( 89 list_model_type, 90 GTK_TYPE_TREE_DRAG_DEST, 91 &list_model_dnd_dest_interface_info); 92 g_type_add_interface_static( 93 list_model_type, 94 GTK_TYPE_TREE_DRAG_SOURCE, 95 &list_model_dnd_src_interface_info); 96 } 97 98 static void list_model_class_init(GObjectClass *cl, gpointer data) { 99 cl->dispose = ui_list_model_dispose; 100 cl->finalize = ui_list_model_finalize; 101 102 } 103 104 static void list_model_interface_init(GtkTreeModelIface *i, gpointer data) { 105 i->get_flags = ui_list_model_get_flags; 106 i->get_n_columns = ui_list_model_get_n_columns; 107 i->get_column_type = ui_list_model_get_column_type; 108 i->get_iter = ui_list_model_get_iter; 109 i->get_path = ui_list_model_get_path; 110 i->get_value = ui_list_model_get_value; 111 i->iter_next = ui_list_model_iter_next; 112 i->iter_children = ui_list_model_iter_children; 113 i->iter_has_child = ui_list_model_iter_has_child; 114 i->iter_n_children = ui_list_model_iter_n_children; 115 i->iter_nth_child = ui_list_model_iter_nth_child; 116 i->iter_parent = ui_list_model_iter_parent; 117 } 118 119 static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data) { 120 i->drag_data_received = ui_list_model_drag_data_received; 121 i->row_drop_possible = ui_list_model_row_drop_possible; 122 } 123 124 static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data) { 125 i->drag_data_delete = ui_list_model_drag_data_delete; 126 i->drag_data_get = ui_list_model_drag_data_get; 127 i->row_draggable = ui_list_model_row_draggable; 128 } 129 130 static void list_model_init(UiListModel *instance, GObjectClass *cl) { 131 instance->columntypes = NULL; 132 instance->var = NULL; 133 instance->numcolumns = 0; 134 instance->stamp = g_random_int(); 135 } 136 137 static GType ui_gtk_type(UiModelType type) { 138 switch(type) { 139 default: break; 140 case UI_STRING: return G_TYPE_STRING; 141 case UI_INTEGER: return G_TYPE_INT; 142 } 143 return G_TYPE_INVALID; 144 } 145 146 static void ui_model_set_value(GType type, void *data, GValue *value) { 147 switch(type) { 148 default: break; 149 case G_TYPE_OBJECT: { 150 value->g_type = G_TYPE_OBJECT; 151 g_value_set_object(value, data); 152 return; 153 } 154 case G_TYPE_STRING: { 155 value->g_type = G_TYPE_STRING; 156 g_value_set_string(value, data); 157 return; 158 } 159 case G_TYPE_INT: { 160 value->g_type = G_TYPE_INT; 161 int *i = data; 162 g_value_set_int(value, *i); 163 return; 164 } 165 } 166 value->g_type = G_TYPE_INVALID; 167 } 168 169 UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info) { 170 UiListModel *model = g_object_new(list_model_type, NULL); 171 model->obj = obj; 172 model->model = info; 173 model->var = var; 174 model->columntypes = calloc(sizeof(GType), 2 * info->columns); 175 int ncol = 0; 176 for(int i=0;i<info->columns;i++) { 177 UiModelType type = info->types[i]; 178 if(type == UI_ICON_TEXT) { 179 model->columntypes[ncol] = G_TYPE_OBJECT; 180 ncol++; 181 model->columntypes[ncol] = G_TYPE_STRING; 182 } else { 183 model->columntypes[ncol] = ui_gtk_type(info->types[i]); 184 } 185 ncol++; 186 } 187 model->numcolumns = ncol; 188 return model; 189 } 190 191 void ui_list_model_dispose(GObject *obj) { 192 193 } 194 195 void ui_list_model_finalize(GObject *obj) { 196 197 } 198 199 200 GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model) { 201 return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST); 202 } 203 204 gint ui_list_model_get_n_columns(GtkTreeModel *tree_model) { 205 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), 0); 206 UiListModel *model = UI_LIST_MODEL(tree_model); 207 return model->numcolumns; 208 } 209 210 GType ui_list_model_get_column_type(GtkTreeModel *tree_model, gint index) { 211 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), G_TYPE_INVALID); 212 UiListModel *model = UI_LIST_MODEL(tree_model); 213 g_return_val_if_fail(index < model->numcolumns, G_TYPE_INVALID); 214 return model->columntypes[index]; 215 } 216 217 gboolean ui_list_model_get_iter( 218 GtkTreeModel *tree_model, 219 GtkTreeIter *iter, 220 GtkTreePath *path) 221 { 222 g_assert(IS_UI_LIST_MODEL(tree_model)); 223 UiListModel *model = UI_LIST_MODEL(tree_model); 224 UiList *list = model->var->value; 225 226 // check the depth of the path 227 // a list must have a depth of 1 228 gint depth = gtk_tree_path_get_depth(path); 229 g_assert(depth == 1); 230 231 // get row 232 gint *indices = gtk_tree_path_get_indices(path); 233 gint row = indices[0]; 234 235 // check row 236 if(row == 0) { 237 // we don't need to count if the first element is requested 238 if(list->first(list) == NULL) { 239 return FALSE; 240 } 241 } else if(row >= list->count(list)) { 242 return FALSE; 243 } 244 245 // the UiList has an integrated iterator 246 // we only get a value to adjust it 247 void *val = NULL; 248 if(row == 0) { 249 val = list->first(list); 250 } else { 251 val = list->get(list, row); 252 } 253 254 iter->stamp = model->stamp; 255 iter->user_data = list->iter; 256 iter->user_data2 = (gpointer)(intptr_t)row; // list->index 257 iter->user_data3 = val; 258 259 return val ? TRUE : FALSE; 260 } 261 262 GtkTreePath* ui_list_model_get_path( 263 GtkTreeModel *tree_model, 264 GtkTreeIter *iter) 265 { 266 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), NULL); 267 g_return_val_if_fail(iter != NULL, NULL); 268 g_return_val_if_fail(iter->user_data != NULL, NULL); 269 270 UiListModel *model = UI_LIST_MODEL(tree_model); 271 272 GtkTreePath *path = gtk_tree_path_new(); 273 gtk_tree_path_append_index(path, (int)(intptr_t)iter->user_data2); // list->index 274 275 return path; 276 } 277 278 void ui_list_model_get_value( 279 GtkTreeModel *tree_model, 280 GtkTreeIter *iter, 281 gint column, 282 GValue *value) 283 { 284 g_return_if_fail(IS_UI_LIST_MODEL(tree_model)); 285 g_return_if_fail(iter != NULL); 286 g_return_if_fail(iter->user_data != NULL); 287 288 UiListModel *model = UI_LIST_MODEL(tree_model); 289 UiList *list = model->var->value; 290 291 g_return_if_fail(column < model->numcolumns); 292 293 // TODO: return correct value from column 294 295 //value->g_type = G_TYPE_STRING; 296 list->iter = iter->user_data; 297 //list->index = (int)(intptr_t)iter->user_data2; 298 //list->current = iter->user_data3; 299 if(model->model->getvalue) { 300 void *data = model->model->getvalue(iter->user_data3, column); 301 if(model->columntypes[column] == G_TYPE_OBJECT) { 302 UiImage *img = data; 303 ui_model_set_value(model->columntypes[column], img->pixbuf, value); 304 } else { 305 ui_model_set_value(model->columntypes[column], data, value); 306 } 307 } else { 308 value->g_type = G_TYPE_INVALID; 309 } 310 } 311 312 gboolean ui_list_model_iter_next( 313 GtkTreeModel *tree_model, 314 GtkTreeIter *iter) 315 { 316 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); 317 g_return_val_if_fail(iter != NULL, FALSE); 318 //g_return_val_if_fail(iter->user_data != NULL, FALSE); 319 320 if(!iter->user_data) { 321 return FALSE; 322 } 323 324 UiListModel *model = UI_LIST_MODEL(tree_model); 325 UiList *list = model->var->value; 326 list->iter = iter->user_data; 327 //list->index = (int)(intptr_t)iter->user_data2; 328 void *val = list->next(list); 329 iter->user_data = list->iter; 330 intptr_t index = (intptr_t)iter->user_data2; 331 index++; 332 //iter->user_data2 = (gpointer)(intptr_t)list->index; 333 iter->user_data2 = (gpointer)index; 334 iter->user_data3 = val; 335 return val ? TRUE : FALSE; 336 } 337 338 gboolean ui_list_model_iter_children( 339 GtkTreeModel *tree_model, 340 GtkTreeIter *iter, 341 GtkTreeIter *parent) 342 { 343 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); 344 345 UiListModel *model = UI_LIST_MODEL(tree_model); 346 UiList *list = model->var->value; 347 348 if(parent) { 349 return FALSE; 350 } 351 352 /* 353 * a list element has no children 354 * we set the iter to the first element 355 */ 356 void *val = list->first(list); 357 iter->stamp = model->stamp; 358 iter->user_data = list->iter; 359 iter->user_data2 = (gpointer)0; 360 iter->user_data3 = val; 361 362 return val ? TRUE : FALSE; 363 } 364 365 gboolean ui_list_model_iter_has_child( 366 GtkTreeModel *tree_model, 367 GtkTreeIter *iter) 368 { 369 return FALSE; 370 } 371 372 gint ui_list_model_iter_n_children( 373 GtkTreeModel *tree_model, 374 GtkTreeIter *iter) 375 { 376 g_assert(IS_UI_LIST_MODEL(tree_model)); 377 378 if(!iter) { 379 // return number of rows 380 UiListModel *model = UI_LIST_MODEL(tree_model); 381 UiList *list = model->var->value; 382 return list->count(list); 383 } 384 385 return 0; 386 } 387 388 gboolean ui_list_model_iter_nth_child( 389 GtkTreeModel *tree_model, 390 GtkTreeIter *iter, 391 GtkTreeIter *parent, 392 gint n) 393 { 394 g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); 395 396 if(parent) { 397 return FALSE; 398 } 399 400 UiListModel *model = UI_LIST_MODEL(tree_model); 401 UiList *list = model->var->value; 402 403 // check n 404 if(n == 0) { 405 // we don't need to count if the first element is requested 406 if(list->first(list) == NULL) { 407 return FALSE; 408 } 409 } else if(n >= list->count(list)) { 410 return FALSE; 411 } 412 413 void *val = list->get(list, n); 414 iter->stamp = model->stamp; 415 iter->user_data = list->iter; 416 iter->user_data2 = (gpointer)(intptr_t)n; // list->index 417 iter->user_data3 = val; 418 419 return val ? TRUE : FALSE; 420 } 421 422 gboolean ui_list_model_iter_parent( 423 GtkTreeModel *tree_model, 424 GtkTreeIter *iter, 425 GtkTreeIter *child) 426 { 427 return FALSE; 428 } 429 430 // ****** dnd ****** 431 432 gboolean ui_list_model_drag_data_received( 433 GtkTreeDragDest *drag_dest, 434 GtkTreePath *dest_path, 435 GtkSelectionData *selection_data) 436 { 437 //printf("drag received\n"); 438 UiListModel *model = UI_LIST_MODEL(drag_dest); 439 if(model->model->drop) { 440 gint *indices = gtk_tree_path_get_indices(dest_path); 441 gint row = indices[0]; 442 UiEvent e; 443 e.obj = model->obj; 444 e.window = e.obj->window; 445 e.document = e.obj->ctx->document; 446 e.eventdata = NULL; 447 e.intval = 0; 448 UiSelection s; 449 s.data = selection_data; 450 model->model->drop(&e, &s, model->var->value, row); 451 } 452 return TRUE; 453 } 454 455 gboolean ui_list_model_row_drop_possible( 456 GtkTreeDragDest *drag_dest, 457 GtkTreePath *dest_path, 458 GtkSelectionData *selection_data) 459 { 460 //printf("row_drop_possible\n"); 461 UiListModel *model = UI_LIST_MODEL(drag_dest); 462 if(model->model->candrop) { 463 gint *indices = gtk_tree_path_get_indices(dest_path); 464 gint row = indices[0]; 465 UiEvent e; 466 e.obj = model->obj; 467 e.window = e.obj->window; 468 e.document = e.obj->ctx->document; 469 e.eventdata = NULL; 470 e.intval = 0; 471 UiSelection s; 472 s.data = selection_data; 473 return model->model->candrop(&e, &s, model->var->value, row); 474 } 475 return TRUE; 476 } 477 478 gboolean ui_list_model_row_draggable( 479 GtkTreeDragSource *drag_source, 480 GtkTreePath *path) 481 { 482 //printf("row_draggable\n"); 483 UiListModel *model = UI_LIST_MODEL(drag_source); 484 if(model->model->candrag) { 485 gint *indices = gtk_tree_path_get_indices(path); 486 gint row = indices[0]; 487 UiEvent e; 488 e.obj = model->obj; 489 e.window = e.obj->window; 490 e.document = e.obj->ctx->document; 491 e.eventdata = NULL; 492 e.intval = 0; 493 return model->model->candrag(&e, model->var->value, row); 494 } 495 return TRUE; 496 } 497 498 gboolean ui_list_model_drag_data_get( 499 GtkTreeDragSource *drag_source, 500 GtkTreePath *path, 501 GtkSelectionData *selection_data) 502 { 503 //printf("drag_data_get\n"); 504 UiListModel *model = UI_LIST_MODEL(drag_source); 505 if(model->model->data_get) { 506 gint *indices = gtk_tree_path_get_indices(path); 507 gint row = indices[0]; 508 UiEvent e; 509 e.obj = model->obj; 510 e.window = e.obj->window; 511 e.document = e.obj->ctx->document; 512 e.eventdata = NULL; 513 e.intval = 0; 514 UiSelection s; 515 s.data = selection_data; 516 model->model->data_get(&e, &s, model->var->value, row); 517 } 518 return TRUE; 519 } 520 521 gboolean ui_list_model_drag_data_delete( 522 GtkTreeDragSource *drag_source, 523 GtkTreePath *path) 524 { 525 //printf("drag_data_delete\n"); 526 UiListModel *model = UI_LIST_MODEL(drag_source); 527 if(model->model->data_get) { 528 gint *indices = gtk_tree_path_get_indices(path); 529 gint row = indices[0]; 530 UiEvent e; 531 e.obj = model->obj; 532 e.window = e.obj->window; 533 e.document = e.obj->ctx->document; 534 e.eventdata = NULL; 535 e.intval = 0; 536 model->model->data_delete(&e, model->var->value, row); 537 } 538 return TRUE; 539 } 540