]> matita.cs.unibo.it Git - helm.git/blob - helm/helmpot/guiGTK.c
added preliminary support for maction
[helm.git] / helm / helmpot / guiGTK.c
1 /*
2  * Copyright (C) 2000, Luca Padovani <luca.padovani@cs.unibo.it>.
3  * 
4  * This file is part of HelmPot, a minimal browser for HELM.
5  * 
6  * HelmPot is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * 
11  * HelmPot 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 HelmPot; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  * 
20  * For details, see the HelmPot World-Wide-Web page,
21  * http://cs.unibo.it/helm/helmview, or send a mail to
22  * <luca.padovani@cs.unibo.it>
23  */
24
25 #include <config.h>
26
27 #include <glib.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #if HAVE_UNISTD_H
32 # include <sys/types.h>
33 # include <sys/stat.h>
34 # include <fcntl.h>
35 # include <unistd.h>
36 #endif
37 #include <gtk/gtk.h>
38 #include <gdk/gdkkeysyms.h>
39
40 #include "gtkmathview.h"
41 #include "guiGTK.h"
42
43 #define XLINK_NS_URI "http://www.w3.org/1999/xlink"
44
45 #define pot_width 16
46 #define pot_height 16
47 PRIVATE unsigned char pot_bits[] = {
48    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x07,
49    0x00, 0x00, 0xe6, 0x2f, 0x34, 0x5f, 0xdc, 0x5f, 0xf8, 0x3f, 0xf8, 0x3f,
50    0xf0, 0x1f, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00};
51
52 #define pot_mask_width 16
53 #define pot_mask_height 16
54 static unsigned char pot_mask_bits[] = {
55    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xe0, 0x0f, 0xe0, 0x0f,
56    0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f,
57    0xfc, 0x7f, 0xf8, 0x3f, 0xf0, 0x1f, 0x00, 0x00};
58
59 PRIVATE GtkWidget* window;
60 PRIVATE GtkWidget* main_area;
61 PRIVATE GtkWidget* scrolled_area;
62 PRIVATE GtkWidget* status_bar;
63 PRIVATE GtkMenuItem* kerning_item;
64 PRIVATE GtkMenuItem* anti_aliasing_item;
65 PRIVATE GtkMenuItem* font_size_item;
66 PRIVATE gchar* file_name = NULL;
67
68 PRIVATE GdkCursor* pot;
69
70 PRIVATE guint statusbar_context;
71
72 PRIVATE void create_widget_set(void);
73 PRIVATE GtkWidget* get_main_menu(void);
74 PRIVATE void options_font_size(GtkWidget*, guint);
75 PRIVATE void options_font_manager(GtkWidget*, guint);
76 PRIVATE void options_verbosity(GtkWidget*, guint);
77 PRIVATE void options_kerning(GtkWidget*, gpointer);
78 PRIVATE void options_anti_aliasing(GtkWidget*, gpointer);
79 PRIVATE void help_about(GtkWidget*, gpointer);
80 PRIVATE void save_as(GtkWidget*);
81 PRIVATE void export_to_ps(GtkWidget*);
82
83 PRIVATE GtkItemFactoryEntry menu_items[] = {
84   { "/_File",                          NULL,         NULL,          0, "<Branch>" },
85   { "/File/Save _As...",               NULL,         save_as,       0, NULL },
86   { "/File/_Export to PostScript...",  NULL,         export_to_ps,  0, NULL },
87   { "/File/sep1",                      NULL,         NULL,          0, "<Separator>" },
88   { "/File/_Quit",                     "<control>Q", gtk_main_quit, 0, NULL },
89
90   { "/_Options",                       NULL, NULL,                  0,  "<Branch>" },
91   { "/Options/Default _Font Size",     NULL, NULL,                  0,  "<Branch>" },
92   { "/Options/Default Font Size/8pt",  NULL, options_font_size,     8,  "<RadioItem>" },
93   { "/Options/Default Font Size/10pt", NULL, options_font_size,     10, "/Options/Default Font Size/8pt" },
94   { "/Options/Default Font Size/12pt", NULL, options_font_size,     12, "/Options/Default Font Size/8pt" },
95   { "/Options/Default Font Size/14pt", NULL, options_font_size,     14, "/Options/Default Font Size/8pt" },
96   { "/Options/Default Font Size/18pt", NULL, options_font_size,     18, "/Options/Default Font Size/8pt" },
97   { "/Options/Default Font Size/24pt", NULL, options_font_size,     24, "/Options/Default Font Size/8pt" },
98   { "/Options/Font Manager",           NULL, NULL,                  0,  "<Branch>" },
99   { "/Options/Font Manager/_GTK",      NULL, options_font_manager,  0,  "<RadioItem>" },
100   { "/Options/Font Manager/_Type 1",   NULL, options_font_manager,  1,  "/Options/Font Manager/GTK" },
101   { "/Options/Verbosity",              NULL, NULL,                  0,  "<Branch>" },
102   { "/Options/Verbosity/_Errors",      NULL, options_verbosity,     0,  "<RadioItem>" },
103   { "/Options/Verbosity/_Warnings",    NULL, options_verbosity,     1,  "/Options/Verbosity/Errors" },
104   { "/Options/Verbosity/_Info",        NULL, options_verbosity,     2,  "/Options/Verbosity/Errors" },
105   { "/Options/Verbosity/_Debug",       NULL, options_verbosity,     3,  "/Options/Verbosity/Errors" },
106   { "/Options/sep1",                   NULL, NULL,                  0,  "<Separator>" },
107   { "/Options/_Kerning",               NULL, options_kerning,       0,  "<ToggleItem>" },
108   { "/Options/_Anti Aliasing",         NULL, options_anti_aliasing, 0,  "<ToggleItem>" },
109
110   { "/_Help" ,        NULL,         NULL,          0, "<LastBranch>" },
111   { "/Help/About...", NULL,         help_about,    0, NULL }
112 };
113
114 PRIVATE void
115 quick_message(const gchar* msg)
116 {
117   GtkWidget* dialog;
118   GtkWidget* label;
119   GtkWidget* okay_button;
120      
121   /* Create the widgets */
122      
123   dialog = gtk_dialog_new();
124   label = gtk_label_new (msg);
125   okay_button = gtk_button_new_with_label("OK");
126
127   gtk_widget_set_usize(dialog, 300, 100);
128
129   /* Ensure that the dialog box is destroyed when the user clicks ok. */
130      
131   gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
132                              GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT(dialog));
133   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
134                      okay_button);
135   
136   /* Add the label, and show everything we've added to the dialog. */
137   
138   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label);
139   gtk_widget_show_all (dialog);
140 }
141
142 PRIVATE void
143 load_error_msg(const gchar* name)
144 {
145   gchar* msg = g_strdup_printf("Could not load\n`%s'", name);
146   quick_message(msg);
147   g_free(msg);
148 }
149
150 PRIVATE void
151 save_error_msg(const gchar* name)
152 {
153   gchar* msg = g_strdup_printf("Could not save\n`%s'", name);
154   quick_message(msg);
155   g_free(msg);
156 }
157
158 void
159 GUI_init(gint* argc, gchar*** argv, gchar* title, guint width, guint height, GtkFunction f, guint32 timeout)
160 {
161   GdkPixmap* source;
162   GdkPixmap* mask;
163
164   GdkColor fg = { 0, 65535, 65535, 65535 };
165   GdkColor bg = { 0, 0, 0, 0 };
166
167   gtk_init(argc, argv);
168
169   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
170   gtk_window_set_title(GTK_WINDOW(window), title);
171   gtk_window_set_default_size(GTK_WINDOW(window), width, height);
172   gtk_signal_connect(GTK_OBJECT(window), "delete_event", (GtkSignalFunc) gtk_main_quit, NULL);
173   create_widget_set();
174
175   gtk_widget_show(window);
176
177   gtk_timeout_add(timeout, f, NULL);
178
179   source = gdk_bitmap_create_from_data (NULL, pot_bits,
180                                         pot_width, pot_height);
181   mask = gdk_bitmap_create_from_data (NULL, pot_mask_bits,
182                                       pot_mask_width, pot_mask_height);
183   pot = gdk_cursor_new_from_pixmap (source, mask, &fg, &bg, 8, 8);
184   gdk_pixmap_unref (source);
185   gdk_pixmap_unref (mask);  
186 }
187
188 void
189 GUI_uninit()
190 {
191 }
192
193 int
194 GUI_load_document(const gchar* name)
195 {
196   GtkMathView* math_view;
197   GtkMathViewClass* klass;
198
199   g_return_val_if_fail(name != NULL, -1);
200   g_return_val_if_fail(main_area != NULL, -1);
201   g_return_val_if_fail(GTK_IS_MATH_VIEW(main_area), -1);
202
203   math_view = GTK_MATH_VIEW(main_area);
204   g_return_val_if_fail(math_view != NULL, -1);
205
206   klass = (GtkMathViewClass*) gtk_type_class(gtk_math_view_get_type());
207   g_return_val_if_fail(klass != NULL, -1);
208
209   gdk_window_set_cursor (main_area->window, pot);  
210
211   if (!gtk_math_view_load(math_view, name)) {
212     load_error_msg(name);
213     return -1;
214   }
215
216   gtk_statusbar_pop(GTK_STATUSBAR(status_bar), statusbar_context);
217   if (strlen(name) > 40) name += strlen(name) - 40;
218   gtk_statusbar_push(GTK_STATUSBAR(status_bar), statusbar_context, name);
219
220   /*gdk_window_set_cursor(gtk_widget_get_parent_window(main_area), klass->normal_cursor);*/
221
222   if (file_name != NULL) g_free(file_name);
223   file_name = g_strdup(name);
224
225   return 0;
226 }
227
228 void
229 GUI_unload_document()
230 {
231   GtkMathView* math_view;
232
233   g_return_if_fail(main_area != NULL);
234   g_return_if_fail(GTK_IS_MATH_VIEW(main_area));
235
236   math_view = GTK_MATH_VIEW(main_area);
237
238   gtk_math_view_unload(math_view);
239 }
240
241 void
242 GUI_run()
243 {
244   gtk_main();
245 }
246
247 PRIVATE void
248 options_font_size(GtkWidget* widget, guint size)
249 {
250   GtkMathView* math_view;
251
252   g_return_if_fail(main_area != NULL);
253   g_return_if_fail(GTK_IS_MATH_VIEW(main_area));
254
255   math_view = GTK_MATH_VIEW(main_area);
256
257   gtk_math_view_set_font_size(math_view, size);
258 }
259
260 PRIVATE void
261 options_font_manager(GtkWidget* widget, guint id)
262 {
263   GtkMathView* math_view;
264
265   g_return_if_fail(main_area != NULL);
266   g_return_if_fail(GTK_IS_MATH_VIEW(main_area));
267   
268   math_view = GTK_MATH_VIEW(main_area);
269
270   gtk_math_view_set_font_manager_type(math_view, id);
271 }
272
273 PRIVATE void
274 options_anti_aliasing(GtkWidget* widget, gpointer data)
275 {
276   gboolean aa = gtk_math_view_get_anti_aliasing(GTK_MATH_VIEW(main_area));
277   gtk_math_view_set_anti_aliasing(GTK_MATH_VIEW(main_area), !aa);
278 }
279
280 PRIVATE void
281 options_kerning(GtkWidget* widget, gpointer data)
282 {
283   gboolean k = gtk_math_view_get_kerning(GTK_MATH_VIEW(main_area));
284   gtk_math_view_set_kerning(GTK_MATH_VIEW(main_area), !k);
285 }
286
287 PRIVATE void
288 options_verbosity(GtkWidget* widget, guint level)
289 {
290   gtk_math_view_set_log_verbosity(GTK_MATH_VIEW(main_area), level);
291 }
292
293 PRIVATE void
294 help_about(GtkWidget* widget, gpointer data)
295 {
296   GtkWidget* dialog;
297   GtkWidget* label;
298   GtkWidget* ok;
299
300   dialog = gtk_dialog_new();
301   label = gtk_label_new("\n    HELM PoT    \n    Copyright (C) 2001 Luca Padovani    \n");
302   ok = gtk_button_new_with_label("Close");
303
304   gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
305                              GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) dialog);
306   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
307                      ok);
308
309   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label);
310
311   gtk_widget_show_all (dialog);
312 }
313
314 PRIVATE void
315 export_filename(GtkFileSelection* selector, gpointer user_data)
316 {
317   FILE* f;
318   GtkMathView* math_view;
319   gchar* selected_filename;
320   
321   selected_filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(user_data));
322
323   math_view = GTK_MATH_VIEW(main_area);
324
325   f = fopen(selected_filename, "wt");
326   if (f == NULL) {
327     save_error_msg(selected_filename);
328     /*g_free(selected_filename);*/
329     return;
330   }
331
332   gtk_math_view_export_to_postscript(math_view,
333                                      (21 * SCALED_POINTS_PER_CM) / SCALED_POINTS_PER_PX,
334                                      (29 * SCALED_POINTS_PER_CM) / SCALED_POINTS_PER_PX,
335                                      SCALED_POINTS_PER_IN / SCALED_POINTS_PER_PX,
336                                      SCALED_POINTS_PER_IN / SCALED_POINTS_PER_PX,
337                                      FALSE,
338                                      f);
339
340   fclose(f);
341   /*g_free(selected_filename);*/
342 }
343
344 PRIVATE void
345 save_filename(GtkFileSelection* selector, gpointer user_data)
346 {
347   FILE* source;
348   FILE* dest;
349   gchar* buffer;
350   gchar* selected_filename;
351
352   if (file_name == NULL) return;
353
354   source = fopen(file_name, "rt");
355   if (source == NULL) {
356     load_error_msg(file_name);
357     return;
358   }
359
360   selected_filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(user_data));
361   if (selected_filename == NULL) return;
362
363   dest = fopen(selected_filename, "wt");
364   if (dest == NULL) {
365     save_error_msg(selected_filename);
366     /*g_free(selected_filename);*/
367     return;
368   }
369
370   /*g_free(selected_filename);*/
371
372   buffer = g_new(gchar, 2048);
373   while (!feof(source)) {
374     size_t n = fread(buffer, sizeof(gchar), 2048, source);
375     fwrite(buffer, sizeof(gchar), n, dest);
376   }
377
378   g_free(buffer);
379   fclose(source);
380   fclose(dest);
381 }
382
383 PRIVATE void
384 file_dialog(const gchar* title, GtkSignalFunc f)
385 {
386   GtkWidget* fs;
387
388   g_return_if_fail(title != NULL);
389   g_return_if_fail(f != NULL);
390
391   fs = gtk_file_selection_new(title);
392
393   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
394                       "clicked", f, fs);
395                              
396   /* Ensure that the dialog box is destroyed when the user clicks a button. */
397      
398   gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
399                              "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
400                              (gpointer) fs);
401
402   gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button),
403                              "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
404                              (gpointer) fs);
405      
406   /* Display that dialog */
407      
408   gtk_widget_show (fs);
409 }
410
411 PRIVATE void
412 export_to_ps(GtkWidget* widget)
413 {
414   file_dialog("Export to PostScript", export_filename);
415 #if 0
416   static GList* items = NULL;
417
418   GtkWidget* dialog;
419   GtkWidget* tmp;
420
421   if (items == NULL) {
422     items = g_list_append(items, "A4");
423     items = g_list_append(items, "A5");
424   }
425
426   dialog = gtk_dialog_new();
427   tmp = gtk_label_new("Paper size");
428   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), tmp);
429   tmp = gtk_combo_new();
430   gtk_combo_set_popdown_strings(GTK_COMBO(tmp), items);
431   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), tmp);
432 #if 0
433   tmp = gtk_check_button_new_with_label("Disable Colors");
434   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), tmp);
435 #endif
436
437   tmp = gtk_button_new_with_label("OK");
438   gtk_signal_connect_object(GTK_OBJECT(tmp), "clicked", GTK_SIGNAL_FUNC(export_to_ps_get_file_name), dialog);
439   gtk_signal_connect_object(GTK_OBJECT(tmp), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), dialog);
440   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), tmp);
441   tmp = gtk_button_new_with_label("Cancel");
442   gtk_signal_connect_object(GTK_OBJECT(tmp), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), dialog);
443   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), tmp);
444
445   gtk_widget_show_all(dialog);
446 #endif
447 }
448
449 PRIVATE void
450 save_as(GtkWidget* widget)
451 {
452   file_dialog("Save As...", save_filename);
453 }
454
455 PRIVATE void
456 selection_changed(GtkMathView* math_view, mDOMNodeRef node)
457 {
458   g_return_if_fail(math_view != NULL);
459   g_return_if_fail(GTK_IS_MATH_VIEW(math_view));
460   gtk_math_view_set_selection(math_view, node);
461 }
462
463 PRIVATE void
464 jump(GtkMathView* math_view, mDOMNodeRef node)
465 {
466   mDOMStringRef href;
467
468   g_return_if_fail(node != NULL);
469   href = mdom_node_get_attribute_ns(node, DOM_CONST_STRING("href"), XLINK_NS_URI);
470
471   if (href != NULL) {
472     pid_t pid;
473
474     g_assert(main_area != NULL);
475     gdk_window_set_cursor (main_area->window, pot);  
476
477     pid = fork();
478     if (pid == -1) exit(-1);
479     if (pid == 0) {
480       gchar* open_url = g_strdup_printf("openURL(%s,cic)", href);
481       gint fd;
482
483       close(0);
484       close(1);
485       close(2);
486
487       fd = open("/dev/null", O_RDWR);
488       dup(fd);
489       dup(fd);
490
491       execlp("netscape", "netscape", "-noraise", "-remote", open_url, NULL);
492       perror("exec failed:");
493       exit(-1);
494     }
495     mdom_string_free(href);
496   }
497 }
498
499 PRIVATE void
500 clicked(GtkMathView* math_view, gpointer user_data)
501 {
502   if (gtk_math_view_get_action(math_view) != NULL)
503     gtk_math_view_action_toggle(math_view);
504 }
505
506 PRIVATE void
507 create_widget_set()
508 {
509   GtkWidget* main_vbox;
510   GtkWidget* menu_bar;
511
512   main_vbox = gtk_vbox_new(FALSE, 1);
513   gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
514   gtk_container_add(GTK_CONTAINER(window), main_vbox);
515   gtk_widget_show(main_vbox);
516
517   menu_bar = get_main_menu();
518   gtk_box_pack_start(GTK_BOX(main_vbox), menu_bar, FALSE, TRUE, 0);
519   gtk_widget_show(menu_bar);
520
521   main_area = gtk_math_view_new(NULL, NULL);
522   gtk_widget_show(main_area);
523
524   gtk_signal_connect_object (GTK_OBJECT (main_area),
525                              "selection_changed", GTK_SIGNAL_FUNC (selection_changed),
526                              (gpointer) main_area);
527
528   gtk_signal_connect_object (GTK_OBJECT (main_area),
529                              "jump", GTK_SIGNAL_FUNC(jump),
530                              (gpointer) main_area);
531
532   gtk_signal_connect_object (GTK_OBJECT (main_area), 
533                              "clicked", GTK_SIGNAL_FUNC(clicked),
534                              (gpointer) main_area);
535                              
536   scrolled_area = gtk_scrolled_window_new(NULL, NULL);
537   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_area),
538                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
539   gtk_widget_show(scrolled_area);
540   gtk_container_add(GTK_CONTAINER(scrolled_area), main_area);
541   gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_area, TRUE, TRUE, 0);
542
543   status_bar = gtk_statusbar_new();
544   gtk_widget_show(status_bar);
545   gtk_box_pack_start(GTK_BOX(main_vbox), status_bar, FALSE, TRUE, 0);
546   statusbar_context = gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar), "filename");
547
548   gtk_widget_show(main_vbox);
549
550   if (gtk_math_view_get_anti_aliasing(GTK_MATH_VIEW(main_area)))
551     gtk_menu_item_activate(anti_aliasing_item);
552
553   if (gtk_math_view_get_kerning(GTK_MATH_VIEW(main_area)))
554     gtk_menu_item_activate(kerning_item);
555
556   gtk_math_view_set_font_size(GTK_MATH_VIEW(main_area), DEFAULT_FONT_SIZE);
557   gtk_menu_item_activate(font_size_item);
558 }
559
560 GtkWidget*
561 get_main_menu()
562 {
563   GtkItemFactory* item_factory;
564   GtkAccelGroup* accel_group;
565   GtkWidget* menu_item;
566
567   gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
568
569   accel_group = gtk_accel_group_new();
570
571   item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", accel_group);
572
573   gtk_item_factory_create_items(item_factory, nmenu_items, menu_items, NULL);
574
575   gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
576
577   menu_item = gtk_item_factory_get_widget(item_factory, "/Options/Kerning");
578   kerning_item = GTK_MENU_ITEM(menu_item);
579
580   menu_item = gtk_item_factory_get_widget(item_factory, "/Options/Anti Aliasing");
581   anti_aliasing_item = GTK_MENU_ITEM(menu_item);
582
583   /* !!!BEWARE!!! the default font size must be kept aligned with the definition
584    * in defs.h
585    */
586   menu_item = gtk_item_factory_get_widget(item_factory, "/Options/Default Font Size/14pt");
587   font_size_item = GTK_MENU_ITEM(menu_item);
588
589   return gtk_item_factory_get_widget(item_factory, "<main>");
590 }