missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / gschem_messages_dockable.c
blob4215f13af23d0ad545a9195e279a13f6f44aeef0
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2019 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 /*! \file gschem_messages_dockable.c
22 * \brief Report problems with the current schematic or symbol.
25 #include <config.h>
27 #include <stdio.h>
28 #ifdef HAVE_STDLIB_H
29 #include <stdlib.h>
30 #endif
31 #ifdef HAVE_STRING_H
32 #include <string.h>
33 #endif
35 #include <math.h> /* for floor(3) */
37 #include "gschem.h"
38 #include "../include/gschem_messages_dockable.h"
41 typedef enum {
42 type_symversion,
43 type_symversion_error,
44 type_other = -1
45 } GschemMessageType;
47 typedef enum {
48 severity_error,
49 severity_warning,
50 severity_message,
51 severity_supplemental_text
52 } GschemMessageSeverity;
54 enum {
55 COLUMN_TYPE,
56 COLUMN_SEVERITY,
57 COLUMN_FILENAME,
58 COLUMN_REFDES,
59 COLUMN_MESSAGE,
60 COLUMN_TOOLTIP,
61 COLUMN_OBJECT,
62 COLUMN_REMOVED,
63 N_COLUMNS
67 static gpointer parent_class = NULL;
69 static void class_init (GschemMessagesDockableClass *class);
70 static void instance_init (GschemMessagesDockable *messages_dockable);
71 static void dispose (GObject *object);
73 static GtkWidget *create_widget (GschemDockable *dockable);
75 static void clear_weak_refs (GschemMessagesDockable *messages_dockable);
78 GType
79 gschem_messages_dockable_get_type ()
81 static GType type = 0;
83 if (type == 0) {
84 static const GTypeInfo info = {
85 sizeof (GschemMessagesDockableClass),
86 NULL, /* base_init */
87 NULL, /* base_finalize */
88 (GClassInitFunc) class_init,
89 NULL, /* class_finalize */
90 NULL, /* class_data */
91 sizeof (GschemMessagesDockable),
92 0, /* n_preallocs */
93 (GInstanceInitFunc) instance_init,
94 NULL /* value_table */
97 type = g_type_register_static (GSCHEM_TYPE_DOCKABLE,
98 "GschemMessagesDockable",
99 &info, 0);
102 return type;
106 static void
107 class_init (GschemMessagesDockableClass *class)
109 parent_class = g_type_class_peek_parent (class);
111 G_OBJECT_CLASS (class)->dispose = dispose;
112 GSCHEM_DOCKABLE_CLASS (class)->create_widget = create_widget;
116 static void
117 instance_init (GschemMessagesDockable *messages_dockable)
119 messages_dockable->store = gtk_list_store_new (
120 N_COLUMNS,
121 G_TYPE_INT, /* type */
122 G_TYPE_INT, /* severity */
123 G_TYPE_STRING, /* filename */
124 G_TYPE_STRING, /* refdes */
125 G_TYPE_STRING, /* message */
126 G_TYPE_STRING, /* tooltip */
127 G_TYPE_POINTER, /* object */
128 G_TYPE_BOOLEAN); /* removed */
132 static void
133 dispose (GObject *object)
135 clear_weak_refs (GSCHEM_MESSAGES_DOCKABLE (object));
136 g_clear_object (&GSCHEM_MESSAGES_DOCKABLE (object)->store);
138 G_OBJECT_CLASS (parent_class)->dispose (object);
142 static void
143 weakref_notify_func (void *dead_obj, void *user_data)
145 GschemMessagesDockable *messages_dockable =
146 GSCHEM_MESSAGES_DOCKABLE (user_data);
147 GtkTreeModel *model = GTK_TREE_MODEL (messages_dockable->store);
148 GtkTreeIter iter;
150 g_return_if_fail (dead_obj != NULL);
152 /* clear the 'object' field in all matching messages */
153 if (gtk_tree_model_get_iter_first (model, &iter))
154 do {
155 OBJECT *obj;
156 gtk_tree_model_get (model, &iter,
157 COLUMN_OBJECT, &obj,
158 -1);
159 if (obj == dead_obj)
160 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
161 COLUMN_OBJECT, NULL,
162 COLUMN_REMOVED, TRUE,
163 COLUMN_TOOLTIP,
164 _("<i>Press F5 to update messages</i>"),
165 -1);
166 } while (gtk_tree_model_iter_next (model, &iter));
168 /* don't remove the weak reference because this would mess up
169 weakref invocation (and happens automatically, anyway) */
173 static void
174 add_message (GschemMessagesDockable *messages_dockable,
175 gint type, gint severity,
176 const gchar *filename, const gchar *refdes, OBJECT *obj,
177 const gchar *tooltip,
178 const gchar *format, ...)
180 va_list args;
181 gchar *msg;
182 GtkTreeIter iter;
184 va_start (args, format);
185 msg = g_strdup_vprintf (format, args);
186 va_end (args);
188 gtk_list_store_append (messages_dockable->store, &iter);
189 gtk_list_store_set (messages_dockable->store, &iter,
190 COLUMN_TYPE, type,
191 COLUMN_SEVERITY, severity,
192 COLUMN_FILENAME, filename,
193 COLUMN_REFDES, refdes,
194 COLUMN_OBJECT, obj,
195 COLUMN_MESSAGE, msg,
196 COLUMN_TOOLTIP, tooltip,
197 -1);
199 g_free (msg);
201 if (obj != NULL)
202 /* duplicate weakrefs don't hurt, so don't bother checking */
203 s_object_weak_ref (obj, weakref_notify_func, messages_dockable);
207 static void
208 clear_weak_refs (GschemMessagesDockable *messages_dockable)
210 GtkTreeModel *model = GTK_TREE_MODEL (messages_dockable->store);
211 GtkTreeIter iter;
213 if (gtk_tree_model_get_iter_first (model, &iter))
214 do {
215 OBJECT *obj;
216 gtk_tree_model_get (model, &iter,
217 COLUMN_OBJECT, &obj,
218 -1);
219 if (obj != NULL)
220 s_object_weak_unref (obj, weakref_notify_func, messages_dockable);
221 } while (gtk_tree_model_iter_next (model, &iter));
225 static void
226 goto_object (GschemToplevel *w_current, OBJECT *obj)
228 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
229 int x0, y0, x1, y1;
230 GschemPageView *page_view;
232 g_return_if_fail (obj != NULL);
234 g_return_if_fail (world_get_single_object_bounds (
235 toplevel, obj, &x0, &y0, &x1, &y1));
237 page_view = gschem_toplevel_get_current_page_view (w_current);
238 g_return_if_fail (page_view != NULL);
239 gschem_page_view_pan (page_view, (x0 + x1) / 2, (y0 + y1) / 2);
243 static void
244 select_object (GschemToplevel *w_current, OBJECT *obj)
246 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
247 SELECTION *selection = toplevel->page_current->selection_list;
249 g_return_if_fail (obj != NULL);
251 o_redraw_cleanstates (w_current);
252 o_select_unselect_all (w_current);
254 g_run_hook_object (w_current, "%select-objects-hook", obj);
255 o_selection_add (toplevel, selection, obj);
256 o_attrib_add_selected (w_current, selection, obj);
258 i_set_state (w_current, SELECT);
259 i_action_stop (w_current);
260 i_update_menus (w_current);
264 static void
265 confirm_object_symversion (GschemToplevel *w_current, OBJECT *object)
267 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
269 OBJECT *inherited;
270 gboolean symversion_exists;
271 gboolean changed;
273 g_return_if_fail (object != NULL);
274 g_return_if_fail (object->complex != NULL);
275 g_return_if_fail (object->complex->prim_objs != NULL);
276 g_return_if_fail (object->page != NULL);
278 /* find inherited symversion= attribute */
280 inherited = NULL;
282 for (const GList *l = object->complex->prim_objs; l != NULL; l = l->next) {
283 OBJECT *o_current = l->data;
284 gchar *name;
285 gboolean is_symversion;
287 /* skip non-text objects, attached attributes, and text which doesn't
288 constitute a valid attribute (e.g. general text placed on the page) */
289 if (o_current->type != OBJ_TEXT ||
290 o_current->attached_to != NULL ||
291 !o_attrib_get_name_value (o_current, &name, NULL))
292 continue;
294 is_symversion = strcmp (name, "symversion") == 0;
295 g_free (name);
297 if (is_symversion) {
298 inherited = o_current;
299 break;
303 if (inherited == NULL) {
304 s_log_message (_("Can't find inherited symversion attribute\n"));
305 return;
308 /* search for and update existing symversion= attribute */
310 symversion_exists = FALSE;
311 changed = FALSE;
313 for (const GList *l = object->attribs; l != NULL; l = l->next) {
314 OBJECT *attrib = l->data;
315 char *name;
317 if (attrib->type != OBJ_TEXT ||
318 !o_attrib_get_name_value (attrib, &name, NULL))
319 continue;
321 if (strcmp (name, "symversion") == 0) {
322 if (strcmp (attrib->text->string, inherited->text->string) != 0) {
323 o_text_change (w_current, attrib, inherited->text->string,
324 attrib->visibility, attrib->show_name_value);
325 changed = TRUE;
327 symversion_exists = TRUE;
330 g_free (name);
333 /* promote symversion= attribute if it doesn't exist yet */
335 if (!symversion_exists) {
336 OBJECT *o_new = o_object_copy (toplevel, inherited);
337 o_set_visibility (toplevel, o_new, VISIBLE);
338 s_page_append (toplevel, object->page, o_new);
339 o_attrib_attach (toplevel, o_new, object, TRUE);
340 g_run_hook_object (w_current, "%add-objects-hook", o_new);
342 SELECTION *selection = object->page->selection_list;
343 if (g_list_find (geda_list_get_glist (selection), object) != NULL) {
344 g_run_hook_object (w_current, "%select-objects-hook", o_new);
345 o_selection_add (toplevel, selection, o_new);
346 o_attrib_add_selected (w_current, selection, o_new);
349 changed = TRUE;
352 if (changed) {
353 gschem_toplevel_page_content_changed (w_current, object->page);
354 o_undo_savestate_old (w_current, UNDO_ALL, _("Confirm Symbol Version"));
359 /******************************************************************************/
362 static void
363 icon_cell_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
364 GtkTreeModel *tree_model, GtkTreeIter *iter,
365 gpointer data)
367 gint severity;
368 gtk_tree_model_get (tree_model, iter,
369 COLUMN_SEVERITY, &severity,
370 -1);
372 g_object_set (cell,
373 "icon-name",
374 severity == severity_error ? GTK_STOCK_DIALOG_ERROR :
375 severity == severity_warning ? GTK_STOCK_DIALOG_WARNING :
376 severity == severity_message ? GTK_STOCK_DIALOG_INFO : "",
377 NULL);
381 static void
382 tree_selection_changed (GtkTreeSelection *selection, gpointer user_data)
384 GtkTreeModel *model;
385 GtkTreeIter iter;
386 OBJECT *obj;
388 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
389 return;
391 gtk_tree_model_get (model, &iter,
392 COLUMN_OBJECT, &obj,
393 -1);
395 if (obj != NULL)
396 goto_object (GSCHEM_DOCKABLE (user_data)->w_current, obj);
400 static void
401 tree_view_row_activated (GtkTreeView *tree_view, GtkTreePath *path,
402 GtkTreeViewColumn *column, gpointer user_data)
404 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
405 GtkTreeIter iter;
406 OBJECT *obj;
408 g_return_if_fail (gtk_tree_model_get_iter (model, &iter, path));
410 gtk_tree_model_get (model, &iter,
411 COLUMN_OBJECT, &obj,
412 -1);
414 if (obj != NULL)
415 select_object (GSCHEM_DOCKABLE (user_data)->w_current, obj);
419 static OBJECT *
420 get_message_object (GschemMessagesDockable *messages_dockable)
422 GtkTreeView *tree_view = GTK_TREE_VIEW (messages_dockable->tree_view);
423 GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
424 GtkTreeModel *model;
425 GtkTreeIter iter;
426 OBJECT *obj;
428 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
429 return NULL;
431 gtk_tree_model_get (model, &iter,
432 COLUMN_OBJECT, &obj,
433 -1);
434 return obj;
438 static void
439 select_item_activate (GtkMenuItem *menu_item, gpointer user_data)
441 GschemMessagesDockable *messages_dockable =
442 GSCHEM_MESSAGES_DOCKABLE (user_data);
444 OBJECT *obj = get_message_object (messages_dockable);
446 select_object (GSCHEM_DOCKABLE (user_data)->w_current, obj);
450 static void
451 confirm_item_activate (GtkMenuItem *menu_item, gpointer user_data)
453 GschemMessagesDockable *messages_dockable =
454 GSCHEM_MESSAGES_DOCKABLE (user_data);
456 OBJECT *obj = get_message_object (messages_dockable);
458 confirm_object_symversion (GSCHEM_DOCKABLE (user_data)->w_current, obj);
462 static void
463 create_menu (GschemMessagesDockable *messages_dockable)
465 GtkWidget *menu, *item;
466 g_return_if_fail (messages_dockable->menu == NULL);
468 menu = messages_dockable->menu = gtk_menu_new ();
470 item = messages_dockable->select_item =
471 gtk_menu_item_new_with_mnemonic (_("_Select Object"));
472 g_signal_connect (item, "activate",
473 G_CALLBACK (select_item_activate), messages_dockable);
474 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
476 item = messages_dockable->confirm_item =
477 gtk_menu_item_new_with_mnemonic (_("_Confirm Symbol Version"));
478 g_signal_connect (item, "activate",
479 G_CALLBACK (confirm_item_activate), messages_dockable);
480 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
482 gtk_widget_show_all (menu);
484 /* make sure the menu will be destroyed when the widget is destroyed and
485 moves between screens correctly if the widgets moves between screens */
486 gtk_menu_attach_to_widget (
487 GTK_MENU (menu), messages_dockable->tree_view, NULL);
491 static void
492 menu_position_func (GtkMenu *menu, gint *x, gint *y, gboolean *push_in,
493 gpointer user_data)
495 gint *pos = user_data;
497 *x = pos[0];
498 *y = pos[1];
502 static void
503 popup (GschemMessagesDockable *messages_dockable,
504 GtkTreeModel *model, GtkTreeIter *iter,
505 GdkEventButton *event)
507 gint type, severity;
508 OBJECT *obj;
510 gtk_tree_model_get (model, iter,
511 COLUMN_TYPE, &type,
512 COLUMN_SEVERITY, &severity,
513 COLUMN_OBJECT, &obj,
514 -1);
516 if (severity == severity_supplemental_text || obj == NULL)
517 /* there's no popup for extra lines and removed items */
518 return;
520 if (messages_dockable->menu == NULL)
521 create_menu (messages_dockable);
523 gtk_widget_set_sensitive (messages_dockable->select_item, obj != NULL);
524 gtk_widget_set_sensitive (messages_dockable->confirm_item,
525 type == type_symversion);
527 if (event != NULL)
528 gtk_menu_popup (GTK_MENU (messages_dockable->menu), NULL, NULL,
529 NULL, NULL, event->button, event->time);
530 else {
531 GtkTreeView *tree_view = GTK_TREE_VIEW (messages_dockable->tree_view);
532 GtkTreePath *path = gtk_tree_model_get_path (model, iter);
533 GdkRectangle rect;
534 gint pos[2];
536 gtk_tree_view_get_cell_area (tree_view, path, NULL, &rect);
537 gtk_tree_path_free (path);
539 (void) gdk_window_get_origin (gtk_tree_view_get_bin_window (tree_view),
540 &pos[0], &pos[1]);
541 pos[0] += rect.x;
542 pos[1] += rect.y + rect.height;
544 gtk_menu_popup (GTK_MENU (messages_dockable->menu), NULL, NULL,
545 menu_position_func, pos, 0, gtk_get_current_event_time ());
546 gtk_menu_shell_select_first (GTK_MENU_SHELL (messages_dockable->menu),
547 FALSE);
552 static gboolean
553 tree_view_button_press_event (GtkWidget *widget, GdkEventButton *event,
554 gpointer user_data)
556 GschemMessagesDockable *messages_dockable =
557 GSCHEM_MESSAGES_DOCKABLE (user_data);
559 GtkTreeView *tree_view = GTK_TREE_VIEW (messages_dockable->tree_view);
560 GtkTreePath *path = NULL;
561 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
562 GtkTreeIter iter;
563 GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
565 /* only show popup if this is a single right click */
566 if (event->button != 3 || event->type != GDK_BUTTON_PRESS)
567 return FALSE;
569 /* coordinates are expected to be relative to bin window */
570 if (event->window != gtk_tree_view_get_bin_window (tree_view))
571 return FALSE;
573 if (!gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
574 &path, NULL, NULL, NULL) ||
575 !gtk_tree_model_get_iter (model, &iter, path)) {
576 gtk_tree_path_free (path);
577 return FALSE;
580 gtk_tree_selection_select_path (selection, path);
581 gtk_tree_path_free (path);
583 popup (messages_dockable, model, &iter, event);
584 return TRUE;
588 static gboolean
589 tree_view_popup_menu (GtkWidget *widget, gpointer user_data)
591 GschemMessagesDockable *messages_dockable =
592 GSCHEM_MESSAGES_DOCKABLE (user_data);
594 GtkTreeSelection *selection = gtk_tree_view_get_selection (
595 GTK_TREE_VIEW (messages_dockable->tree_view));
596 GtkTreeModel *model;
597 GtkTreeIter iter;
599 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
600 return FALSE;
602 popup (messages_dockable, model, &iter, NULL);
603 return TRUE;
607 static GtkWidget *
608 create_widget (GschemDockable *dockable)
610 GschemMessagesDockable *messages_dockable =
611 GSCHEM_MESSAGES_DOCKABLE (dockable);
613 GtkWidget *tree_view;
614 GtkTreeSelection *selection;
615 GtkTreeViewColumn *column;
616 GtkCellRenderer *renderer;
617 GtkWidget *scrolled;
619 tree_view = GTK_WIDGET (
620 g_object_new (GTK_TYPE_TREE_VIEW,
621 /* GtkTreeView */
622 "headers-visible", FALSE,
623 "model", GTK_TREE_MODEL (messages_dockable->store),
624 "tooltip-column", COLUMN_TOOLTIP,
625 NULL));
626 messages_dockable->tree_view = tree_view;
628 g_signal_connect (tree_view, "row-activated",
629 G_CALLBACK (tree_view_row_activated), messages_dockable);
630 g_signal_connect (tree_view, "button-press-event",
631 G_CALLBACK (tree_view_button_press_event),
632 messages_dockable);
633 g_signal_connect (tree_view, "popup-menu",
634 G_CALLBACK (tree_view_popup_menu), messages_dockable);
636 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
637 g_signal_connect (selection, "changed",
638 G_CALLBACK (tree_selection_changed), messages_dockable);
641 /* filename column */
642 column = gtk_tree_view_column_new ();
643 gtk_tree_view_column_set_title (column, _("Filename"));
644 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
646 renderer = gtk_cell_renderer_pixbuf_new ();
647 gtk_tree_view_column_pack_start (column, renderer, FALSE);
648 gtk_tree_view_column_set_cell_data_func (column, renderer,
649 icon_cell_data_func, NULL, NULL);
651 renderer = gtk_cell_renderer_text_new ();
652 gtk_tree_view_column_pack_start (column, renderer, TRUE);
653 gtk_tree_view_column_add_attribute (column, renderer,
654 "text", COLUMN_FILENAME);
655 g_object_set (renderer, "foreground", "#aaa", NULL);
656 gtk_tree_view_column_add_attribute (column, renderer,
657 "foreground-set", COLUMN_REMOVED);
659 /* refdes column */
660 column = gtk_tree_view_column_new ();
661 gtk_tree_view_column_set_title (column, _("Comp/Pin"));
662 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
664 renderer = gtk_cell_renderer_text_new ();
665 gtk_tree_view_column_pack_start (column, renderer, TRUE);
666 gtk_tree_view_column_add_attribute (column, renderer,
667 "text", COLUMN_REFDES);
668 g_object_set (renderer, "foreground", "#aaa", NULL);
669 gtk_tree_view_column_add_attribute (column, renderer,
670 "foreground-set", COLUMN_REMOVED);
672 /* message column */
673 column = gtk_tree_view_column_new ();
674 gtk_tree_view_column_set_title (column, _("Message"));
675 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
677 renderer = gtk_cell_renderer_text_new ();
678 gtk_tree_view_column_pack_start (column, renderer, TRUE);
679 gtk_tree_view_column_add_attribute (column, renderer,
680 "text", COLUMN_MESSAGE);
681 g_object_set (renderer, "foreground", "#aaa", NULL);
682 gtk_tree_view_column_add_attribute (column, renderer,
683 "foreground-set", COLUMN_REMOVED);
686 scrolled = gtk_scrolled_window_new (NULL, NULL);
687 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
688 GTK_POLICY_AUTOMATIC,
689 GTK_POLICY_ALWAYS);
690 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
691 GTK_SHADOW_NONE);
692 gtk_container_add (GTK_CONTAINER (scrolled), tree_view);
694 gtk_widget_show_all (scrolled);
695 return scrolled;
699 /******************************************************************************/
702 static void
703 check_floating_symversion (GschemMessagesDockable *messages_dockable,
704 const gchar *filename)
706 GschemToplevel *w_current = GSCHEM_DOCKABLE (messages_dockable)->w_current;
707 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
709 char *symversion = o_attrib_search_floating_attribs_by_name (
710 s_page_objects (toplevel->page_current), "symversion", 0);
712 if (symversion == NULL)
713 return;
715 char *endptr = NULL;
716 double symversion_value = strtod (symversion, &endptr);
718 if (symversion_value == 0 && symversion == endptr)
719 add_message (messages_dockable,
720 type_other, severity_warning, filename, "", NULL, _(
721 "See http://wiki.geda-project.org/geda:master_attributes_list#symversion for "
722 "the semantics of the symversion= attribute."),
723 _("Invalid symversion= attribute \"%s\""),
724 symversion);
726 g_free (symversion);
730 /*! \brief Check symversion of a component.
732 * Compares the symversion= attribute attached to a component in the
733 * schematic with the symversion= attribute inherited from the symbol.
735 static void
736 check_symversion (GschemMessagesDockable *messages_dockable,
737 const gchar *filename, const char *refdes, OBJECT *object)
739 char *inside = NULL;
740 char *outside = NULL;
741 double inside_value = -1.0;
742 double outside_value = -1.0;
743 char *err_check = NULL;
744 int inside_present = FALSE;
745 int outside_present = FALSE;
746 double inside_major, inside_minor;
747 double outside_major, outside_minor;
749 g_return_if_fail (object != NULL);
750 g_return_if_fail ((object->type == OBJ_COMPLEX ||
751 object->type == OBJ_PLACEHOLDER));
752 g_return_if_fail (object->complex != NULL);
754 /* first look on the inside for the symversion= attribute */
755 inside = o_attrib_search_inherited_attribs_by_name (object, "symversion", 0);
757 /* now look for the symversion= attached to object */
758 outside = o_attrib_search_attached_attribs_by_name (object, "symversion", 0);
760 if (inside)
762 inside_value = strtod(inside, &err_check);
763 if (inside_value == 0 && inside == err_check)
765 add_message (messages_dockable,
766 type_symversion_error, severity_warning,
767 filename, refdes, object,
768 _("This is an error in the symbol and should be fixed by "
769 "the library author."),
770 _("Symbol version parse error: could not parse attribute "
771 "\"symversion=%s\" in symbol file \"%s\""),
772 inside, object->complex_basename);
773 goto done;
775 inside_present = TRUE;
776 } else {
777 inside_present = FALSE; /* attribute not inside */
780 if (outside)
782 outside_value = strtod(outside, &err_check);
783 if (outside_value == 0 && outside == err_check)
785 add_message (messages_dockable,
786 type_symversion_error, severity_warning,
787 filename, refdes, object, NULL,
788 _("Symbol version parse error: could not parse attribute "
789 "\"symversion=%s\" attached to symbol \"%s\""),
790 outside, object->complex_basename);
791 goto done;
793 outside_present = TRUE;
794 } else {
795 outside_present = FALSE; /* attribute not outside */
798 #if DEBUG
799 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object->name,
800 inside_value, outside_value);
801 #endif
803 /* symversion= is not present anywhere */
804 if (!inside_present && !outside_present)
806 /* symbol is legacy and versioned okay */
807 goto done;
810 /* No symversion inside, but a version is outside, this is a weird case */
811 if (!inside_present && outside_present)
813 add_message (messages_dockable,
814 type_symversion_error, severity_warning,
815 filename, refdes, object, NULL,
816 _("Symbol version oddity: "
817 "symversion=%s attached to instantiated symbol, "
818 "but no symversion= attribute inside symbol file \"%s\""),
819 outside, object->complex_basename);
820 goto done;
823 /* inside & not outside is a valid case, means symbol in library is newer */
824 /* also if inside_value is greater than outside_value, then symbol in */
825 /* library is newer */
826 if ((inside_present && !outside_present) ||
827 ((inside_present && outside_present) && (inside_value > outside_value)))
829 /* break up the version values into major.minor numbers */
830 inside_major = floor(inside_value);
831 inside_minor = inside_value - inside_major;
833 if (outside_present)
835 outside_major = floor(outside_value);
836 outside_minor = outside_value - outside_major;
837 } else {
838 /* symversion was not attached to the symbol, set all to zero */
839 outside_major = 0.0;
840 outside_minor = 0.0;
841 outside_value = 0.0;
844 #if DEBUG
845 printf("i: %f %f %f\n", inside_value, inside_major, inside_minor);
846 printf("o: %f %f %f\n", outside_value, outside_major, outside_minor);
847 #endif
849 const char *tooltip = _(
850 "The symbol has changed in the library, and its author has updated the "
851 "symversion= attribute in order to make you aware of this fact.\n\n"
852 "<i>Major</i> version changes usually indicate that things like pin "
853 "endpoints have changed which need some manual fixing of the schematic.\n"
854 "<i>Minor</i> version changes should be reviewed but don't usually "
855 "necessitate fixing.\n\n"
856 "Once you have verified that your schematic works correctly with the new "
857 "version of the symbol, you can update the component's symversion= attribute "
858 "to match the library version (or right-click and select \"Confirm Symbol "
859 "Version\") in order to hide this message.");
861 if (inside_major > outside_major)
863 add_message (messages_dockable,
864 type_symversion, severity_warning,
865 filename, refdes, object, tooltip,
866 _("Major version change: symbol \"%s\" in library (%s) "
867 "is newer than instantiated symbol (%s)"),
868 object->complex_basename,
869 inside != NULL ? inside : _("no version"),
870 outside != NULL ? outside : _("no version"));
872 /* don't bother checking minor changes if there are major ones */
873 goto done;
876 if (inside_minor > outside_minor)
878 add_message (messages_dockable,
879 type_symversion, severity_message,
880 filename, refdes, object, tooltip,
881 _("Minor version change: symbol \"%s\" in library (%s) "
882 "is newer than instantiated symbol (%s)"),
883 object->complex_basename,
884 inside != NULL ? inside : _("no version"),
885 outside != NULL ? outside : _("no version"));
887 else
888 add_message (messages_dockable,
889 type_symversion, severity_warning,
890 filename, refdes, object, tooltip,
891 _("Symbol version mismatch: symbol \"%s\" in library (%s) "
892 "is newer than instantiated symbol (%s)"),
893 object->complex_basename,
894 inside != NULL ? inside : _("no version"),
895 outside != NULL ? outside : _("no version"));
898 goto done;
901 /* outside value is greater than inside value, this is weird case */
902 if ((inside_present && outside_present) && (outside_value > inside_value))
904 add_message (messages_dockable,
905 type_symversion, severity_warning, filename, refdes, object,
906 _("This probably means you are trying to load a schematic "
907 "with an older (and probably incompatible) version of the "
908 "library."),
909 _("Symbol version oddity: instantiated symbol (%s) "
910 "is newer than symbol \"%s\" in library (%s)"),
911 object->complex_basename,
912 inside != NULL ? inside : _("no version"),
913 outside != NULL ? outside : _("no version"));
914 goto done;
917 /* if inside_value and outside_value match, then symbol versions are okay */
919 done:
920 g_free(inside);
921 g_free(outside);
925 static void
926 check_symbol (GschemMessagesDockable *messages_dockable,
927 const gchar *filename, OBJECT *obj)
929 GList *symlist;
930 unsigned int len;
931 char *refdes;
933 g_return_if_fail (obj != NULL);
934 g_return_if_fail (obj->type == OBJ_COMPLEX || obj->type == OBJ_PLACEHOLDER);
936 if (obj->complex_embedded)
937 return;
939 symlist = s_clib_search (obj->complex_basename, CLIB_EXACT);
940 len = g_list_length (symlist);
941 g_list_free (symlist);
943 refdes = o_attrib_search_object_attribs_by_name (obj, "refdes", 0);
944 if (refdes == NULL)
945 refdes = g_strdup (_("(unknown)"));
947 if (len == 0)
948 add_message (messages_dockable,
949 type_other, severity_error, filename, refdes, obj, NULL,
950 _("symbol \"%s\" not found"),
951 obj->complex_basename);
952 else if (len > 1) {
953 add_message (messages_dockable,
954 type_other, severity_warning, filename, refdes, obj, NULL,
955 _("There are %d symbols with name \"%s\" in the library."),
956 len, obj->complex_basename);
957 add_message (messages_dockable,
958 type_other, severity_supplemental_text, "", "", obj, NULL,
959 _("Picking one at random--this may not be what you want..."));
962 if (len >= 1)
963 check_symversion (messages_dockable, filename, refdes, obj);
965 g_free (refdes);
969 static gboolean
970 has_severity (GschemMessagesDockable *messages_dockable,
971 GschemMessageSeverity severity)
973 GtkTreeModel *tree_model = GTK_TREE_MODEL (messages_dockable->store);
974 GtkTreeIter iter;
976 if (gtk_tree_model_get_iter_first (tree_model, &iter))
977 do {
978 gint s;
979 gtk_tree_model_get (tree_model, &iter,
980 COLUMN_SEVERITY, &s,
981 -1);
982 if (severity >= s)
983 return TRUE;
984 } while (gtk_tree_model_iter_next (tree_model, &iter));
986 return FALSE;
990 static void
991 update_messages (GschemMessagesDockable *messages_dockable)
993 GschemToplevel *w_current = GSCHEM_DOCKABLE (messages_dockable)->w_current;
994 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
995 PAGE *page = toplevel->page_current;
996 g_return_if_fail (page != NULL);
998 gchar *basename = g_path_get_basename (page->page_filename);
1000 clear_weak_refs (messages_dockable);
1001 gtk_list_store_clear (messages_dockable->store);
1003 check_floating_symversion (messages_dockable, basename);
1005 for (const GList *l = s_page_objects (page); l != NULL; l = l->next) {
1006 OBJECT *obj = (OBJECT *) l->data;
1007 if (obj->type == OBJ_COMPLEX || obj->type == OBJ_PLACEHOLDER)
1008 check_symbol (messages_dockable, basename, obj);
1011 g_free (basename);
1013 /* If there were warnings, make sure the messages are visible.
1014 Try not to focus the dockable, though, as this might confuse users. */
1015 if (has_severity (messages_dockable, severity_warning)) {
1016 GtkWidget *widget, *notebook;
1017 switch (gschem_dockable_get_state (w_current->messages_dockable)) {
1018 case GSCHEM_DOCKABLE_STATE_DOCKED_LEFT:
1019 case GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM:
1020 case GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT:
1021 widget = w_current->messages_dockable->widget;
1022 notebook = gtk_widget_get_parent (widget);
1023 if (GTK_IS_NOTEBOOK (notebook)) {
1024 gint i = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), widget);
1025 if (i != -1)
1026 gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
1028 break;
1029 case GSCHEM_DOCKABLE_STATE_WINDOW:
1030 /* nothing to do */
1031 break;
1032 default:
1033 gschem_dockable_present (w_current->messages_dockable);
1039 void
1040 x_messages_page_changed (GschemToplevel *w_current)
1042 GschemMessagesDockable *messages_dockable =
1043 GSCHEM_MESSAGES_DOCKABLE (w_current->messages_dockable);
1045 update_messages (messages_dockable);
1049 void
1050 x_messages_update (GschemToplevel *w_current)
1052 GschemMessagesDockable *messages_dockable =
1053 GSCHEM_MESSAGES_DOCKABLE (w_current->messages_dockable);
1055 update_messages (messages_dockable);