Root/
Source at commit 103933acf59927d84df7f99d9580dfc0f45059c2 created 2 years 10 months ago. By Werner Almesberger, fix a few whitespace issues in previous commit | |
---|---|
1 | /* |
2 | * gui.c - Editor GUI core |
3 | * |
4 | * Written 2009-2012, 2015-2016 by Werner Almesberger |
5 | * Copyright 2009-2012, 2015-2016 by Werner Almesberger |
6 | * Copyright 2016, Erich Heinzle (gEDA additions) |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | */ |
13 | |
14 | |
15 | #include <stdlib.h> |
16 | #include <locale.h> |
17 | #include <gtk/gtk.h> |
18 | |
19 | #include "inst.h" |
20 | #include "file.h" |
21 | #include "gui_util.h" |
22 | #include "gui_style.h" |
23 | #include "gui_status.h" |
24 | #include "gui_canvas.h" |
25 | #include "gui_tool.h" |
26 | #include "gui_frame.h" |
27 | #include "gui.h" |
28 | #include "fped.h" |
29 | |
30 | #include "icons/stuff.xpm" |
31 | #include "icons/stuff_off.xpm" |
32 | #include "icons/meas.xpm" |
33 | #include "icons/meas_off.xpm" |
34 | #include "icons/all.xpm" |
35 | #include "icons/all_off.xpm" |
36 | #include "icons/bright.xpm" |
37 | #include "icons/bright_off.xpm" |
38 | |
39 | |
40 | GtkWidget *root; |
41 | int show_all = 1; |
42 | int show_stuff = 1; |
43 | int show_meas = 1; |
44 | int show_bright = 0; |
45 | |
46 | |
47 | static GtkWidget *paned; |
48 | static GtkWidget *frames_box; |
49 | static GtkWidget *ev_stuff, *ev_meas, *ev_all, *ev_bright; |
50 | static GtkWidget *stuff_image[2], *meas_image[2], *all_image[2]; |
51 | static GtkWidget *bright_image[2]; |
52 | |
53 | static GtkItemFactory *menu_factory; |
54 | |
55 | static void do_build_frames(void); |
56 | |
57 | |
58 | /* ----- save callbacks ---------------------------------------------------- */ |
59 | |
60 | |
61 | static void save_as_fpd(void) |
62 | { |
63 | GtkWidget *dialog; |
64 | |
65 | dialog = gtk_file_chooser_dialog_new("Save File", |
66 | NULL, GTK_FILE_CHOOSER_ACTION_SAVE, |
67 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
68 | GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); |
69 | gtk_file_chooser_set_do_overwrite_confirmation( |
70 | GTK_FILE_CHOOSER(dialog), TRUE); |
71 | if (save_file_name) |
72 | gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), |
73 | save_file_name); |
74 | if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { |
75 | save_file_name = |
76 | gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); |
77 | save_fpd(); |
78 | /* @@@ we may leak save_file_name */ |
79 | no_save = 0; |
80 | } |
81 | gtk_widget_destroy(dialog); |
82 | } |
83 | |
84 | |
85 | /* ----- view callbacks ---------------------------------------------------- */ |
86 | |
87 | |
88 | static void show_var(void) |
89 | { |
90 | sidebar = sidebar_var; |
91 | change_world(); |
92 | } |
93 | |
94 | |
95 | static void show_code(void) |
96 | { |
97 | sidebar = sidebar_code; |
98 | change_world(); |
99 | } |
100 | |
101 | |
102 | static void show_pkg(void) |
103 | { |
104 | sidebar = sidebar_pkg; |
105 | change_world(); |
106 | } |
107 | |
108 | |
109 | /* ----- allow callbacks --------------------------------------------------- */ |
110 | |
111 | |
112 | static void allow_touch(void) |
113 | { |
114 | allow_overlap = ao_touch; |
115 | change_world(); |
116 | } |
117 | |
118 | |
119 | static void allow_any_overlap(void) |
120 | { |
121 | allow_overlap = ao_any; |
122 | change_world(); |
123 | } |
124 | |
125 | |
126 | static void allow_neither(void) |
127 | { |
128 | allow_overlap = ao_none; |
129 | change_world(); |
130 | } |
131 | |
132 | |
133 | static void allow_holes(void) |
134 | { |
135 | GtkCheckMenuItem *item = |
136 | GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(menu_factory, |
137 | "/Allow/Holes")); |
138 | |
139 | holes_linked = !gtk_check_menu_item_get_active(item); |
140 | change_world(); |
141 | } |
142 | |
143 | |
144 | /* ----- menu bar ---------------------------------------------------------- */ |
145 | |
146 | |
147 | static GtkItemFactoryEntry menu_entries[] = { |
148 | { "/File", NULL, NULL, 0, "<Branch>" }, |
149 | { "/File/Save", NULL, save_fpd, 0, "<Item>" }, |
150 | { "/File/Save as", NULL, save_as_fpd, 0, "<Item>" }, |
151 | { "/File/sep1", NULL, NULL, 0, "<Separator>" }, |
152 | { "/File/Write KiCad", NULL, write_kicad, 0, "<Item>" }, |
153 | { "/File/Write gEDA", NULL, write_geda, 0, "<Item>" }, |
154 | { "/File/Write Postscript", |
155 | NULL, write_ps, 0, "<Item>" }, |
156 | { "/File/sep2", NULL, NULL, 0, "<Separator>" }, |
157 | { "/File/Reload", NULL, reload, 0, "<Item>" }, |
158 | { "/File/sep3", NULL, NULL, 0, "<Separator>" }, |
159 | { "/File/Quit", NULL, gtk_main_quit, 0, "<Item>" }, |
160 | { "/View", NULL, NULL, 0, "<Branch>" }, |
161 | { "/View/Zoom in", NULL, zoom_in_center, 0, "<Item>" }, |
162 | { "/View/Zoom out", NULL, zoom_out_center,0, "<Item>" }, |
163 | { "/View/Zoom all", NULL, zoom_to_extents,0, "<Item>" }, |
164 | { "/View/Zoom frame", NULL, zoom_to_frame, 0, "<Item>" }, |
165 | { "/View/sep1", NULL, NULL, 0, "<Separator>" }, |
166 | { "/View/Show variables", |
167 | NULL, show_var, 0, "<RadioItem>" }, |
168 | { "/View/Show code", NULL, show_code, 0, |
169 | "/View/Show variables" }, |
170 | { "/View/Show packages",NULL, show_pkg, 0, |
171 | "/View/Show variables" }, |
172 | { "/Allow/Touch", NULL, allow_touch, 0, "<RadioItem>" }, |
173 | { "/Allow/Overlap", NULL, allow_any_overlap, |
174 | 0, "/Allow/Touch" }, |
175 | { "/Allow/Neither", NULL, allow_neither, 0, "/Allow/Touch" }, |
176 | { "/Allow/sep1", NULL, NULL, 0, "<Separator>" }, |
177 | { "/Allow/Holes", NULL, allow_holes, 0, "<CheckItem>" } |
178 | }; |
179 | |
180 | |
181 | static void make_menu_bar(GtkWidget *hbox) |
182 | { |
183 | GtkWidget *bar; |
184 | |
185 | menu_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<FpedMenu>", |
186 | NULL); |
187 | gtk_item_factory_create_items(menu_factory, |
188 | sizeof(menu_entries)/sizeof(*menu_entries), menu_entries, NULL); |
189 | |
190 | bar = gtk_item_factory_get_widget(menu_factory, "<FpedMenu>"); |
191 | gtk_box_pack_start(GTK_BOX(hbox), bar, TRUE, TRUE, 0); |
192 | |
193 | gtk_widget_set_sensitive( |
194 | gtk_item_factory_get_item(menu_factory, "/File/Save"), !no_save); |
195 | gtk_widget_set_sensitive( |
196 | gtk_item_factory_get_item(menu_factory, "/File/Reload"), |
197 | no_save && !!save_file_name); |
198 | } |
199 | |
200 | |
201 | void update_menu_bar(void) |
202 | { |
203 | const char *s; |
204 | |
205 | switch (sidebar) { |
206 | case sidebar_var: |
207 | s = "/View/Show variables"; |
208 | break; |
209 | case sidebar_code: |
210 | s = "/View/Show code"; |
211 | break; |
212 | case sidebar_pkg: |
213 | s = "/View/Show packages"; |
214 | break; |
215 | default: |
216 | abort(); |
217 | } |
218 | gtk_check_menu_item_set_active( |
219 | GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(menu_factory, s)), |
220 | TRUE); |
221 | |
222 | switch (allow_overlap) { |
223 | case ao_none: |
224 | s = "/Allow/Neither"; |
225 | break; |
226 | case ao_touch: |
227 | s = "/Allow/Touch"; |
228 | break; |
229 | case ao_any: |
230 | s = "/Allow/Overlap"; |
231 | break; |
232 | default: |
233 | abort(); |
234 | } |
235 | gtk_check_menu_item_set_active( |
236 | GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(menu_factory, s)), |
237 | TRUE); |
238 | |
239 | |
240 | gtk_check_menu_item_set_active( |
241 | GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(menu_factory, |
242 | "/Allow/Holes")), !holes_linked); |
243 | } |
244 | |
245 | |
246 | static gboolean toggle_all(GtkWidget *widget, GdkEventButton *event, |
247 | gpointer data) |
248 | { |
249 | switch (event->button) { |
250 | case 1: |
251 | show_all = !show_all; |
252 | set_image(ev_all, all_image[show_all]); |
253 | inst_deselect(); |
254 | redraw(); |
255 | break; |
256 | } |
257 | return TRUE; |
258 | } |
259 | |
260 | |
261 | static gboolean toggle_stuff(GtkWidget *widget, GdkEventButton *event, |
262 | gpointer data) |
263 | { |
264 | switch (event->button) { |
265 | case 1: |
266 | show_stuff = !show_stuff; |
267 | set_image(ev_stuff, stuff_image[show_stuff]); |
268 | inst_deselect(); |
269 | redraw(); |
270 | break; |
271 | } |
272 | return TRUE; |
273 | } |
274 | |
275 | |
276 | static gboolean toggle_meas(GtkWidget *widget, GdkEventButton *event, |
277 | gpointer data) |
278 | { |
279 | switch (event->button) { |
280 | case 1: |
281 | show_meas = !show_meas; |
282 | set_image(ev_meas, meas_image[show_meas]); |
283 | inst_deselect(); |
284 | redraw(); |
285 | break; |
286 | } |
287 | return TRUE; |
288 | } |
289 | |
290 | |
291 | static gboolean toggle_bright(GtkWidget *widget, GdkEventButton *event, |
292 | gpointer data) |
293 | { |
294 | switch (event->button) { |
295 | case 1: |
296 | show_bright = !show_bright; |
297 | set_image(ev_bright, bright_image[show_bright]); |
298 | inst_deselect(); |
299 | redraw(); |
300 | break; |
301 | } |
302 | return TRUE; |
303 | } |
304 | |
305 | |
306 | static void make_tool_bar(GtkWidget *hbox, GdkDrawable *drawable) |
307 | { |
308 | GtkWidget *bar; |
309 | |
310 | bar = gtk_toolbar_new(); |
311 | gtk_box_pack_end(GTK_BOX(hbox), bar, TRUE, TRUE, 0); |
312 | //gtk_box_pack_end(GTK_BOX(hbox), bar, FALSE, FALSE, 0); |
313 | gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_ICONS); |
314 | |
315 | ev_all = tool_button(bar, drawable, NULL, NULL, toggle_all, NULL); |
316 | ev_stuff = tool_button(bar, drawable, NULL, NULL, toggle_stuff, NULL); |
317 | ev_meas = tool_button(bar, drawable, NULL, NULL, toggle_meas, NULL); |
318 | ev_bright = tool_button(bar, drawable, NULL, NULL, toggle_bright, NULL); |
319 | |
320 | stuff_image[0] = gtk_widget_ref(make_image(drawable, xpm_stuff_off, |
321 | "Show vectors and frame references (disabled)")); |
322 | stuff_image[1] = gtk_widget_ref(make_image(drawable, xpm_stuff, |
323 | "Show vectors and frame references (enabled)")); |
324 | meas_image[0] = gtk_widget_ref(make_image(drawable, xpm_meas_off, |
325 | "Show measurements (disabled)")); |
326 | meas_image[1] = gtk_widget_ref(make_image(drawable, xpm_meas, |
327 | "Show measurements (enabled)")); |
328 | all_image[0] = gtk_widget_ref(make_image(drawable, xpm_all_off, |
329 | "Show all frames (currently showing only the active frame)")); |
330 | all_image[1] = gtk_widget_ref(make_image(drawable, xpm_all, |
331 | "Show all frames (enabled)")); |
332 | bright_image[0] = gtk_widget_ref(make_image(drawable, xpm_bright_off, |
333 | "Highlight elements (disabled)")); |
334 | bright_image[1] = gtk_widget_ref(make_image(drawable, xpm_bright, |
335 | "Highlight elements (enabled)")); |
336 | |
337 | set_image(ev_stuff, stuff_image[show_stuff]); |
338 | set_image(ev_meas, meas_image[show_meas]); |
339 | set_image(ev_all, all_image[show_all]); |
340 | set_image(ev_bright, bright_image[show_bright]); |
341 | } |
342 | |
343 | |
344 | static void cleanup_tool_bar(void) |
345 | { |
346 | g_object_unref(stuff_image[0]); |
347 | g_object_unref(stuff_image[1]); |
348 | g_object_unref(meas_image[0]); |
349 | g_object_unref(meas_image[1]); |
350 | g_object_unref(all_image[0]); |
351 | g_object_unref(all_image[1]); |
352 | g_object_unref(bright_image[0]); |
353 | g_object_unref(bright_image[1]); |
354 | } |
355 | |
356 | |
357 | static void make_top_bar(GtkWidget *vbox) |
358 | { |
359 | GtkWidget *hbox; |
360 | |
361 | hbox = gtk_hbox_new(FALSE, 0); |
362 | make_menu_bar(hbox); |
363 | make_tool_bar(hbox, root->window); |
364 | gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
365 | } |
366 | |
367 | |
368 | /* ----- central screen area ----------------------------------------------- */ |
369 | |
370 | |
371 | static void resize_frames_area(GtkWidget *widget, GtkAllocation *allocation, |
372 | gpointer user_data) |
373 | { |
374 | static int width = 0; |
375 | |
376 | if (allocation->width == width) |
377 | return; |
378 | width = allocation->width; |
379 | do_build_frames(); |
380 | } |
381 | |
382 | |
383 | static void make_center_area(GtkWidget *vbox) |
384 | { |
385 | GtkWidget *hbox, *frames_area;//, *paned; |
386 | GtkWidget *tools; |
387 | |
388 | hbox = gtk_hbox_new(FALSE, 0); |
389 | gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); |
390 | |
391 | paned = gtk_hpaned_new(); |
392 | gtk_box_pack_start(GTK_BOX(hbox), paned, TRUE, TRUE, 0); |
393 | |
394 | /* Frames */ |
395 | |
396 | frames_area = gtk_scrolled_window_new(NULL, NULL); |
397 | gtk_paned_add1(GTK_PANED(paned), frames_area); |
398 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(frames_area), |
399 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
400 | gtk_widget_set_size_request(frames_area, |
401 | DEFAULT_FRAME_AREA_WIDTH, DEFAULT_FRAME_AREA_HEIGHT); |
402 | |
403 | frames_box = gtk_vbox_new(FALSE, 0); |
404 | build_frames(frames_box, DEFAULT_FRAME_AREA_WIDTH); |
405 | |
406 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(frames_area), |
407 | frames_box); |
408 | |
409 | g_signal_connect(G_OBJECT(frames_area), "size-allocate", |
410 | G_CALLBACK(resize_frames_area), NULL); |
411 | |
412 | /* Canvas */ |
413 | |
414 | gtk_paned_add2(GTK_PANED(paned), make_canvas()); |
415 | |
416 | /* Icon bar */ |
417 | |
418 | tools = gui_setup_tools(root->window); |
419 | gtk_box_pack_end(GTK_BOX(hbox), tools, FALSE, FALSE, 0); |
420 | } |
421 | |
422 | |
423 | /* ----- GUI construction -------------------------------------------------- */ |
424 | |
425 | |
426 | static void do_build_frames(void) |
427 | { |
428 | int width; |
429 | |
430 | width = gtk_paned_get_position(GTK_PANED(paned)); |
431 | build_frames(frames_box, width > 0 ? width : DEFAULT_FRAME_AREA_WIDTH); |
432 | } |
433 | |
434 | |
435 | void change_world(void) |
436 | { |
437 | struct bbox before, after; |
438 | int reachable_is_active; |
439 | |
440 | inst_deselect(); |
441 | status_begin_reporting(); |
442 | before = inst_get_bbox(NULL); |
443 | reachable_is_active = reachable_pkg && reachable_pkg == active_pkg; |
444 | instantiate(); |
445 | if (reachable_is_active && reachable_pkg && |
446 | reachable_pkg != active_pkg) { |
447 | active_pkg = reachable_pkg; |
448 | instantiate(); |
449 | } |
450 | after = inst_get_bbox(NULL); |
451 | do_build_frames(); |
452 | if (after.min.x < before.min.x || after.min.y < before.min.y || |
453 | after.max.x > before.max.x || after.max.y > before.max.y) |
454 | zoom_to_extents(); |
455 | else |
456 | redraw(); |
457 | } |
458 | |
459 | |
460 | void change_world_reselect(void) |
461 | { |
462 | struct obj *selected_obj; |
463 | |
464 | /* |
465 | * We can edit an object even if it's not selected if it was picked |
466 | * via the item view. inst_select_obj tries to find an instance, but |
467 | * if there's never been a successful instantiation since creation of |
468 | * the object or if the object is unreachable for some other reason, |
469 | * then selected_inst will be NULL. |
470 | */ |
471 | if (!selected_inst) { |
472 | change_world(); |
473 | return; |
474 | } |
475 | selected_obj = selected_inst->obj; |
476 | change_world(); |
477 | inst_select_obj(selected_obj); |
478 | } |
479 | |
480 | |
481 | static void make_screen(GtkWidget *window) |
482 | { |
483 | GtkWidget *vbox; |
484 | |
485 | vbox = gtk_vbox_new(FALSE, 0); |
486 | gtk_container_add(GTK_CONTAINER(window), vbox); |
487 | |
488 | make_top_bar(vbox); |
489 | make_center_area(vbox); |
490 | make_status_area(vbox); |
491 | } |
492 | |
493 | |
494 | int gui_init(int *argc, char ***argv) |
495 | { |
496 | gtk_init(argc, argv); |
497 | setlocale(LC_ALL, "C"); /* damage control */ |
498 | return 0; |
499 | } |
500 | |
501 | |
502 | int gui_main(void) |
503 | { |
504 | root = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
505 | gtk_window_set_position(GTK_WINDOW(root), GTK_WIN_POS_CENTER); |
506 | gtk_window_set_default_size(GTK_WINDOW(root), 620, 460); |
507 | if (*VERSION) |
508 | gtk_window_set_title(GTK_WINDOW(root), |
509 | "fped (rev " VERSION ")"); |
510 | else |
511 | gtk_window_set_title(GTK_WINDOW(root), "fped"); |
512 | |
513 | /* get root->window */ |
514 | gtk_widget_show_all(root); |
515 | |
516 | g_signal_connect(G_OBJECT(root), "destroy", |
517 | G_CALLBACK(gtk_main_quit), NULL); |
518 | |
519 | make_screen(root); |
520 | |
521 | gtk_widget_show_all(root); |
522 | |
523 | gui_setup_style(root->window); |
524 | init_canvas(); |
525 | edit_nothing(); |
526 | select_frame(frames); |
527 | make_popups(); |
528 | update_menu_bar(); |
529 | |
530 | gtk_main(); |
531 | |
532 | gui_cleanup_style(); |
533 | gui_cleanup_tools(); |
534 | cleanup_tool_bar(); |
535 | cleanup_status_area(); |
536 | |
537 | return 0; |
538 | } |
539 |
Branches:
master