/*************************************************************************** CMenu.cpp (c) 2000-2007 Benoit Minisini This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************/ #define __CMENU_CPP #include #include #include #include #include #include #include #include #include "gambas.h" #include "CWidget.h" #include "CWindow.h" #include "CMenu.h" //#define DEBUG_MENU //static void *CLASS_Menu; //static void *CLASS_Window; DECLARE_EVENT(EVENT_Click); DECLARE_EVENT(EVENT_Show); DECLARE_EVENT(EVENT_Hide); DECLARE_METHOD(CCONTROL_window); DECLARE_METHOD(CCONTROL_name); static int menu_id = 0; static int check_menu(void *object) { return ((CMENU *)object)->deleted; } static void set_text(CMENU *item, char *text) { #ifdef DEBUG_MENU qDebug("set_text: item->text = '%s' text = '%s'", item->text, text); #endif if (item->text) { GB.FreeString(&item->text); item->text = NULL; } if (text) GB.NewString(&item->text, text, 0); } static void hide_menu(CMENU *item) { if (!CWIDGET_test_flag(item, WF_VISIBLE)) return; #ifdef DEBUG_MENU qDebug("hide_menu: item = %p (%d)", item, item->id); #endif //index = item->container->indexOf(item->id); //if (index >= 0) item->container->removeItem(item->id); if (CMENU_is_top(item)) { if (item->container->count() == 0) { ((QMenuBar *)item->container)->hide(); ((MyMainWindow *)item->toplevel)->configure(); } } CWIDGET_clear_flag(item, WF_VISIBLE); } static void update_accel(CMENU *item) { if (!item->accel->isEmpty() && item->parent) { if (item->enabled && !item->noshortcut && item->parent->enabled) { QString s = *item->accel; //qDebug("set shortcut '%s' to menu '%s' / parent %p", s.latin1(), item->text, item->parent); item->container->setAccel(*(item->accel), item->id); } else item->container->setAccel(QKeySequence(), item->id); } } static void show_menu(CMENU *item) { CMENU *parent; int pos; //bool change; QIconSet icon; QString text; if (CWIDGET_test_flag(item, WF_VISIBLE)) return; #ifdef DEBUG_MENU qDebug("show_menu: item = %p (%d) parent = %p (%d)", item, item->id, item->parent, item->parent ? item->parent->id : 0); #endif parent = item->parent; if (parent) { item->container = QPOPUPMENU(parent); } for (pos = 0; pos < (int)item->container->count(); pos++) { if (item->container->idAt(pos) >= item->id) break; } if (item->picture && !item->checked) { int size; if (item->stretch) size = MAIN_scale * 2 + 2; else size = -1; CWIDGET_iconset(icon, *(item->picture->pixmap), size); } text = TO_QSTRING(item->text); if (CMENU_is_separator(item)) { #ifdef DEBUG_MENU qDebug("show_menu: insertSeparator(%d)", pos); #endif //item->container->insertSeparator(pos); item->container->insertItem((QWidget *)0, item->id, pos); } else if (QPOPUPMENU(item) != 0) { #ifdef DEBUG_MENU qDebug("show_menu: insertItem('%s', %p, %d, %d)", item->text, QPOPUPMENU(item), item->id, pos); #endif if (icon.isNull()) item->container->insertItem(text, QPOPUPMENU(item), item->id, pos); else item->container->insertItem(icon, text, QPOPUPMENU(item), item->id, pos); } else { #ifdef DEBUG_MENU qDebug("show_menu: insertItem('%s', %d, %d)", item->text, item->id, pos); #endif if (icon.isNull()) item->container->insertItem(text, item->id, pos); else item->container->insertItem(icon, text, item->id, pos); } //if (pos < 0) // item->pos = item->container->indexOf(item->id); item->pos = pos; item->container->setItemEnabled(item->id, item->enabled); item->container->setItemChecked(item->id, item->checked); update_accel(item); if (CMENU_is_top(item)) { if (item->container->count() == 1) { ((QMenuBar *)item->container)->show(); ((MyMainWindow *)item->toplevel)->configure(); } } #ifdef DEBUG_MENU qDebug("show_menu: pos = %d", pos); #endif CWIDGET_set_flag(item, WF_VISIBLE); } static void init_menu(CMENU *item) { CMENU *parent; //int pos; bool change; QString text; #ifdef DEBUG_MENU qDebug("init_menu: item = %p (%d) parent = %p (%d)", item, item->id, item->parent, item->parent ? item->parent->id : 0); #endif parent = item->parent; if (parent) { if (QPOPUPMENU(parent) == 0) { #ifdef DEBUG_MENU qDebug("init_menu: add_popup"); #endif QPopupMenu *popup = new QPopupMenu(item->toplevel); change = CMENU_is_visible(parent); if (change) hide_menu(parent); //QPOPUPMENU(parent) = popup; SET_WIDGET(parent, popup); //parent->dict = new QIntDict; QObject::connect(popup, SIGNAL(activated(int)), &CMenu::manager, SLOT(activated(int))); QObject::connect(popup, SIGNAL(aboutToShow()), &CMenu::manager, SLOT(shown())); QObject::connect(popup, SIGNAL(aboutToHide()), &CMenu::manager, SLOT(hidden())); QObject::connect(popup, SIGNAL(destroyed()), &CMenu::manager, SLOT(destroy())); // un popupmenu est r��enc�deux fois ! // Et il ne faut pas effacer le tag ! //qDebug("*** show_menu %p (Popup)", parent); CWIDGET_new(popup, (void *)parent, true, true, true); if (change) show_menu(parent); } item->container = QPOPUPMENU(parent); } if (CMENU_is_top(item)) { if (item->container->count() == 0) { ((QMenuBar *)item->container)->hide(); ((MyMainWindow *)item->toplevel)->configure(); } } } static void delete_menu(CMENU *item) { #ifdef DEBUG_MENU qDebug("delete_menu: item = %p (%d)", item, item->id); #endif if (item->deleted) return; hide_menu(item); /* Evite de se retrouver dans la liste des enfants Dans CMENU_free, le removeRef() n'aura aucun effet */ if (item->parent) item->parent->children->removeRef(item); GB.Detach(item); if (QPOPUPMENU(item)) { delete QPOPUPMENU(item); //QPOPUPMENU(item) = 0; CLEAR_WIDGET(item); item->deleted = true; } else { item->deleted = true; GB.Unref(POINTER(&item)); } } static void unregister_menu(CMENU *_object) { CACTION_register(CONTROL, NULL); } BEGIN_METHOD(CMENU_new, GB_OBJECT parent; GB_BOOLEAN hidden) CMENU *item = OBJECT(CMENU); void *parent = VARG(parent); QWidget *topLevel = 0; QMenuBar *menuBar = 0; QList **list; //printf("CMENU_new %p\n", _object); if (GB.Is(parent, CLASS_Menu)) topLevel = ((CMENU *)parent)->toplevel; else if (GB.Is(parent, CLASS_Window)) { CWINDOW *window = (CWINDOW *)parent; topLevel = QWIDGET(CWidget::getWindow((CWIDGET *)parent)); //->topLevelWidget(); menuBar = window->menuBar; if (!menuBar) { if (topLevel->isA("QMainWindow")) menuBar = ((QMainWindow *)topLevel)->menuBar(); else menuBar = new QMenuBar(topLevel); window->menuBar = menuBar; } // parent = CWidget::get(QWIDGET(parent)->topLevelWidget()); //if (parent) // qwindow = (QMainWindow *)QWIDGET(parent); } else { GB.Error("Type mismatch. The parent control of a Menu must be a Window or another Menu."); return; } if (GB.CheckObject(parent)) return; CLEAR_WIDGET(item); item->widget.tag.type = GB_T_NULL; item->widget.name = NULL; item->children = NULL; item->text = NULL; item->picture = NULL; item->toplevel = topLevel; item->accel = new QKeySequence(); item->enabled = true; item->deleted = false; item->stretch = false; CWIDGET_init_name((CWIDGET *)item); menu_id++; item->id = menu_id; CWIDGET_clear_flag(item, WF_VISIBLE); if (GB.Is(parent, CLASS_Menu)) { CMENU *pmenu = (CMENU *)parent; item->container = NULL; /* plus tard */ item->parent = pmenu; list = &(pmenu->children); GB.Ref(parent); } else { item->container = menuBar; menuBar->setSeparator(QMenuBar::Never); item->parent = NULL; list = &(((CWINDOW *)parent)->menu); } CMenu::dict.insert(item->id, item); if (*list == NULL) *list = new QList; (*list)->append(item); #ifdef DEBUG_MENU qDebug("CMENU_new: item = %p (%d) parent = %p (%d) toplevel = %p", item, item->id, item->parent, item->parent ? item->parent->id : 0, item->toplevel); #endif //qDebug("*** CMENU_new %p", _object); GB.Ref(_object); init_menu(item); if (VARGOPT(hidden, FALSE)) hide_menu(item); else show_menu(item); END_METHOD BEGIN_METHOD_VOID(CMENU_free) CMENU *item = OBJECT(CMENU); #ifdef DEBUG_MENU qDebug("CMENU_free: item = %p '%s' (%d) parent = %p (%d)", item, item->text, item->id, item->parent, item->parent ? item->parent->id : 0); #endif set_text(item, NULL); GB.StoreObject(NULL, POINTER(&(THIS->picture))); #ifdef DEBUG_MENU qDebug("*** CMENU_free: free tag"); #endif GB.StoreVariant(NULL, &CONTROL->tag); CMenu::dict.remove(item->id); if (item->parent) { item->parent->children->removeRef(item); GB.Unref(POINTER(&item->parent)); } else { // item->toplevel may have been destroyed, and another widget can have taken the same address! // But if we ensure that the returned control is a CWINDOW, and that CWINDOW has a menu list, // it is safe to call removeRef on it, as it cannot have the same item address in it! CWINDOW *parent = (CWINDOW *)CWidget::getReal(item->toplevel); #ifdef DEBUG_MENU qDebug("CMENU_free: item = %p '%s' (%d) parent = %p (%d) toplevel = %p => parent = %p", item, item->text, item->id, item->parent, item->parent ? item->parent->id : 0, item->toplevel, parent); #endif if (parent && GB.Is(parent, CLASS_Window) && parent->menu) { parent->menu->removeRef(item); //GB.Unref(POINTER(&parent)); } } if (item->children) { delete item->children; item->children = NULL; } delete item->accel; GB.FreeString(&item->widget.name); #ifdef DEBUG_MENU qDebug("CMENU_free: item = %p '%s' (%d) is freed!", item, item->text, item->id); #endif //qDebug("< CMENU_free"); //if (item->parent != NULL) // if (item->parent->dict != NULL) // item->parent->dict->remove(item->id); //if (item->children != NULL) //{ // delete item->dict; // item->dict = NULL; //} END_METHOD BEGIN_PROPERTY(CMENU_text) if (READ_PROPERTY) { GB.ReturnString(THIS->text); } else { bool change = CMENU_is_visible(THIS); if (change) hide_menu(THIS); set_text(THIS, GB.ToZeroString(PROP(GB_STRING))); if (change) show_menu(THIS); //change_menu_item(item, GB.ToZeroString(PROPERTY(GB_STRING))); } END_PROPERTY BEGIN_PROPERTY(CMENU_picture) if (READ_PROPERTY) GB.ReturnObject(THIS->picture); else { bool change = CMENU_is_visible(THIS); if (change) hide_menu(THIS); GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->picture))); if (change) show_menu(THIS); } END_PROPERTY #if 0 BEGIN_PROPERTY(CMENU_stretch) if (READ_PROPERTY) GB.ReturnBoolean(THIS->stretch); else { bool change = CMENU_is_visible(THIS); if (change) hide_menu(THIS); THIS->stretch = VPROP(GB_BOOLEAN); if (change) show_menu(THIS); } END_PROPERTY #endif BEGIN_PROPERTY(CMENUITEM_enabled) CMENU *item = OBJECT(CMENU); bool enabled; if (item->parent == 0) { if (READ_PROPERTY) GB.ReturnBoolean(((QMenuBar *)item->container)->isItemEnabled(item->id)); else { enabled = VPROP(GB_BOOLEAN); item->enabled = enabled; ((QMenuBar *)item->container)->setItemEnabled(item->id, enabled); //qDebug("setItemEnabled"); } } else { if (READ_PROPERTY) GB.ReturnBoolean(((QPopupMenu *)item->container)->isItemEnabled(item->id)); else { enabled = VPROP(GB_BOOLEAN); item->enabled = enabled; ((QPopupMenu *)item->container)->setItemEnabled(item->id, enabled); } } if (!READ_PROPERTY) { //qDebug("CMENUITEM_enabled: %s %s '%s'", item->widget.name, enabled ? "1" : "0", ((QString)(*item->accel)).latin1()); CMenu::enableAccel(item, enabled); } END_PROPERTY BEGIN_PROPERTY(CMENU_checked) CMENU *item = OBJECT(CMENU); if (item->parent == 0) { if (READ_PROPERTY) //GB.ReturnBoolean(((QMenuBar *)item->container)->isItemChecked(item->id)); GB.ReturnBoolean(0); //else //((QMenuBar *)item->container)->setItemChecked(item->id, PROPERTY(char) != 0); } else { if (READ_PROPERTY) GB.ReturnBoolean(((QPopupMenu *)item->container)->isItemChecked(item->id)); else { bool change = CMENU_is_visible(THIS); if (change) hide_menu(THIS); item->checked = VPROP(GB_BOOLEAN); if (change) show_menu(THIS); } } END_PROPERTY static void toggle_menu(CMENU *_object) { QPopupMenu *cont; if (!THIS->parent) return; //qDebug("toggle_menu: %s", THIS->text); cont = (QPopupMenu *)THIS->container; cont->setItemChecked(THIS->id, !cont->isItemChecked(THIS->id)); THIS->checked = cont->isItemChecked(THIS->id); } BEGIN_PROPERTY(CMENU_toggle) if (READ_PROPERTY) GB.ReturnBoolean(THIS->toggle); else THIS->toggle = VPROP(GB_BOOLEAN); END_PROPERTY static void send_click_event(CMENU *_object); BEGIN_PROPERTY(CMENU_value) CMENU *item = OBJECT(CMENU); if (THIS->toggle) { CMENU_checked(_object, _param); return; } if (READ_PROPERTY) { GB.ReturnBoolean(0); } else if (item->parent) { GB.Ref(THIS); send_click_event(THIS); } END_PROPERTY BEGIN_PROPERTY(CMENU_shortcut) CMENU *item = OBJECT(CMENU); QPopupMenu *parent; if (CMENU_is_popup(item) || CMENU_is_top(item)) { if (READ_PROPERTY) GB.ReturnNull(); return; } parent = (QPopupMenu *)item->container; if (READ_PROPERTY) { if (((int)(*(item->accel))) == 0) GB.ReturnNull(); else GB.ReturnNewZeroString(((QString)*(item->accel)).latin1()); } else { QString s = QSTRING_PROP(); delete item->accel; if (s.length() == 0) item->accel = new QKeySequence(" "); else item->accel = new QKeySequence(s); update_accel(item); } END_PROPERTY BEGIN_PROPERTY(CMENU_visible) CMENU *item = OBJECT(CMENU); if (READ_PROPERTY) GB.ReturnBoolean(CWIDGET_test_flag(item, WF_VISIBLE)); else { if (VPROP(GB_BOOLEAN)) show_menu(item); else hide_menu(item); } END_PROPERTY BEGIN_METHOD_VOID(CMENU_show) show_menu(THIS); END_METHOD BEGIN_METHOD_VOID(CMENU_hide) hide_menu(THIS); END_METHOD BEGIN_METHOD_VOID(CMENU_delete) CMENU *item = OBJECT(CMENU); delete_menu(item); END_METHOD BEGIN_PROPERTY(CMENU_count) CMENU *item = OBJECT(CMENU); if (CMENU_is_popup(item)) GB.ReturnInteger(item->children->count()); else GB.ReturnInteger(0); END_PROPERTY BEGIN_METHOD_VOID(CMENU_next) CMENU *item = OBJECT(CMENU); unsigned int index; if (item->children == NULL) { GB.StopEnum(); return; } index = ENUM(int); if (index >= item->children->count()) { GB.StopEnum(); return; } GB.ReturnObject(item->children->at(index)); ENUM(int) = index + 1; END_METHOD BEGIN_METHOD(CMENU_get, GB_INTEGER index) CMENU *item = OBJECT(CMENU); int index = VARG(index); if (item->children == NULL || index < 0 || index >= (int)item->children->count()) { GB.Error(GB_ERR_BOUND); return; } GB.ReturnObject(item->children->at(index)); END_METHOD BEGIN_METHOD_VOID(CMENU_clear) CMENU *item = OBJECT(CMENU); CMENU *child; if (CMENU_is_popup(item)) { QListIterator it(*(item->children)); while ((child = it.current())) { ++it; delete_menu(child); } } END_METHOD BEGIN_METHOD(CMENU_popup, GB_INTEGER x; GB_INTEGER y) CMENU *item = OBJECT(CMENU); if (CMENU_is_popup(item) && !item->exec) { QPopupMenu *popup = QPOPUPMENU(item); if (popup) { item->exec = true; CMenu::enableAccel(item, true); if (MISSING(x) || MISSING(y)) popup->exec(QCursor::pos()); else popup->exec(QPoint(VARG(x), VARG(y))); item->exec = false; MAIN_process_events(); } } END_METHOD BEGIN_PROPERTY(CMENU_window) GB.ReturnObject(CWidget::get(THIS->toplevel)); END_PROPERTY GB_DESC CMenuChildrenDesc[] = { GB_DECLARE(".MenuChildren", sizeof(CMENU)), GB_VIRTUAL_CLASS(), GB_METHOD("_next", "Menu", CMENU_next, NULL), GB_METHOD("_get", "Menu", CMENU_get, "(Index)i"), GB_METHOD("Clear", NULL, CMENU_clear, NULL), GB_PROPERTY_READ("Count", "i", CMENU_count), GB_END_DECLARE }; GB_DESC CMenuDesc[] = { GB_DECLARE("Menu", sizeof(CMENU)), //GB_HOOK_CHECK(CWIDGET_check), GB_HOOK_CHECK(check_menu), //GB_STATIC_METHOD("_init", NULL, CMENU_init, NULL), GB_METHOD("_new", NULL, CMENU_new, "(Parent)o[(Hidden)b]"), //GB_METHOD("_new", NULL, CMENU_new, "(Parent)o[(Visible)b]"), GB_METHOD("_free", NULL, CMENU_free, NULL), //GB_PROPERTY("Name", "s", CWIDGET_name), //GB_PROPERTY_READ("Count", "i", CMENU_count), //GB_PROPERTY_READ("Parent", "Control", CWIDGET_parent), GB_PROPERTY("Name", "s", CCONTROL_name), GB_PROPERTY("Caption", "s", CMENU_text), GB_PROPERTY("Text", "s", CMENU_text), GB_PROPERTY("Enabled", "b", CMENUITEM_enabled), GB_PROPERTY("Checked", "b", CMENU_checked), GB_PROPERTY("Tag", "v", CCONTROL_tag), GB_PROPERTY("Picture", "Picture", CMENU_picture), //GB_PROPERTY("Stretch", "b", CMENU_stretch), GB_PROPERTY("Shortcut", "s", CMENU_shortcut), GB_PROPERTY("Visible", "b", CMENU_visible), GB_PROPERTY("Toggle", "b", CMENU_toggle), GB_PROPERTY("Value", "b", CMENU_value), GB_PROPERTY("Action", "s", CCONTROL_action), GB_PROPERTY_READ("Window", "Window", CMENU_window), GB_PROPERTY_SELF("Children", ".MenuChildren"), //GB_PROPERTY_READ("Index", "i", CMENU_item_index), MENU_DESCRIPTION, GB_METHOD("Popup", NULL, CMENU_popup, "[(X)i(Y)i]"), GB_METHOD("Delete", NULL, CMENU_delete, NULL), GB_METHOD("Show", NULL, CMENU_show, NULL), GB_METHOD("Hide", NULL, CMENU_hide, NULL), //GB_EVENT("Delete", NULL, NULL, &EVENT_Destroy), // Must be first GB_EVENT("Click", NULL, NULL, &EVENT_Click), GB_EVENT("Show", NULL, NULL, &EVENT_Show), GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), GB_END_DECLARE }; /* CMenu class */ CMenu CMenu::manager; CMenuDict CMenu::dict; static void send_click_event(CMENU *_object) { if (THIS->toggle) toggle_menu(THIS); GB.Raise(THIS, EVENT_Click, 0); CACTION_raise(CONTROL); GB.Unref(POINTER(&_object)); } static void send_menu_event(CMENU *_object, intptr_t event) { GB.Raise(THIS, event, 0); GB.Unref(POINTER(&_object)); } void CMenu::activated(int id) { CMENU *menu = CMenu::dict[id]; GB.Ref(menu); GB.Post((GB_POST_FUNC)send_click_event, (intptr_t)menu); } void CMenu::shown(void) { GET_SENDER(menu); hideSeparators((CMENU *)menu); GB.Raise(menu, EVENT_Show, 0); } void CMenu::hidden(void) { GET_SENDER(menu); if (GB.CanRaise(menu, EVENT_Hide)) { GB.Ref(menu); GB.Post2((GB_POST_FUNC)send_menu_event, (intptr_t)menu, EVENT_Hide); } } void CMenu::enableAccel(CMENU *item, bool enable) { // Do not disable shortcuts when a menu is executed if (item->exec && !enable) return; item->noshortcut = !enable; update_accel(item); if (item->children) { CMENU *child; QListIterator it(*item->children); while ((child = it.current())) { ++it; CMenu::enableAccel(child, enable); } } } void CMenu::hideSeparators(CMENU *item) { if (item->children) { CMENU *child; CMENU *last_child; QListIterator it(*item->children); bool is_sep; bool last_sep; //qDebug("checking separators"); last_sep = true; last_child = 0; for(;;) { child = it.current(); if (!child) break; ++it; is_sep = CMENU_is_separator(child); //qDebug("separator = %d visible = %d (%p -> %p)", is_sep, CMENU_is_visible(child), child, it.current()); if (is_sep) { if (last_sep) { hide_menu(child); } else { show_menu(child); last_sep = true; last_child = child; } } else { if (CMENU_is_visible(child)) last_sep = false; } } if (last_sep && last_child) hide_menu(last_child); } } void CMenu::unrefChildren(CMenuList *list) { CMENU *child; QListIterator it(*list); while ((child = it.current())) { ++it; /* ne d���encer que les enfant simples. */ if (child->children == 0) { GB.Detach(child); unregister_menu(child); //qDebug("*** CMenu::destroy %p (child)", child); GB.Unref(POINTER(&child)); } } } void CMenu::destroy(void) { GET_SENDER(_object); #ifdef DEBUG_MENU qDebug("*** { CMenu::destroy %p", menu); #endif unrefChildren(THIS->children); #ifdef DEBUG_MENU qDebug("*** CMenu::destroy %p (UNREF)", menu); #endif unregister_menu(THIS); GB.Unref(POINTER(&_object)); //menu->dict = dict; #ifdef DEBUG_MENU qDebug("*** } CMenu::destroy: %p", menu); #endif }