pcb 4.1.1
An interactive printed circuit board layout editor.
|
00001 00035 #include <glib.h> 00036 #include <glib-object.h> 00037 #include <gtk/gtk.h> 00038 00039 #include "gtkhid.h" 00040 #include "gui.h" 00041 #include "pcb-printf.h" 00042 00043 #include "ghid-main-menu.h" 00044 #include "ghid-layer-selector.h" 00045 #include "ghid-route-style-selector.h" 00046 00047 void Message (const char *, ...); 00048 00049 static int action_counter; 00050 00051 struct _GHidMainMenu 00052 { 00053 GtkMenuBar parent; 00054 00055 GtkActionGroup *action_group; 00056 GtkAccelGroup *accel_group; 00057 00058 gint layer_view_pos; 00059 gint layer_pick_pos; 00060 gint route_style_pos; 00061 00062 GtkMenuShell *layer_view_shell; 00063 GtkMenuShell *layer_pick_shell; 00064 GtkMenuShell *route_style_shell; 00065 00066 GList *actions; 00067 GHashTable *popup_table; 00068 00069 gint n_layer_views; 00070 gint n_layer_picks; 00071 gint n_route_styles; 00072 00073 GCallback action_cb; 00074 void (*special_key_cb) (const char *accel, GtkAction *action, 00075 const Resource *node); 00076 }; 00077 00078 struct _GHidMainMenuClass 00079 { 00080 GtkMenuBarClass parent_class; 00081 }; 00082 00083 /* TODO: write finalize function */ 00084 00085 /* SIGNAL HANDLERS */ 00086 00087 /* RESOURCE HANDLER */ 00088 00100 static gchar * 00101 translate_accelerator (const char *text) 00102 { 00103 GString *ret_val = g_string_new (""); 00104 static struct { const char *in, *out; } key_table[] = 00105 { 00106 {"Enter", "Return"}, 00107 {"Alt", "<alt>"}, 00108 {"Shift", "<shift>"}, 00109 {"Ctrl", "<ctrl>"}, 00110 {" ", ""}, 00111 {":", "colon"}, 00112 {"=", "equal"}, 00113 {"/", "slash"}, 00114 {"[", "bracketleft"}, 00115 {"]", "bracketright"}, 00116 {".", "period"}, 00117 {"|", "bar"}, 00118 {"+", "plus"}, 00119 {"-", "minus"}, 00120 {NULL, NULL} 00121 }; 00122 00123 enum {MOD, KEY} state = MOD; 00124 while (*text != '\0') 00125 { 00126 static gboolean gave_msg; 00127 gboolean found = FALSE; 00128 int i; 00129 00130 if (state == MOD && strncmp (text, "<Key>", 5) == 0) 00131 { 00132 state = KEY; 00133 text += 5; 00134 } 00135 for (i = 0; key_table[i].in != NULL; ++i) 00136 { 00137 int len = strlen (key_table[i].in); 00138 if (strncmp (text, key_table[i].in, len) == 0) 00139 { 00140 found = TRUE; 00141 g_string_append (ret_val, key_table[i].out); 00142 text += len; 00143 } 00144 } 00145 if (found == FALSE) 00146 switch (state) 00147 { 00148 case MOD: 00149 Message (_("Don't know how to parse \"%s\" as an " 00150 "accelerator in the menu resource file.\n"), 00151 text); 00152 if (!gave_msg) 00153 { 00154 gave_msg = TRUE; 00155 Message (_("Format is:\n" 00156 "modifiers<Key>k\n" 00157 "where \"modifiers\" is a space " 00158 "separated list of key modifiers\n" 00159 "and \"k\" is the name of the key.\n" 00160 "Allowed modifiers are:\n" 00161 " Ctrl\n" 00162 " Shift\n" 00163 " Alt\n" 00164 "Please note that case is important.\n")); 00165 } 00166 break; 00167 case KEY: 00168 g_string_append_c (ret_val, *text); 00169 ++text; 00170 break; 00171 } 00172 } 00173 return g_string_free (ret_val, FALSE); 00174 } 00175 00176 static gboolean 00177 g_str_case_equal (gconstpointer v1, gconstpointer v2) 00178 { 00179 return strcasecmp (v1, v2); 00180 } 00181 00185 static const char * 00186 check_unique_accel (const char *accelerator) 00187 { 00188 static GHashTable *accel_table; 00189 00190 if (!accelerator ||*accelerator) 00191 return accelerator; 00192 00193 if (!accel_table) 00194 accel_table = g_hash_table_new (g_str_hash, g_str_case_equal); 00195 00196 if (g_hash_table_lookup (accel_table, accelerator)) 00197 { 00198 Message (_("Duplicate accelerator found: \"%s\"\n" 00199 "The second occurrence will be dropped\n"), 00200 accelerator); 00201 return NULL; 00202 } 00203 00204 g_hash_table_insert (accel_table, 00205 (gpointer) accelerator, (gpointer) accelerator); 00206 00207 return accelerator; 00208 } 00209 00210 00218 void 00219 ghid_main_menu_real_add_resource (GHidMainMenu *menu, GtkMenuShell *shell, 00220 const Resource *res) 00221 { 00222 int i, j; 00223 const Resource *tmp_res; 00224 gchar mnemonic = 0; 00225 00226 for (i = 0; i < res->c; ++i) 00227 { 00228 const gchar *accel = NULL; 00229 char *menu_label; 00230 const char *res_val; 00231 const Resource *sub_res = res->v[i].subres; 00232 GtkAction *action = NULL; 00233 00234 switch (resource_type (res->v[i])) 00235 { 00236 case 101: /* name, subres: passthrough */ 00237 ghid_main_menu_real_add_resource (menu, shell, sub_res); 00238 break; 00239 case 1: /* no name, subres */ 00240 tmp_res = resource_subres (sub_res, "a"); /* accelerator */ 00241 res_val = resource_value (sub_res, "m"); /* mnemonic */ 00242 if (res_val) 00243 mnemonic = res_val[0]; 00244 /* The accelerator resource will have two values, like 00245 * a={"Ctrl-Q" "Ctrl<Key>q"} 00246 * The first Gtk ignores. The second needs to be translated. */ 00247 if (tmp_res) 00248 accel = check_unique_accel 00249 (translate_accelerator (tmp_res->v[1].value)); 00250 00251 /* Now look for the first unnamed value (not a subresource) to 00252 * figure out the name of the menu or the menuitem. */ 00253 res_val = "button"; 00254 for (j = 0; j < sub_res->c; ++j) 00255 if (resource_type (sub_res->v[j]) == 10) 00256 { 00257 res_val = _(sub_res->v[j].value); 00258 break; 00259 } 00260 /* Hack '_' in based on mnemonic value */ 00261 if (!mnemonic) 00262 menu_label = g_strdup (res_val); 00263 else 00264 { 00265 char *post_ = strchr (res_val, mnemonic); 00266 if (post_ == NULL) 00267 menu_label = g_strdup (res_val); 00268 else 00269 { 00270 GString *tmp = g_string_new (""); 00271 g_string_append_len (tmp, res_val, post_ - res_val); 00272 g_string_append_c (tmp, '_'); 00273 g_string_append (tmp, post_); 00274 menu_label = g_string_free (tmp, FALSE); 00275 } 00276 } 00277 /* If the subresource we're processing also has unnamed 00278 * subresources, it's a submenu, not a regular menuitem. */ 00279 if (sub_res->flags & FLAG_S) 00280 { 00281 /* SUBMENU */ 00282 GtkWidget *submenu = gtk_menu_new (); 00283 GtkWidget *item = gtk_menu_item_new_with_mnemonic (menu_label); 00284 GtkWidget *tearoff = gtk_tearoff_menu_item_new (); 00285 00286 gtk_menu_shell_append (shell, item); 00287 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu); 00288 00289 /* add tearoff to menu */ 00290 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), tearoff); 00291 /* recurse on the newly-added submenu */ 00292 ghid_main_menu_real_add_resource (menu, 00293 GTK_MENU_SHELL (submenu), 00294 sub_res); 00295 } 00296 else 00297 { 00298 /* NON-SUBMENU: MENU ITEM */ 00299 const char *checked = resource_value (sub_res, "checked"); 00300 const char *label = resource_value (sub_res, "sensitive"); 00301 const char *tip = resource_value (sub_res, "tip"); 00302 if (checked) 00303 { 00304 /* TOGGLE ITEM */ 00305 gchar *name = g_strdup_printf ("MainMenuAction%d", 00306 action_counter++); 00307 00308 action = GTK_ACTION (gtk_toggle_action_new (name, menu_label, 00309 tip, NULL)); 00310 /* checked=foo is a binary flag (checkbox) 00311 * checked=foo,bar is a flag compared to a value (radio) */ 00312 gtk_toggle_action_set_draw_as_radio 00313 (GTK_TOGGLE_ACTION (action), !!strchr (checked, ',')); 00314 } 00315 else if (label && strcmp (label, "false") == 0) 00316 { 00317 /* INSENSITIVE ITEM */ 00318 GtkWidget *item = gtk_menu_item_new_with_label (menu_label); 00319 gtk_widget_set_sensitive (item, FALSE); 00320 gtk_menu_shell_append (shell, item); 00321 } 00322 else 00323 { 00324 /* NORMAL ITEM */ 00325 gchar *name = g_strdup_printf ("MainMenuAction%d", action_counter++); 00326 action = gtk_action_new (name, menu_label, tip, NULL); 00327 } 00328 } 00329 /* Connect accelerator, if there is one */ 00330 if (action) 00331 { 00332 GtkWidget *item; 00333 gtk_action_set_accel_group (action, menu->accel_group); 00334 gtk_action_group_add_action_with_accel (menu->action_group, 00335 action, accel); 00336 gtk_action_connect_accelerator (action); 00337 g_signal_connect (G_OBJECT (action), "activate", menu->action_cb, 00338 (gpointer) sub_res); 00339 g_object_set_data (G_OBJECT (action), "resource", 00340 (gpointer) sub_res); 00341 item = gtk_action_create_menu_item (action); 00342 gtk_menu_shell_append (shell, item); 00343 menu->actions = g_list_append (menu->actions, action); 00344 menu->special_key_cb (accel, action, sub_res); 00345 } 00346 /* Scan rest of resource in case there is more work */ 00347 for (j = 0; j < sub_res->c; j++) 00348 { 00349 const char *res_name; 00350 /* named value = X resource */ 00351 if (resource_type (sub_res->v[j]) == 110) 00352 { 00353 res_name = sub_res->v[j].name; 00354 00355 /* translate bg, fg to background, foreground */ 00356 if (strcmp (res_name, "fg") == 0) res_name = "foreground"; 00357 if (strcmp (res_name, "bg") == 0) res_name = "background"; 00358 00359 /* ignore special named values (m, a, sensitive) */ 00360 if (strcmp (res_name, "m") == 0 00361 || strcmp (res_name, "a") == 0 00362 || strcmp (res_name, "sensitive") == 0 00363 || strcmp (res_name, "tip") == 0) 00364 break; 00365 00366 /* log checked and active special values */ 00367 if (action && strcmp (res_name, "checked") == 0) 00368 g_object_set_data (G_OBJECT (action), "checked-flag", 00369 sub_res->v[j].value); 00370 else if (action && strcmp (res_name, "active") == 0) 00371 g_object_set_data (G_OBJECT (action), "active-flag", 00372 sub_res->v[j].value); 00373 else 00374 /* if we got this far it is supposed to be an X 00375 * resource. For now ignore it and warn the user */ 00376 Message (_("The gtk gui currently ignores \"%s\"" 00377 "as part of a menuitem resource.\n" 00378 "Feel free to provide patches\n"), 00379 sub_res->v[j].value); 00380 } 00381 } 00382 break; 00383 case 10: /* no name, value */ 00384 /* If we get here, the resource is "-" or "@foo" for some foo */ 00385 if (res->v[i].value[0] == '@') 00386 { 00387 GList *children; 00388 int pos; 00389 00390 children = gtk_container_get_children (GTK_CONTAINER (shell)); 00391 pos = g_list_length (children); 00392 g_list_free (children); 00393 00394 if (strcmp (res->v[i].value, "@layerview") == 0) 00395 { 00396 menu->layer_view_shell = shell; 00397 menu->layer_view_pos = pos; 00398 } 00399 else if (strcmp (res->v[i].value, "@layerpick") == 0) 00400 { 00401 menu->layer_pick_shell = shell; 00402 menu->layer_pick_pos = pos; 00403 } 00404 else if (strcmp (res->v[i].value, "@routestyles") == 0) 00405 { 00406 menu->route_style_shell = shell; 00407 menu->route_style_pos = pos; 00408 } 00409 else 00410 Message (_("GTK GUI currently ignores \"%s\" in the menu\n" 00411 "resource file.\n"), res->v[i].value); 00412 } 00413 else if (strcmp (res->v[i].value, "-") == 0) 00414 { 00415 GtkWidget *item = gtk_separator_menu_item_new (); 00416 gtk_menu_shell_append (shell, item); 00417 } 00418 else if (i > 0) 00419 { 00420 /* This is an action-less menuitem. It is really only useful 00421 * when you're starting to build a new menu and you're looking 00422 * to get the layout right. */ 00423 GtkWidget *item 00424 = gtk_menu_item_new_with_label (_(res->v[i].value)); 00425 gtk_menu_shell_append (shell, item); 00426 } 00427 break; 00428 } 00429 } 00430 } 00431 00432 /* CONSTRUCTOR */ 00433 static void 00434 ghid_main_menu_init (GHidMainMenu *mm) 00435 { 00436 /* Hookup signal handlers */ 00437 } 00438 00439 static void 00440 ghid_main_menu_class_init (GHidMainMenuClass *klass) 00441 { 00442 } 00443 00444 /* PUBLIC FUNCTIONS */ 00445 GType 00446 ghid_main_menu_get_type (void) 00447 { 00448 static GType mm_type = 0; 00449 00450 if (!mm_type) 00451 { 00452 const GTypeInfo mm_info = 00453 { 00454 sizeof (GHidMainMenuClass), 00455 NULL, /* base_init */ 00456 NULL, /* base_finalize */ 00457 (GClassInitFunc) ghid_main_menu_class_init, 00458 NULL, /* class_finalize */ 00459 NULL, /* class_data */ 00460 sizeof (GHidMainMenu), 00461 0, /* n_preallocs */ 00462 (GInstanceInitFunc) ghid_main_menu_init, 00463 }; 00464 00465 mm_type = g_type_register_static (GTK_TYPE_MENU_BAR, 00466 "GHidMainMenu", 00467 &mm_info, 0); 00468 } 00469 00470 return mm_type; 00471 } 00472 00478 GtkWidget * 00479 ghid_main_menu_new (GCallback action_cb, 00480 void (*special_key_cb) (const char *accel, 00481 GtkAction *action, 00482 const Resource *node)) 00483 { 00484 GHidMainMenu *mm = g_object_new (GHID_MAIN_MENU_TYPE, NULL); 00485 00486 mm->accel_group = gtk_accel_group_new (); 00487 mm->action_group = gtk_action_group_new ("MainMenu"); 00488 00489 mm->layer_view_pos = 0; 00490 mm->layer_pick_pos = 0; 00491 mm->route_style_pos = 0; 00492 mm->n_layer_views = 0; 00493 mm->n_layer_picks = 0; 00494 mm->n_route_styles = 0; 00495 mm->layer_view_shell = NULL; 00496 mm->layer_pick_shell = NULL; 00497 mm->route_style_shell = NULL; 00498 00499 mm->special_key_cb = special_key_cb; 00500 mm->action_cb = action_cb; 00501 mm->actions = NULL; 00502 mm->popup_table = g_hash_table_new (g_str_hash, g_str_equal); 00503 00504 return GTK_WIDGET (mm); 00505 } 00506 00510 void 00511 ghid_main_menu_add_resource (GHidMainMenu *menu, const Resource *res) 00512 { 00513 ghid_main_menu_real_add_resource (menu, GTK_MENU_SHELL (menu), res); 00514 } 00515 00519 void 00520 ghid_main_menu_add_popup_resource (GHidMainMenu *menu, const char *name, 00521 const Resource *res) 00522 { 00523 GtkWidget *new_menu = gtk_menu_new (); 00524 g_object_ref_sink (new_menu); 00525 ghid_main_menu_real_add_resource (menu, GTK_MENU_SHELL (new_menu), res); 00526 g_hash_table_insert (menu->popup_table, (gpointer) name, new_menu); 00527 gtk_widget_show_all (new_menu); 00528 } 00529 00533 GtkMenu * 00534 ghid_main_menu_get_popup (GHidMainMenu *menu, const char *name) 00535 { 00536 return g_hash_table_lookup (menu->popup_table, name); 00537 } 00538 00539 00551 void 00552 ghid_main_menu_update_toggle_state (GHidMainMenu *menu, 00553 void (*cb) (GtkAction *, 00554 const char *toggle_flag, 00555 const char *active_flag)) 00556 { 00557 GList *list; 00558 for (list = menu->actions; list; list = list->next) 00559 { 00560 Resource *res = g_object_get_data (G_OBJECT (list->data), "resource"); 00561 const char *tf = g_object_get_data (G_OBJECT (list->data), 00562 "checked-flag"); 00563 const char *af = g_object_get_data (G_OBJECT (list->data), 00564 "active-flag"); 00565 g_signal_handlers_block_by_func (G_OBJECT (list->data), 00566 menu->action_cb, res); 00567 cb (GTK_ACTION (list->data), tf, af); 00568 g_signal_handlers_unblock_by_func (G_OBJECT (list->data), 00569 menu->action_cb, res); 00570 } 00571 } 00572 00576 void 00577 ghid_main_menu_install_layer_selector (GHidMainMenu *mm, 00578 GHidLayerSelector *ls) 00579 { 00580 GList *children, *iter; 00581 00582 /* @layerview */ 00583 if (mm->layer_view_shell) 00584 { 00585 /* Remove old children */ 00586 children = gtk_container_get_children 00587 (GTK_CONTAINER (mm->layer_view_shell)); 00588 for (iter = g_list_nth (children, mm->layer_view_pos); 00589 iter != NULL && mm->n_layer_views > 0; 00590 iter = g_list_next (iter), mm->n_layer_views --) 00591 gtk_container_remove (GTK_CONTAINER (mm->layer_view_shell), 00592 iter->data); 00593 g_list_free (children); 00594 00595 /* Install new ones */ 00596 mm->n_layer_views = ghid_layer_selector_install_view_items 00597 (ls, mm->layer_view_shell, mm->layer_view_pos); 00598 } 00599 00600 /* @layerpick */ 00601 if (mm->layer_pick_shell) 00602 { 00603 /* Remove old children */ 00604 children = gtk_container_get_children 00605 (GTK_CONTAINER (mm->layer_pick_shell)); 00606 for (iter = g_list_nth (children, mm->layer_pick_pos); 00607 iter != NULL && mm->n_layer_picks > 0; 00608 iter = g_list_next (iter), mm->n_layer_picks --) 00609 gtk_container_remove (GTK_CONTAINER (mm->layer_pick_shell), 00610 iter->data); 00611 g_list_free (children); 00612 00613 /* Install new ones */ 00614 mm->n_layer_picks = ghid_layer_selector_install_pick_items 00615 (ls, mm->layer_pick_shell, mm->layer_pick_pos); 00616 } 00617 } 00618 00622 void 00623 ghid_main_menu_install_route_style_selector (GHidMainMenu *mm, 00624 GHidRouteStyleSelector *rss) 00625 { 00626 GList *children, *iter; 00627 /* @routestyles */ 00628 if (mm->route_style_shell) 00629 { 00630 /* Remove old children */ 00631 children = gtk_container_get_children 00632 (GTK_CONTAINER (mm->route_style_shell)); 00633 for (iter = g_list_nth (children, mm->route_style_pos); 00634 iter != NULL && mm->n_route_styles > 0; 00635 iter = g_list_next (iter), mm->n_route_styles --) 00636 gtk_container_remove (GTK_CONTAINER (mm->route_style_shell), 00637 iter->data); 00638 g_list_free (children); 00639 /* Install new ones */ 00640 mm->n_route_styles = ghid_route_style_selector_install_items 00641 (rss, mm->route_style_shell, mm->route_style_pos); 00642 } 00643 } 00644 00648 GtkAccelGroup * 00649 ghid_main_menu_get_accel_group (GHidMainMenu *menu) 00650 { 00651 return menu->accel_group; 00652 } 00653