1 /* Lepton EDA Schematic Capture
2  * Copyright (C) 2013 Ales Hvezda
3  * Copyright (C) 2013-2015 gEDA Contributors
4  * Copyright (C) 2017-2021 Lepton EDA Contributors
5  *
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.
10  *
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.
15  *
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
19  */
20 /*!
21  * \file x_newtext.c
22  *
23  * \brief A dialog box for adding new text to a schematic.
24  */
25 
26 #include <config.h>
27 
28 #include <stdio.h>
29 #ifdef HAVE_STDLIB_H
30 #include <stdlib.h>
31 #endif
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #endif
35 
36 #include "gschem.h"
37 #include <gdk/gdkkeysyms.h>
38 
39 
40 
41 #define TYPE_NEWTEXT           (newtext_get_type())
42 #define NEWTEXT(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_NEWTEXT, NewText))
43 #define NEWTEXT_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass),  TYPE_NEWTEXT, NewTextClass))
44 #define IS_NEWTEXT(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_NEWTEXT))
45 
46 typedef struct _NewTextClass NewTextClass;
47 typedef struct _NewText NewText;
48 
49 struct _NewTextClass {
50   GschemDialogClass parent_class;
51 };
52 
53 struct _NewText {
54     GschemDialog parent;
55 
56     GtkWidget *aligncb;
57     GtkWidget *colorcb;
58     GtkWidget *rotatecb;
59     GtkWidget *textsizecb;
60     GtkWidget *text_view;
61 };
62 
63 
64 
65 /*! \brief Handles the user response when apply is selected
66  *
67  *  \par Function Description
68  *  This function applies the text from the text entry dialog.
69  *
70  *  \param [in] dialog The new text dialog
71  */
72 static void
dialog_response_apply(NewText * dialog)73 dialog_response_apply (NewText *dialog)
74 {
75   g_return_if_fail (dialog != NULL);
76 
77   GschemToplevel *w_current = NULL;
78   g_object_get (GSCHEM_DIALOG (dialog), "gschem-toplevel", &w_current, NULL);
79   g_return_if_fail (w_current != NULL);
80 
81   int size = w_current->text_size;
82 
83   int align = LOWER_LEFT;
84   int color = TEXT_COLOR;
85   int rotate = 0;
86   gchar *string = NULL;
87   gchar *tmp = NULL;
88   GtkTextBuffer *textbuffer;
89   GtkTextIter start, end;
90   int value;
91 
92   textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(dialog->text_view));
93   gtk_text_buffer_get_bounds (textbuffer, &start, &end);
94   string =  gtk_text_iter_get_text (&start, &end);
95 
96   if (string[0] == '\0' )
97     return;
98 
99   switch(dialog->parent.w_current->text_caps) {
100     case(LOWER):
101       tmp = g_utf8_strdown (string, -1);
102       break;
103 
104     case(UPPER):
105       tmp = g_utf8_strup (string, -1);
106       break;
107 
108     case(BOTH):
109     default:
110       /* do nothing */
111       break;
112   }
113 
114   value = x_colorcb_get_index (dialog->colorcb);
115   if (value >= 0) {
116     color = value;
117   }
118 
119   value = gschem_alignment_combo_get_align (dialog->aligncb);
120   if (value >= 0) {
121     align = value;
122   }
123 
124   value = gschem_integer_combo_box_get_value (dialog->textsizecb);
125   if (value > 0) {
126     size = value;
127   }
128 
129   value = gschem_rotation_combo_get_angle (dialog->rotatecb);
130   if (value >= 0) {
131     rotate = value;
132   }
133 
134   /* select the text, so you can continue immediatly writing the next text */
135   select_all_text_in_textview(GTK_TEXT_VIEW(dialog->text_view));
136   gtk_widget_grab_focus(dialog->text_view);
137 
138   o_text_prepare_place (dialog->parent.w_current,
139                         tmp == NULL ? string : tmp,
140                         color,
141                         align,
142                         rotate,
143                         size);
144 
145   g_free (string);
146   g_free (tmp);
147 }
148 
149 
150 
151 /*! \brief Handles the user response when cancel is selected
152  *
153  *
154  *
155  *  \param [in,out] dialog The new text dialog
156  */
dialog_response_cancel(NewText * dialog)157 static void dialog_response_cancel(NewText *dialog)
158 {
159   i_callback_cancel (NULL, dialog->parent.w_current);
160   gtk_widget_destroy(dialog->parent.w_current->tiwindow);
161   dialog->parent.w_current->tiwindow=NULL;
162 }
163 
164 
165 
166 /*! \brief Handles user responses from the new text dialog box
167 
168  *  \par Function Description
169  *  Callback function for the text entry dialog.
170  *
171  *  \param [in,out] widget The new text dialog
172  *  \param [na]     unused Unused parameter
173  */
text_input_dialog_response(NewText * dialog,gint response,gpointer unused)174 static void text_input_dialog_response(NewText *dialog, gint response, gpointer unused)
175 {
176   switch(response) {
177     case GTK_RESPONSE_APPLY:
178       dialog_response_apply(dialog);
179       break;
180     case GTK_RESPONSE_CLOSE:
181     case GTK_RESPONSE_DELETE_EVENT:
182       dialog_response_cancel(dialog);
183       break;
184     default:
185       printf("text_edit_dialog_response(): strange signal %d\n", response);
186   }
187 }
188 
189 
190 /*! \brief Initialize NewText class
191  *
192  *  \par Function Description
193  *
194  *  GType class initialiser for Multiattrib. We override our parent
195  *  virtual class methods as needed and register our GObject properties.
196  *
197  *  \param [in] klass
198  */
newtext_class_init(NewTextClass * klass)199 static void newtext_class_init(NewTextClass *klass)
200 {
201 }
202 
203 
204 
205 /*! \brief Initialize NewText instance
206  *
207  *  \param [in,out] dialog The new text dialog
208  */
newtext_init(NewText * dialog)209 static void newtext_init(NewText *dialog)
210 {
211   GtkWidget *alignment;
212   GtkWidget *vbox;
213   GtkWidget *label = NULL;
214   GtkWidget *viewport1 = NULL;
215   GtkWidget *scrolled_window = NULL;
216   PangoTabArray *tab_array;
217   int real_tab_width;
218 #ifdef ENABLE_GTK3
219   GtkWidget *grid;
220 #else
221   GtkWidget *table;
222 #endif
223 
224   gtk_dialog_add_button (GTK_DIALOG (dialog),
225                          GTK_STOCK_CLOSE,
226                          GTK_RESPONSE_CLOSE);
227 
228   gtk_dialog_add_button (GTK_DIALOG (dialog),
229                          GTK_STOCK_APPLY,
230                          GTK_RESPONSE_APPLY);
231 
232   /* Set the alternative button order (ok, cancel, help) for other systems */
233   gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
234                                           GTK_RESPONSE_APPLY,
235                                           GTK_RESPONSE_CLOSE,
236                                           -1);
237 
238   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_NONE);
239 
240   g_signal_connect (G_OBJECT (dialog), "response",
241                     G_CALLBACK (text_input_dialog_response),
242                     NULL);
243 
244   gtk_dialog_set_default_response(GTK_DIALOG(dialog),
245                                   GTK_RESPONSE_ACCEPT);
246 
247   vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
248   gtk_box_set_spacing(GTK_BOX(vbox),DIALOG_V_SPACING);
249 
250 #ifdef ENABLE_GTK3
251   grid = gtk_grid_new ();
252   gtk_grid_set_row_spacing (GTK_GRID (grid), DIALOG_V_SPACING);
253   gtk_grid_set_column_spacing (GTK_GRID (grid), DIALOG_H_SPACING);
254 #else
255   table = gtk_table_new(4, 2, FALSE);
256   gtk_table_set_row_spacings(GTK_TABLE(table), DIALOG_V_SPACING);
257   gtk_table_set_col_spacings(GTK_TABLE(table), DIALOG_H_SPACING);
258 #endif
259 
260   label = gtk_label_new(_("<b>Text Properties</b>"));
261   gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
262   gtk_misc_set_alignment(GTK_MISC(label),0,0);
263   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
264 
265   alignment = gtk_alignment_new(0,0,1,1);
266   gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
267                             DIALOG_INDENTATION, 0);
268   gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
269 
270 #ifdef ENABLE_GTK3
271   gtk_container_add (GTK_CONTAINER (alignment), grid);
272 #else
273   gtk_container_add(GTK_CONTAINER(alignment), table);
274 #endif
275 
276   label = gtk_label_new (_("<b>Text Content</b>"));
277   gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
278   gtk_misc_set_alignment(GTK_MISC(label),0,0);
279   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
280 
281 
282   label = gtk_label_new_with_mnemonic (_("Colo_r:"));
283   gtk_misc_set_alignment(GTK_MISC(label),0,0);
284 #ifdef ENABLE_GTK3
285   gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
286 #else
287   gtk_table_attach (GTK_TABLE(table),
288                     label,
289                     0,
290                     1,
291                     0,
292                     1,
293                     GTK_FILL,
294                     (GtkAttachOptions) 0,
295                     0,
296                     0);
297 #endif
298 
299   dialog->colorcb = x_colorcb_new ();
300   x_colorcb_set_index(dialog->colorcb, TEXT_COLOR);
301 
302   gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->colorcb);
303 
304 #ifdef ENABLE_GTK3
305   gtk_grid_attach (GTK_GRID (grid), dialog->colorcb, 1, 0, 1, 1);
306 #else
307   gtk_table_attach_defaults(GTK_TABLE(table), dialog->colorcb, 1,2,0,1);
308 #endif
309 
310   label = gtk_label_new_with_mnemonic (_("_Size:"));
311   gtk_misc_set_alignment(GTK_MISC(label),0,0);
312 #ifdef ENABLE_GTK3
313   gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
314 #else
315   gtk_table_attach (GTK_TABLE(table),
316                     label,
317                     0,
318                     1,
319                     1,
320                     2,
321                     GTK_FILL,
322                     (GtkAttachOptions) 0,
323                     0,
324                     0);
325 #endif
326 
327   dialog->textsizecb = gschem_integer_combo_box_new();
328 
329   gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->textsizecb);
330 
331 #ifdef ENABLE_GTK3
332   gtk_grid_attach (GTK_GRID (grid), dialog->textsizecb, 1, 1, 1, 1);
333 #else
334   gtk_table_attach_defaults(GTK_TABLE(table), dialog->textsizecb, 1,2,1,2);
335 #endif
336 
337   label = gtk_label_new_with_mnemonic (_("Ali_gnment:"));
338   gtk_misc_set_alignment(GTK_MISC(label),0,0);
339 #ifdef ENABLE_GTK3
340   gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
341 #else
342   gtk_table_attach (GTK_TABLE(table),
343                     label,
344                     0,
345                     1,
346                     2,
347                     3,
348                     GTK_FILL,
349                     (GtkAttachOptions) 0,
350                     0,
351                     0);
352 #endif
353 
354   dialog->aligncb = gschem_alignment_combo_new ();
355   gschem_alignment_combo_set_align(dialog->aligncb, LOWER_LEFT);
356 
357   gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->aligncb);
358 
359 #ifdef ENABLE_GTK3
360   gtk_grid_attach (GTK_GRID (grid), dialog->aligncb, 1, 2, 1, 1);
361 #else
362   gtk_table_attach_defaults(GTK_TABLE(table), dialog->aligncb, 1,2,2,3);
363 #endif
364 
365   label = gtk_label_new_with_mnemonic (_("Ro_tation:"));
366   gtk_misc_set_alignment(GTK_MISC(label),0,0);
367 #ifdef ENABLE_GTK3
368   gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 1, 1);
369 #else
370   gtk_table_attach (GTK_TABLE(table),
371                     label,
372                     0,
373                     1,
374                     3,
375                     4,
376                     GTK_FILL,
377                     (GtkAttachOptions) 0,
378                     0,
379                     0);
380 #endif
381 
382   dialog->rotatecb = gschem_rotation_combo_new ();
383   gschem_rotation_combo_set_angle(dialog->rotatecb, 0);
384 
385   gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->rotatecb);
386 
387 #ifdef ENABLE_GTK3
388   gtk_grid_attach (GTK_GRID (grid), dialog->rotatecb, 1, 3, 1, 1);
389 #else
390   gtk_table_attach_defaults(GTK_TABLE(table), dialog->rotatecb, 1,2,3,4);
391 #endif
392 
393   viewport1 = gtk_viewport_new (NULL, NULL);
394   gtk_widget_show (viewport1);
395 
396   scrolled_window = gtk_scrolled_window_new(NULL, NULL);
397   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
398                                    GTK_POLICY_AUTOMATIC,
399                                    GTK_POLICY_AUTOMATIC);
400   gtk_container_add (GTK_CONTAINER (viewport1), scrolled_window);
401   gtk_box_pack_start( GTK_BOX(vbox), viewport1, TRUE, TRUE, 0);
402 
403   dialog->text_view = gtk_text_view_new();
404   gtk_text_view_set_editable(GTK_TEXT_VIEW(dialog->text_view), TRUE);
405   select_all_text_in_textview(GTK_TEXT_VIEW(dialog->text_view));
406 
407   /* Set the tab width, using pango tab array */
408   /*! \bug FIXME: This doesn't work. Why? */
409   tab_array = pango_tab_array_new (1, TRUE);
410   real_tab_width = text_view_calculate_real_tab_width(GTK_TEXT_VIEW(dialog->text_view),
411                                                         tab_in_chars);
412   if (real_tab_width >= 0) {
413     pango_tab_array_set_tab (tab_array, 0, PANGO_TAB_LEFT, real_tab_width);
414     /* printf("Real tab width: %i\n", real_tab_width);*/
415     gtk_text_view_set_tabs (GTK_TEXT_VIEW (dialog->text_view),
416                             tab_array);
417   }
418   else {
419     g_warning ("text_input_dialog: Impossible to set tab width.\n");
420   }
421 
422   pango_tab_array_free (tab_array);
423   gtk_container_add(GTK_CONTAINER(scrolled_window), dialog->text_view);
424 }
425 
426 
427 
428 /*! \brief Get/register NewText type.
429  */
newtext_get_type()430 GType newtext_get_type()
431 {
432   static GType type = 0;
433 
434   if (type == 0) {
435     static const GTypeInfo info = {
436       sizeof(NewTextClass),
437       NULL,                                   /* base_init */
438       NULL,                                   /* base_finalize */
439       (GClassInitFunc) newtext_class_init,
440       NULL,                                   /* class_finalize */
441       NULL,                                   /* class_data */
442       sizeof(NewText),
443       0,                                      /* n_preallocs */
444       (GInstanceInitFunc) newtext_init,
445     };
446 
447     type = g_type_register_static (GSCHEM_TYPE_DIALOG,
448                                    "NewText",
449                                    &info,
450                                    (GTypeFlags) 0);
451   }
452 
453   return type;
454 }
455 
456 
457 
458 /*! \brief Open the dialog box to add new text
459  *
460  *  \par Function Description
461  *  This function creates or raises the modal text entry dialog
462  *
463  *  \param [in] w_current The gschem toplevel
464  */
465 void
text_input_dialog(GschemToplevel * w_current)466 text_input_dialog (GschemToplevel *w_current)
467 {
468   if (w_current->tiwindow == NULL) {
469     /* dialog not created yet */
470     w_current->tiwindow =
471       GTK_WIDGET (g_object_new (TYPE_NEWTEXT,
472                                 /* GtkContainer */
473                                 "border-width",     DIALOG_BORDER_SPACING,
474                                 /* GtkWindow */
475                                 "title",            _("Add Text"),
476                                 "default-width",    320,
477                                 "default-height",   350,
478                                 "window-position",  GTK_WIN_POS_MOUSE,
479                                 "modal",            FALSE,
480 #ifndef ENABLE_GTK3
481                                 "allow-grow",       TRUE,
482                                 "allow-shrink",     FALSE,
483                                 /* GtkDialog */
484                                 "has-separator",    TRUE,
485 #endif
486                                 /* GschemDialog */
487                                 "settings-name",    "text-entry",
488                                 "gschem-toplevel",  w_current,
489                                 NULL));
490 
491     gtk_window_set_transient_for (GTK_WINDOW (w_current->tiwindow),
492                                   GTK_WINDOW (w_current->main_window));
493 
494     gschem_integer_combo_box_set_model (NEWTEXT (w_current->tiwindow)->textsizecb,
495                            gschem_toplevel_get_text_size_list_store (w_current));
496 
497     gschem_integer_combo_box_set_value (NEWTEXT (w_current->tiwindow)->textsizecb,
498                                         w_current->text_size);
499 
500     gtk_widget_show_all (w_current->tiwindow);
501   }
502   else {
503     /* dialog already created */
504     gtk_window_present (GTK_WINDOW(w_current->tiwindow));
505   }
506 
507   /* always select the text in the entry */
508   select_all_text_in_textview (GTK_TEXT_VIEW (NEWTEXT (w_current->tiwindow)->text_view));
509   gtk_widget_grab_focus (NEWTEXT (w_current->tiwindow)->text_view);
510 }
511