1 /*
2  * schematic.c
3  *
4  *
5  * Authors:
6  *  Richard Hult <rhult@hem.passagen.se>
7  *  Ricardo Markiewicz <rmarkie@fi.uba.ar>
8  *  Andres de Barbara <adebarbara@fi.uba.ar>
9  *  Marc Lorber <lorber.marc@wanadoo.fr>
10  *  Bernhard Schuster <bernhard@ahoi.io>
11  *  Guido Trentalancia <guido@trentalancia.com>
12  *
13  * Web page: https://ahoi.io/project/oregano
14  *
15  * Copyright (C) 1999-2001  Richard Hult
16  * Copyright (C) 2003,2006  Ricardo Markiewicz
17  * Copyright (C) 2009-2012  Marc Lorber
18  * Copyright (C) 2013       Bernhard Schuster
19  * Copyright (C) 2017       Guido Trentalancia
20  *
21  * This program is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU General Public License as
23  * published by the Free Software Foundation; either version 2 of the
24  * License, or (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
29  * General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public
32  * License along with this program; if not, write to the
33  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
34  * Boston, MA 02110-1301, USA.
35  */
36 
37 #include <gtk/gtk.h>
38 #include <string.h>
39 #include <glib.h>
40 #include <glib/gi18n.h>
41 #include <math.h>
42 
43 #include "schematic.h"
44 #include "node-store.h"
45 #include "file-manager.h"
46 #include "settings.h"
47 #include "../sim-settings-gui.h"
48 #include "simulation.h"
49 #include "errors.h"
50 #include "schematic-print-context.h"
51 #include "log.h"
52 
53 #include "debug.h"
54 typedef struct _SchematicsPrintOptions
55 {
56 	GtkColorButton *components;
57 	GtkColorButton *labels;
58 	GtkColorButton *wires;
59 	GtkColorButton *text;
60 	GtkColorButton *background;
61 } SchematicPrintOptions;
62 
63 struct _SchematicPriv
64 {
65 	char *oregano_version;
66 	char *title;
67 	char *filename;
68 	char *author;
69 	char *comments;
70 	char *netlist_filename;
71 
72 	SchematicColors colors;
73 	SchematicPrintOptions *printoptions;
74 
75 	// Data for various dialogs.
76 	gpointer settings;
77 	SimSettingsGui *sim_settings;
78 	gpointer simulation;
79 
80 	GList *current_items;
81 
82 	NodeStore *store;
83 	GHashTable *symbols;
84 	GHashTable *refdes_values;
85 
86 	guint width;
87 	guint height;
88 
89 	double zoom;
90 
91 	gboolean dirty;
92 
93 	Log *logstore;
94 
95 	GtkTextBuffer *log;
96 	GtkTextTag *tag_error;
97 };
98 
99 enum {
100 	TITLE_CHANGED,
101 	ITEM_DATA_ADDED,
102 	LOG_UPDATED,
103 	NODE_DOT_ADDED,
104 	NODE_DOT_REMOVED,
105 	LAST_SCHEMATIC_DESTROYED,
106 	LAST_SIGNAL
107 };
108 
109 G_DEFINE_TYPE (Schematic, schematic, G_TYPE_OBJECT);
110 
111 static void schematic_init (Schematic *schematic);
112 static void schematic_class_init (SchematicClass *klass);
113 static void schematic_finalize (GObject *object);
114 static void schematic_dispose (GObject *object);
115 static void item_data_destroy_callback (gpointer s, GObject *data);
116 static void item_moved_callback (ItemData *data, Coords *pos, Schematic *sm);
117 
118 static int schematic_get_lowest_available_refdes (Schematic *schematic, char *prefix);
119 static void schematic_set_lowest_available_refdes (Schematic *schematic, char *prefix, int num);
120 
121 static GObjectClass *parent_class = NULL;
122 static guint schematic_signals[LAST_SIGNAL] = {0};
123 
124 static GList *schematic_list = NULL;
125 static int schematic_count_ = 0;
126 
schematic_class_init(SchematicClass * klass)127 static void schematic_class_init (SchematicClass *klass)
128 {
129 	GObjectClass *object_class;
130 
131 	object_class = G_OBJECT_CLASS (klass);
132 	parent_class = g_type_class_peek_parent (klass);
133 
134 	schematic_signals[TITLE_CHANGED] =
135 	    g_signal_new ("title_changed", TYPE_SCHEMATIC, G_SIGNAL_RUN_FIRST,
136 	                  G_STRUCT_OFFSET (SchematicClass, title_changed), NULL, NULL,
137 	                  g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
138 
139 	schematic_signals[LAST_SCHEMATIC_DESTROYED] =
140 	    g_signal_new ("last_schematic_destroyed", TYPE_SCHEMATIC, G_SIGNAL_RUN_FIRST,
141 	                  G_STRUCT_OFFSET (SchematicClass, last_schematic_destroyed), NULL, NULL,
142 	                  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
143 
144 	schematic_signals[ITEM_DATA_ADDED] =
145 	    g_signal_new ("item_data_added", TYPE_SCHEMATIC, G_SIGNAL_RUN_FIRST,
146 	                  G_STRUCT_OFFSET (SchematicClass, item_data_added), NULL, NULL,
147 	                  g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
148 
149 	schematic_signals[NODE_DOT_ADDED] =
150 	    g_signal_new ("node_dot_added", TYPE_SCHEMATIC, G_SIGNAL_RUN_FIRST,
151 	                  G_STRUCT_OFFSET (SchematicClass, node_dot_added), NULL, NULL,
152 	                  g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
153 
154 	schematic_signals[NODE_DOT_REMOVED] =
155 	    g_signal_new ("node_dot_removed", TYPE_SCHEMATIC, G_SIGNAL_RUN_FIRST,
156 	                  G_STRUCT_OFFSET (SchematicClass, node_dot_removed), NULL, NULL,
157 	                  g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
158 
159 	schematic_signals[LOG_UPDATED] =
160 	    g_signal_new ("log_updated", TYPE_SCHEMATIC, G_SIGNAL_RUN_FIRST,
161 	                  G_STRUCT_OFFSET (SchematicClass, log_updated), NULL, NULL,
162 	                  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
163 
164 	object_class->finalize = schematic_finalize;
165 	object_class->dispose = schematic_dispose;
166 }
167 
node_dot_added_callback(NodeStore * store,Coords * pos,Schematic * schematic)168 static void node_dot_added_callback (NodeStore *store, Coords *pos, Schematic *schematic)
169 {
170 	g_return_if_fail (schematic != NULL);
171 	g_return_if_fail (IS_SCHEMATIC (schematic));
172 
173 	g_signal_emit_by_name (schematic, "node_dot_added", pos);
174 }
175 
node_dot_removed_callback(NodeStore * store,Coords * pos,Schematic * schematic)176 static void node_dot_removed_callback (NodeStore *store, Coords *pos, Schematic *schematic)
177 {
178 	g_return_if_fail (schematic != NULL);
179 	g_return_if_fail (IS_SCHEMATIC (schematic));
180 
181 	g_signal_emit_by_name (schematic, "node_dot_removed", pos);
182 }
183 
schematic_init(Schematic * schematic)184 static void schematic_init (Schematic *schematic)
185 {
186 	SchematicPriv *priv;
187 
188 	priv = schematic->priv = g_new0 (SchematicPriv, 1);
189 
190 	priv->printoptions = NULL;
191 	// Colors
192 	priv->colors.components.red = 1.0;
193 	priv->colors.components.green = 0;
194 	priv->colors.components.blue = 0;
195 	priv->colors.components.alpha = 1.0;
196 	priv->colors.labels.red = 0;
197 	priv->colors.labels.green = 0.5451;
198 	priv->colors.labels.blue = 0.5451;
199 	priv->colors.labels.alpha = 1.0;
200 	priv->colors.wires.red = 0;
201 	priv->colors.wires.green = 0;
202 	priv->colors.wires.blue = 1.0;
203 	priv->colors.wires.alpha = 1.0;
204 	priv->colors.text.red = 1.0;
205 	priv->colors.text.green = 1.0;
206 	priv->colors.text.blue = 1.0;
207 	priv->colors.text.alpha = 1.0;
208 
209 	priv->symbols = g_hash_table_new (g_str_hash, g_str_equal);
210 	priv->refdes_values = g_hash_table_new (g_str_hash, g_str_equal);
211 	priv->store = node_store_new ();
212 	priv->width = 0;
213 	priv->height = 0;
214 	priv->dirty = FALSE;
215 	priv->logstore = log_new ();
216 	priv->log = gtk_text_buffer_new (NULL); // LEGACY
217 	priv->tag_error = gtk_text_buffer_create_tag (priv->log, "error", "foreground", "red", "weight",
218 	                                              PANGO_WEIGHT_BOLD, NULL);
219 
220 	g_signal_connect_object (priv->store, "node_dot_added", G_CALLBACK (node_dot_added_callback),
221 	                         G_OBJECT (schematic), G_CONNECT_AFTER);
222 
223 	g_signal_connect_object (priv->store, "node_dot_removed",
224 	                         G_CALLBACK (node_dot_removed_callback), G_OBJECT (schematic),
225 	                         G_CONNECT_AFTER);
226 
227 	priv->sim_settings = sim_settings_gui_new (schematic);
228 	priv->settings = settings_new (schematic);
229 	priv->simulation = simulation_new (schematic, priv->logstore);
230 
231 	priv->filename = NULL;
232 	priv->netlist_filename = NULL;
233 	priv->author = g_strdup (g_get_user_name ());
234 	priv->comments = g_strdup ("");
235 }
236 
schematic_new(void)237 Schematic *schematic_new (void)
238 {
239 	Schematic *schematic;
240 
241 	schematic = SCHEMATIC (g_object_new (TYPE_SCHEMATIC, NULL));
242 
243 	schematic_count_++;
244 	schematic_list = g_list_prepend (schematic_list, schematic);
245 
246 	return schematic;
247 }
248 
schematic_dispose(GObject * object)249 static void schematic_dispose (GObject *object)
250 {
251 	Schematic *schematic;
252 	GList *list;
253 
254 	schematic = SCHEMATIC (object);
255 
256 	// Disconnect weak item signal
257 	for (list = schematic->priv->current_items; list; list = list->next)
258 		g_object_weak_unref (G_OBJECT (list->data), item_data_destroy_callback,
259 		                     G_OBJECT (schematic));
260 
261 	g_clear_object (&schematic->priv->log);
262 
263 	schematic_count_--;
264 	schematic_list = g_list_remove (schematic_list, schematic);
265 
266 	if (schematic_count_ == 0) {
267 		g_signal_emit_by_name (schematic, "last_schematic_destroyed", NULL);
268 	}
269 
270 	g_list_free_full (list, g_object_unref);
271 
272 	G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (schematic));
273 }
274 
schematic_finalize(GObject * object)275 static void schematic_finalize (GObject *object)
276 {
277 	Schematic *sm = SCHEMATIC (object);
278 	SchematicPriv *priv = sm->priv;
279 	if (priv) {
280 		g_free (priv->simulation);
281 		g_hash_table_destroy (priv->symbols);
282 		g_hash_table_destroy (priv->refdes_values);
283 		g_clear_object (&priv->store);
284 		g_clear_object (&priv->logstore);
285 		g_free (priv->netlist_filename);
286 		g_free (priv->comments);
287 		g_free (priv->author);
288 		g_free (priv->filename);
289 		g_free (priv->settings);
290 		sim_settings_gui_finalize(priv->sim_settings);
291 		g_free (priv);
292 	}
293 
294 	G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (sm));
295 }
296 
schematic_get_title(Schematic * schematic)297 char *schematic_get_title (Schematic *schematic)
298 {
299 	g_return_val_if_fail (schematic != NULL, NULL);
300 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
301 
302 	return schematic->priv->title;
303 }
304 
schematic_get_author(Schematic * schematic)305 char *schematic_get_author (Schematic *schematic)
306 {
307 	g_return_val_if_fail (schematic != NULL, NULL);
308 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
309 
310 	return schematic->priv->author;
311 }
312 
schematic_get_version(Schematic * schematic)313 char *schematic_get_version (Schematic *schematic)
314 {
315 	g_return_val_if_fail (schematic != NULL, NULL);
316 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
317 
318 	return schematic->priv->oregano_version;
319 }
320 
schematic_get_comments(Schematic * schematic)321 char *schematic_get_comments (Schematic *schematic)
322 {
323 	g_return_val_if_fail (schematic != NULL, NULL);
324 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
325 
326 	return schematic->priv->comments;
327 }
328 
schematic_set_title(Schematic * schematic,const gchar * title)329 void schematic_set_title (Schematic *schematic, const gchar *title)
330 {
331 	g_return_if_fail (schematic != NULL);
332 	g_return_if_fail (IS_SCHEMATIC (schematic));
333 
334 	if (!title)
335 		return;
336 
337 	g_free (schematic->priv->title);
338 	schematic->priv->title = g_strdup (title);
339 
340 	g_signal_emit_by_name (schematic, "title_changed", schematic->priv->title);
341 }
342 
schematic_set_author(Schematic * schematic,const gchar * author)343 void schematic_set_author (Schematic *schematic, const gchar *author)
344 {
345 	g_return_if_fail (schematic != NULL);
346 	g_return_if_fail (IS_SCHEMATIC (schematic));
347 
348 	if (!author)
349 		return;
350 
351 	g_free (schematic->priv->author);
352 	schematic->priv->author = g_strdup (author);
353 }
354 
schematic_set_version(Schematic * schematic,const gchar * oregano_version)355 void schematic_set_version (Schematic *schematic, const gchar *oregano_version)
356 {
357 	g_return_if_fail (schematic != NULL);
358 	g_return_if_fail (IS_SCHEMATIC (schematic));
359 
360 	if (!oregano_version)
361 		return;
362 
363 	g_free (schematic->priv->oregano_version);
364 	schematic->priv->oregano_version = g_strdup (oregano_version);
365 }
366 
schematic_set_comments(Schematic * schematic,const gchar * comments)367 void schematic_set_comments (Schematic *schematic, const gchar *comments)
368 {
369 	g_return_if_fail (schematic != NULL);
370 	g_return_if_fail (IS_SCHEMATIC (schematic));
371 
372 	g_free (schematic->priv->comments);
373 	schematic->priv->comments = g_strdup (comments);
374 }
375 
schematic_get_filename(Schematic * schematic)376 char *schematic_get_filename (Schematic *schematic)
377 {
378 	g_return_val_if_fail (schematic != NULL, NULL);
379 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
380 
381 	return schematic->priv->filename;
382 }
383 
schematic_set_filename(Schematic * schematic,const gchar * filename)384 void schematic_set_filename (Schematic *schematic, const gchar *filename)
385 {
386 	g_return_if_fail (schematic != NULL);
387 	g_return_if_fail (IS_SCHEMATIC (schematic));
388 
389 	g_free (schematic->priv->filename);
390 	schematic->priv->filename = g_strdup (filename);
391 }
392 
schematic_get_netlist_filename(Schematic * schematic)393 char *schematic_get_netlist_filename (Schematic *schematic)
394 {
395 	g_return_val_if_fail (schematic != NULL, NULL);
396 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
397 
398 	return schematic->priv->netlist_filename;
399 }
400 
schematic_set_netlist_filename(Schematic * schematic,char * filename)401 void schematic_set_netlist_filename (Schematic *schematic, char *filename)
402 {
403 	g_return_if_fail (schematic != NULL);
404 	g_return_if_fail (IS_SCHEMATIC (schematic));
405 
406 	g_free (schematic->priv->netlist_filename);
407 
408 	schematic->priv->netlist_filename = g_strdup (filename);
409 }
410 
schematic_get_width(const Schematic * schematic)411 guint schematic_get_width (const Schematic *schematic)
412 {
413 	g_return_val_if_fail (schematic != NULL, 0.);
414 	g_return_val_if_fail (IS_SCHEMATIC (schematic), 0.);
415 
416 	return schematic->priv->width;
417 }
418 
schematic_set_width(Schematic * schematic,const guint width)419 void schematic_set_width (Schematic *schematic, const guint width)
420 {
421 	g_return_if_fail (schematic != NULL);
422 	g_return_if_fail (IS_SCHEMATIC (schematic));
423 
424 	schematic->priv->width = width;
425 }
426 
schematic_get_height(const Schematic * schematic)427 guint schematic_get_height (const Schematic *schematic)
428 {
429 	g_return_val_if_fail (schematic != NULL, 0.);
430 	g_return_val_if_fail (IS_SCHEMATIC (schematic), 0.);
431 
432 	return schematic->priv->height;
433 }
434 
schematic_set_height(Schematic * schematic,const guint height)435 void schematic_set_height (Schematic *schematic, const guint height)
436 {
437 	g_return_if_fail (schematic != NULL);
438 	g_return_if_fail (IS_SCHEMATIC (schematic));
439 
440 	schematic->priv->height = height;
441 }
442 
schematic_get_zoom(Schematic * schematic)443 double schematic_get_zoom (Schematic *schematic)
444 {
445 	g_return_val_if_fail (schematic != NULL, 1.0);
446 	g_return_val_if_fail (IS_SCHEMATIC (schematic), 1.0);
447 
448 	return schematic->priv->zoom;
449 }
450 
schematic_set_zoom(Schematic * schematic,double zoom)451 void schematic_set_zoom (Schematic *schematic, double zoom)
452 {
453 	g_return_if_fail (schematic != NULL);
454 	g_return_if_fail (IS_SCHEMATIC (schematic));
455 
456 	schematic->priv->zoom = zoom;
457 }
458 
schematic_get_store(Schematic * schematic)459 NodeStore *schematic_get_store (Schematic *schematic)
460 {
461 	g_return_val_if_fail (schematic != NULL, NULL);
462 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
463 
464 	return schematic->priv->store;
465 }
466 
schematic_get_settings(Schematic * schematic)467 gpointer schematic_get_settings (Schematic *schematic)
468 {
469 	g_return_val_if_fail (schematic != NULL, NULL);
470 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
471 
472 	return schematic->priv->settings;
473 }
474 
schematic_get_sim_settings(Schematic * schematic)475 SimSettings *schematic_get_sim_settings (Schematic *schematic)
476 {
477 	g_return_val_if_fail (schematic != NULL, NULL);
478 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
479 
480 	return schematic->priv->sim_settings->sim_settings;
481 }
482 
schematic_get_sim_settings_gui(Schematic * schematic)483 SimSettingsGui *schematic_get_sim_settings_gui (Schematic *schematic)
484 {
485 	g_return_val_if_fail (schematic != NULL, NULL);
486 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
487 
488 	return schematic->priv->sim_settings;
489 }
490 
schematic_get_simulation(Schematic * schematic)491 gpointer schematic_get_simulation (Schematic *schematic)
492 {
493 	g_return_val_if_fail (schematic != NULL, NULL);
494 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
495 
496 	return schematic->priv->simulation;
497 }
498 
schematic_get_log_store(Schematic * schematic)499 Log *schematic_get_log_store (Schematic *schematic)
500 {
501 	g_return_val_if_fail (schematic != NULL, NULL);
502 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
503 
504 	return schematic->priv->logstore;
505 }
506 
schematic_log_append(Schematic * schematic,const char * message)507 void schematic_log_append (Schematic *schematic, const char *message)
508 {
509 	g_return_if_fail (schematic != NULL);
510 	g_return_if_fail (IS_SCHEMATIC (schematic));
511 
512 	log_append (schematic->priv->logstore, "Schematic Info", message);
513 
514 	// LEGACY
515 	gtk_text_buffer_insert_at_cursor (schematic->priv->log, message, strlen (message));
516 }
517 
schematic_log_append_error(Schematic * schematic,const char * message)518 void schematic_log_append_error (Schematic *schematic, const char *message)
519 {
520 	GtkTextIter iter;
521 	SchematicPriv *priv;
522 
523 	g_return_if_fail (schematic != NULL);
524 	g_return_if_fail (IS_SCHEMATIC (schematic));
525 
526 	priv = schematic->priv;
527 
528 	log_append (schematic->priv->logstore, "simulation engine Error", message);
529 
530 	// LEGACY
531 	gtk_text_buffer_get_end_iter (priv->log, &iter);
532 	gtk_text_buffer_insert_with_tags (priv->log, &iter, message, -1, priv->tag_error, NULL);
533 }
534 
schematic_log_show(Schematic * schematic)535 void schematic_log_show (Schematic *schematic)
536 {
537 	g_return_if_fail (schematic != NULL);
538 	g_return_if_fail (IS_SCHEMATIC (schematic));
539 
540 	g_signal_emit_by_name (schematic, "log_updated", schematic->priv->log);
541 }
542 
schematic_log_clear(Schematic * schematic)543 void schematic_log_clear (Schematic *schematic)
544 {
545 	g_return_if_fail (schematic != NULL);
546 	g_return_if_fail (IS_SCHEMATIC (schematic));
547 
548 	gtk_text_buffer_set_text (schematic->priv->log, "", -1);
549 }
550 
schematic_get_log_text(Schematic * schematic)551 GtkTextBuffer *schematic_get_log_text (Schematic *schematic)
552 {
553 	g_return_val_if_fail (schematic != NULL, NULL);
554 	g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
555 
556 	return schematic->priv->log;
557 }
558 
schematic_count(void)559 int schematic_count (void) { return schematic_count_; }
560 
schematic_read(const char * name,GError ** error)561 Schematic *schematic_read (const char *name, GError **error)
562 {
563 	Schematic *new_sm;
564 	const char *fname;
565 	GError *e = NULL;
566 	FileType *ft;
567 
568 	g_return_val_if_fail (name != NULL, NULL);
569 
570 	fname = g_filename_from_uri (name, NULL, &e);
571 
572 	if (!fname) {
573 		fname = name;
574 		g_clear_error (&e);
575 	}
576 
577 	if (!g_file_test (fname, G_FILE_TEST_EXISTS)) {
578 		g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND,
579 		             _ ("File %s does not exist."), fname);
580 		return NULL;
581 	}
582 
583 	// Get File Handler
584 	ft = file_manager_get_handler (fname);
585 	if (ft == NULL) {
586 		g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND,
587 		             _ ("Unknown file format for %s."), fname);
588 		return NULL;
589 	}
590 
591 	new_sm = schematic_new ();
592 
593 	ft->load_func (new_sm, fname, &e);
594 	if (e) {
595 		g_propagate_error (error, e);
596 		g_clear_object (&new_sm);
597 		return NULL;
598 	}
599 
600 	schematic_set_dirty (new_sm, FALSE);
601 
602 	return new_sm;
603 }
604 
schematic_save_file(Schematic * sm,GError ** error)605 gint schematic_save_file (Schematic *sm, GError **error)
606 {
607 	FileType *ft;
608 	GError *e = NULL;
609 
610 	g_return_val_if_fail (sm != NULL, FALSE);
611 
612 	ft = file_manager_get_handler (schematic_get_filename (sm));
613 
614 	if (ft == NULL) {
615 		g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND,
616 		             _ ("Unknown file format for %s."), schematic_get_filename (sm));
617 		return FALSE;
618 	}
619 
620 	if (ft->save_func (sm, &e)) {
621 		schematic_set_title (sm, g_path_get_basename (sm->priv->filename));
622 		schematic_set_dirty (sm, FALSE);
623 		return TRUE;
624 	}
625 
626 	g_propagate_error (error, e);
627 
628 	return FALSE; // Save fails!
629 }
630 
631 /**
632  * \brief add an ItemData object to a Schematic
633  *
634  * @param sm the schematic the item will be added to
635  * @param data fully initilalized ItemData object
636  */
schematic_add_item(Schematic * sm,ItemData * data)637 void schematic_add_item (Schematic *sm, ItemData *data)
638 {
639 	NodeStore *store;
640 	char *prefix = NULL, *refdes = NULL;
641 	int num;
642 
643 	g_return_if_fail (sm);
644 	g_return_if_fail (IS_SCHEMATIC (sm));
645 	g_return_if_fail (data);
646 	g_return_if_fail (IS_ITEM_DATA (data));
647 
648 	store = sm->priv->store;
649 	g_assert (store);
650 	g_assert (IS_NODE_STORE (store));
651 
652 	g_object_set (G_OBJECT (data), "store", store, NULL);
653 
654 	// item data will call the child register function
655 	// for parts e.g. this ends up in <node_store_add_part>
656 	// which requires a valid position to add the node dots
657 	if (item_data_register (data) == -1) {
658 		return;
659 	}
660 
661 	// Some items need a reference designator, so get a good one
662 	prefix = item_data_get_refdes_prefix (data);
663 	if (prefix != NULL) {
664 		num = schematic_get_lowest_available_refdes (sm, prefix);
665 		refdes = g_strdup_printf ("%s%d", prefix, num);
666 		item_data_set_property (data, "refdes", refdes);
667 
668 		schematic_set_lowest_available_refdes (sm, prefix, num + 1);
669 	}
670 	g_free (prefix);
671 	g_free (refdes);
672 
673 	sm->priv->current_items = g_list_prepend (sm->priv->current_items, data);
674 	g_object_weak_ref (G_OBJECT (data), item_data_destroy_callback, G_OBJECT (sm));
675 
676 	sm->priv->dirty = TRUE;
677 
678 	// if the item gets moved mark the schematic as dirty
679 	g_signal_connect_object (data, "moved", G_CALLBACK (item_moved_callback), sm, 0);
680 
681 	// causes a canvas item (view) to be generated
682 	g_signal_emit_by_name (sm, "item_data_added", data);
683 }
684 
schematic_parts_foreach(Schematic * schematic,ForeachItemDataFunc func,gpointer user_data)685 void schematic_parts_foreach (Schematic *schematic, ForeachItemDataFunc func, gpointer user_data)
686 {
687 	GList *list;
688 
689 	g_return_if_fail (schematic != NULL);
690 	g_return_if_fail (IS_SCHEMATIC (schematic));
691 
692 	if (func == NULL)
693 		return;
694 
695 	for (list = node_store_get_parts (schematic->priv->store); list; list = list->next) {
696 		func (list->data, user_data);
697 	}
698 }
699 
schematic_wires_foreach(Schematic * schematic,ForeachItemDataFunc func,gpointer user_data)700 void schematic_wires_foreach (Schematic *schematic, ForeachItemDataFunc func, gpointer user_data)
701 {
702 	GList *list;
703 
704 	g_return_if_fail (schematic != NULL);
705 	g_return_if_fail (IS_SCHEMATIC (schematic));
706 
707 	if (func == NULL)
708 		return;
709 
710 	for (list = node_store_get_wires (schematic->priv->store); list; list = list->next) {
711 		func (list->data, user_data);
712 	}
713 }
714 
schematic_items_foreach(Schematic * schematic,ForeachItemDataFunc func,gpointer user_data)715 void schematic_items_foreach (Schematic *schematic, ForeachItemDataFunc func, gpointer user_data)
716 {
717 	GList *list;
718 
719 	g_return_if_fail (schematic != NULL);
720 	g_return_if_fail (IS_SCHEMATIC (schematic));
721 
722 	if (func == NULL)
723 		return;
724 
725 	for (list = schematic->priv->current_items; list; list = list->next) {
726 		func (list->data, user_data);
727 	}
728 }
729 
schematic_get_items(Schematic * sm)730 GList *schematic_get_items (Schematic *sm)
731 {
732 	g_return_val_if_fail (sm != NULL, NULL);
733 	g_return_val_if_fail (IS_SCHEMATIC (sm), NULL);
734 
735 	return sm->priv->current_items;
736 }
737 
item_data_destroy_callback(gpointer s,GObject * data)738 static void item_data_destroy_callback (gpointer s, GObject *data)
739 {
740 	Schematic *sm = SCHEMATIC (s);
741 	schematic_set_dirty (sm, TRUE);
742 	if (sm->priv) {
743 		sm->priv->current_items = g_list_remove (sm->priv->current_items, data);
744 	}
745 }
746 
schematic_get_lowest_available_refdes(Schematic * schematic,char * prefix)747 static int schematic_get_lowest_available_refdes (Schematic *schematic, char *prefix)
748 {
749 	gpointer key, value;
750 
751 	g_return_val_if_fail (schematic != NULL, -1);
752 	g_return_val_if_fail (IS_SCHEMATIC (schematic), -1);
753 	g_return_val_if_fail (prefix != NULL, -1);
754 
755 	if (g_hash_table_lookup_extended (schematic->priv->refdes_values, prefix, &key, &value)) {
756 		return GPOINTER_TO_INT (value);
757 	} else {
758 		return 1;
759 	}
760 }
761 
schematic_set_lowest_available_refdes(Schematic * schematic,char * prefix,int num)762 static void schematic_set_lowest_available_refdes (Schematic *schematic, char *prefix, int num)
763 {
764 	gpointer key, value;
765 
766 	g_return_if_fail (schematic != NULL);
767 	g_return_if_fail (IS_SCHEMATIC (schematic));
768 	g_return_if_fail (prefix != NULL);
769 
770 	// If there already is a key, use it, otherwise copy the prefix and
771 	// use as key.
772 	if (!g_hash_table_lookup_extended (schematic->priv->refdes_values, prefix, &key, &value))
773 		key = g_strdup (prefix);
774 
775 	g_hash_table_insert (schematic->priv->refdes_values, key, GINT_TO_POINTER (num));
776 }
777 
schematic_is_dirty(Schematic * sm)778 gboolean schematic_is_dirty (Schematic *sm)
779 {
780 	g_return_val_if_fail (sm != NULL, FALSE);
781 	g_return_val_if_fail (IS_SCHEMATIC (sm), FALSE);
782 
783 	return sm->priv->dirty;
784 }
785 
schematic_set_dirty(Schematic * sm,gboolean b)786 void schematic_set_dirty (Schematic *sm, gboolean b)
787 {
788 	g_return_if_fail (sm != NULL);
789 	g_return_if_fail (IS_SCHEMATIC (sm));
790 
791 	sm->priv->dirty = b;
792 }
793 
item_moved_callback(ItemData * data,Coords * pos,Schematic * sm)794 static void item_moved_callback (ItemData *data, Coords *pos, Schematic *sm)
795 {
796 	g_return_if_fail (data != NULL);
797 	g_return_if_fail (IS_ITEM_DATA (data));
798 
799 	schematic_set_dirty (sm, TRUE);
800 }
801 
schematic_render(Schematic * sm,cairo_t * cr)802 static void schematic_render (Schematic *sm, cairo_t *cr)
803 {
804 	NodeStore *store;
805 	SchematicPrintContext schematic_print_context;
806 	schematic_print_context.colors = sm->priv->colors;
807 	store = schematic_get_store (sm);
808 
809 	node_store_print_items (store, cr, &schematic_print_context);
810 }
811 
convert_to_grayscale(GdkRGBA * source)812 GdkRGBA convert_to_grayscale (GdkRGBA *source)
813 {
814 	GdkRGBA color;
815 	gdouble factor;
816 
817 	factor = source->red * 0.299 + source->green * 0.587 + source->blue * 0.114;
818 
819 	color.red = factor;
820 	color.green = factor;
821 	color.blue = factor;
822 	color.alpha = source->alpha;
823 
824 	return color;
825 }
826 
schematic_export(Schematic * sm,const gchar * filename,gint img_w,gint img_h,int bg,int color,int format)827 void schematic_export (Schematic *sm, const gchar *filename, gint img_w, gint img_h, int bg,
828                        int color, int format)
829 {
830 	NodeRect bbox;
831 	NodeStore *store;
832 	cairo_surface_t *surface;
833 	cairo_t *cr;
834 	gdouble graph_w, graph_h;
835 	gdouble scale, scalew, scaleh;
836 	SchematicColors colors;
837 
838 	if (!color) {
839 		colors = sm->priv->colors;
840 		sm->priv->colors.components = convert_to_grayscale (&sm->priv->colors.components);
841 		sm->priv->colors.labels = convert_to_grayscale (&sm->priv->colors.labels);
842 		sm->priv->colors.wires = convert_to_grayscale (&sm->priv->colors.wires);
843 		sm->priv->colors.text = convert_to_grayscale (&sm->priv->colors.text);
844 		sm->priv->colors.background = convert_to_grayscale (&sm->priv->colors.background);
845 	}
846 
847 	store = schematic_get_store (sm);
848 	node_store_get_bounds (store, &bbox);
849 
850 	switch (format) {
851 #ifdef CAIRO_HAS_SVG_SURFACE
852 	case 0:
853 		surface = cairo_svg_surface_create (filename, img_w, img_h);
854 		break;
855 #endif
856 #ifdef CAIRO_HAS_PDF_SURFACE
857 	case 1:
858 		surface = cairo_pdf_surface_create (filename, img_w, img_h);
859 		break;
860 #endif
861 #ifdef CAIRO_HAS_PS_SURFACE
862 	case 2:
863 		surface = cairo_ps_surface_create (filename, img_w, img_h);
864 		break;
865 #endif
866 	default:
867 		surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, img_w, img_h);
868 	}
869 	cr = cairo_create (surface);
870 
871 	// Background
872 	switch (bg) {
873 	case 1: // White
874 		cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
875 		cairo_rectangle (cr, 0, 0, img_w, img_h);
876 		cairo_fill (cr);
877 		break;
878 	case 2: // Black
879 		cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
880 		cairo_rectangle (cr, 0, 0, img_w, img_h);
881 		cairo_fill (cr);
882 	}
883 
884 	graph_w = img_w * 0.8;
885 	graph_h = img_h * 0.8;
886 	scalew = graph_w / (bbox.x1 - bbox.x0);
887 	scaleh = graph_h / (bbox.y1 - bbox.y0);
888 	if (scalew < scaleh)
889 		scale = scalew;
890 	else
891 		scale = scaleh;
892 
893 	// Preparing...
894 	cairo_save (cr);
895 	cairo_translate (cr, (img_w - graph_w) / 2.0, (img_h - graph_h) / 2.0);
896 	cairo_scale (cr, scale, scale);
897 	cairo_translate (cr, -bbox.x0, -bbox.y0);
898 	cairo_set_line_width (cr, 0.5);
899 
900 	// Render...
901 	schematic_render (sm, cr);
902 
903 	cairo_restore (cr);
904 	cairo_show_page (cr);
905 
906 	// Saving...
907 	if (format >= 3)
908 		cairo_surface_write_to_png (surface, filename);
909 	cairo_destroy (cr);
910 	cairo_surface_destroy (surface);
911 
912 	// Restore color information
913 	if (!color) {
914 		sm->priv->colors = colors;
915 	}
916 }
917 
draw_rotule(Schematic * sm,cairo_t * cr)918 static void draw_rotule (Schematic *sm, cairo_t *cr)
919 {
920 	cairo_save (cr);
921 	cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
922 	cairo_set_line_width (cr, 0.5);
923 	cairo_rectangle (cr, 0, 0, 180, 20);
924 	cairo_rectangle (cr, 0, 20, 180, 10);
925 	cairo_stroke (cr);
926 	cairo_restore (cr);
927 }
928 
draw_page(GtkPrintOperation * operation,GtkPrintContext * context,int page_nr,Schematic * sm)929 static void draw_page (GtkPrintOperation *operation, GtkPrintContext *context, int page_nr,
930                        Schematic *sm)
931 {
932 	NodeStore *store;
933 	NodeRect bbox;
934 	gdouble page_w, page_h;
935 
936 	page_w = gtk_print_context_get_width (context);
937 	page_h = gtk_print_context_get_height (context);
938 
939 	cairo_t *cr = gtk_print_context_get_cairo_context (context);
940 
941 	// Draw a red rectangle, as wide as the paper (inside the margins)
942 	cairo_save (cr);
943 	cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
944 	cairo_set_line_width (cr, 0.5);
945 	cairo_rectangle (cr, 20, 10, page_w - 30, page_h - 20);
946 	cairo_stroke (cr);
947 	cairo_restore (cr);
948 
949 	cairo_save (cr);
950 	cairo_translate (cr, page_w - 190, page_h - 40);
951 	draw_rotule (sm, cr);
952 	cairo_restore (cr);
953 
954 	store = schematic_get_store (sm);
955 
956 	node_store_get_bounds (store, &bbox);
957 
958 	cairo_save (cr);
959 	cairo_set_line_width (cr, 0.5);
960 	cairo_set_source_rgb (cr, 0, 0, 0);
961 	cairo_translate (cr, page_w * 0.1, page_h * 0.1);
962 	// 0.4 is the convert factor between Model unit and
963 	// milimeters, unit used in printing
964 	cairo_scale (cr, 0.4, 0.4);
965 	cairo_translate (cr, -bbox.x0, -bbox.y0);
966 	schematic_render (sm, cr);
967 	cairo_restore (cr);
968 }
969 
print_options(GtkPrintOperation * operation,Schematic * sm)970 static GObject *print_options (GtkPrintOperation *operation, Schematic *sm)
971 {
972 	GtkBuilder *gui;
973 	GError *perror = NULL;
974 
975 	if ((gui = gtk_builder_new ()) == NULL) {
976 		return G_OBJECT (gtk_label_new (_ ("Error loading print-options.ui")));
977 	}
978 
979 	if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/print-options.ui", &perror) <= 0) {
980 		g_error_free (perror);
981 		return G_OBJECT (gtk_label_new (_ ("Error loading print-options.ui")));
982 	}
983 
984 	g_free (sm->priv->printoptions);
985 	sm->priv->printoptions = g_new0 (SchematicPrintOptions, 1);
986 
987 	sm->priv->printoptions->components =
988 	    GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_components"));
989 	sm->priv->printoptions->labels =
990 	    GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_labels"));
991 	sm->priv->printoptions->wires = GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_wires"));
992 	sm->priv->printoptions->text = GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_text"));
993 	sm->priv->printoptions->background =
994 	    GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_background"));
995 
996 	// Set default colors
997 	gtk_color_chooser_set_rgba (
998 	    GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_components")),
999 	    &sm->priv->colors.components);
1000 	gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_labels")),
1001 	                            &sm->priv->colors.labels);
1002 	gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_wires")),
1003 	                            &sm->priv->colors.wires);
1004 	gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_text")),
1005 	                            &sm->priv->colors.text);
1006 	gtk_color_chooser_set_rgba (
1007 	    GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_background")),
1008 	    &sm->priv->colors.background);
1009 
1010 	return gtk_builder_get_object (gui, "widget");
1011 }
1012 
read_print_options(GtkPrintOperation * operation,GtkWidget * widget,Schematic * sm)1013 static void read_print_options (GtkPrintOperation *operation, GtkWidget *widget, Schematic *sm)
1014 {
1015 	SchematicPrintOptions *colors = sm->priv->printoptions;
1016 
1017 	gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->components),
1018 	                            &sm->priv->colors.components);
1019 	gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->labels), &sm->priv->colors.labels);
1020 	gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->wires), &sm->priv->colors.wires);
1021 	gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->text), &sm->priv->colors.text);
1022 	gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->background),
1023 	                            &sm->priv->colors.background);
1024 
1025 	g_free (sm->priv->printoptions);
1026 	sm->priv->printoptions = NULL;
1027 }
1028 
schematic_print(Schematic * sm,GtkPageSetup * page,GtkPrintSettings * settings,gboolean preview)1029 void schematic_print (Schematic *sm, GtkPageSetup *page, GtkPrintSettings *settings,
1030                       gboolean preview)
1031 {
1032 	GtkPrintOperation *op;
1033 	GtkPrintOperationResult res;
1034 
1035 	op = gtk_print_operation_new ();
1036 
1037 	if (settings != NULL)
1038 		gtk_print_operation_set_print_settings (op, settings);
1039 	gtk_print_operation_set_default_page_setup (op, page);
1040 	gtk_print_operation_set_n_pages (op, 1);
1041 	gtk_print_operation_set_unit (op, GTK_UNIT_MM);
1042 	gtk_print_operation_set_use_full_page (op, TRUE);
1043 
1044 	g_signal_connect (op, "create-custom-widget", G_CALLBACK (print_options), sm);
1045 	g_signal_connect (op, "custom-widget-apply", G_CALLBACK (read_print_options), sm);
1046 	g_signal_connect (op, "draw_page", G_CALLBACK (draw_page), sm);
1047 
1048 	gtk_print_operation_set_custom_tab_label (op, _ ("Schematic"));
1049 
1050 	if (preview)
1051 		res = gtk_print_operation_run (op, GTK_PRINT_OPERATION_ACTION_PREVIEW, NULL, NULL);
1052 	else
1053 		res = gtk_print_operation_run (op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL, NULL);
1054 
1055 	if (res == GTK_PRINT_OPERATION_RESULT_CANCEL) {
1056 	} else if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
1057 		if (settings != NULL)
1058 			g_object_unref (settings);
1059 		settings = g_object_ref (gtk_print_operation_get_print_settings (op));
1060 	}
1061 
1062 	g_object_unref (op);
1063 }
1064