// $Id: Cxx_Fs_Signal.cc,v 1.34 2004/05/10 13:18:00 christof Exp $ /* glade--: C++ frontend for glade (Gtk+ User Interface Builder) * Copyright (C) 1998 Christof Petig * Copyright (C) 1999-2000 Adolf Petig GmbH & Co. KG, written by Christof Petig * * 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 2 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "Cxx.hh" #include "Configuration.hh" #include "writers/WriterBase.hh" #include "WidgetMap.hh" static bool WidgetIsContained(const Widget &parent,const Widget &w) { if (w==parent) return true; for (Widget::const_contained_iterator i=parent.begin_contained(Internal_Both); i!=parent.end_contained();++i) { // std::cerr << (*i).Name() << ',' << w.Name() << ' ' << (*i).getTagPtr() << ',' << w.getTagPtr() << '\n'; if ((*i)==w) return true; if ((*i).Name()==w.Name()) { (*i).getTag().debug(); w.getTag().debug(); } } return false; } static std::string UserDataDeclaration(const std::string &arg) { if (arg=="true" || arg=="false") { return "bool user_data"; } if (!arg.empty() && isdigit(arg[0])) { std::string::const_iterator i=arg.begin(); for (;i!=arg.end() && isdigit(*i);++i); if (i==arg.end()) return "int user_data"; } // you can't easily pass strings through signals, perhaps with 1.1? return "const gchar *user_data"; } static std::string UserDataPassing(const std::string &arg) { if (arg=="true" || arg=="false") { return arg; } if (!arg.empty() && isdigit(arg[0])) { std::string::const_iterator i=arg.begin(); for (;i!=arg.end() && isdigit(*i);++i); if (i==arg.end()) return arg; } return "(const gchar*)"+Configuration.Translatable(arg); } /// concatenate two (maybe empty) argument lists static std::string arg_concat(const std::string &a,const std::string &b) { if (a.empty() || b.empty()) return a+b; return a+", "+b; } static std::string GlobalType(const std::string &object,const Tag **tgp=0) { WidgetMap_t::const_iterator i=WidgetMap.find(object); if (i!=WidgetMap.end()) { Widget w(*(i->second)); if (tgp) *tgp=i->second; const WriterBase &wr(LookupWriter(w,true)); return wr.TypeName(w); } #if 0 cerr << "object '" << object << "' not found\n"; for (WidgetMap_t::const_iterator j=WidgetMap.begin();j!=WidgetMap.end();++j) std::cerr << j->first << ' '; cerr << '\n'; #endif return ""; } bool Cxx_Fileset::ProcedureDeclared(const std::string &name) const { return find(ProcedureDeclList.begin(),ProcedureDeclList.end(),name)!=ProcedureDeclList.end(); } void Cxx_Fileset::ProcedureDeclared(const std::string &name,bool dummy) { assert(dummy); ProcedureDeclList.push_back(name); } /* * Note that we mustn't pass true to Lookup Writer since we are interested * in the Widget it is derived from, we should make this more explicit */ void Cxx_Fileset::DeclareSignalHandler(const WriterBase &wr, const Widget &w, const Widget &top, bool container, bool emit_virtual) { // signals are part of the widget declaration if (w.getBoolProperty(CXX_SEPERATE_CLASS) && !container) return; for (Widget::const_iterator i=w.get_Signals();i!=w.end();++i) { std::string signal(i->getGladeAttr("name","nosignal")); std::string handlername(Configuration.CName(i->getGladeAttr("handler","unknown"))); std::string rettype("void"); std::string scope; const std::string args(wr.SignalHandlerArgs(w,signal,rettype,scope)); std::string args22(args); const std::string user_data(i->hasGladeAttr("data") ?UserDataDeclaration(i->getGladeAttr("data")):std::string()); const std::string allargs(arg_concat(args,user_data)); if (GTKMM24) { Pkg_Version help=Configuration.gtkmm_version; Configuration.gtkmm_version=Pkg_Version(2,2,0); args22=wr.SignalHandlerArgs(w,signal,rettype,scope); Configuration.gtkmm_version=help; } // I can't add this member declaration, yet // (involves second tree scan (after registering) and adding // cross signal tags) if (i->hasGladeAttr("object") && i->getGladeAttr("object")!=top.Name()) { static bool did_print_head=false; std::string object(i->getGladeAttr("object")); const Tag *tg; std::string gt(GlobalType(object,&tg)); if (gt.empty()) { std::cerr << "I don't know the type of " << object << ", so I can't connect " << w.Name() << "'s " << signal << '\n'; continue; } bool is_signal; if (LookupWriter(Widget(*tg)).isInternalMethod(Widget(*tg), handlername,allargs,gt,is_signal)) continue; if (!did_print_head) { std::cerr << "Please make sure that these member functions exist, I can't declare them, yet.\n"; did_print_head=true; } std::cerr << '\t' << rettype << ' ' << gt << "::" << handlername << '(' << allargs << ")\n"; continue; } else { bool is_signal; std::string gt(Configuration.TypeName(top.Name())); if (LookupWriter(top).isInternalMethod(top, handlername,allargs,gt,is_signal)) continue; } if (ProcedureDeclared(handlername+allargs+(emit_virtual?"V":" "))) continue; if ((GTKMM2 || signal!="switch_page") && !Configuration.non_virtual_callbacks && emit_virtual) { gh.Private(); if (args!=args22) WriterBase::If24(gh); gh.Declaration() << "virtual "; gh.Funct_ReturnType(rettype).FunctionName(handlername) .FunctionArg(allargs).FunctionEndArgs(); gh.Assignment("0"); gh.EndLine(); if (args!=args22) { gh.CppElse(); gh.Declaration() << "virtual "; gh.Funct_ReturnType(rettype).FunctionName(handlername) .FunctionArg(arg_concat(args22,user_data)).FunctionEndArgs(); gh.Assignment("0"); gh.EndLine(); gh.EndIf(); } } else if (!emit_virtual) { h.Private().Declaration().Funct_ReturnType(rettype) .FunctionName(handlername).FunctionArg(allargs); h.EndLine(); } ProcedureDeclared(handlername+allargs+(emit_virtual?"V":" "),true); } } void Cxx_Fileset::ConnectSignalHandler(const WriterBase &wr, const Widget &w, const Widget &top, const std::string &_instance) { for (Widget::const_iterator i=w.get_Signals();i!=w.end();++i) { std::string instance(_instance); // we might need to modify it (see HACK) std::string signal=i->getGladeAttr("name",""); std::string handlername=i->getGladeAttr("handler","unknown"); bool after=i->getGladeBoolAttr("after"); bool non_virtual_callback=Configuration.non_virtual_callbacks; bool is_proxy=wr.isProxySignal(w,signal,after); if (!non_virtual_callback && GTKMM1 && signal=="switch_page") non_virtual_callback=true; // OPTIONMENU HACK BEGINS if (signal=="deactivate" && wr.TypeName(w)==(wr.GtkPrefix()+"OptionMenu")) instance+="get_menu()->"; else if (signal=="changed" && wr.TypeName(w)==(wr.GtkPrefix()+"TextView")) instance+="get_buffer()->"; // OPTIONMENU HACK ENDS gc.Statement() << instance; if (GTKMM2) gc << "signal_"; gc << Configuration.CName(signal); if (GTKMM2) gc << "()"; gc << ".connect" << (after && GTKMM1 && is_proxy?"_after":"") << "("; // dynamic cast(y) ? if (i->hasGladeAttr("data")) { gc << "SigC::bind("; } const std::string user_data(i->hasGladeAttr("data") ?UserDataDeclaration(i->getGladeAttr("data")):std::string("")); std::string rettype("void"); bool is_signal=false; std::string gt; if (i->hasGladeAttr("object") && i->getGladeAttr("object")!=top.Name()) { std::string object(i->getGladeAttr("object")); const Tag *tg(0); gt=GlobalType(object,&tg); if (gt.empty()) { std::cerr << "Internal error\n"; continue; } Widget w2(*tg); // fix gt and handlername std::string scope; LookupWriter(w2).isInternalMethod(w2,handlername, arg_concat(wr.SignalHandlerArgs(w,signal,rettype,scope),user_data), gt,is_signal); std::string WidgetPointer; if (WidgetIsContained(top,w2)) WidgetPointer=WriterBase::Pointer(w2); else { if (w2.getProperty("cxx_visibility","private")!="public") { std::cerr << w2.Name() << " needs to be public for signal(\""<" << Configuration.CName(handlername) << ".slot()"; } else { gc << "SigC::slot("; if (GTKMM2) gc << "*"; gc << WidgetPointer << ", &" << gt << "::" << Configuration.CName(handlername) << ')'; } } else { gt=Configuration.TypeName(top.Name(),!non_virtual_callback); const WriterBase &top_wr(LookupWriter(top)); std::string scope; top_wr.isInternalMethod(top,handlername, arg_concat(top_wr.SignalHandlerArgs(top,signal,rettype,scope),user_data), gt,is_signal); if (!is_signal || GTKMM2) { gc << "SigC::slot("; if (GTKMM2) gc << "*"; if (non_virtual_callback) gc << "static_cast(this)"; else gc << "this"; gc << ", &" << gt << "::" << Configuration.CName(handlername) << ')'; } else gc << WriterBase::Pointer(top) << "->" << Configuration.CName(handlername) << ".slot()"; } if (i->hasGladeAttr("data")) { gc << ", " << UserDataPassing(i->getGladeAttr("data")) << ')'; } if (GTKMM2 && is_proxy) { if (after) gc << ", true"; // now the default else gc << ", false"; } gc << ')'; gc.EndLine(); } } #include // perhaps an ordered container would be nice typedef std::list ProcedureList_t; static ProcedureList_t ProcedureList; static bool ProcedureDefined(const std::string &name) { return find(ProcedureList.begin(),ProcedureList.end(),name)!=ProcedureList.end(); } static void ProcedureDefined(const std::string &name,bool dummy) { assert(dummy); ProcedureList.push_back(name); } void Cxx_Fileset::DefineSignalHandler(const WriterBase &wr,const Widget &w,const Widget &top) { for (Widget::const_iterator i=w.get_Signals();i!=w.end();++i) { std::string signal(i->getGladeAttr("name","nosignal")); std::string handlername(i->getGladeAttr("handler","unknown")); std::string rettype("void"); std::string scope; const std::string args(wr.SignalHandlerArgs(w,signal,rettype,scope)); const std::string user_data(i->hasGladeAttr("data") ?UserDataDeclaration(i->getGladeAttr("data")):std::string("")); const std::string allargs(arg_concat(args,user_data)); std::string name_space; bool is_signal=false; if (i->hasGladeAttr("object") && i->getGladeAttr("object")!=top.Name()) { std::string object(i->getGladeAttr("object")); const Tag *tg; name_space=GlobalType(object,&tg); if (name_space.empty()) continue; if (LookupWriter(Widget(*tg)).isInternalMethod(Widget(*tg), handlername,allargs,name_space,is_signal)) continue; } else { name_space=Configuration.TypeName(top.Name()); if (LookupWriter(top).isInternalMethod(top, handlername,allargs,name_space,is_signal)) continue; } if (ProcedureDefined(name_space+"::"+handlername)) continue; c.Definition().Funct_ReturnType(rettype).FunctionName() << name_space << "::" << Configuration.CName(handlername); c.FunctionArg(allargs); c.StartBlock(); if (Configuration.sample_code) { c.Statement() << "std::cout << \"" << name_space << "::" << Configuration.CName(handlername) << '('; if (!user_data.empty()) c << "\" << user_data << \""; c << ") called\\n\""; } if (rettype!="void") c.Statement("return 0"); c.EndBlock(); ProcedureDefined(name_space+"::"+handlername,true); } }