1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 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
24 #include <sys/types.h>
25 #ifdef HAVE_SYS_PARAM_H
26 #include <sys/param.h>
37 #include "actions.decl.x"
38 #include <gdk/gdkkeysyms.h>
40 #include "../include/gschem_compselect_dockable.h"
42 /*! \def COMPSELECT_FILTER_INTERVAL
43 * \brief The time interval between request and actual filtering
45 * This constant is the time-lag between user modifications in the
46 * filter entry and the actual evaluation of the filter which
47 * ultimately update the display. It helps reduce the frequency of
48 * evaluation of the filter as user types.
50 * Unit is milliseconds.
52 #define COMPSELECT_FILTER_INTERVAL 200
55 enum compselect_behavior
{
67 LIB_COLUMN_SYMBOL_OR_SOURCE
,
73 /* Both the inuse model and symbol rows of the lib model store the
74 symbol pointer in column 0. Define a special constant which is
75 used whenever we depend on this fact. */
81 ATTRIBUTE_COLUMN_NAME
= 0,
82 ATTRIBUTE_COLUMN_VALUE
,
87 static GObjectClass
*compselect_parent_class
= NULL
;
90 static void compselect_class_init (GschemCompselectDockableClass
*class);
91 static void compselect_constructed (GObject
*object
);
92 static void compselect_dispose (GObject
*object
);
93 static void compselect_finalize (GObject
*object
);
95 static GtkWidget
*compselect_create_widget (GschemDockable
*dockable
);
99 update_attributes_model (GschemCompselectDockable
*compselect
, gchar
*filename
);
101 compselect_callback_tree_selection_changed (GtkTreeSelection
*selection
,
106 compselect_place (GschemCompselectDockable
*compselect
)
108 GschemToplevel
*w_current
= GSCHEM_DOCKABLE (compselect
)->w_current
;
109 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
111 enum compselect_behavior behavior
=
112 gtk_combo_box_get_active (compselect
->combobox_behaviors
);
114 w_current
->include_complex
= w_current
->embed_complex
= 0;
116 case BEHAVIOR_REFERENCE
:
119 w_current
->embed_complex
= 1;
121 case BEHAVIOR_INCLUDE
:
122 w_current
->include_complex
= 1;
125 g_assert_not_reached ();
128 if (w_current
->event_state
== COMPMODE
) {
129 /* Delete the component which was being placed */
130 if (w_current
->rubber_visible
)
131 o_place_invalidate_rubber (w_current
, FALSE
);
132 w_current
->rubber_visible
= 0;
133 s_delete_object_glist (toplevel
,
134 toplevel
->page_current
->place_list
);
135 toplevel
->page_current
->place_list
= NULL
;
137 /* Cancel whatever other action is currently in progress */
138 o_redraw_cleanstates (w_current
);
141 if (compselect
->selected_symbol
== NULL
) {
142 /* If there is no symbol selected, switch to SELECT mode */
143 i_set_state (w_current
, SELECT
);
144 i_action_stop (w_current
);
146 /* Otherwise set the new symbol to place */
147 o_complex_prepare_place (w_current
, compselect
->selected_symbol
);
153 select_symbol (GschemCompselectDockable
*compselect
, CLibSymbol
*symbol
)
155 if (symbol
== compselect
->selected_symbol
)
157 compselect
->selected_symbol
= symbol
;
159 /* update in-use and library selections */
160 GtkTreeSelection
*selection
;
164 selection
= gtk_tree_view_get_selection (compselect
->inusetreeview
);
165 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
166 CLibSymbol
*sym
= NULL
;
167 gtk_tree_model_get (model
, &iter
, INUSE_COLUMN_SYMBOL
, &sym
, -1);
169 g_signal_handlers_block_by_func (
170 selection
, compselect_callback_tree_selection_changed
, compselect
);
171 gtk_tree_selection_unselect_all (selection
);
172 g_signal_handlers_unblock_by_func (
173 selection
, compselect_callback_tree_selection_changed
, compselect
);
177 selection
= gtk_tree_view_get_selection (compselect
->libtreeview
);
178 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
179 CLibSymbol
*sym
= NULL
;
180 gboolean is_sym
= FALSE
;
181 gtk_tree_model_get (model
, &iter
, LIB_COLUMN_SYMBOL_OR_SOURCE
, &sym
,
182 LIB_COLUMN_IS_SYMBOL
, &is_sym
, -1);
183 if (is_sym
&& sym
!= symbol
) {
184 g_signal_handlers_block_by_func (
185 selection
, compselect_callback_tree_selection_changed
, compselect
);
186 gtk_tree_selection_unselect_all (selection
);
187 g_signal_handlers_unblock_by_func (
188 selection
, compselect_callback_tree_selection_changed
, compselect
);
192 /* update the preview with new symbol data */
193 gchar
*buffer
= symbol
? s_clib_symbol_get_data (symbol
) : NULL
;
194 g_object_set (compselect
->preview
,
196 "active", buffer
!= NULL
,
200 /* update the attributes with the toplevel of the preview widget*/
201 if (symbol
== NULL
) {
202 compselect
->is_selected
= FALSE
;
203 update_attributes_model (compselect
, NULL
);
205 g_free (compselect
->selected_filename
);
206 compselect
->selected_filename
= s_clib_symbol_get_filename (symbol
);
207 compselect
->is_selected
= TRUE
;
208 update_attributes_model (compselect
, compselect
->selected_filename
);
210 gschem_action_set_sensitive (action_add_last_component
,
211 compselect
->selected_filename
!= NULL
,
212 GSCHEM_DOCKABLE (compselect
)->w_current
);
215 /* signal a component has been selected to parent of dockable */
216 compselect_place (compselect
);
221 compselect_cancel (GschemDockable
*dockable
)
223 GschemToplevel
*w_current
= dockable
->w_current
;
225 if (w_current
->event_state
== COMPMODE
) {
226 /* Cancel the place operation currently in progress */
227 o_redraw_cleanstates (w_current
);
229 /* return to the default state */
230 i_set_state (w_current
, SELECT
);
231 i_action_stop (w_current
);
237 compselect_post_present (GschemDockable
*dockable
)
239 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (dockable
);
241 GtkWidget
*current_tab
, *entry_filter
;
242 GtkNotebook
*compselect_notebook
;
244 gtk_editable_select_region (GTK_EDITABLE (compselect
->entry_filter
), 0, -1);
246 /* Set the focus to the filter entry only if it is in the current
248 compselect_notebook
= GTK_NOTEBOOK (compselect
->viewtabs
);
249 current_tab
= gtk_notebook_get_nth_page (compselect_notebook
,
250 gtk_notebook_get_current_page (compselect_notebook
));
251 entry_filter
= GTK_WIDGET (compselect
->entry_filter
);
252 if (gtk_widget_is_ancestor (entry_filter
, current_tab
)) {
253 gtk_widget_grab_focus (entry_filter
);
259 x_compselect_deselect (GschemToplevel
*w_current
)
261 GschemCompselectDockable
*compselect
=
262 GSCHEM_COMPSELECT_DOCKABLE (w_current
->compselect_dockable
);
264 select_symbol (compselect
, NULL
);
268 /*! \brief Sets data for a particular cell of the in use treeview.
269 * \par Function Description
270 * This function determines what data is to be displayed in the
271 * "in use" symbol selection view.
273 * The model is a list of symbols. s_clib_symbol_get_name() is called
274 * to get the text to display.
277 inuse_treeview_set_cell_data (GtkTreeViewColumn
*tree_column
,
278 GtkCellRenderer
*cell
,
279 GtkTreeModel
*tree_model
,
285 gtk_tree_model_get (tree_model
, iter
, INUSE_COLUMN_SYMBOL
, &symbol
, -1);
286 g_object_set (G_OBJECT (cell
), "text", s_clib_symbol_get_name (symbol
), NULL
);
289 /*! \brief Returns whether a library treeview node represents a symbol. */
291 static gboolean
is_symbol(GtkTreeModel
*tree_model
, GtkTreeIter
*iter
)
294 gtk_tree_model_get (tree_model
, iter
, LIB_COLUMN_IS_SYMBOL
, &result
, -1);
298 /*! \brief Determines visibility of items of the library treeview.
299 * \par Function Description
300 * This is the function used to filter entries of the component
303 * \param [in] model The current selection in the treeview.
304 * \param [in] iter An iterator on a component or folder in the tree.
305 * \param [in] data The component selection dockable.
306 * \returns TRUE if item should be visible, FALSE otherwise.
309 lib_model_filter_visible_func (GtkTreeModel
*model
,
313 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (data
);
315 const gchar
*compname
;
316 gchar
*compname_upper
, *text_upper
, *pattern
;
320 g_assert (GSCHEM_IS_COMPSELECT_DOCKABLE (data
));
322 if (compselect
->entry_filter
== NULL
)
324 text
= gtk_entry_get_text (compselect
->entry_filter
);
325 if (g_ascii_strcasecmp (text
, "") == 0) {
329 /* If this is a source, only display it if it has children that
331 if (!is_symbol (model
, iter
)) {
335 if (gtk_tree_model_iter_children (model
, &iter2
, iter
))
337 if (lib_model_filter_visible_func (model
, &iter2
, data
)) {
341 } while (gtk_tree_model_iter_next (model
, &iter2
));
343 gtk_tree_model_get (model
, iter
,
344 LIB_COLUMN_SYMBOL_OR_SOURCE
, &sym
,
346 compname
= s_clib_symbol_get_name (sym
);
347 /* Do a case insensitive comparison, converting the strings
349 compname_upper
= g_ascii_strup (compname
, -1);
350 text_upper
= g_ascii_strup (text
, -1);
351 pattern
= g_strconcat ("*", text_upper
, "*", NULL
);
352 ret
= g_pattern_match_simple (pattern
, compname_upper
);
353 g_free (compname_upper
);
362 /*! \brief Handles activation (e.g. double-clicking) of a component row
363 * \par Function Description
364 * Component row activated handler:
365 * As a convenience to the user, expand / contract any node with children.
366 * Hide the component selector if a node without children is activated.
368 * \param [in] tree_view The component treeview.
369 * \param [in] path The GtkTreePath to the activated row.
370 * \param [in] column The GtkTreeViewColumn in which the activation occurred.
371 * \param [in] user_data The component selection dockable.
374 tree_row_activated (GtkTreeView
*tree_view
,
376 GtkTreeViewColumn
*column
,
381 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
383 model
= gtk_tree_view_get_model (tree_view
);
384 gtk_tree_model_get_iter (model
, &iter
, path
);
386 if (tree_view
== compselect
->inusetreeview
||
387 /* No special handling required */
388 (tree_view
== compselect
->libtreeview
&& is_symbol (model
, &iter
))) {
389 /* Tree view needs to check that we're at a symbol node */
390 CLibSymbol
*symbol
= NULL
;
391 gtk_tree_model_get (model
, &iter
, COMMON_COLUMN_SYMBOL
, &symbol
, -1);
392 select_symbol (compselect
, symbol
);
394 GschemDockable
*dockable
= GSCHEM_DOCKABLE (compselect
);
395 switch (gschem_dockable_get_state (dockable
)) {
396 case GSCHEM_DOCKABLE_STATE_DIALOG
:
397 /* if shown as dialog, hide */
398 gschem_dockable_hide (dockable
);
400 case GSCHEM_DOCKABLE_STATE_WINDOW
:
401 /* if shown as detached window, focus main window */
402 gtk_widget_grab_focus (dockable
->w_current
->drawing_area
);
403 x_window_present (dockable
->w_current
);
405 /* if docked, focus drawing area */
406 gtk_widget_grab_focus (dockable
->w_current
->drawing_area
);
411 if (gtk_tree_view_row_expanded (tree_view
, path
))
412 gtk_tree_view_collapse_row (tree_view
, path
);
414 gtk_tree_view_expand_row (tree_view
, path
, FALSE
);
417 /*! \brief GCompareFunc to sort an text object list by the object strings
420 sort_object_text (OBJECT
*a
, OBJECT
*b
)
422 return strcmp (a
->text
->string
, b
->text
->string
);
425 /*! \brief Update the model of the attributes treeview
426 * \par Function Description
427 * This function takes the toplevel attributes from the preview widget and
428 * puts them into the model of the <b>attrtreeview</b> widget.
429 * \param [in] compselect The dockable compselect
430 * \param [in] preview_toplevel The toplevel of the preview widget
433 update_attributes_model (GschemCompselectDockable
*compselect
, gchar
*filename
)
437 GtkTreeViewColumn
*column
;
438 GList
*o_iter
, *o_attrlist
;
446 model
= GTK_LIST_STORE (gtk_tree_view_get_model (compselect
->attrtreeview
));
447 gtk_list_store_clear (model
);
449 /* Invalidate the column width for the attribute value column, so
450 * the column is re-sized based on the new data being shown. Symbols
451 * with long values are common, and we don't want having viewed those
452 * forcing a h-scroll-bar on symbols with short valued attributes.
454 * We might also consider invalidating the attribute name columns,
455 * however that gives an inconsistent column division when swithing
456 * between symbols, which doesn't look nice. For now, assume that
457 * the name column can keep the max width gained whilst previewing.
459 column
= gtk_tree_view_get_column (compselect
->attrtreeview
,
460 ATTRIBUTE_COLUMN_VALUE
);
461 gtk_tree_view_column_queue_resize (column
);
463 PAGE
*preview_page
= gschem_page_view_get_page (
464 GSCHEM_PAGE_VIEW (compselect
->preview
));
465 if (preview_page
== NULL
)
468 o_attrlist
= o_attrib_find_floating_attribs (s_page_objects (preview_page
));
470 cfg
= filename
!= NULL
? eda_config_get_context_for_path (filename
)
471 : eda_config_get_user_context ();
472 filter_list
= eda_config_get_string_list (cfg
, "gschem.library",
473 "component-attributes", &n
, NULL
);
475 if (filter_list
== NULL
|| (n
> 0 && strcmp (filter_list
[0], "*") == 0)) {
476 /* display all attributes in alphabetical order */
477 o_attrlist
= g_list_sort (o_attrlist
, (GCompareFunc
) sort_object_text
);
478 for (o_iter
= o_attrlist
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
479 o_current
= o_iter
->data
;
480 o_attrib_get_name_value (o_current
, &name
, &value
);
481 gtk_list_store_append (model
, &iter
);
482 gtk_list_store_set (model
, &iter
,
483 ATTRIBUTE_COLUMN_NAME
, name
,
484 ATTRIBUTE_COLUMN_VALUE
, value
, -1);
489 /* display only attribute that are in the filter list */
490 for (i
= 0; i
< n
; i
++) {
491 for (o_iter
= o_attrlist
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
492 o_current
= o_iter
->data
;
493 if (o_attrib_get_name_value (o_current
, &name
, &value
)) {
494 if (strcmp (name
, filter_list
[i
]) == 0) {
495 gtk_list_store_append (model
, &iter
);
496 gtk_list_store_set (model
, &iter
,
497 ATTRIBUTE_COLUMN_NAME
, name
,
498 ATTRIBUTE_COLUMN_VALUE
, value
, -1);
507 g_strfreev (filter_list
);
508 g_list_free (o_attrlist
);
511 /*! \brief Handles changes in the treeview selection.
512 * \par Function Description
513 * This is the callback function that is called every time the user
514 * select a row in either component treeview of the dockable.
516 * If the selection is not a selection of a component (a directory
517 * name), it does nothing. Otherwise it retrieves the #CLibSymbol
520 * It then calls compselect_place to let its parent know that a
521 * component has been selected.
523 * \param [in] selection The current selection in the treeview.
524 * \param [in] user_data The component selection dockable.
527 compselect_callback_tree_selection_changed (GtkTreeSelection
*selection
,
533 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
534 CLibSymbol
*sym
= NULL
;
536 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
538 view
= gtk_tree_selection_get_tree_view (selection
);
539 if (view
== compselect
->inusetreeview
||
540 /* No special handling required */
541 (view
== compselect
->libtreeview
&& is_symbol (model
, &iter
))) {
542 /* Tree view needs to check that we're at a symbol node */
544 gtk_tree_model_get (model
, &iter
, COMMON_COLUMN_SYMBOL
, &sym
, -1);
548 select_symbol (compselect
, sym
);
551 /*! \brief Requests re-evaluation of the filter.
552 * \par Function Description
553 * This is the timeout function for the filtering of component in the
554 * tree of the dockable.
556 * The timeout this callback is attached to is removed after the
559 * \param [in] data The component selection dockable.
560 * \returns FALSE to remove the timeout.
563 compselect_filter_timeout (gpointer data
)
565 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (data
);
568 /* resets the source id in compselect */
569 compselect
->filter_timeout
= 0;
571 model
= gtk_tree_view_get_model (compselect
->libtreeview
);
574 const gchar
*text
= gtk_entry_get_text (compselect
->entry_filter
);
575 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model
));
576 if (strcmp (text
, "") != 0) {
577 /* filter text not-empty */
578 gtk_tree_view_expand_all (compselect
->libtreeview
);
580 /* filter text is empty, collapse expanded tree */
581 gtk_tree_view_collapse_all (compselect
->libtreeview
);
585 /* return FALSE to remove the source */
589 /*! \brief Callback function for the changed signal of the filter entry.
590 * \par Function Description
591 * This function monitors changes in the entry filter of the dockable.
593 * It specifically manages the sensitivity of the clear button of the
594 * entry depending on its contents. It also requests an update of the
595 * component list by re-evaluating filter at every changes.
597 * \param [in] editable The filter text entry.
598 * \param [in] user_data The component selection dockable.
601 compselect_callback_filter_entry_changed (GtkEditable
*editable
,
604 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
608 /* turns button off if filter entry is empty */
609 /* turns it on otherwise */
610 button
= GTK_WIDGET (compselect
->button_clear
);
612 (g_ascii_strcasecmp (gtk_entry_get_text (compselect
->entry_filter
),
614 if (gtk_widget_is_sensitive (button
) != sensitive
) {
615 gtk_widget_set_sensitive (button
, sensitive
);
618 /* Cancel any pending update of the component list filter */
619 if (compselect
->filter_timeout
!= 0)
620 g_source_remove (compselect
->filter_timeout
);
622 /* Schedule an update of the component list filter in
623 * COMPSELECT_FILTER_INTERVAL milliseconds */
624 compselect
->filter_timeout
= g_timeout_add (COMPSELECT_FILTER_INTERVAL
,
625 compselect_filter_timeout
,
630 /*! \brief Handles a click on the clear button.
631 * \par Function Description
632 * This is the callback function called every time the user press the
633 * clear button associated with the filter.
635 * It resets the filter entry, indirectly causing re-evaluation
636 * of the filter on the list of symbols to update the display.
638 * \param [in] button The clear button
639 * \param [in] user_data The component selection dockable.
642 compselect_callback_filter_button_clicked (GtkButton
*button
,
645 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
647 /* clears text in text entry for filter */
648 gtk_entry_set_text (compselect
->entry_filter
, "");
652 /*! \brief Handles changes of behavior.
653 * \par Function Description
654 * This function is called every time the value of the option menu
655 * for behaviors is modified.
657 * It calls compselect_place to let the parent know that the
658 * requested behavior for the next adding of a component has been
661 * \param [in] optionmenu The behavior option menu.
662 * \param [in] user_data The component selection dockable.
665 compselect_callback_behavior_changed (GtkOptionMenu
*optionmenu
,
668 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
670 compselect_place (compselect
);
673 /* \brief Create the tree model for the "In Use" view.
674 * \par Function Description
675 * Creates a straightforward list of symbols which are currently in
676 * use, using s_toplevel_get_symbols().
679 create_inuse_tree_model (GschemCompselectDockable
*compselect
)
682 GList
*symhead
, *symlist
;
685 store
= GTK_LIST_STORE (gtk_list_store_new (N_INUSE_COLUMNS
, G_TYPE_POINTER
));
687 symhead
= s_toplevel_get_symbols (
688 GSCHEM_DOCKABLE (compselect
)->w_current
->toplevel
);
690 for (symlist
= symhead
;
692 symlist
= g_list_next (symlist
)) {
694 gtk_list_store_append (store
, &iter
);
696 gtk_list_store_set (store
, &iter
,
697 INUSE_COLUMN_SYMBOL
, symlist
->data
,
701 g_list_free (symhead
);
703 gtk_tree_view_set_model (compselect
->inusetreeview
, GTK_TREE_MODEL (store
));
704 g_object_unref (store
); /* release initially owned reference */
707 /* \brief Helper function for create_lib_tree_model. */
709 static void populate_component_store(GtkTreeStore
*store
, GList
**srclist
,
710 GtkTreeIter
*parent
, const char *prefix
)
712 CLibSource
*source
= (CLibSource
*)(*srclist
)->data
;
713 const char *name
= s_clib_source_get_name (source
);
715 char *text
, *new_prefix
;
722 } else if (*name
!= '/') {
723 /* directory added by component-library */
724 text
= g_strdup (name
);
728 /* directory added by component-library-search */
729 size_t prefix_len
= strlen (prefix
);
730 g_assert (strncmp (name
, prefix
, prefix_len
) == 0);
731 char *p
= strchr (name
+ prefix_len
+ 1, '/');
734 /* There is a parent directory that was skipped
735 because it doesn't contain symbols. */
737 text
= g_strndup (name
+ prefix_len
, p
- name
- prefix_len
);
738 new_prefix
= g_strndup (name
, p
- name
+ 1);
739 new_srclist
= *srclist
;
741 size_t name_len
= strlen(name
);
742 text
= g_strndup (name
+ prefix_len
, name_len
- prefix_len
);
743 new_prefix
= g_strndup (name
, name_len
+ 1); /* reserve one extra byte */
744 new_prefix
[name_len
] = '/';
745 new_prefix
[name_len
+ 1] = '\0';
746 new_srclist
= g_list_next (*srclist
);
751 gtk_tree_store_append (store
, &iter
, parent
);
752 gtk_tree_store_set (store
, &iter
,
753 LIB_COLUMN_SYMBOL_OR_SOURCE
, source
,
754 LIB_COLUMN_NAME
, text
,
755 LIB_COLUMN_IS_SYMBOL
, FALSE
,
759 /* Look ahead, adding subdirectories. */
760 while (new_srclist
!= NULL
&&
761 strncmp(s_clib_source_get_name ((CLibSource
*)new_srclist
->data
),
762 new_prefix
, strlen(new_prefix
)) == 0) {
763 *srclist
= new_srclist
;
764 populate_component_store(store
, srclist
, &iter
, new_prefix
);
765 new_srclist
= g_list_next (*srclist
);
769 /* populate symbols */
770 GList
*symhead
, *symlist
;
772 symhead
= s_clib_source_get_symbols (source
);
773 for (symlist
= symhead
;
775 symlist
= g_list_next (symlist
)) {
777 gtk_tree_store_append (store
, &iter2
, &iter
);
778 gtk_tree_store_set (store
, &iter2
,
779 LIB_COLUMN_SYMBOL_OR_SOURCE
, symlist
->data
,
781 s_clib_symbol_get_name ((CLibSymbol
*)symlist
->data
),
782 LIB_COLUMN_IS_SYMBOL
, TRUE
,
785 g_list_free (symhead
);
788 /* \brief Create the tree model for the "Library" view.
789 * \par Function Description
790 * Creates a tree where the branches are the available component
791 * sources and the leaves are the symbols.
794 create_lib_tree_model (GschemCompselectDockable
*compselect
)
797 GList
*srchead
, *srclist
;
798 EdaConfig
*cfg
= eda_config_get_user_context ();
799 gboolean sort
= eda_config_get_boolean (cfg
, "gschem.library", "sort", NULL
);
801 store
= GTK_TREE_STORE (gtk_tree_store_new (N_LIB_COLUMNS
, G_TYPE_POINTER
,
805 /* populate component store */
806 srchead
= s_clib_get_sources (sort
);
807 for (srclist
= srchead
;
809 srclist
= g_list_next (srclist
)) {
810 populate_component_store(store
, &srclist
, NULL
, "/");
812 g_list_free (srchead
);
815 /* create filtered model */
816 GtkTreeModel
*model
= GTK_TREE_MODEL (
817 g_object_new (GTK_TYPE_TREE_MODEL_FILTER
,
818 "child-model", GTK_TREE_MODEL (store
),
819 "virtual-root", NULL
,
821 g_object_unref (store
); /* release initially owned reference */
823 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model
),
824 lib_model_filter_visible_func
,
827 gtk_tree_view_set_model (compselect
->libtreeview
, model
);
828 g_object_unref (model
); /* release initially owned reference */
830 /* re-expand library tree if filter text is not-empty */
831 if (compselect
->entry_filter
!= NULL
&&
832 gtk_entry_get_text (compselect
->entry_filter
)[0] != '\0')
833 gtk_tree_view_expand_all (compselect
->libtreeview
);
836 /*! \brief Helper function for \ref select_symbol_by_filename.
839 find_tree_iter_by_filename (GtkTreeModel
*tree_model
,
840 GtkTreeIter
*iter_return
,
842 const gchar
*filename
)
846 if (gtk_tree_model_iter_children (tree_model
, &iter
, parent
))
848 if (gtk_tree_model_get_n_columns (tree_model
) == N_INUSE_COLUMNS
||
849 is_symbol (tree_model
, &iter
)) {
850 /* node is a symbol */
852 gtk_tree_model_get (tree_model
, &iter
,
853 COMMON_COLUMN_SYMBOL
, &symbol
, -1);
854 gchar
*fn
= s_clib_symbol_get_filename (symbol
);
855 if (strcmp (fn
, filename
) == 0) {
864 /* node is a source */
866 gtk_tree_model_get (tree_model
, &iter
,
867 LIB_COLUMN_SYMBOL_OR_SOURCE
, &source
, -1);
871 /* this is a virtual node that was added for sub-directory sources */
874 const gchar
*directory
= s_clib_source_get_directory (source
);
875 recurse
= strncmp (directory
, filename
, strlen (directory
)) == 0 &&
876 filename
[strlen (directory
)] == '/';
879 find_tree_iter_by_filename (tree_model
, iter_return
, &iter
, filename
))
881 } while (gtk_tree_model_iter_next (tree_model
, &iter
));
887 select_symbol_by_filename (GschemCompselectDockable
*compselect
,
888 const gchar
*filename
)
890 g_return_if_fail (filename
!= NULL
);
892 GtkTreeView
*tree_view
= NULL
;
893 switch (gtk_notebook_get_current_page (compselect
->viewtabs
)) {
895 tree_view
= compselect
->inusetreeview
;
898 tree_view
= compselect
->libtreeview
;
901 g_assert_not_reached ();
904 GtkTreeModel
*model
= gtk_tree_view_get_model (tree_view
);
906 if (!find_tree_iter_by_filename (model
, &iter
, NULL
, filename
))
907 /* no matching symbol found */
910 /* expand the path to the node so it can be selected */
911 GtkTreePath
*path
= gtk_tree_model_get_path (model
, &iter
);
912 if (gtk_tree_path_up (path
))
913 gtk_tree_view_expand_to_path (tree_view
, path
);
914 gtk_tree_path_free (path
);
916 /* now select the node */
917 gtk_tree_selection_select_iter (
918 gtk_tree_view_get_selection (tree_view
), &iter
);
922 library_updated (gpointer user_data
)
924 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
925 gboolean was_selected
= compselect
->is_selected
;
927 /* Refresh the "Library" view */
928 create_lib_tree_model (compselect
);
930 /* Refresh the "In Use" view */
931 create_inuse_tree_model (compselect
);
933 /* re-select previously selected symbol */
934 if (was_selected
&& compselect
->selected_filename
!= NULL
)
935 select_symbol_by_filename (compselect
, compselect
->selected_filename
);
939 x_compselect_select_previous_symbol (GschemToplevel
*w_current
)
941 GschemCompselectDockable
*compselect
=
942 GSCHEM_COMPSELECT_DOCKABLE (w_current
->compselect_dockable
);
944 if (compselect
->selected_filename
!= NULL
)
945 select_symbol_by_filename (compselect
, compselect
->selected_filename
);
948 /* \brief On-demand refresh of the component library.
949 * \par Function Description
950 * Requests a rescan of the component library in order to pick up any
951 * new signals, and then updates the component selector.
954 compselect_callback_refresh_library (GtkButton
*button
, gpointer user_data
)
956 /* Rescan the libraries for symbols */
960 /*! \brief Creates the treeview for the "In Use" view. */
962 create_inuse_treeview (GschemCompselectDockable
*compselect
)
964 GtkWidget
*scrolled_win
, *treeview
, *vbox
, *hbox
, *button
;
965 GtkTreeSelection
*selection
;
966 GtkCellRenderer
*renderer
;
967 GtkTreeViewColumn
*column
;
969 vbox
= GTK_WIDGET (g_object_new (GTK_TYPE_VBOX
,
973 "homogeneous", FALSE
,
977 /* Create a scrolled window to accomodate the treeview */
978 scrolled_win
= GTK_WIDGET (
979 g_object_new (GTK_TYPE_SCROLLED_WINDOW
,
982 /* GtkScrolledWindow */
983 "hscrollbar-policy", GTK_POLICY_AUTOMATIC
,
984 "vscrollbar-policy", GTK_POLICY_ALWAYS
,
985 "shadow-type", GTK_SHADOW_ETCHED_IN
,
988 /* Create the treeview */
989 treeview
= GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW
,
992 "headers-visible", FALSE
,
995 /* set the inuse treeview of compselect */
996 compselect
->inusetreeview
= GTK_TREE_VIEW (treeview
);
998 create_inuse_tree_model (compselect
);
1000 g_signal_connect (treeview
,
1002 G_CALLBACK (tree_row_activated
),
1005 /* Connect callback to selection */
1006 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview
));
1007 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_SINGLE
);
1008 g_signal_connect (selection
,
1010 G_CALLBACK (compselect_callback_tree_selection_changed
),
1013 /* Insert a column for symbol name */
1014 renderer
= GTK_CELL_RENDERER (
1015 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT
,
1016 /* GtkCellRendererText */
1019 column
= GTK_TREE_VIEW_COLUMN (
1020 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
1021 /* GtkTreeViewColumn */
1022 "title", _("Symbols"),
1024 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
1025 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
1026 inuse_treeview_set_cell_data
,
1028 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
1030 /* Add the treeview to the scrolled window */
1031 gtk_container_add (GTK_CONTAINER (scrolled_win
), treeview
);
1033 /* add the scrolled window for directories to the vertical box */
1034 gtk_box_pack_start (GTK_BOX (vbox
), scrolled_win
,
1037 /* -- refresh button area -- */
1038 hbox
= GTK_WIDGET (g_object_new (GTK_TYPE_HBOX
,
1040 "homogeneous", FALSE
,
1043 /* create the refresh button */
1044 button
= GTK_WIDGET (g_object_new (GTK_TYPE_BUTTON
,
1048 "relief", GTK_RELIEF_NONE
,
1050 gtk_container_add (GTK_CONTAINER (button
),
1051 gtk_image_new_from_stock (GTK_STOCK_REFRESH
,
1052 GTK_ICON_SIZE_SMALL_TOOLBAR
));
1053 /* add the refresh button to the horizontal box at the end */
1054 gtk_box_pack_end (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
1055 g_signal_connect (button
,
1057 G_CALLBACK (compselect_callback_refresh_library
),
1060 /* add the refresh button area to the vertical box */
1061 gtk_box_pack_start (GTK_BOX (vbox
), hbox
,
1067 /*! \brief Creates the treeview for the "Library" view */
1069 create_lib_treeview (GschemCompselectDockable
*compselect
)
1071 GtkWidget
*libtreeview
, *vbox
, *scrolled_win
, *label
,
1072 *hbox
, *entry
, *button
;
1073 GtkTreeSelection
*selection
;
1074 GtkCellRenderer
*renderer
;
1075 GtkTreeViewColumn
*column
;
1077 /* -- library selection view -- */
1079 /* vertical box for component selection and search entry */
1080 vbox
= GTK_WIDGET (g_object_new (GTK_TYPE_VBOX
,
1084 "homogeneous", FALSE
,
1088 scrolled_win
= GTK_WIDGET (
1089 g_object_new (GTK_TYPE_SCROLLED_WINDOW
,
1092 /* GtkScrolledWindow */
1093 "hscrollbar-policy", GTK_POLICY_AUTOMATIC
,
1094 "vscrollbar-policy", GTK_POLICY_ALWAYS
,
1095 "shadow-type", GTK_SHADOW_ETCHED_IN
,
1097 /* create the treeview */
1098 libtreeview
= GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW
,
1101 "headers-visible", FALSE
,
1104 /* set directory/component treeview of compselect */
1105 compselect
->libtreeview
= GTK_TREE_VIEW (libtreeview
);
1107 create_lib_tree_model (compselect
);
1109 g_signal_connect (libtreeview
,
1111 G_CALLBACK (tree_row_activated
),
1114 /* connect callback to selection */
1115 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (libtreeview
));
1116 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_SINGLE
);
1117 g_signal_connect (selection
,
1119 G_CALLBACK (compselect_callback_tree_selection_changed
),
1122 /* insert a column to treeview for library/symbol name */
1123 renderer
= GTK_CELL_RENDERER (
1124 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT
,
1125 /* GtkCellRendererText */
1128 column
= GTK_TREE_VIEW_COLUMN (
1129 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
1130 /* GtkTreeViewColumn */
1131 "title", _("Symbols"),
1133 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
1134 gtk_tree_view_column_add_attribute (column
, renderer
, "text",
1136 gtk_tree_view_append_column (GTK_TREE_VIEW (libtreeview
), column
);
1138 /* add the treeview to the scrolled window */
1139 gtk_container_add (GTK_CONTAINER (scrolled_win
), libtreeview
);
1141 /* add the scrolled window for directories to the vertical box */
1142 gtk_box_pack_start (GTK_BOX (vbox
), scrolled_win
,
1146 /* -- filter area -- */
1147 hbox
= GTK_WIDGET (g_object_new (GTK_TYPE_HBOX
,
1149 "homogeneous", FALSE
,
1153 /* create the entry label */
1154 label
= GTK_WIDGET (g_object_new (GTK_TYPE_LABEL
,
1158 "label", _("Filter:"),
1160 /* add the search label to the filter area */
1161 gtk_box_pack_start (GTK_BOX (hbox
), label
,
1164 /* create the text entry for filter in components */
1165 entry
= GTK_WIDGET (g_object_new (GTK_TYPE_ENTRY
,
1169 gtk_widget_set_size_request (entry
, 10, -1);
1170 g_signal_connect (entry
,
1172 G_CALLBACK (compselect_callback_filter_entry_changed
),
1175 /* add the filter entry to the filter area */
1176 gtk_box_pack_start (GTK_BOX (hbox
), entry
,
1178 /* set filter entry of compselect */
1179 compselect
->entry_filter
= GTK_ENTRY (entry
);
1180 /* and init the event source for component filter */
1181 compselect
->filter_timeout
= 0;
1183 /* create the erase button for filter entry */
1184 button
= GTK_WIDGET (g_object_new (GTK_TYPE_BUTTON
,
1188 "relief", GTK_RELIEF_NONE
,
1191 gtk_container_add (GTK_CONTAINER (button
),
1192 gtk_image_new_from_stock (GTK_STOCK_CLEAR
,
1193 GTK_ICON_SIZE_SMALL_TOOLBAR
));
1194 g_signal_connect (button
,
1196 G_CALLBACK (compselect_callback_filter_button_clicked
),
1198 /* add the clear button to the filter area */
1199 gtk_box_pack_start (GTK_BOX (hbox
), button
,
1201 /* set clear button of compselect */
1202 compselect
->button_clear
= GTK_BUTTON (button
);
1204 /* create the refresh button */
1205 button
= GTK_WIDGET (g_object_new (GTK_TYPE_BUTTON
,
1209 "relief", GTK_RELIEF_NONE
,
1211 gtk_container_add (GTK_CONTAINER (button
),
1212 gtk_image_new_from_stock (GTK_STOCK_REFRESH
,
1213 GTK_ICON_SIZE_SMALL_TOOLBAR
));
1214 /* add the refresh button to the filter area */
1215 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
1216 g_signal_connect (button
,
1218 G_CALLBACK (compselect_callback_refresh_library
),
1221 /* add the filter area to the vertical box */
1222 gtk_box_pack_start (GTK_BOX (vbox
), hbox
,
1225 compselect
->libtreeview
= GTK_TREE_VIEW (libtreeview
);
1230 /*! \brief Creates the treeview widget for the attributes
1233 create_attributes_treeview (GschemCompselectDockable
*compselect
)
1235 GtkWidget
*attrtreeview
, *scrolled_win
;
1236 GtkListStore
*model
;
1237 GtkCellRenderer
*renderer
;
1238 GtkTreeViewColumn
*column
;
1240 model
= gtk_list_store_new (NUM_ATTRIBUTE_COLUMNS
,
1241 G_TYPE_STRING
, G_TYPE_STRING
);
1243 attrtreeview
= GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW
,
1246 "headers-visible", FALSE
,
1249 ATTRIBUTE_COLUMN_VALUE
,
1252 /* two columns for name and value of the attributes */
1253 renderer
= GTK_CELL_RENDERER (g_object_new (GTK_TYPE_CELL_RENDERER_TEXT
,
1257 column
= GTK_TREE_VIEW_COLUMN (g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
1261 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
1262 gtk_tree_view_column_add_attribute (column
, renderer
, "text",
1263 ATTRIBUTE_COLUMN_NAME
);
1264 gtk_tree_view_append_column (GTK_TREE_VIEW (attrtreeview
), column
);
1266 column
= GTK_TREE_VIEW_COLUMN (g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
1267 "title", _("Value"),
1270 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
1271 gtk_tree_view_column_add_attribute (column
, renderer
, "text",
1272 ATTRIBUTE_COLUMN_VALUE
);
1273 gtk_tree_view_append_column (GTK_TREE_VIEW (attrtreeview
), column
);
1275 scrolled_win
= GTK_WIDGET (g_object_new (GTK_TYPE_SCROLLED_WINDOW
,
1276 /* GtkScrolledWindow */
1277 "hscrollbar-policy", GTK_POLICY_AUTOMATIC
,
1278 "vscrollbar-policy", GTK_POLICY_AUTOMATIC
,
1279 "shadow-type", GTK_SHADOW_ETCHED_IN
,
1282 gtk_container_add (GTK_CONTAINER (scrolled_win
), attrtreeview
);
1284 compselect
->attrtreeview
= GTK_TREE_VIEW (attrtreeview
);
1286 return scrolled_win
;
1289 /*! \brief Create the combo box for behaviors.
1290 * \par Function Description
1291 * This function creates and returns a <B>GtkComboBox</B> for
1292 * selecting the behavior when a component is added to the sheet.
1295 create_behaviors_combo_box (void)
1297 GtkWidget
*combobox
;
1299 combobox
= gtk_combo_box_new_text ();
1301 /* Note: order of items in menu is important */
1302 /* BEHAVIOR_REFERENCE */
1303 gtk_combo_box_append_text (GTK_COMBO_BOX (combobox
),
1304 _("Reference symbol (default)"));
1305 /* BEHAVIOR_EMBED */
1306 gtk_combo_box_append_text (GTK_COMBO_BOX (combobox
),
1307 _("Embed symbol in schematic"));
1308 /* BEHAVIOR_INCLUDE */
1309 gtk_combo_box_append_text (GTK_COMBO_BOX (combobox
),
1310 _("Include symbol as individual objects"));
1312 gtk_combo_box_set_active (GTK_COMBO_BOX (combobox
), 0);
1318 gschem_compselect_dockable_get_type ()
1320 static GType compselect_type
= 0;
1322 if (!compselect_type
) {
1323 static const GTypeInfo compselect_info
= {
1324 sizeof (GschemCompselectDockableClass
),
1325 NULL
, /* base_init */
1326 NULL
, /* base_finalize */
1327 (GClassInitFunc
) compselect_class_init
,
1328 NULL
, /* class_finalize */
1329 NULL
, /* class_data */
1330 sizeof (GschemCompselectDockable
),
1331 0, /* n_preallocs */
1332 NULL
/* instance_init */
1335 compselect_type
= g_type_register_static (GSCHEM_TYPE_DOCKABLE
,
1336 "GschemCompselectDockable",
1337 &compselect_info
, 0);
1340 return compselect_type
;
1344 /*! \brief GschemDockable "save_internal_geometry" class method handler
1346 * \par Function Description
1347 * Save the dockable's current internal geometry.
1349 * \param [in] dockable The GschemCompselectDockable to save the geometry of.
1350 * \param [in] key_file The GKeyFile to save the geometry data to.
1351 * \param [in] group_name The group name in the key file to store the data under.
1354 compselect_save_internal_geometry (GschemDockable
*dockable
,
1358 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (dockable
);
1361 position
= gtk_paned_get_position (GTK_PANED (compselect
->hpaned
));
1362 eda_config_set_int (cfg
, group_name
, "hpaned", position
);
1364 position
= gtk_paned_get_position (GTK_PANED (compselect
->vpaned
));
1365 eda_config_set_int (cfg
, group_name
, "vpaned", position
);
1367 position
= gtk_notebook_get_current_page (compselect
->viewtabs
);
1368 eda_config_set_int (cfg
, group_name
, "source-tab", position
);
1370 eda_config_set_boolean (cfg
, group_name
, "preview-expanded",
1371 gtk_expander_get_expanded (
1372 GTK_EXPANDER (compselect
->preview_expander
)));
1374 eda_config_set_boolean (cfg
, group_name
, "attribs-expanded",
1375 gtk_expander_get_expanded (
1376 GTK_EXPANDER (compselect
->attribs_expander
)));
1380 /*! \brief GschemDockable "restore_internal_geometry" class method handler
1382 * \par Function Description
1383 * Restore the dockable's current internal geometry.
1385 * \param [in] dockable The GschemCompselectDockable to restore the geometry of.
1386 * \param [in] key_file The GKeyFile to save the geometry data to.
1387 * \param [in] group_name The group name in the key file to store the data under.
1390 compselect_restore_internal_geometry (GschemDockable
*dockable
,
1394 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (dockable
);
1397 GError
*error
= NULL
;
1399 position
= eda_config_get_int (cfg
, group_name
, "hpaned", NULL
);
1402 gtk_paned_set_position (GTK_PANED (compselect
->hpaned
), position
);
1404 position
= eda_config_get_int (cfg
, group_name
, "vpaned", NULL
);
1406 gtk_paned_set_position (GTK_PANED (compselect
->vpaned
), position
);
1408 position
= eda_config_get_int (cfg
, group_name
, "source-tab", &error
);
1409 if (error
!= NULL
) {
1411 g_clear_error (&error
);
1413 gtk_notebook_set_current_page (compselect
->viewtabs
, position
);
1415 expanded
= eda_config_get_boolean (cfg
, group_name
, "preview-expanded", &error
);
1416 if (error
!= NULL
) {
1418 g_clear_error (&error
);
1420 gtk_expander_set_expanded (GTK_EXPANDER (compselect
->preview_expander
),
1423 expanded
= eda_config_get_boolean (cfg
, group_name
, "attribs-expanded", &error
);
1424 if (error
!= NULL
) {
1426 g_clear_error (&error
);
1428 gtk_expander_set_expanded (GTK_EXPANDER (compselect
->attribs_expander
),
1434 compselect_class_init (GschemCompselectDockableClass
*klass
)
1436 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
1437 GschemDockableClass
*gschem_dockable_class
= GSCHEM_DOCKABLE_CLASS (klass
);
1439 gschem_dockable_class
->create_widget
= compselect_create_widget
;
1440 gschem_dockable_class
->post_present
= compselect_post_present
;
1441 gschem_dockable_class
->cancel
= compselect_cancel
;
1443 gschem_dockable_class
->save_internal_geometry
=
1444 compselect_save_internal_geometry
;
1445 gschem_dockable_class
->restore_internal_geometry
=
1446 compselect_restore_internal_geometry
;
1448 gobject_class
->constructed
= compselect_constructed
;
1449 gobject_class
->dispose
= compselect_dispose
;
1450 gobject_class
->finalize
= compselect_finalize
;
1452 compselect_parent_class
= g_type_class_peek_parent (klass
);
1455 /*! \brief Make <widget> the child of <container>, removing other
1456 * parent-child relations if necessary.
1459 reparent (GtkWidget
*container
, GtkWidget
*widget
)
1461 GtkWidget
*old_child
= gtk_bin_get_child (GTK_BIN (container
));
1462 GtkWidget
*old_parent
= widget
? gtk_widget_get_parent (widget
) : NULL
;
1463 if (old_child
== widget
&& old_parent
== container
)
1466 if (old_child
!= NULL
)
1467 gtk_container_remove (GTK_CONTAINER (container
), old_child
);
1468 if (old_parent
!= NULL
)
1469 gtk_container_remove (GTK_CONTAINER (old_parent
), widget
);
1471 if (container
!= NULL
&& widget
!= NULL
)
1472 gtk_container_add (GTK_CONTAINER (container
), widget
);
1475 /*! \brief Make <top> and <bottom> the children of <container>,
1476 * removing other parent-child relations if necessary.
1479 reparent2 (GtkWidget
*container
, GtkWidget
*top
, GtkWidget
*bottom
)
1481 GtkWidget
*top_parent
= gtk_widget_get_parent (top
);
1482 GtkWidget
*bottom_parent
= gtk_widget_get_parent (bottom
);
1483 if (top_parent
== container
&& bottom_parent
== container
)
1486 if (top_parent
!= NULL
)
1487 gtk_container_remove (GTK_CONTAINER (top_parent
), top
);
1488 if (bottom_parent
!= NULL
)
1489 gtk_container_remove (GTK_CONTAINER (bottom_parent
), bottom
);
1492 GList
*old_children
=
1493 gtk_container_get_children (GTK_CONTAINER (container
));
1494 if (old_children
== NULL
)
1496 gtk_container_remove (GTK_CONTAINER (container
), old_children
->data
);
1499 if (GTK_IS_PANED (container
)) {
1500 gtk_paned_pack1 (GTK_PANED (container
), top
, TRUE
, FALSE
);
1501 gtk_paned_pack2 (GTK_PANED (container
), bottom
, FALSE
, FALSE
);
1502 } else if (GTK_IS_BOX (container
)) {
1503 gtk_box_pack_start (GTK_BOX (container
), top
, TRUE
, TRUE
, 0);
1504 gtk_box_pack_start (GTK_BOX (container
), bottom
, FALSE
, FALSE
, 0);
1506 g_assert_not_reached ();
1509 /*! \brief Put the appropriate widget hierarchy for vertical layout in place.
1512 compselect_update_vertical_hierarchy (GschemCompselectDockable
*compselect
)
1514 gboolean expanded0
= gtk_expander_get_expanded (
1515 GTK_EXPANDER (compselect
->preview_expander
));
1516 gboolean expanded1
= gtk_expander_get_expanded (
1517 GTK_EXPANDER (compselect
->attribs_expander
));
1519 GtkWidget
*container0
= expanded0
? compselect
->preview_paned
1520 : compselect
->preview_box
;
1521 GtkWidget
*container1
= expanded1
? compselect
->attribs_paned
1522 : compselect
->attribs_box
;
1524 reparent (compselect
->preview_expander
,
1525 expanded0
? compselect
->preview_content
: NULL
);
1526 reparent (compselect
->attribs_expander
,
1527 expanded1
? compselect
->attribs_content
: NULL
);
1529 reparent2 (container0
, compselect
->vbox
, compselect
->preview_expander
);
1530 reparent2 (container1
, container0
, compselect
->attribs_expander
);
1531 reparent (compselect
->top
, container1
);
1534 /*! \brief Set the current layout for this dockable.
1536 * Tiled layout is the classical behavior of gschem: the tree view is
1537 * on the left, preview and attributes are on the right.
1539 * Vertical layout is used for tall aspect ratios: the tree view is at
1540 * the top, preview and attributes are below and expandable.
1543 compselect_set_tiled (GschemCompselectDockable
*compselect
, gboolean tiled
)
1545 GtkWidget
*top_widget
= gtk_bin_get_child (GTK_BIN (compselect
->top
));
1548 if (top_widget
== compselect
->vbox
)
1551 gtk_container_set_border_width (
1552 GTK_CONTAINER (compselect
->preview_content
), 5);
1553 gtk_container_set_border_width (
1554 GTK_CONTAINER (compselect
->attribs_content
), 5);
1556 reparent (compselect
->preview_frame
, compselect
->preview_content
);
1557 reparent (compselect
->attribs_frame
, compselect
->attribs_content
);
1558 reparent (compselect
->top
, compselect
->vbox
);
1559 gtk_widget_show (compselect
->vpaned
);
1562 if (top_widget
== compselect
->attribs_paned
||
1563 top_widget
== compselect
->attribs_box
)
1566 gtk_container_set_border_width (
1567 GTK_CONTAINER (compselect
->preview_content
), 0);
1568 gtk_container_set_border_width (
1569 GTK_CONTAINER (compselect
->attribs_content
), 0);
1571 gtk_widget_hide (compselect
->vpaned
);
1572 compselect_update_vertical_hierarchy (compselect
);
1576 /*! \brief Return whether tiled layout is in effect.
1579 compselect_is_tiled (GschemCompselectDockable
*compselect
)
1581 GtkWidget
*top_widget
= gtk_bin_get_child (GTK_BIN (compselect
->top
));
1583 return top_widget
!= compselect
->attribs_paned
&&
1584 top_widget
!= compselect
->attribs_box
;
1587 /*! \brief Remove prelight state from an expander.
1589 * Expanders are prelit (painted in a shaded color) while the pointer
1590 * hovers over them. This prelight state is not correctly removed
1591 * when the widget hierarchy changes. This function synthesizes a
1592 * leave event to un-prelight the expander explicitly.
1595 compselect_unprelight_expander (GschemCompselectDockable
*compselect
,
1596 GtkWidget
*expander
)
1598 GdkWindow
*window
= NULL
;
1599 if (expander
== compselect
->preview_expander
)
1600 window
= compselect
->preview_expander_event_window
;
1601 if (expander
== compselect
->attribs_expander
)
1602 window
= compselect
->attribs_expander_event_window
;
1607 GdkEvent
*event
= gdk_event_new (GDK_LEAVE_NOTIFY
);
1608 event
->crossing
.window
= g_object_ref (window
);
1609 event
->crossing
.send_event
= TRUE
;
1610 event
->crossing
.subwindow
= g_object_ref (window
);
1611 event
->crossing
.time
= GDK_CURRENT_TIME
;
1612 event
->crossing
.x
= 0;
1613 event
->crossing
.y
= 0;
1614 event
->crossing
.x_root
= 0;
1615 event
->crossing
.y_root
= 0;
1616 event
->crossing
.mode
= GDK_CROSSING_STATE_CHANGED
;
1617 event
->crossing
.detail
= GDK_NOTIFY_UNKNOWN
;
1618 event
->crossing
.focus
= FALSE
;
1619 event
->crossing
.state
= 0;
1621 gtk_widget_event (expander
, event
);
1622 gdk_event_free(event
);
1625 /*! \brief Callback function: Size of the whole dockable changed.
1627 * Changes from and to tiled layout when the aspect ratio of the
1628 * dockable becomes wider and taller than 1:2, respectively.
1631 compselect_top_size_allocate (GtkWidget
*widget
,
1632 GdkRectangle
*allocation
,
1635 compselect_set_tiled (GSCHEM_COMPSELECT_DOCKABLE (user_data
),
1636 allocation
->width
* 2 > allocation
->height
);
1639 /*! \brief Callback function: Pointer hovers over expander.
1641 * In order to synthesize leave notify events for the expanders, we
1642 * need pointers to their event windows. These can't be accessed
1643 * directly, so as a workaround, wait for an enter notify event and
1644 * store the pointers for later.
1647 compselect_expander_enter_notify_event (GtkWidget
*widget
,
1651 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
1653 if (widget
== compselect
->preview_expander
)
1654 compselect
->preview_expander_event_window
= event
->crossing
.window
;
1655 if (widget
== compselect
->attribs_expander
)
1656 compselect
->attribs_expander_event_window
= event
->crossing
.window
;
1661 /*! \brief Callback function: Expander activated.
1663 * Update the widget hierarchy and prelight state of the expanders.
1666 compselect_expander_notify_expanded (GObject
*expander
,
1670 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
1672 if (!compselect_is_tiled (compselect
)) {
1673 compselect_unprelight_expander (compselect
, GTK_WIDGET (expander
));
1674 compselect_update_vertical_hierarchy (compselect
);
1678 /*! \brief Callback function: Size of preview/attribute area changed.
1680 * This function does two things:
1682 * - When the content widgets are assigned a size for the first time,
1683 * it calculates the difference between the stored and actual size
1684 * and moves the corresponding vpaned's handle accordingly. (This
1685 * is necessary because there is no direct way to set the handle
1686 * position relative to the far end of the paned.)
1688 * - On subsequent size changes, it updates the stored size for that
1692 compselect_content_size_allocate (GtkWidget
*widget
,
1693 GdkRectangle
*allocation
,
1696 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (user_data
);
1697 if (compselect_is_tiled (compselect
))
1700 gboolean
*size_allocated
;
1702 gint default_height
;
1705 if (widget
== compselect
->preview_content
) {
1706 size_allocated
= &compselect
->preview_size_allocated
;
1707 key
= "preview-height";
1708 default_height
= 150;
1709 vpaned
= GTK_PANED (compselect
->preview_paned
);
1710 } else if (widget
== compselect
->attribs_content
) {
1711 size_allocated
= &compselect
->attribs_size_allocated
;
1712 key
= "attribs-height";
1713 default_height
= 150;
1714 vpaned
= GTK_PANED (compselect
->attribs_paned
);
1716 g_assert_not_reached ();
1718 if (*size_allocated
== FALSE
) {
1719 gint height
= eda_config_get_int (eda_config_get_user_context (),
1720 GSCHEM_DOCKABLE (compselect
)->group_name
,
1723 height
= default_height
;
1725 gtk_paned_set_position (vpaned
,
1726 gtk_paned_get_position (vpaned
)
1728 + allocation
->height
);
1729 *size_allocated
= TRUE
;
1733 if (allocation
->height
> 0)
1734 eda_config_set_int (eda_config_get_user_context (),
1735 GSCHEM_DOCKABLE (compselect
)->group_name
,
1736 key
, allocation
->height
);
1740 compselect_create_widget (GschemDockable
*dockable
)
1742 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (dockable
);
1743 GtkWidget
*inuseview
, *libview
, *notebook
;
1744 GtkWidget
*combobox
, *preview
;
1746 /* notebook for library and inuse views */
1747 inuseview
= create_inuse_treeview (compselect
);
1748 libview
= create_lib_treeview (compselect
);
1749 notebook
= gtk_notebook_new ();
1750 compselect
->viewtabs
= GTK_NOTEBOOK (notebook
);
1751 gtk_notebook_append_page (GTK_NOTEBOOK (notebook
), inuseview
,
1752 gtk_label_new (_("In Use")));
1753 gtk_notebook_append_page (GTK_NOTEBOOK (notebook
), libview
,
1754 gtk_label_new (_("Libraries")));
1756 /* vertical pane containing preview and attributes */
1757 compselect
->preview_frame
= gtk_frame_new (_("Preview"));
1758 compselect
->attribs_frame
= gtk_frame_new (_("Attributes"));
1759 compselect
->vpaned
= gtk_vpaned_new ();
1760 gtk_widget_set_size_request (compselect
->vpaned
, 25, -1);
1761 gtk_paned_pack1 (GTK_PANED (compselect
->vpaned
), compselect
->preview_frame
,
1763 gtk_paned_pack2 (GTK_PANED (compselect
->vpaned
), compselect
->attribs_frame
,
1766 /* horizontal pane containing selection and preview/attributes */
1767 compselect
->hpaned
= gtk_hpaned_new ();
1768 gtk_paned_pack1 (GTK_PANED (compselect
->hpaned
), notebook
, TRUE
, FALSE
);
1769 gtk_paned_pack2 (GTK_PANED (compselect
->hpaned
), compselect
->vpaned
,
1772 /* behavior combo box at the bottom */
1773 combobox
= create_behaviors_combo_box ();
1774 compselect
->combobox_behaviors
= GTK_COMBO_BOX (combobox
);
1775 g_signal_connect (combobox
, "changed",
1776 G_CALLBACK (compselect_callback_behavior_changed
),
1779 /* top-level vbox */
1780 compselect
->vbox
= gtk_vbox_new (FALSE
, DIALOG_V_SPACING
);
1781 gtk_box_pack_start (GTK_BOX (compselect
->vbox
), compselect
->hpaned
,
1783 gtk_box_pack_start (GTK_BOX (compselect
->vbox
), combobox
, FALSE
, FALSE
, 0);
1784 gtk_widget_show_all (compselect
->vbox
);
1785 g_object_ref (compselect
->vbox
);
1789 preview
= gschem_preview_new ();
1790 compselect
->preview
= GSCHEM_PREVIEW (preview
);
1791 gtk_widget_set_size_request (preview
, 160, 120);
1793 compselect
->preview_content
= gtk_alignment_new (.5, .5, 1., 1.);
1794 gtk_widget_set_size_request (compselect
->preview_content
, 0, 15);
1795 g_signal_connect (compselect
->preview_content
, "size-allocate",
1796 G_CALLBACK (compselect_content_size_allocate
), compselect
);
1797 gtk_container_add (GTK_CONTAINER (compselect
->preview_content
), preview
);
1798 gtk_widget_show_all (compselect
->preview_content
);
1799 g_object_ref (compselect
->preview_content
);
1801 compselect
->preview_box
= gtk_vbox_new (FALSE
, 3);
1802 gtk_widget_show (compselect
->preview_box
);
1803 g_object_ref (compselect
->preview_box
);
1805 compselect
->preview_paned
= gtk_vpaned_new ();
1806 gtk_widget_show (compselect
->preview_paned
);
1807 g_object_ref (compselect
->preview_paned
);
1809 compselect
->preview_expander
= gtk_expander_new_with_mnemonic (_("Preview"));
1810 gtk_expander_set_spacing (GTK_EXPANDER (compselect
->preview_expander
), 3);
1811 g_signal_connect (compselect
->preview_expander
, "enter-notify-event",
1812 G_CALLBACK (compselect_expander_enter_notify_event
),
1814 g_signal_connect (compselect
->preview_expander
, "notify::expanded",
1815 G_CALLBACK (compselect_expander_notify_expanded
),
1817 gtk_widget_show (compselect
->preview_expander
);
1818 g_object_ref (compselect
->preview_expander
);
1820 compselect
->preview_expander_event_window
= NULL
;
1821 compselect
->preview_size_allocated
= FALSE
;
1824 /* attributes area */
1825 compselect
->attribs_content
= create_attributes_treeview (compselect
);
1826 gtk_widget_set_size_request (compselect
->attribs_content
, -1, 20);
1827 g_signal_connect (compselect
->attribs_content
, "size-allocate",
1828 G_CALLBACK (compselect_content_size_allocate
), compselect
);
1829 gtk_widget_show_all (compselect
->attribs_content
);
1830 g_object_ref (compselect
->attribs_content
);
1832 compselect
->attribs_box
= gtk_vbox_new (FALSE
, 3);
1833 gtk_widget_show (compselect
->attribs_box
);
1834 g_object_ref (compselect
->attribs_box
);
1836 compselect
->attribs_paned
= gtk_vpaned_new ();
1837 gtk_widget_show (compselect
->attribs_paned
);
1838 g_object_ref (compselect
->attribs_paned
);
1840 compselect
->attribs_expander
=
1841 gtk_expander_new_with_mnemonic (_("Attributes"));
1842 gtk_expander_set_spacing (GTK_EXPANDER (compselect
->attribs_expander
), 3);
1843 g_signal_connect (compselect
->attribs_expander
, "enter-notify-event",
1844 G_CALLBACK (compselect_expander_enter_notify_event
),
1846 g_signal_connect (compselect
->attribs_expander
, "notify::expanded",
1847 G_CALLBACK (compselect_expander_notify_expanded
),
1849 gtk_widget_show (compselect
->attribs_expander
);
1850 g_object_ref (compselect
->attribs_expander
);
1852 compselect
->attribs_expander_event_window
= NULL
;
1853 compselect
->attribs_size_allocated
= FALSE
;
1856 /* top-level container widget (will contain one of the other widgets
1857 according to the selected layout) */
1858 compselect
->top
= gtk_alignment_new (.5, .5, 1., 1.);
1859 gtk_container_set_border_width (GTK_CONTAINER (compselect
->top
),
1860 DIALOG_BORDER_SPACING
);
1861 g_signal_connect (compselect
->top
, "size-allocate",
1862 G_CALLBACK (compselect_top_size_allocate
), compselect
);
1863 gtk_widget_show (compselect
->top
);
1864 return compselect
->top
;
1868 compselect_constructed (GObject
*object
)
1870 G_OBJECT_CLASS (compselect_parent_class
)->constructed (object
);
1872 s_clib_add_update_callback (library_updated
, object
);
1876 compselect_dispose (GObject
*object
)
1878 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (object
);
1880 s_clib_remove_update_callback (object
);
1882 g_clear_object (&compselect
->vbox
);
1884 g_clear_object (&compselect
->preview_content
);
1885 g_clear_object (&compselect
->preview_box
);
1886 g_clear_object (&compselect
->preview_paned
);
1887 g_clear_object (&compselect
->preview_expander
);
1889 g_clear_object (&compselect
->attribs_content
);
1890 g_clear_object (&compselect
->attribs_box
);
1891 g_clear_object (&compselect
->attribs_paned
);
1892 g_clear_object (&compselect
->attribs_expander
);
1894 G_OBJECT_CLASS (compselect_parent_class
)->dispose (object
);
1898 compselect_finalize (GObject
*object
)
1900 GschemCompselectDockable
*compselect
= GSCHEM_COMPSELECT_DOCKABLE (object
);
1902 g_free (compselect
->selected_filename
);
1904 if (compselect
->filter_timeout
!= 0) {
1905 g_source_remove (compselect
->filter_timeout
);
1906 compselect
->filter_timeout
= 0;
1909 G_OBJECT_CLASS (compselect_parent_class
)->finalize (object
);