gtk-sat-tree.c
1 /* 2 Gpredict: Real-time satellite tracking and orbit prediction program 3 4 Copyright (C) 2001-2010 Alexandru Csete, OZ9AEC. 5 6 Authors: Alexandru Csete <oz9aec@gmail.com> 7 8 Comments, questions and bugreports should be submitted via 9 http://sourceforge.net/projects/gpredict/ 10 More details can be found at the project home page: 11 12 http://gpredict.oz9aec.net/ 13 14 This program is free software; you can redistribute it and/or modify 15 it under the terms of the GNU General Public License as published by 16 the Free Software Foundation; either version 2 of the License, or 17 (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, 20 but WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 GNU General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, visit http://www.fsf.org/ 26 */ 27 28 #ifdef HAVE_CONFIG_H 29 #include <build-config.h> 30 #endif 31 32 #include <glib/gi18n.h> 33 #include <gtk/gtk.h> 34 #include "compat.h" 35 #include "gtk-sat-tree.h" 36 #include "sat-log.h" 37 38 39 static void gtk_sat_tree_class_init(GtkSatTreeClass * class); 40 static void gtk_sat_tree_init(GtkSatTree * sat_tree); 41 static void gtk_sat_tree_destroy(GtkObject * object); 42 static GtkTreeModel *create_and_fill_model(guint flags); 43 static void column_toggled(GtkCellRendererToggle * cell, 44 gchar * path_str, gpointer data); 45 static gint scan_tle_file(const gchar * path, 46 GtkTreeStore * store, GtkTreeIter * node); 47 static gboolean check_and_select_sat(GtkTreeModel * model, 48 GtkTreePath * path, 49 GtkTreeIter * iter, gpointer data); 50 static gboolean uncheck_sat(GtkTreeModel * model, 51 GtkTreePath * path, 52 GtkTreeIter * iter, gpointer data); 53 static gint compare_func(GtkTreeModel * model, 54 GtkTreeIter * a, 55 GtkTreeIter * b, gpointer userdata); 56 static void expand_cb(GtkWidget * button, gpointer tree); 57 static void collapse_cb(GtkWidget * button, gpointer tree); 58 59 60 static GtkVBoxClass *parent_class = NULL; 61 62 63 GType gtk_sat_tree_get_type() 64 { 65 static GType gtk_sat_tree_type = 0; 66 67 if (!gtk_sat_tree_type) 68 { 69 static const GTypeInfo gtk_sat_tree_info = { 70 sizeof(GtkSatTreeClass), 71 NULL, /* base_init */ 72 NULL, /* base_finalize */ 73 (GClassInitFunc) gtk_sat_tree_class_init, 74 NULL, /* class_finalize */ 75 NULL, /* class_data */ 76 sizeof(GtkSatTree), 77 1, /* n_preallocs */ 78 (GInstanceInitFunc) gtk_sat_tree_init, 79 NULL 80 }; 81 82 gtk_sat_tree_type = g_type_register_static(GTK_TYPE_VBOX, 83 "GtkSatTree", 84 >k_sat_tree_info, 0); 85 } 86 87 return gtk_sat_tree_type; 88 } 89 90 static void gtk_sat_tree_class_init(GtkSatTreeClass * class) 91 { 92 GObjectClass *gobject_class; 93 GtkObjectClass *object_class; 94 GtkWidgetClass *widget_class; 95 GtkContainerClass *container_class; 96 97 gobject_class = G_OBJECT_CLASS(class); 98 object_class = (GtkObjectClass *) class; 99 widget_class = (GtkWidgetClass *) class; 100 container_class = (GtkContainerClass *) class; 101 102 parent_class = g_type_class_peek_parent(class); 103 104 object_class->destroy = gtk_sat_tree_destroy; 105 } 106 107 static void gtk_sat_tree_init(GtkSatTree * sat_tree) 108 { 109 (void)sat_tree; 110 } 111 112 static void gtk_sat_tree_destroy(GtkObject * object) 113 { 114 GtkSatTree *sat_tree = GTK_SAT_TREE(object); 115 116 /* clear list of selected satellites */ 117 /* crashes on 2. instance: g_slist_free (sat_tree->selection); */ 118 guint n, i; 119 gpointer data; 120 121 n = g_slist_length(sat_tree->selection); 122 123 for (i = 0; i < n; i++) 124 { 125 /* get the first element and delete it */ 126 data = g_slist_nth_data(sat_tree->selection, 0); 127 sat_tree->selection = g_slist_remove(sat_tree->selection, data); 128 } 129 130 (*GTK_OBJECT_CLASS(parent_class)->destroy) (object); 131 } 132 133 /** 134 * Create a new GtkSatTree widget 135 * 136 * @param flags Flags indicating which columns should be visible 137 * (see gtk_sat_tree_flag_t) 138 * @return A GtkSatTree widget. 139 */ 140 GtkWidget *gtk_sat_tree_new(guint flags) 141 { 142 GtkWidget *widget; 143 GtkSatTree *sat_tree; 144 GtkTreeModel *model; 145 GtkCellRenderer *renderer; 146 GtkTreeViewColumn *column; 147 GtkWidget *hbox; 148 GtkWidget *expbut; 149 GtkWidget *colbut; 150 151 if (!flags) 152 flags = GTK_SAT_TREE_DEFAULT_FLAGS; 153 154 widget = g_object_new(GTK_TYPE_SAT_TREE, NULL); 155 sat_tree = GTK_SAT_TREE(widget); 156 157 sat_tree->flags = flags; 158 159 /* create list and model */ 160 sat_tree->tree = gtk_tree_view_new(); 161 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(sat_tree->tree), TRUE); 162 model = create_and_fill_model(flags); 163 gtk_tree_view_set_model(GTK_TREE_VIEW(sat_tree->tree), model); 164 g_object_unref(model); 165 166 /* sort the tree by name */ 167 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), 168 GTK_SAT_TREE_COL_NAME, 169 compare_func, NULL, NULL); 170 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), 171 GTK_SAT_TREE_COL_NAME, 172 GTK_SORT_ASCENDING); 173 174 /* create tree view columns */ 175 /* label column */ 176 renderer = gtk_cell_renderer_text_new(); 177 column = gtk_tree_view_column_new_with_attributes(_("Satellite"), renderer, 178 "text", 179 GTK_SAT_TREE_COL_NAME, 180 NULL); 181 gtk_tree_view_insert_column(GTK_TREE_VIEW(sat_tree->tree), column, -1); 182 if (!(flags & GTK_SAT_TREE_FLAG_NAME)) 183 gtk_tree_view_column_set_visible(column, FALSE); 184 185 /* catalogue number */ 186 renderer = gtk_cell_renderer_text_new(); 187 column = gtk_tree_view_column_new_with_attributes(_("Catnum"), renderer, 188 "text", 189 GTK_SAT_TREE_COL_CATNUM, 190 "visible", 191 GTK_SAT_TREE_COL_VIS, 192 NULL); 193 gtk_tree_view_insert_column(GTK_TREE_VIEW(sat_tree->tree), column, -1); 194 if (!(flags & GTK_SAT_TREE_FLAG_CATNUM)) 195 gtk_tree_view_column_set_visible(column, FALSE); 196 197 /* epoch */ 198 renderer = gtk_cell_renderer_text_new(); 199 column = gtk_tree_view_column_new_with_attributes(_("Epoch"), renderer, 200 "text", 201 GTK_SAT_TREE_COL_EPOCH, 202 "visible", 203 GTK_SAT_TREE_COL_VIS, 204 NULL); 205 gtk_tree_view_insert_column(GTK_TREE_VIEW(sat_tree->tree), column, -1); 206 if (!(flags & GTK_SAT_TREE_FLAG_EPOCH)) 207 gtk_tree_view_column_set_visible(column, FALSE); 208 209 /* checkbox column */ 210 renderer = gtk_cell_renderer_toggle_new(); 211 sat_tree->handler_id = g_signal_connect(renderer, "toggled", 212 G_CALLBACK(column_toggled), 213 widget); 214 215 column = gtk_tree_view_column_new_with_attributes(_("Selected"), renderer, 216 "active", 217 GTK_SAT_TREE_COL_SEL, 218 "visible", 219 GTK_SAT_TREE_COL_VIS, 220 NULL); 221 gtk_tree_view_append_column(GTK_TREE_VIEW(sat_tree->tree), column); 222 gtk_tree_view_column_set_alignment(column, 0.5); 223 if (!(flags & GTK_SAT_TREE_FLAG_SEL)) 224 gtk_tree_view_column_set_visible(column, FALSE); 225 226 227 /* scrolled window */ 228 GTK_SAT_TREE(widget)->swin = gtk_scrolled_window_new(NULL, NULL); 229 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW 230 (GTK_SAT_TREE(widget)->swin), 231 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); 232 233 gtk_container_add(GTK_CONTAINER(GTK_SAT_TREE(widget)->swin), 234 GTK_SAT_TREE(widget)->tree); 235 236 //gtk_container_add (GTK_CONTAINER (widget), GTK_SAT_TREE (widget)->swin); 237 gtk_box_pack_start(GTK_BOX(widget), GTK_SAT_TREE(widget)->swin, TRUE, TRUE, 238 0); 239 240 /* expand and collabse buttons */ 241 expbut = gtk_button_new_with_label(_("Expand")); 242 gtk_widget_set_tooltip_text(expbut, 243 _ 244 ("Expand all nodes in the tree to make it searchable")); 245 g_signal_connect(expbut, "clicked", G_CALLBACK(expand_cb), sat_tree); 246 247 colbut = gtk_button_new_with_label(_("Collapse")); 248 gtk_widget_set_tooltip_text(colbut, _("Collapse all nodes in the tree")); 249 g_signal_connect(colbut, "clicked", G_CALLBACK(collapse_cb), sat_tree); 250 251 hbox = gtk_hbutton_box_new(); 252 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_START); 253 gtk_box_pack_start(GTK_BOX(hbox), expbut, FALSE, TRUE, 0); 254 gtk_box_pack_start(GTK_BOX(hbox), colbut, FALSE, TRUE, 0); 255 256 gtk_box_pack_start(GTK_BOX(widget), hbox, FALSE, FALSE, 5); 257 258 gtk_widget_show_all(widget); 259 260 /* initialise selection */ 261 GTK_SAT_TREE(widget)->selection = NULL; 262 263 return widget; 264 } 265 266 /** FIXME: flags not needed here */ 267 static GtkTreeModel *create_and_fill_model(guint flags) 268 { 269 GtkTreeStore *store; /* the list store data structure */ 270 GtkTreeIter node; /* new top level node added to the tree store */ 271 GDir *dir; 272 gchar *dirname; 273 gchar *path; 274 gchar *nodename; 275 gchar **buffv; 276 const gchar *fname; 277 guint num = 0; 278 279 (void)flags; 280 281 /* create a new tree store */ 282 store = gtk_tree_store_new(GTK_SAT_TREE_COL_NUM, G_TYPE_STRING, // name 283 G_TYPE_INT, // catnum 284 G_TYPE_STRING, // epoch 285 G_TYPE_BOOLEAN, // selected 286 G_TYPE_BOOLEAN // visible 287 ); 288 289 dirname = g_strconcat(g_get_home_dir(), 290 G_DIR_SEPARATOR_S, ".gpredict2", 291 G_DIR_SEPARATOR_S, "tle", NULL); 292 293 sat_log_log(SAT_LOG_LEVEL_DEBUG, 294 _("%s:%d: Directory is: %s"), __FILE__, __LINE__, dirname); 295 296 dir = g_dir_open(dirname, 0, NULL); 297 298 /* no tle files */ 299 if (!dir) 300 { 301 sat_log_log(SAT_LOG_LEVEL_ERROR, 302 _("%s:%d: No .tle files found in %s."), 303 __FILE__, __LINE__, dirname); 304 305 g_free(dirname); 306 307 return GTK_TREE_MODEL(store);; 308 } 309 310 /* Scan data directory for .tle files. 311 For each file scan through the file and 312 add entry to the tree. 313 */ 314 while ((fname = g_dir_read_name(dir))) 315 { 316 317 if (g_str_has_suffix(fname, ".tle")) 318 { 319 320 buffv = g_strsplit(fname, ".tle", 0); 321 nodename = g_strdup(buffv[0]); 322 nodename[0] = g_ascii_toupper(nodename[0]); 323 324 /* create a new top level node in the tree */ 325 gtk_tree_store_append(store, &node, NULL); 326 gtk_tree_store_set(store, &node, 327 GTK_SAT_TREE_COL_NAME, nodename, 328 GTK_SAT_TREE_COL_VIS, FALSE, -1); 329 330 /* build full path til file and sweep it for sats */ 331 path = g_strconcat(dirname, G_DIR_SEPARATOR_S, fname, NULL); 332 333 num = scan_tle_file(path, store, &node); 334 335 g_free(path); 336 g_free(nodename); 337 g_strfreev(buffv); 338 339 sat_log_log(SAT_LOG_LEVEL_INFO, 340 _("%s:%d: Read %d sats from %s "), 341 __FILE__, __LINE__, num, fname); 342 } 343 } 344 345 g_dir_close(dir); 346 g_free(dirname); 347 348 return GTK_TREE_MODEL(store); 349 } 350 351 /** 352 * Scan .tle file and add satellites to GtkTreeStore. 353 * 354 * @param path Full path of the tle file. 355 * @param store The GtkTreeStore to store the satellites into. 356 * @param node The parent node for the satellites 357 * @return The number of satellites that have been read into the tree. 358 */ 359 static gint scan_tle_file(const gchar * path, GtkTreeStore * store, 360 GtkTreeIter * node) 361 { 362 guint i = 0; 363 guint j; 364 GIOChannel *tlefile; 365 GError *error = NULL; 366 GtkTreeIter sat_iter; 367 gchar *line; 368 gsize length; 369 gchar catstr[6]; 370 371 gchar *satnam; 372 guint catnum; 373 374 /* open IO channel and read 3 lines at a time */ 375 tlefile = g_io_channel_new_file(path, "r", &error); 376 377 if (error != NULL) 378 { 379 sat_log_log(SAT_LOG_LEVEL_ERROR, 380 _("%s:%d: Failed to open %s (%s)"), 381 __FILE__, __LINE__, path, error->message); 382 g_clear_error(&error); 383 } 384 else if (tlefile) 385 { 386 387 /*** FIXME: More error handling please */ 388 389 while (g_io_channel_read_line(tlefile, &line, &length, NULL, NULL) != 390 G_IO_STATUS_EOF) 391 { 392 393 /* satellite name can be found in the first line */ 394 satnam = g_strdup(line); 395 g_strchomp(satnam); 396 397 /* free allocated line */ 398 g_free(line); 399 400 /* extract catnum from second line; index 2..6 */ 401 g_io_channel_read_line(tlefile, &line, &length, NULL, NULL); 402 403 for (j = 2; j < 7; j++) 404 { 405 catstr[j - 2] = line[j]; 406 } 407 catstr[5] = '\0'; 408 409 catnum = (guint) g_ascii_strtod(catstr, NULL); 410 411 /* insert satnam and catnum */ 412 gtk_tree_store_append(store, &sat_iter, node); 413 gtk_tree_store_set(store, &sat_iter, 414 GTK_SAT_TREE_COL_NAME, satnam, 415 GTK_SAT_TREE_COL_CATNUM, catnum, 416 GTK_SAT_TREE_COL_SEL, FALSE, 417 GTK_SAT_TREE_COL_VIS, TRUE, -1); 418 419 g_free(satnam); 420 g_free(line); 421 422 /* read the third line */ 423 g_io_channel_read_line(tlefile, &line, &length, NULL, NULL); 424 425 g_free(line); 426 i++; 427 } 428 429 /* close IO channel; don't care about status */ 430 g_io_channel_shutdown(tlefile, TRUE, NULL); 431 g_io_channel_unref(tlefile); 432 } 433 434 return i; 435 } 436 437 /** 438 * Manage toggle signals. 439 * 440 * @param cell cell. 441 * @param path_str Path string. 442 * @param data Pointer to the GtkSatTree widget. 443 * 444 * This function is called when the user toggles the visibility for a column. 445 * It will add or remove the toggled satellite from the list of selected sats. 446 */ 447 static void column_toggled(GtkCellRendererToggle * cell, 448 gchar * path_str, gpointer data) 449 { 450 GtkSatTree *sat_tree = GTK_SAT_TREE(data); 451 GtkTreeModel *model = 452 gtk_tree_view_get_model(GTK_TREE_VIEW(sat_tree->tree)); 453 GtkTreePath *path = gtk_tree_path_new_from_string(path_str); 454 GtkTreeIter iter; 455 gboolean toggle_item; 456 guint catnum; 457 458 (void)cell; 459 460 /* get toggled iter */ 461 gtk_tree_model_get_iter(model, &iter, path); 462 gtk_tree_model_get(model, &iter, 463 GTK_SAT_TREE_COL_CATNUM, &catnum, 464 GTK_SAT_TREE_COL_SEL, &toggle_item, -1); 465 466 /* do something with the value */ 467 toggle_item ^= 1; 468 469 if (toggle_item) 470 { 471 472 /* only append if sat not already in list */ 473 if (!g_slist_find(sat_tree->selection, GUINT_TO_POINTER(catnum))) 474 { 475 sat_tree->selection = g_slist_append(sat_tree->selection, 476 GUINT_TO_POINTER(catnum)); 477 sat_log_log(SAT_LOG_LEVEL_DEBUG, 478 _("%s:%d: Satellite %d selected."), 479 __FILE__, __LINE__, catnum); 480 481 /* Scan the tree for other instances of this sat. For example is 482 CUTE-1.7 present in both AMATEUR and CUBESAT. 483 We will need access to both the sat_tree and the catnum in the 484 foreach callback, so we attach catnum as data to the sat_tree 485 */ 486 g_object_set_data(G_OBJECT(sat_tree), "tmp", 487 GUINT_TO_POINTER(catnum)); 488 489 /* find the satellite in the tree */ 490 gtk_tree_model_foreach(model, check_and_select_sat, sat_tree); 491 492 } 493 else 494 { 495 sat_log_log(SAT_LOG_LEVEL_INFO, 496 _("%s:%d: Satellite %d already selected; skip..."), 497 __FILE__, __LINE__, catnum); 498 } 499 } 500 else 501 { 502 sat_tree->selection = g_slist_remove(sat_tree->selection, 503 GUINT_TO_POINTER(catnum)); 504 sat_log_log(SAT_LOG_LEVEL_DEBUG, 505 _("%s:%d: Satellite %d de-selected."), 506 __FILE__, __LINE__, catnum); 507 508 /* Scan the tree for other instances of this sat. For example is 509 CUTE-1.7 present in both AMATEUR and CUBESAT. 510 We will need access to both the sat_tree and the catnum in the 511 foreach callback, so we attach catnum as data to the sat_tree 512 */ 513 g_object_set_data(G_OBJECT(sat_tree), "tmp", GUINT_TO_POINTER(catnum)); 514 515 /* find the satellite in the tree */ 516 gtk_tree_model_foreach(model, uncheck_sat, sat_tree); 517 } 518 519 /* set new value */ 520 gtk_tree_store_set(GTK_TREE_STORE(model), &iter, 521 GTK_SAT_TREE_COL_SEL, toggle_item, -1); 522 523 gtk_tree_path_free(path); 524 } 525 526 /** 527 * Select a satellite in the GtkSatTree. 528 * 529 * @param sat_tree The GtkSatTree widget. 530 * @param catnum Catalogue number of satellite to be selected. 531 */ 532 void gtk_sat_tree_select(GtkSatTree * sat_tree, guint catnum) 533 { 534 /* sanity check */ 535 if ((sat_tree == NULL) || !IS_GTK_SAT_TREE(sat_tree)) 536 { 537 538 sat_log_log(SAT_LOG_LEVEL_ERROR, 539 _("%s: Invalid GtkSatTree!"), __func__); 540 541 return; 542 } 543 544 if (!g_slist_find(sat_tree->selection, GUINT_TO_POINTER(catnum))) 545 { 546 547 GtkTreeModel *model = 548 gtk_tree_view_get_model(GTK_TREE_VIEW(sat_tree->tree)); 549 550 /* we will need access to both the sat_tree and the catnum in the 551 foreach callback, so we attach catnum as data to the sat_tree 552 */ 553 g_object_set_data(G_OBJECT(sat_tree), "tmp", GUINT_TO_POINTER(catnum)); 554 555 /* find the satellite in the tree */ 556 gtk_tree_model_foreach(model, check_and_select_sat, sat_tree); 557 558 } 559 else 560 { 561 /* else do nothing since the sat is already selected */ 562 sat_log_log(SAT_LOG_LEVEL_INFO, 563 _("%s: Satellite %d already selected; skip..."), 564 __func__, catnum); 565 } 566 } 567 568 /** 569 * Foreach callback for checking and selecting a satellite. 570 * 571 * @param model The GtkTreeModel. 572 * @param path The GtkTreePath of the current item. 573 * @param iter The GtkTreeIter of the current item. 574 * @param data Pointer to the GtkSatTree structure. 575 * @return Always FALSE to let the for-each run to till end. 576 * 577 * This function is used as foreach-callback in the gtk_sat_tree_select function. 578 * The purpoise of the function is to set the check box to chacked state and add 579 * the satellite in question to the selection list. The catalogue number of the 580 * satellite to be selected is attached as data to the GtkSatTree (key = tmp). 581 * 582 * The function is also used in the column_toggled callback function with the 583 * purpose of locating and selecting other instances of the satellite than the 584 * one, on which the user clicked on (meaning: some sats can be found in several 585 * TLE file and we want to chak them all, not just the clicked instance). 586 */ 587 static gboolean check_and_select_sat(GtkTreeModel * model, 588 GtkTreePath * path, 589 GtkTreeIter * iter, gpointer data) 590 { 591 GtkSatTree *sat_tree = GTK_SAT_TREE(data); 592 guint cat1, cat2; 593 594 (void)path; 595 596 cat1 = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(data), "tmp")); 597 gtk_tree_model_get(model, iter, GTK_SAT_TREE_COL_CATNUM, &cat2, -1); 598 599 if (cat1 == cat2) 600 { 601 /* we have a match */ 602 gtk_tree_store_set(GTK_TREE_STORE(model), iter, 603 GTK_SAT_TREE_COL_SEL, TRUE, -1); 604 605 /* only append if sat not already in list */ 606 if (!g_slist_find(sat_tree->selection, GUINT_TO_POINTER(cat1))) 607 { 608 sat_tree->selection = g_slist_append(sat_tree->selection, 609 GUINT_TO_POINTER(cat1)); 610 sat_log_log(SAT_LOG_LEVEL_DEBUG, 611 _("%s:%d: Satellite %d selected."), 612 __FILE__, __LINE__, cat1); 613 } 614 else 615 { 616 sat_log_log(SAT_LOG_LEVEL_INFO, 617 _("%s:%d: Satellite %d already selected; skip..."), 618 __FILE__, __LINE__, cat1); 619 } 620 621 /* If we return TRUE here, the foreach would terminate. 622 We let it run to allow GtkSatTree to mark all instances 623 of sat the satellite (some sats may be present in two or 624 more .tle files. 625 */ 626 //return TRUE; 627 } 628 629 /* continue in order to catch ALL instances of sat */ 630 return FALSE; 631 } 632 633 /** 634 * Foreach callback for unchecking a satellite. 635 * 636 * @param model The GtkTreeModel. 637 * @param path The GtkTreePath of the current item. 638 * @param iter The GtkTreeIter of the current item. 639 * @param data Pointer to the GtkSatTree structure. 640 * @return Always FALSE to let the for-each run to till end. 641 * 642 * This function is very similar to the check_and_select callback except that it 643 * is used only to uncheck a deselected satellite. 644 */ 645 static gboolean uncheck_sat(GtkTreeModel * model, 646 GtkTreePath * path, 647 GtkTreeIter * iter, gpointer data) 648 { 649 guint cat1, cat2; 650 651 (void)path; 652 653 cat1 = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(data), "tmp")); 654 gtk_tree_model_get(model, iter, GTK_SAT_TREE_COL_CATNUM, &cat2, -1); 655 656 if (cat1 == cat2) 657 { 658 /* we have a match */ 659 gtk_tree_store_set(GTK_TREE_STORE(model), iter, 660 GTK_SAT_TREE_COL_SEL, FALSE, -1); 661 } 662 663 /* continue in order to catch ALL instances of sat */ 664 return FALSE; 665 } 666 667 /** 668 * Get list of selected satellites. 669 * 670 * @param sat_tree The GtkSatTree 671 * @param size Return location for number of selected sats. 672 * @return A newly allocated array containing the selected satellites or 673 * NULL if no satellites are selected. 674 * 675 * The returned array should be g_freed when no longer needed. 676 */ 677 guint *gtk_sat_tree_get_selected(GtkSatTree * sat_tree, gsize * size) 678 { 679 guint i; 680 gsize s; 681 guint *ret; 682 683 /* sanity check */ 684 if ((sat_tree == NULL) || !IS_GTK_SAT_TREE(sat_tree)) 685 { 686 687 sat_log_log(SAT_LOG_LEVEL_ERROR, 688 _("%s: Invalid GtkSatTree!"), __func__); 689 690 return NULL; 691 } 692 693 /* parameter are ok */ 694 s = g_slist_length(sat_tree->selection); 695 696 if (s < 1) 697 { 698 sat_log_log(SAT_LOG_LEVEL_DEBUG, 699 _("%s: There are no satellites selected => NULL."), 700 __func__); 701 702 *size = 0; 703 704 return NULL; 705 } 706 707 ret = (guint *) g_try_malloc(s * sizeof(guint)); 708 709 for (i = 0; i < s; i++) 710 { 711 ret[i] = GPOINTER_TO_UINT(g_slist_nth_data(sat_tree->selection, i)); 712 } 713 714 if (size != NULL) 715 *size = s; 716 717 return ret; 718 } 719 720 /** 721 * Compare two rows of the GtkSatTree. 722 * 723 * @param model The tree model of the GtkSatTree. 724 * @param a The first row. 725 * @param b The second row. 726 * @param userdata Not used. 727 * 728 * This function is used by the sorting algorithm to compare two rows of the 729 * GtkSatTree widget. The unctions works by comparing the character strings 730 * in the name column. 731 */ 732 static gint compare_func(GtkTreeModel * model, 733 GtkTreeIter * a, GtkTreeIter * b, gpointer userdata) 734 { 735 gchar *sat1, *sat2; 736 gint ret = 0; 737 738 (void)userdata; 739 740 gtk_tree_model_get(model, a, GTK_SAT_TREE_COL_NAME, &sat1, -1); 741 gtk_tree_model_get(model, b, GTK_SAT_TREE_COL_NAME, &sat2, -1); 742 743 ret = g_ascii_strcasecmp(sat1, sat2); 744 745 g_free(sat1); 746 g_free(sat2); 747 748 return ret; 749 } 750 751 /** 752 * Expand all nodes in the GtkSatTree. 753 * 754 * @param button The GtkButton that received the signal. 755 * @param tree Pointer to the GtkSatTree widget. 756 * 757 * This function expands all rows in the tree view in order to make it 758 * searchable. 759 */ 760 static void expand_cb(GtkWidget * button, gpointer tree) 761 { 762 (void)button; 763 764 gtk_tree_view_expand_all(GTK_TREE_VIEW(GTK_SAT_TREE(tree)->tree)); 765 } 766 767 /** 768 * Collapse all nodes in the GtkSatTree. 769 * 770 * @param button The GtkButton that received the signal. 771 * @param tree Pointer to the GtkSatTree widget. 772 * 773 * This function collapses all rows in the tree view. 774 */ 775 static void collapse_cb(GtkWidget * button, gpointer tree) 776 { 777 (void)button; 778 779 gtk_tree_view_collapse_all(GTK_TREE_VIEW(GTK_SAT_TREE(tree)->tree)); 780 }