\def\cweb.{\caps{CWEB}} @* Menu bars and pull-down menus. These are a set of functions that can be used to implement a menubar with pull-down menus. They have been written to help implement the ``Skew Diagram Manipulation into Mathematical Pictures Interface'' project by James Faux. It uses the `X Windows' standard libraries, with no help from the `XToolkit' libraries. @ All C programs are required to include specific standard header files and `X Windows' programs are no exception. @h @h @h @h @ There is one general header file \.{pictures.h} that does all the includes needed for the graphical interface compilation units, in a proper order. @h "pictures.h" @ The construction of the header file \.{menus.h} occurs here. It includes all the global definition, macro, typedef and function declaration modules that are mentioned below. @( menu.h @>= @< Global definitions and macro definitions @>@; @< Global typedefs @>@; @< Global function declarations @>@; @ Local functions will precede the globally visible ones (which are defined in unnamed section); this slightly facilitates a top-down presentation of the functions. @d local static @f local static @c @< Local function declarations @>@; @< Global variable declarations @>@; @< Local variable declarations @>@; @< Local functions @>@; @ For clarity we define here the terminology used for menus. A menu bar is a wide but shallow rectangular area at the top of a window that contains the menu titles. When depressing the mouse button when the pointer is on one of these titles, a pull-down menu appears below it that contains a vertical list of menu entries. Having pressed the mouse button in one of these titles, the other menus can be invoked by entering a different menu title. To select the entry, the mouse pointer must be run down the menu entries and released, when covering the required function. Since all menu structures are fixed during the program, no harm is done by placing arbitrary limits on the number of menu titles, on the number of entries per menu, and on the number of characters for each menu title or entry. The following definitions define these limits, (called |MAX_NUM_MENUS|, |MAX_MENU_LEN|, and |MAX_MENU_STRING_LEN| respectively), and can be increased if the menus get so elaborate as to require this. @< Global definitions and macro definitions @>= #define MAX_NUM_MENUS 10 #define MAX_MENU_LEN 10 #define MAX_MENU_STRING_LEN 20 @ Each individual menu entry has an associated structure of type |m_entry|. Since the menu entry will be highlighted when the mouse moves over it, it is convenient to define a separate window (i.e., rectangle) for each entry, and its window identifier is kept in the |win| field; the |title| field holds the name of the entry. @< Global typedefs @>= typedef struct m_entry { win_p win; char title[MAX_MENU_STRING_LEN]; void (*f) (win_p); struct m_list* parent; }* p_m_entry; @ The data associated with a menu title is stored in the structure |m_list|. Like menu entries, a separate window, |h_win|, is reserved for the title, and the string forming the title is called |header| in this case. There is a window, |m_win|, that contains all the whole list of menu entry windows; this will limit the expose events for the windows below to a minimum. Furthermore the coordinate of the right edge is recorded in |end| and the width of the menu entries (which will be calculated to be wide enough to hold the widest entry) in |width|. The boolean |open| tells whether the menu is currently dropped-down, and the |menu| field contains the data for the individual menu entries. The final |parent| field is a link back to the menu bar containing the current title, so that we can check the status of that menu bar when the mouse enters a menu title. @< Global typedefs @>= typedef struct m_list { win_p h_win, m_win; int end; int width; bool open; char header[MAX_MENU_STRING_LEN]; p_m_entry menu[MAX_MENU_LEN]; struct m_bar* parent; } *p_m_list; @ Finally the data structure for the complete menu bar. It too has an associated window, which surrounds all those of the menu titles; no further data other than the records for the menu titles is recorded. @< Global typedefs @>= typedef struct m_bar { win_p win; bool pressed; p_m_list bar[MAX_NUM_MENUS]; win_p parent; /* index of outer window on which menu is mapped */ } *p_m_bar; @ Whenever we are handling a menu selection we keep track of this centrally, so that we can quickly clear the menu state when the mouse button is released (the window in which this happens can be arbitrary, to it gives us no hint about the proper menu to clear). @< Local var... @>= bool in_menu=false; p_m_bar current_menu_bar; p_m_list current_menu_list; @ To transmit the structure of the menu tree, we can use lists of strings in a regular fashion. The type |menu_tree| can be statically initialised in an obvious way to transmit the information to the routines defined below. After the last real menu, one with the empty string as title should be specified so that the end can be recognised. Each sublist of menu entries will be similarly terminated by a null pointer (which we provide explicitly, to have the compiler check that there is indeed a slot available for the sentinel). @< Global typedefs @>= typedef struct { char title[MAX_MENU_STRING_LEN]; struct { char* name; void (*f)(win_p); } entry[MAX_MENU_LEN]; } menu_tree[MAX_NUM_MENUS]; @ For the moment we define three menu trees, whose structures are given here. The first being the menus for the main window, second being the menus for the diagram windows and the last, being for the picture window menus. @< Local variable declarations @>= local menu_tree main_m = { @/{ "File" , { { "Box Size", main_menu_box_size_func } , { "Help", main_menu_help_func } , { "Quit", main_menu_quit_func } , { NULL, NULL} } }, @/{ "Functions" , { { "New Diagram", main_menu_new_diag_func } , { "Create Picture", main_menu_create_pict_func } , { "Robinson-Schensted", main_menu_robin_func } , { NULL, NULL} }, }, @/{""} @/}; local menu_tree diag_m = { @/{ "Diagram" , { { "Select", diag_menu_select_func } , { "Deselect", diag_menu_deselect_func } , { "Clear", diag_menu_clear_func } , { "Duplicate", diag_menu_dup_func } , { "Iconise", diag_menu_iconise_func } , { "Done", diag_menu_done_func } , { NULL, NULL } } }, @/{ "Transformations" , { { "Add", diag_menu_add_func } , { "Remove BR", diag_menu_remove_br_func } , { "Remove TL", diag_menu_remove_tl_func } , { "Transpose", diag_menu_trans_func } , { "Negate", diag_menu_neg_func } , { "Anti-Diagonal", diag_menu_anti_func } , { "Query", diag_menu_query_func } , { "Create Picture", diag_menu_create_picture_func } , { NULL, NULL } } }, @/{ "Movement" , { { "Find", diag_menu_find_func } , { "Normalise", diag_menu_origin_func } , { NULL, NULL } } }, @/{""} @/}; local menu_tree pict_m ={ @/{ "Picture" , { { "Select", pict_menu_select_func } , { "Deselect", pict_menu_deselect_func } , { "Clear", pict_menu_clear_func } , { "Deconstruct", pict_menu_decons_func } , { "Duplicate", pict_menu_dup_func } , { "Iconise", pict_menu_iconise_func } , { "Done", pict_menu_done_func } , { NULL, NULL} } }, @/{ "Transformations" , { { "Inverse", pict_menu_inv_func } , { "Negate", pict_menu_neg_func } , { "Transpose", pict_menu_trans_func } , { "Robinson-Schensted", pict_menu_robin_func } , { "Inverse R.-S.", pict_menu_inv_RS_func } , { "Schutzenberger", pict_menu_schutz_func } , { "Transpose both sides", pict_menu_full_transp_func } , { NULL, NULL} } }, @/{ "Movement" , { { "Find domain", pict_menu_d_find_func } , { "Find Image", pict_menu_i_find_func } , { "Normalise domain", pict_menu_d_origin_func } , { "Normalise image", pict_menu_i_origin_func } , { NULL, NULL } } }, @/{""} @/}; @* Functions to create a menu. The function |create_main_menus|, |create_diag_menus| and |create_pict_menus| are used to create the menu structures, and map the menu bar window with the appropriate associated setup. Presently no menu tree is supplied explicitly. @< Global function declarations @>= extern p_m_bar create_main_menus(win_p, unsigned int); extern p_m_bar create_diag_menus(win_p, unsigned int); extern p_m_bar create_pict_menus(win_p, unsigned int); @~We call the internal function |build_menus| that does have an addition |menu_tree| parameter. This parameter depends on which menus are required. @c p_m_bar create_main_menus(win_p parent, unsigned int new_window_width) { return build_menus(parent, main_m, new_window_width); @+} p_m_bar create_diag_menus(win_p parent, unsigned int new_window_width) { return build_menus(parent, diag_m, new_window_width); @+} p_m_bar create_pict_menus(win_p parent, unsigned int new_window_width) { return build_menus(parent, pict_m, new_window_width); @+ } @ From the menu tree passed to it, this function creates the menubar window, the header windows and the menu entry windows but only maps the first two. @< Local functions @>= local p_m_bar build_menus (win_p parent, menu_tree m, unsigned int new_window_width) { int i, cur_pos=1; /* horizontal position on menu bar */ p_m_bar mb = new(@[struct m_bar@]); @< Create and map the menu bar window, setting its attibutes @> for (i = 0; m[i].title[0]!='\0'; ++i) @< Install menu number |i| @> mb->bar[i] = NULL; /* sentinel pointer */ mb->pressed = false; mb->parent=parent; return mb; } @ @< Create and map the menu bar... @>= { mb->win = make_window(menu_bar_win,parent, 0, 0, new_window_width-4, bf_height+8); XDefineCursor(display, mb->win->w,XCreateFontCursor(display,XC_hand2)); map_window(mb->win); } @~ @< Global var... @>= event_handler_table menu_bar_handlers=@| { 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; @ This module creates the menu title button for each menu title. The position for each button is found by looking at the end component of the previous buttons structure. A but is added to make it look nice and then the next button is mapped. @< Install menu number |i| @>= { p_m_list menu= mb->bar[i] = new(@[struct m_list@]); strcpy(menu->header,m[i].title); { int string_width = XTextWidth(button_font,menu->header,strlen(menu->header)); menu->h_win = make_thin_window(menu_title_win ,mb->win,cur_pos,1,string_width+6,bf_height+4); menu->h_win->data.menu=menu; /* menu list */ menu->end = cur_pos += string_width+10; } menu->parent=mb; map_window(menu->h_win); create_menu_list(parent, mb, m, i); } @~For menu titles we set the |ButtonReleaseMask|, and the corresponding event will occur if the mouse button is reased either in a menu title or in a window which does not set this mask; this event is handled directly in |event_handler| (removing the pull-down menu), so that no further handler is needed here. @< Global var... @>= event_handler_table menu_title_handlers=@| { ExposureMask | EnterWindowMask @|| ButtonPressMask | ButtonReleaseMask | OwnerGrabButtonMask , menu_title_expose, menu_title_enter, NULL, menu_title_press , NULL, NULL, NULL, NULL }; @ This function creates the menu windows that display that actual entries. The |titles| array holds all the entries of all the pull-down menus. It first find the maximum length that the entry string could be. Then the windows are created and mapped one below another, with the origin of the first below the menu header. Finally the string is copied into the menu structure and the menu open flag is set to |True|. @< Local function declarations @>= local void create_menu_list(win_p, p_m_bar, menu_tree, int); @~ @c local void create_menu_list (win_p parent, p_m_bar mb, menu_tree m, int i) { p_m_list menu= mb->bar[i]; int j, longest = 0, string_width; for (j = 0; m[i].entry[j].name != NULL; j++) { p_m_entry e = menu->menu[j]=new(@[struct m_entry@]); char* name=m[i].entry[j].name; string_width = XTextWidth(button_font,name,strlen(name)); if (string_width>longest) longest = string_width; strcpy(e->title,name); e->f=m[i].entry[j].f; e->parent=menu; } menu->menu[j]=NULL; /* sentinel */ menu->width = longest; @< Create a base window for the menu list @> for (j = 0; m[i].entry[j].name != NULL; j++) @< Create a window for this menu entry @> menu->open = false; } @ @< Create a base window for the menu list @>= { menu->m_win = make_thin_window(menu_list_win,parent, i == 0 ? 0 : mb->bar[i-1]->end, bf_height+8, menu->width+12, (bf_height+5) * j+1); } @ The menu entries are create and mapped onto the menu list window, but they will not become visible until that window is mapped. @d MENU_ENTRY_EVENT_MASK (ExposureMask | ButtonReleaseMask | EnterWindowMask @/ | LeaveWindowMask) @< Create a window for this menu entry @>= { menu->menu[j]->win = make_invisible_window(menu_entry_win,menu->m_win ,0,(bf_height+5)*j,menu->width+10,bf_height+4); menu->menu[j]->win->data.menu_entry=menu->menu[j]; map_window(menu->menu[j]->win); } @~ @< Global var... @>= event_handler_table menu_list_handlers=@| { 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; event_handler_table menu_entry_handlers=@| { ExposureMask | EnterWindowMask | LeaveWindowMask | ButtonReleaseMask , menu_entry_expose, menu_entry_enter, menu_entry_leave, NULL , menu_entry_release, NULL, NULL, NULL }; @* Menu events. We list here the events handlers for the menu bar, titles, and entries. We start with a function that is called (directly from |event_handler|) whenever the left mouse button is released. @< Global fun... @>= void release_menu (void); @~@c void release_menu (void) { if (in_menu) { current_menu_bar->pressed=in_menu=false; remove_menu_window(current_menu_list); remove_highlighted_menu(current_menu_list); } } @ @< Global fun... @>= void menu_title_expose (win_p); @~@c void menu_title_expose (win_p p) { remove_highlighted_menu(p->data.menu); } @ @< Global fun... @>= void menu_title_press (win_p, XButtonEvent*); @~@c void menu_title_press (win_p p, XButtonEvent* xbutton) { if (xbutton->button==Button1) { p_m_list menu=current_menu_list=p->data.menu; p_m_bar bar=current_menu_bar=menu->parent; highlight_menu(menu); display_menu_window(menu); bar->pressed=in_menu=true; } } @ @< Global fun... @>= void menu_title_enter (win_p); @~@c void menu_title_enter (win_p p) { p_m_list menu=p->data.menu; p_m_bar bar=menu->parent; if (in_menu && current_menu_bar==bar) if (current_menu_list!=menu) /* if menu is already dropped, ignore entry */ { remove_menu_window(current_menu_list); remove_highlighted_menu(current_menu_list); @/highlight_menu(current_menu_list=menu); display_menu_window(menu); } } @ @< Global fun... @>= void menu_entry_expose (win_p); @~@c void menu_entry_expose (win_p p) @+ { remove_highlighted_menu_option(p); @+} @ @< Global fun... @>= void menu_entry_release (win_p, XButtonEvent*); @~@c void menu_entry_release (win_p p, XButtonEvent* xbutton) { if (xbutton->button==Button1) { p_m_entry e=p->data.menu_entry; e->f(e->parent->parent->parent); } } @ @< Global fun... @>= void menu_entry_enter (win_p); void menu_entry_leave (win_p); @~@c void menu_entry_enter (win_p p) @+ { highlight_menu_option(p); @+} void menu_entry_leave (win_p p) @+ { remove_highlighted_menu_option(p); @+ } @* Highlighting and mapping menus. These functions execute the highlighting and removing of highlighted menu headers and menu entries. There is also a set of functions that map and unmap a set of menu entries. There is also a function that frees the memory used by the menu structure. @ These functions highlights and remove a highlight from a window header. This is done in exactly the same way as the highlighting function |highlight_menu_option|. It prints a filled rectangle in the foreground colour and then displays the string in the background colour. The remove highlight function just reverses the foreground and background colours. @< Global function declarations @>= extern void highlight_menu(p_m_list); extern void remove_highlighted_menu(p_m_list); @~@c extern void highlight_menu(p_m_list mbar) { change_menu_highlight(gc1, gc2, mbar); } @~@c extern void remove_highlighted_menu(p_m_list mbar) { change_menu_highlight(gc2, gc1, mbar); } @ This function does the actual printing of the highlights. This function does not actually know whether it is highlighting or removing a highlight - it all depends on which order the graphics contexts are passed in. @< Local function declarations @>= local void change_menu_highlight(GC, GC, p_m_list); @~@c local void change_menu_highlight(GC f_gc, GC b_gc, p_m_list mbar) { int string_length, string_width; string_length = (int)strlen(mbar->header); string_width = XTextWidth(button_font, mbar->header, string_length); XFillRectangle(display,mbar->h_win->w,f_gc,1,1,string_width+4,bf_height+2); XDrawString (display,mbar->h_win->w,b_gc,3,bf_height,mbar->header,string_length); } @ This function displays a menu list, by mapping the base window of the menu list, and setting the menu open flag. @< Global function declarations @>= extern void display_menu_window(p_m_list); @~@c extern void display_menu_window(p_m_list mbar) { XRaiseWindow(display,mbar->m_win->w); map_window(mbar->m_win); mbar->open = true; } @ This function removes a menu, by unmapping the base window of the menu list, and resetting the menu open flag. @< Global function declarations @>= extern void remove_menu_window(p_m_list); @~@c extern void remove_menu_window(p_m_list mbar) { if (mbar->open) { XUnmapWindow(display,mbar->m_win->w); mbar->open = false; } } @ This function highlights a menu entry. This is done by displaying a filled rectangle in the foreground pixel colour in the window and printing the string in the background pixel colour. This means that the window does not have to be cleared using the |XClearWindow| command. @< Global function declarations @>= extern void highlight_menu_option(win_p); extern void remove_highlighted_menu_option(win_p); @~@c extern void highlight_menu_option(win_p p) { change_menu_option_highlight(gc1,gc2,p); } @~@c extern void remove_highlighted_menu_option(win_p p) { change_menu_option_highlight(gc2,gc1,p); } @ This function changes the highlight of a menu entry. It also does not know whether it is highlighting or removing a highlight. It all depends on the order the graphics context's were passed. @< Local function declarations @>= local void change_menu_option_highlight(GC,GC,win_p); @~@c local void change_menu_option_highlight (GC f_gc,GC b_gc,win_p p) { p_m_entry e=p->data.menu_entry; p_m_list list=e->parent; XFillRectangle(display,p->w,f_gc,2,1,list->width+9,bf_height+3); XDrawString (display,p->w,b_gc,3,bf_height,e->title,(int)strlen(e->title)); } @* Menu functions for the main window. @ This function creates all the windows that are required to add settings into. @< Local function declarations @>= local void main_menu_box_size_func(win_p); @~@c local void main_menu_box_size_func(win_p p) { if (!info->scale.mapped) info->scale.mapped=true,map_window(info->scale.win); } @ As I feel that full help options in this program is excessive, there is one help window giving information. This window is created here. @< Local function declarations @>= local void main_menu_help_func(win_p); @~@c local void main_menu_help_func(win_p p) { if (!info->help_mapped) info->help_mapped=true, map_window(info->help_win); } @ When the quit button is pressed, all the diagram and picture windows are unmapped. The the fonts and graphics context's are freed and finally the display is closed. @< Local function declarations @>= local void main_menu_quit_func(win_p); @~@c local void main_menu_quit_func(win_p p) { int i; for (i=0; info->dw[i]!=NULL && info->dw[i]->pane.visibility!=removed; ++i) XDestroyWindow(display, info->dw[i]->pane.win->w); for (i=0; info->pw[i]!=NULL && info->pw[i]->pane.visibility!=removed; ++i) XDestroyWindow(display, info->pw[i]->pane.win->w); XUnloadFont(display, button_font->fid); XUnloadFont(display, coord_font->fid); XFreeGC(display, gc1); XFreeGC(display, gc2); XFreeGC(display, gc3); XFreeGC(display, gc4); XCloseDisplay(display); exit(1); } @ When a new diagram is created, a position in the main structure is found, and a diagram structure is placed in it. Then the initial setting are set and the icon set up. @< Local function declarations @>= local void main_menu_new_diag_func(win_p); @~@c local void main_menu_new_diag_func(win_p p) { (void) new_diagram_window(); } @ When the create picture button is pressed, it will create a picture from the selected diagram, as if it were activated from within the diagram window itself. If no diagram is slected or the diagram is empty, a bell is sounded. @< Local function declarations @>= local void main_menu_create_pict_func(win_p); @~@c local void main_menu_create_pict_func(win_p p) { if(info->sel_diag >= 0 && info->dw[info->sel_diag]->d != NULL) new_picture_window(info->dw[info->sel_diag]->d,NULL); else XBell(display, 100); } @ This is the button robin function. @< Local function declarations @>= local void main_menu_robin_func(win_p); @~@c local void main_menu_robin_func(win_p p) { if (info->sel_pict == -1) XBell(display, 100); else if (info->pw[info->sel_pict]->p==NULL) { pict_p f = new(picture); f = RS_tableau(&info->pw[info->sel_pict]->f); new_picture_window(NULL,f); XClearWindow(display, info->pw[info->sel_pict]->d.win->w); XClearWindow(display, info->pw[info->sel_pict]->i.win->w); print_complete_picture(info->pw[info->sel_pict]); } } @* Diagram menu functions. These are the function called directly by the menu entries. When a menu entry is activated the corresponding menu function is executed. @ The diagram number is used to identify which is selected. This number is stored in |info->sel_diag|. When the diagram is selected, the select icon is displayed in the icon window and the select icon is removed from the other diagram, if any. If no diagram is selected, the |info->sel_diag = -1|. To remove a selected diagram, the |info->sel_diag| value is used to reprint the icon window and then set to |-1|. @< Global function declarations @>= void diag_menu_select_func(win_p); void diag_menu_deselect_func(win_p); @~@c void diag_menu_select_func(win_p p) { p_diagram_window dw=p->data.diag; int i; for (i = 0; info->dw[i] != NULL; i++) { if (info->dw[i]->pane.visibility!=removed && info->dw[i]->pane.win == dw->pane.win) { info->sel_diag = i; display_select_icon(dw->pane.icon); } if (info->dw[i]->pane.visibility!=removed && info->dw[i]->pane.win != dw->pane.win) { XClearWindow(display, info->dw[i]->pane.icon->w); display_icon_graphics(info->dw[i]->pane.icon, info->dw[i]->pane.title, info->dw[i]->pane.visibility); } } } @~@c void diag_menu_deselect_func(win_p p) { p_diagram_window dw=p->data.diag; if (info->sel_diag != -1 && info->dw[info->sel_diag]->pane.win == dw->pane.win) { XClearWindow(display, info->dw[info->sel_diag]->pane.icon->w); display_icon_graphics(info->dw[info->sel_diag]->pane.icon, info->dw[info->sel_diag]->pane.title, info->dw[info->sel_diag]->pane.visibility); info->sel_diag = -1; } else XBell(display, 50); } @ When the clear menu entry is activated, the diagram is deleted, the offsets are reset, the window cleared and the origin cross is redrawn. @< Local function declarations @>= local void diag_menu_clear_func(win_p); @~@c local void diag_menu_clear_func(win_p p) { p_diagram_window dw=p->data.diag; del_diag(dw->d); dw->d = NULL; dw->disp.hor->offset = 3*info->scale.box_size; dw->disp.vert->offset = 3*info->scale.box_size; XClearWindow(display, dw->disp.win->w); print_outline(dw->disp.win, dw->d, dw->disp.hor->offset, dw->disp.vert->offset); } @ This function duplicates and existing diagram, event the mode that is currently set is copied. @< Local function declarations @>= local void diag_menu_dup_func(win_p); @~@c local void diag_menu_dup_func(win_p p) { p_diagram_window dw=p->data.diag; int i= new_diagram_window(); if (i != -1) { info->dw[i]->d = dw->d; info->dw[i]->mode = dw->mode; } } @ When the window is iconised, the |dw->pane.visibility| is set to |iconised|, all the windows are unmapped except the icon window obviously. Finally the tick icon is displayed in the icon window. @< Global function declarations @>= void diag_menu_iconise_func(win_p); @~@c void diag_menu_iconise_func(win_p p) { p_diagram_window dw=p->data.diag; dw->pane.visibility=iconised; XUnmapWindow(display, dw->pane.win->w); display_tick_icon(dw->pane.icon); } @ When the diagram is finished with, the done menu entry is selected. When this is activated all the windows in the diagram are destroyed and the memory allocated to that part of the main structure is freed. The visibility of the diagram window is set to |removed| and the number of diagrams is decremented. Finally all the icons are moved up next to each other. @< Local function declarations @>= local void diag_menu_done_func(win_p); @~@c local void diag_menu_done_func(win_p p) { p_diagram_window dw=p->data.diag; del_diag(dw->d); dw->d=NULL; dw->pane.visibility=removed; --info->num_diags; if (info->sel_diag>=0 && info->dw[info->sel_diag]==dw) info->sel_diag = -1; /* if selected, cancel selection. */ XUnmapWindow(display, dw->pane.win->w); XUnmapWindow(display, dw->pane.icon->w); adjust_icons(inner_diag_win,0); } @ The add mode is active when the diagram window is created. If the mode is changed and the user wishes to return to the add mode, this menu entry corresponding to this function is selected. It just changed the |mode| field of the diagram structure and displays a new comment. @< Local function declarations @>= local void diag_menu_add_func(win_p); @~@c local void diag_menu_add_func(win_p p) { p_diagram_window dw=p->data.diag; dw->mode = add_mode; strcpy(dw->disp.comment, "Add mode."); print_comment(&dw->disp); } @ When the user wants to remove a set of squares, the remove mode is used. When this entry is selected, the |mode| field in the diagram window is changed and the comment is undated. @< Local function declarations @>= local void diag_menu_remove_br_func(win_p); local void diag_menu_remove_tl_func(win_p); @~@c local void diag_menu_remove_br_func(win_p p) { p_diagram_window dw=p->data.diag; dw->mode = remove_br_mode; strcpy(dw->disp.comment, "Remove squares below and to the right."); print_comment(&dw->disp); } @~@c local void diag_menu_remove_tl_func(win_p p) { p_diagram_window dw=p->data.diag; dw->mode = remove_tl_mode; strcpy(dw->disp.comment, "Remove squares above and to the left."); print_comment(&dw->disp); } @ When the diagram is to be transposed, the |transpose_diag| function is called and the newly transposed diagram is displayed. @< Local function declarations @>= local void diag_menu_trans_func(win_p); @~@c local void diag_menu_trans_func(win_p p) { p_diagram_window dw=p->data.diag; dw->d = transpose_diag(dw->d); XClearWindow(display, dw->disp.win->w); print_outline(dw->disp.win, dw->d, dw->disp.hor->offset, dw->disp.vert->offset); } @ Then the diagram is to be negated, all that is done is the diagram is sent to the |minus_diag| function and then the newly negated diagram is displayed. @< Local function declarations @>= local void diag_menu_neg_func(win_p); @~@c local void diag_menu_neg_func(win_p p) { p_diagram_window dw=p->data.diag; minus_diag(&dw->d); XClearWindow(display, dw->disp.win->w); print_outline (dw->disp.win,dw->d,dw->disp.hor->offset,dw->disp.vert->offset); } @ When a anti-diagonal of a square is wanted, then the anti-diagonal mode is set and the comment is undated. @< Local function declarations @>= local void diag_menu_anti_func(win_p); @~@c local void diag_menu_anti_func(win_p p) { p_diagram_window dw=p->data.diag; dw->mode = anti_diagonal_mode; strcpy(dw->disp.comment, "Anti-diagonal mode."); print_comment(&dw->disp); } @ When the user want to query the diagram to find out more information about a square, the query mode is set and the comment is undated. @< Local function declarations @>= local void diag_menu_query_func(win_p); @~@c local void diag_menu_query_func(win_p p) { p_diagram_window dw=p->data.diag; dw->mode = query_mode; strcpy(dw->disp.comment, "Query mode."); print_comment(&dw->disp); } @ When a picture is required to be created, if there is no diagram a comment is displayed and a bell, sounded, Otherwise, a function to create the picture is called. @< Local function declarations @>= local void diag_menu_create_picture_func(win_p); @~@c local void diag_menu_create_picture_func(win_p p) { p_diagram_window dw=p->data.diag; if (dw->d == NULL) { strcpy(dw->disp.comment, "No diagram to create picture from."); print_comment(&dw->disp); XBell(display, 100); } else new_picture_window(dw->d,NULL); } @ If the diagram is moved from view, (most likely when the negate function is used) theis function ``usually'' fings it by finding the top left corner of the rectangle enclosing the diagram and displaying the diagram with this point in the top left corner of the printing window. @< Local function declarations @>= local void diag_menu_find_func(win_p); @~@c local void diag_menu_find_func(win_p p) { p_diagram_window dw=p->data.diag; if (dw->d != NULL) { dw->disp.vert->offset = (1-(dw->d->top))*info->scale.box_size; dw->disp.hor->offset = (1-(dw->d->r[DIAGRAM_DEPTH(dw->d)-1]->start))*info->scale.box_size; XClearWindow(display, dw->disp.win->w); print_outline(dw->disp.win, dw->d, dw->disp.hor->offset, dw->disp.vert->offset); } else { dw->disp.vert->offset = 3*info->scale.box_size; dw->disp.hor->offset = 3*info->scale.box_size; XClearWindow(display, dw->disp.win->w); print_origin_cross(dw->disp.win, dw->disp.hor->offset, dw->disp.vert->offset); } } @ To find the origin, the originate function is called. This moves the origin into the printing window slightly below and to the right of the top left corner of the window. @< Local function declarations @>= local void diag_menu_origin_func(win_p); @~@c local void diag_menu_origin_func(win_p p) { p_diagram_window dw=p->data.diag; dw->disp.hor->offset = 3*info->scale.box_size; dw->disp.vert->offset = 3*info->scale.box_size; XClearWindow(display, dw->disp.win->w); if (dw->d != NULL) print_outline(dw->disp.win, dw->d, dw->disp.hor->offset, dw->disp.vert->offset); else print_origin_cross(dw->disp.win, dw->disp.hor->offset, dw->disp.vert->offset); } @* Picture menu functions. These are the function called directly by the picture windows menu options. When a menu option is activated the corresponding menu function is executed. @ The picture number is used to identify which is selected. This number is stored in |info->sel_pict|. When the picture is selected, the select icon is displayed in the icon window and removed from the previously selected picture, if any. If the picture is to be deselected and if no picture is selected, the |info->sel_pict = -1|. To remove a selected picture, the |info->sel_pict| value is used to reprint the icon window and then set to -1. @< Global function declarations @>= void pict_menu_select_func(win_p); void pict_menu_deselect_func(win_p); @~@c void pict_menu_select_func(win_p p) { p_picture_window pw=p->data.pict; int i; for (i = 0; info->pw[i] != NULL; i++) { if (info->pw[i]->pane.visibility!=removed) { if (info->pw[i]->pane.win == pw->pane.win) { info->sel_pict = i; display_select_icon(pw->pane.icon); } else { XClearWindow(display, info->pw[i]->pane.icon->w); display_icon_graphics(info->pw[i]->pane.icon, info->pw[i]->pane.title, info->pw[i]->pane.visibility); } } } } @~@c void pict_menu_deselect_func(win_p p) { p_picture_window pw=p->data.pict; if (info->sel_pict != -1 && info->pw[info->sel_pict]->pane.win == pw->pane.win) { XClearWindow(display, info->pw[info->sel_pict]->pane.icon->w); display_icon_graphics (info->pw[info->sel_pict]->pane.icon ,info->pw[info->sel_pict]->pane.title ,info->pw[info->sel_pict]->pane.visibility ); info->sel_pict = -1; } else XBell(display, 50); } @ To clear a picture, the domain is sent to set up a new partial picture from and all the printing and comment windows are updated. @< Local function declarations @>= local void pict_menu_clear_func(win_p); @~@c local void pict_menu_clear_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { part_pict* p=pw->p=new(@[part_pict@]); setup_new_pict(p,dup_diag(pw->f->domain)); del_pict(pw->f),pw->f=NULL; strcpy(pw->d.comment, "Creating picture."); print_comment(&pw->d); strcpy(pw->i.comment, ""); XClearWindow(display, pw->i.com->w); XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_partial_picture(pw); } } @ To deconstruct a picture, if the picture is completed, the partial picture is constructed else nothing is done. Then the image is deleted if there is one that does exist. @< Local function declarations @>= local void pict_menu_decons_func(win_p); @~@c local void pict_menu_decons_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { part_pict* p=pw->p=new(@[part_pict@]); p->f = pw->f; pw->f=NULL; p->complete=true; } XClearWindow(display, pw->d.com->w); XClearWindow(display, pw->i.com->w); strcpy(pw->i.comment, ""); if (delete_image(pw->p)) { strcpy(pw->d.comment, "Constructing picture."); XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_partial_picture(pw); } else XBell(display, 100),strcpy(pw->d.comment, "No image to remove."); print_comment(&pw->d); } @ @< Local function declarations @>= local void pict_menu_dup_func(win_p); @~@c local void pict_menu_dup_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { if (new_picture_window(NULL,dup_pict(pw->f))<0) { XBell(display, 100),strcpy(pw->d.comment, "No more picture windows."); print_comment(&pw->d); } } } @ When the window is iconised, the |pw->pane.visibility| field is set to |iconised|. All the windows are unmapped, except obviously the icon window. Finally the tick icon is displayed in the remaining icon window. @< Global function declarations @>= void pict_menu_iconise_func(win_p); @~@c void pict_menu_iconise_func(win_p p) { p_picture_window pw=p->data.pict; pw->pane.visibility=iconised; XUnmapWindow(display, pw->pane.win->w); display_tick_icon(pw->pane.icon); } @ When a user is finished with the done menu option is selected. When this is activated, al;l the windows in the picture are destroyed and the memory allocated to that part of the main structure is freed. The visibility of the picture window is set to |removed| and the number of pictures is decremented. Finally all the icons are moved up next to each other. @< Local function declarations @>= local void pict_menu_done_func(win_p); @~@c local void pict_menu_done_func(win_p p) { p_picture_window pw=p->data.pict; del_pict(pw->f); pw->f=NULL; if (pw->p!=NULL) del_pict(pw->p->f),free(pw->p),pw->p=NULL; pw->pane.visibility=removed; --info->num_picts; if (info->sel_pict>=0 && info->pw[info->sel_pict]==pw) info->sel_pict = -1; /* if selected, cancel selection. */ XUnmapWindow(display, pw->pane.win->w); XUnmapWindow(display, pw->pane.icon->w); adjust_icons(pict_dom_win,0); } @ When the picture is to be transposed, the |transpose_pict| function is called and the newly transposed picture is displayed. @< Local function declarations @>= local void pict_menu_inv_func(win_p); @~@c local void pict_menu_inv_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { pict_p f= inverse_pict(pw->f); del_pict(pw->f),pw->f =f; XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_complete_picture(pw); } } @ Then the picture is to be negated, all that is done is the domain and image parts are neagated and then the new picture is displayed. @< Local function declarations @>= local void pict_menu_neg_func(win_p); @~@c local void pict_menu_neg_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { minus_pict(&pw->f); XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_complete_picture(pw); } } @ When the picture is to be transposed, the |transpose_pict| function is called and the newly transposed picture is displayed. @< Local function declarations @>= local void pict_menu_trans_func(win_p); @~@c local void pict_menu_trans_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { pict_p f= transpose_pict(pw->f); del_pict(pw->f),pw->f =f; XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_complete_picture(pw); } } @ This is the Robinson-Schensted function. @< Local function declarations @>= local void pict_menu_robin_func(win_p); @~@c local void pict_menu_robin_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { int i=new_picture_window(NULL,NULL); if (i==-1) /* do nothing if no more windows are available */ { XBell(display, 100),strcpy(pw->d.comment, "No more picture windows."); print_comment(&pw->d); } else { info->pw[i]->f=RS_tableau(&pw->f); /* new window gets |P|, old |Q| */ XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_complete_picture(pw); /* redisplay old window */ } } } @ This is the inverse Robinson-Schensted function. @< Local function declarations @>= local void pict_menu_inv_RS_func(win_p); @~@c local void pict_menu_inv_RS_func(win_p p) { p_picture_window pw=p->data.pict; int i=info->sel_pict; if (i<0 || info->pw[i]->p!=NULL) { XBell(display, 100), strcpy(pw->d.comment, "Select a (complete) picture first."); print_comment(&pw->d); } else if (pw->p==NULL) { pict_p f=dup_pict(info->pw[i]->f), g=pw->f; if (!RS_permutation(f,&g)) /* then try with pictures interchanged */ { f=pw->f, g=dup_pict(info->pw[i]->f); if (RS_permutation(f,&g)) pw->f=g; /* old |pw->f| was destroyed */ else { XBell(display,100); strcpy(pw->d.comment, "Improper picture pair"); print_comment(&pw->d); return; } } XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_complete_picture(pw); /* redisplay old window */ } } @ This is the Sch\"utzenberger function. @< Local function declarations @>= local void pict_menu_schutz_func(win_p); @~@c local void pict_menu_schutz_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { if (!Schutz_dualise(&pw->f)) { pict_p inv=inverse_pict(pw->f); if (Schutz_dualise(&inv)) del_pict(pw->f), pw->f=inverse_pict(inv), del_pict(inv); else { XBell(display,100); strcpy(pw->d.comment, "Domain or image must be Young diagram"); print_comment(&pw->d); return; } } XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_complete_picture(pw); /* redisplay old window */ } } @ Here is the function that transposes both domain and image, using the Robinson-Schensted, Sch\"utzenberger, and inverse Robinson-Schensted algorithms. @< Local function declarations @>= local void pict_menu_full_transp_func(win_p); @~@c local void pict_menu_full_transp_func(win_p p) { p_picture_window pw=p->data.pict; if (pw->p==NULL) { transpose_both(&pw->f); XClearWindow(display, pw->d.win->w); XClearWindow(display, pw->i.win->w); print_complete_picture(pw); } } @ If the pictures domain (image) is moved from view (most likely when the negated function is used) this function ``usually'' finds it by finding the top left corner of the rectangle enclosing it and displaying the domain (image) with this point in the top left corner of the corresponding diaply window. @< Local function declarations @>= local void pict_menu_d_find_func(win_p); local void pict_menu_i_find_func(win_p); @~@c local void pict_menu_d_find_func(win_p p) { p_picture_window pw=p->data.pict; diag_p d = new(diagram); if (pw->p!=NULL) d = pw->p->f->domain; else d = pw->f->domain; if (d != NULL) { pw->d.vert->offset = (1-(d->top))*info->scale.box_size; pw->d.hor->offset = (1-(d->r[DIAGRAM_DEPTH(d)-1]->start))*info->scale.box_size; XClearWindow(display, pw->d.win->w); if (pw->p!=NULL) print_partial_picture(pw); else print_complete_domain(pw); } } @~@c local void pict_menu_i_find_func(win_p p) { p_picture_window pw=p->data.pict; diag_p d = new(diagram); if (pw->p!=NULL) d = pw->p->f->image; else d = pw->f->image; if (d != NULL) { pw->i.vert->offset = (1-(d->top))*info->scale.box_size; pw->i.hor->offset = (1-(d->r[DIAGRAM_DEPTH(d)-1]->start))*info->scale.box_size; XClearWindow(display, pw->i.win->w); if (pw->p!=NULL) print_partial_picture(pw); else print_complete_image(pw); } else { pw->i.vert->offset = 3*info->scale.box_size; pw->i.hor->offset = 3*info->scale.box_size; XClearWindow(display, pw->i.win->w); print_origin_cross(pw->i.win, pw->i.hor->offset, pw->i.vert->offset); } } @ To find the origin, the originate function is called. This moves the origin of the domain (image) into the printing window slightly below and to the right of the top left corner of the window. @< Local function declarations @>= local void pict_menu_d_origin_func(win_p); local void pict_menu_i_origin_func(win_p); @~@c local void pict_menu_d_origin_func(win_p p) { p_picture_window pw=p->data.pict; pw->d.hor->offset = 3*info->scale.box_size; pw->d.vert->offset = 3*info->scale.box_size; XClearWindow(display, pw->d.win->w); if (pw->p!=NULL) print_partial_picture(pw); else print_complete_domain(pw); } @~@c local void pict_menu_i_origin_func(win_p p) { p_picture_window pw=p->data.pict; pw->i.hor->offset = 3*info->scale.box_size; pw->i.vert->offset = 3*info->scale.box_size; XClearWindow(display, pw->i.win->w); if (pw->p!=NULL) print_partial_picture(pw); else print_complete_image(pw); } @* Resize functions. These functions resize the different aspects of the window when the diagram or picture window is resized. @ These function resizes the menu bar windows on either the diagram window or the picture window. @< Global function declarations @>= void resize_menubar(win_p, int); @~@c void resize_menubar(win_p p, int width) { XResizeWindow(display, p->w, width-4, bf_height+8); } @* Index.