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