Back to index

plt-scheme  4.2.1
Menu.cc
Go to the documentation of this file.
00001 /*                                                      -*- C++ -*-
00002  *
00003  * Purpose: simple menu class
00004  *
00005  * Authors: Markus Holzem and Julian Smart
00006  *
00007  * Copyright: (C) 2004-2009 PLT Scheme Inc.
00008  * Copyright: (C) 1995, AIAI, University of Edinburgh (Julian)
00009  * Copyright: (C) 1995, GNU (Markus)
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00024  * 02110-1301 USA.
00025  */
00026 
00027 #ifdef __GNUG__
00028 #pragma implementation "Menu.h"
00029 #endif
00030 
00031 #define  Uses_XtIntrinsic
00032 #define  Uses_wxGDI
00033 #define  Uses_wxMenu
00034 #include "wx.h"
00035 #define  Uses_ShellWidget
00036 #define  Uses_MenuWidget
00037 #include "widgets.h"
00038 #include "wx_visual.h"
00039 
00040 static wxMenu *popped_up_menu;
00041 
00042 //-----------------------------------------------------------------------------
00043 // constructor and destructor
00044 //-----------------------------------------------------------------------------
00045 
00046 wxMenu::wxMenu(char *_title, wxFunction _func, wxFont *_font)
00047 {
00048     __type = wxTYPE_MENU;
00049 
00050     // widgets are created by PopupMenu and destroyed by EventCallback
00051     X    = NULL;
00052 
00053     requested_width = 0;
00054 
00055     font = (_font ? _font : wxSYSTEM_FONT);
00056     callback = _func;
00057     top = topdummy = title = last = 0;
00058     // if a title is associated with a menu, it may not be removed
00059     if (_title) {
00060        Append(-1, _title);
00061        title = top;
00062        ((menu_item*)title)->type = MENU_TEXT;
00063        AppendSeparator();
00064        AppendSeparator();
00065     } else {
00066        Append(-1, NULL); // to have something if associated to another menu
00067        topdummy = top;
00068     }
00069 
00070 #ifdef MZ_PRECISE_GC
00071     children = DEBUG_NEW wxChildList;
00072 #endif
00073 
00074     WXGC_IGNORE(this, owner);
00075 }
00076 
00077 wxMenu::~wxMenu(void)
00078 {
00079     menu_item *item = (menu_item*)top;
00080 
00081     if (popped_up_menu == this)
00082       popped_up_menu = NULL;
00083 
00084     while (item) {
00085        menu_item *temp = item;
00086        item = item->next;
00087        FREE_MENU_STRING(temp->label);
00088        FREE_MENU_STRING(temp->key_binding);
00089        if (temp->help_text != (char *)-1)
00090          FREE_MENU_STRING(temp->help_text);
00091        if (temp->contents) {       // has submenu?
00092          wxMenu *mnu;
00093          mnu = EXTRACT_TOP_MENU(temp);
00094 #ifdef MZ_PRECISE_GC
00095          children->DeleteObject(mnu);
00096 #endif
00097          DELETE_OBJ mnu;
00098          if (temp->user_data)
00099            FREE_TOP_POINTER(temp->user_data);
00100        }
00101        FREE_MENU_ITEM(temp);
00102     }
00103 
00104     owner = NULL;
00105 
00106     /* Each saferef is attached to a widget.
00107        The widget's destroy proc deletes the saferef.
00108        But we need to zero it here to prevent callbacks. */
00109     while (saferefs) {
00110       void *sr;
00111       sr = saferefs[0];
00112       *(void **)(saferefs[0]) = NULL;
00113       saferefs = (void **)sr;
00114     }
00115 
00116 #ifdef MZ_PRECISE_GC
00117     DELETE_OBJ children;
00118 #endif
00119 }
00120 
00121 //-----------------------------------------------------------------------------
00122 // create and popup menu, will be destroyed by wxMenuCallback
00123 //-----------------------------------------------------------------------------
00124 
00125 extern "C" {
00126   extern void wxAddGrab(Widget);
00127   extern void wxRemoveGrab(Widget);
00128 };
00129 
00130 #ifdef MZ_PRECISE_GC
00131 START_XFORM_SKIP;
00132 #endif
00133 
00134 static void FreeSaferef(Widget WXUNUSED(w), wxMenu** menup,
00135                      XtPointer WXUNUSED(null))
00136 {
00137   FREE_SAFEREF((char *)menup);
00138 
00139   /* No XFORM_RESET_VAR_STACK because this one isn't xformed.  No need
00140      to xform because FREE_SAFEREF won't set the GC variable stack. */
00141 }
00142 
00143 #ifdef MZ_PRECISE_GC
00144 END_XFORM_SKIP;
00145 #endif
00146 
00147 Bool wxMenu::PopupMenu(Widget in_w, int root_x, int root_y, Bool forChoice, int top_extra)
00148 {
00149     Widget wgt;
00150     Position x, y, new_root_x, new_root_y;
00151     int rx, ry;
00152     GC_CAN_IGNORE XEvent xevent;
00153     String a[1];
00154     void *saferef;
00155 
00156     if (X)
00157       return FALSE;
00158 
00159     wxUnpopMenu();
00160 
00161     while (XtParent(in_w)) {
00162       in_w = XtParent(in_w);
00163     }
00164     
00165     X = new wxMenu_Widgets;
00166     X->shell = XtVaCreatePopupShell
00167        ("popup", overrideShellWidgetClass, in_w, 
00168         XtNborderWidth, 0,
00169         XtNvisual, wxAPP_VISUAL,
00170         XtNdepth, wx_visual_depth,
00171         XtNcolormap, wx_default_colormap,
00172         NULL);
00173     wgt = XtVaCreateManagedWidget
00174        ("menu", menuWidgetClass, X->shell,
00175         XtNmenu,       top,
00176         XtNfont,       font->GetInternalFont(),
00177 #ifdef WX_USE_XFT
00178         XtNmenuXftFont, font->GetInternalAAFont(),
00179 #endif
00180         XtNforeground, wxBLACK_PIXEL,
00181         XtNbackground, wxGREY_PIXEL,
00182         XtNhighlightPixel,  wxCTL_HIGHLIGHT_PIXEL,
00183         XtNforChoice,  forChoice,
00184         XtNforPopup,  TRUE,
00185         XtNrequestedWidth, requested_width,
00186         XtNextraTop, top_extra,
00187         NULL);
00188     X->menu = wgt;
00189     XtRealizeWidget(X->shell);
00190 
00191     {
00192       void *p;
00193       p = MALLOC_SAFEREF();
00194       saferef = p;
00195     }
00196     SET_SAFEREF(saferef, this);
00197 
00198     {
00199       void **srs;
00200       srs = new WXGC_PTRS void*[2];
00201       srs[0] = saferef;
00202       srs[1] = saferefs;
00203       saferefs = srs;
00204     }
00205 
00206     XtAddCallback(X->menu, XtNonSelect, wxMenu::EventCallback, saferef);
00207     XtAddCallback(X->menu, XtNonNoSelect, wxMenu::EventCallback, saferef);
00208     XtAddCallback(X->menu, XtNonMDestroy, (XtCallbackProc)FreeSaferef, (XtPointer)saferef);
00209     Xaw3dPopupMenuAtPos((MenuWidget)(X->menu), root_x, root_y);
00210 
00211     /* Get the menu started: */
00212     XtVaGetValues(X->menu, XtNx, &x, XtNy, &y, NULL);
00213     XtTranslateCoords(X->menu, x, y, &new_root_x, &new_root_y);
00214 
00215     XtAddGrab(X->shell, TRUE, FALSE);
00216     wxAddGrab(X->shell);
00217 
00218 
00219     {
00220       Window root, child;
00221       int cx, cy;
00222       unsigned int mask;
00223 
00224       if (!XQueryPointer(wxAPP_DISPLAY, XtWindow(X->shell), 
00225                       &root, &child,
00226                       &rx, &ry, &cx, &cy, &mask)) {
00227        rx = new_root_x + 5;
00228        ry = new_root_y + 5;
00229       }
00230     }
00231 
00232     xevent.xmotion.x_root = rx;
00233     xevent.xmotion.x = rx - new_root_x;
00234     xevent.xmotion.y_root = ry;
00235     xevent.xmotion.y = ry - new_root_y;
00236 
00237     a[0] = "Stay";
00238 
00239     XtCallActionProc(X->menu, "start", &xevent, a, 1);
00240 
00241     popped_up_menu = this;
00242 
00243     return TRUE;
00244 }
00245 
00246 //-----------------------------------------------------------------------------
00247 // add items to menu
00248 //-----------------------------------------------------------------------------
00249 
00250 void wxMenu::Append(long id, char *label, char *help, Bool checkable)
00251 {
00252     menu_item *item;
00253     char *ms;
00254 
00255     Stop();
00256   
00257     item = 0;
00258     // create new menu item or use topdummy
00259     if (topdummy) {
00260        item = (menu_item*)topdummy;
00261        FREE_MENU_STRING(item->label);
00262        FREE_MENU_STRING(item->key_binding);
00263        if (item->user_data)
00264          FREE_TOP_POINTER(item->user_data);
00265        topdummy = 0;
00266     } else {
00267       item = MALLOC_MENU_ITEM();
00268       // chain or initialize menu_item list
00269       if (last) {
00270        menu_item *prev = (menu_item*)last;
00271        prev->next = item;
00272        item->prev = prev;
00273        last = (wxMenuItem*)item;
00274       } else {
00275        top = last = (wxMenuItem*)item;
00276        item->prev = NULL;
00277       }
00278     }
00279     // initialize menu_item
00280     if ((long)help == -1) {
00281       /* Hack to avoid parse: */
00282       char *s;
00283       s = copystring(label);
00284       ms = MAKE_MENU_STRING(s);
00285       item->label = ms;
00286       item->key_binding = NULL;
00287     } else {
00288       wxGetLabelAndKey(label, &item->label, &item->key_binding);
00289       ms = MAKE_MENU_STRING(item->label);
00290       item->label = ms;
00291       ms = MAKE_MENU_STRING(item->key_binding);
00292       item->key_binding = ms;
00293     }
00294     if (help == (char *)-1)
00295       ms = help;
00296     else
00297       ms = MAKE_MENU_STRING(help);
00298     item->help_text = ms;
00299     item->ID        = id; 
00300     item->enabled   = TRUE;
00301     item->set       = FALSE;
00302     item->contents  = NULL;
00303     item->next      = NULL;
00304     item->user_data = NULL;
00305     item->type      = checkable ? MENU_TOGGLE : MENU_BUTTON;
00306 }
00307 
00308 void wxMenu::Append(long id, char *label, wxMenu *submenu, char *help)
00309 {
00310   menu_item *item;
00311   void *tm;
00312 
00313   /* enforce one-menu-owner: */
00314   if (submenu->owner)
00315     return;
00316 
00317   Stop();
00318 
00319   // do the same thing as if appending a "button"
00320   Append(id, label, help, FALSE);
00321   // change data for submenu
00322   item            = (menu_item*)last;
00323   item->type      = MENU_CASCADE;
00324   item->contents  = (menu_item*)submenu->top;
00325   tm = BUNDLE_TOP_MENU(submenu);
00326   item->user_data = tm;
00327   submenu->owner = (wxMenuItem **)item;
00328 #ifdef MZ_PRECISE_GC
00329   children->Append(submenu);
00330 #endif
00331 }
00332 
00333 void wxMenu::AppendSeparator(void)
00334 {
00335     menu_item * item;
00336 
00337     Stop();
00338 
00339     // do the same thing as if appending a "button"
00340     Append(-1, NULL, NULL, FALSE);
00341     // change data for separator
00342     item = (menu_item*)last;
00343     item->type      = MENU_SEPARATOR;
00344 }
00345 
00346 Bool wxMenu::DeleteItem(long id, int pos)
00347 {
00348   menu_item *found, *prev;
00349 
00350   if (id == -1)
00351     return FALSE;
00352 
00353   for (found = (menu_item*)top; found && pos--; found = found->next) {
00354     if ((pos < 0) && (found->ID == id))
00355       break;
00356   }
00357 
00358   prev = found->prev;
00359 
00360   if (found) {
00361     Stop();
00362 
00363     if (!prev) {
00364       top = (wxMenuItem*)found->next;
00365       if (found->next)
00366        found->next->prev = NULL;
00367       if (!top) {
00368        last = 0;
00369        Append(-1, NULL); /* Reinstate topdummy */
00370        topdummy = top;
00371       }
00372       if (owner)
00373        ((menu_item *)owner)->contents = (menu_item *)top;
00374     } else {
00375       prev->next = found->next;
00376       if (prev->next)
00377        prev->next->prev = prev;
00378       if (!found->next)
00379        last = (wxMenuItem*)prev;
00380     }
00381 
00382     FREE_MENU_STRING(found->label);
00383     FREE_MENU_STRING(found->key_binding);
00384     if (found->help_text != (char *)-1)
00385       FREE_MENU_STRING(found->help_text);
00386 
00387     /* If there's a submenu, let it go. */
00388     if (found->contents) {
00389       wxMenu *mnu;
00390       mnu = EXTRACT_TOP_MENU(found);
00391       mnu->owner = NULL;
00392       if (found->user_data)
00393        FREE_TOP_POINTER(found->user_data);
00394 #ifdef MZ_PRECISE_GC
00395       children->DeleteObject(mnu);
00396 #endif
00397     }
00398 
00399     FREE_MENU_ITEM(found);
00400 
00401     return TRUE;
00402   } else
00403     return FALSE;
00404 }
00405 
00406 Bool wxMenu::Delete(long id)
00407 {
00408   return DeleteItem(id, -1);
00409 }
00410 
00411 Bool wxMenu::DeleteByPosition(int pos)
00412 {
00413   if (pos > -1)
00414     return DeleteItem(0, pos);
00415   else
00416     return FALSE;
00417 }
00418 
00419 int wxMenu::Number()
00420 {
00421   menu_item *found;
00422   int n = 0;
00423 
00424   for (found = (menu_item*)top; found; found = found->next) {
00425     n++;
00426   }
00427 
00428   if (n && topdummy)
00429     --n;
00430 
00431   return n;
00432 }
00433 
00434 //-----------------------------------------------------------------------------
00435 // modify items
00436 //-----------------------------------------------------------------------------
00437 
00438 void wxMenu::Check(long id, Bool flag)
00439 {
00440     menu_item *found;
00441     found = (menu_item*)FindItemForId(id);
00442     if (found)
00443        found->set = flag;
00444 }
00445 
00446 Bool wxMenu::Checked(long id)
00447 {
00448     menu_item *found;
00449     found = (menu_item*)FindItemForId(id);
00450     if (found)
00451       return found->set;
00452     return FALSE;
00453 }
00454 
00455 void wxMenu::Enable(long id, Bool flag)
00456 {
00457     menu_item *found;
00458     found = (menu_item*)FindItemForId(id);
00459     if (found) {
00460       if (!flag && found->enabled)
00461        Stop();
00462       found->enabled = flag;
00463     }
00464 }
00465 
00466 char *wxMenu::GetHelpString(long id)
00467 {
00468     menu_item *found;
00469     found = (menu_item*)FindItemForId(id);
00470     if (found)
00471       return found->help_text;
00472     return NULL;
00473 }
00474 
00475 char *wxMenu::GetLabel(long id)
00476 {
00477     menu_item *found;
00478     found = (menu_item*)FindItemForId(id);
00479     if (found)
00480       return found->label;
00481     return NULL;
00482 }
00483 
00484 char *wxMenu::GetTitle(void)
00485 {
00486     if (title)
00487       return ((menu_item*)title)->label;
00488     return NULL;
00489 }
00490 
00491 void wxMenu::SetHelpString(long id, char *help)
00492 {
00493     menu_item *found;
00494     found = (menu_item*)FindItemForId(id);
00495     if (found) {
00496       char *ms;
00497       ms = MAKE_MENU_STRING(help);
00498       found->help_text = ms;
00499     }
00500 }
00501 
00502 void wxMenu::SetLabel(long id, char *label)
00503 {
00504     menu_item *found;
00505     found = (menu_item*)FindItemForId(id);
00506     if (found) {
00507       char *ms;
00508       Stop();
00509       wxGetLabelAndKey(label, &found->label, &found->key_binding);
00510       ms = MAKE_MENU_STRING(found->label);
00511       found->label = ms;
00512       ms = MAKE_MENU_STRING(found->key_binding);
00513       found->key_binding= ms;
00514     }
00515 }
00516 
00517 void wxMenu::SetTitle(char *label)
00518 {
00519     if (title) {
00520       menu_item *item;
00521       Stop();
00522       item = (menu_item*)title;
00523       wxGetLabelAndKey(label, &item->label, &item->key_binding);
00524     }
00525 }
00526 
00527 //-----------------------------------------------------------------------------
00528 // find items by ID or by label
00529 //-----------------------------------------------------------------------------
00530 
00531 int wxMenu::FindItem(char *itemstring, int strip)
00532 {
00533     char *label, *key;
00534     int  answer = -1;
00535 
00536     if (strip)
00537       wxGetLabelAndKey(itemstring, &label, &key);
00538     else
00539       label = itemstring;
00540     for (menu_item *item = (menu_item*)top; item; item=item->next) {
00541        if (!strcmp(label, item->label)) { // label found
00542            answer = item->ID;
00543            break; // found
00544        }
00545        if (item->contents) // has submenu => search in submenu
00546          if ((answer = EXTRACT_TOP_MENU(item)->FindItem(label)) > -1)
00547            break; // found
00548     }
00549     return answer;
00550 }
00551 
00552 wxMenuItem *wxMenu::FindItemForId(long id, wxMenu **req_menu)
00553 {
00554     menu_item *answer=NULL;
00555 
00556     for (menu_item *item = (menu_item*)top; item; item=item->next) {
00557        if (id == item->ID) { // id found
00558            answer = item;
00559            break; // found
00560        }
00561        if (item->contents) // has submenu => search in submenu
00562            if ((answer = (menu_item*)(EXTRACT_TOP_MENU(item)->FindItemForId(id))))
00563              break; // found
00564       }
00565     if (req_menu)
00566       *req_menu = EXTRACT_TOP_MENU(answer);
00567     return ((wxMenuItem*)answer);
00568 }
00569 
00570 void wxMenu::SetWidth(int n)
00571 {
00572   requested_width = n;
00573 }
00574 
00575 //-----------------------------------------------------------------------------
00576 // callback for wxMenu::PopupMenu
00577 //-----------------------------------------------------------------------------
00578 
00579 void wxMenu::EventCallback(Widget WXUNUSED(w), XtPointer dclient, XtPointer dcall)
00580 {
00581   wxMenu    *menu  = (wxMenu *)GET_SAFEREF(dclient);
00582   menu_item *item  = (menu_item*)dcall;
00583 
00584   if (menu) {
00585     if (popped_up_menu == menu)
00586       popped_up_menu = NULL;
00587     
00588     /* remove dclient from the saferefs chain: */
00589     {
00590       void **prev = NULL, **srs = menu->saferefs;
00591       while (srs) {
00592        if ((void *)dclient == srs[0]) {
00593          if (prev)
00594            prev[1] = srs[1];
00595          else
00596            menu->saferefs = (void **)(srs[1]);
00597          break;
00598        } else {
00599          prev = srs;
00600          srs = (void **)(srs[1]);
00601        }
00602       }
00603     }
00604     /* zero out to prevent future callbacks: */
00605     *(void **)dclient = NULL;
00606 
00607     /* remove grab */
00608     XtRemoveGrab(menu->X->shell);
00609     wxRemoveGrab(menu->X->shell);
00610 
00611     // destroy widgets
00612     XtDestroyWidget(menu->X->shell);
00613     menu->X->shell = menu->X->menu = 0;
00614     DELETE_OBJ menu->X;
00615     menu->X=NULL;
00616 
00617     if (item && (item->ID == -1))
00618       item = NULL;
00619 
00620     if (!item && menu->client_data) {
00621       /* Choice item. Throw away event. */
00622 #ifdef MZ_PRECISE_GC
00623       XFORM_RESET_VAR_STACK;
00624 #endif
00625       return;
00626     }
00627 
00628     {
00629       wxPopupEvent *event;
00630 
00631       if (item)
00632        if (item->type == MENU_TOGGLE)
00633          item->set = (!item->set);
00634       
00635       event = new wxPopupEvent();
00636       
00637       event->menuId = (item ? item->ID : 0);
00638 
00639       // call callback function
00640       if (menu->callback)
00641        menu->callback(menu, event);
00642     }
00643   }
00644 
00645 #ifdef MZ_PRECISE_GC
00646   XFORM_RESET_VAR_STACK;
00647 #endif
00648 }
00649 
00650 void wxMenu::Stop()
00651 {
00652   /* No way to get to menu bar right now... */
00653 }
00654 
00655 void wxMenu::Unpop()
00656 {
00657   if (X)
00658     XtCallActionProc(X->menu, "select", NULL, NULL, 0);
00659 }
00660 
00661 
00662 #ifdef MZ_PRECISE_GC
00663 char *copystring_xt(const char *s)
00664 {
00665   int l;
00666   char *r;
00667 
00668   if (!s)
00669     return NULL;
00670 
00671   l = strlen(s);
00672   r = XtMalloc(l + 1);
00673   memcpy(r, s, l + 1);
00674 
00675   return r;
00676 }
00677 #endif
00678 
00679 void wxInitPopupMgr(void)
00680 {
00681   wxREGGLOB(popped_up_menu);
00682 }
00683 
00684 void wxUnpopMenu(void)
00685 {
00686   if (popped_up_menu)
00687     popped_up_menu->Unpop();
00688   popped_up_menu = NULL;
00689 }