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