diff --git a/Makefile.in b/Makefile.in index 9f91c634..8223771f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -20,7 +20,7 @@ MKDEPS := ./config.status PROGS:= yate YLIB := libyate.so.@PACKAGE_VERSION@ SLIBS:= $(YLIB) libyate.so -INCS := yateclass.h yatengine.h yatephone.h +INCS := yateclass.h yatengine.h yatephone.h yatecbase.h GENS := yateversn.h LIBS := MAN8 := yate.8 yate-config.8 @@ -86,7 +86,9 @@ apidocs-build: check-topdir apidocs: @srcdir@/docs/api/index.html -@srcdir@/docs/api/index.html: @srcdir@/yateclass.h @srcdir@/yatengine.h @srcdir@/yatephone.h @srcdir@/contrib/ysip/yatesip.h @srcdir@/contrib/yrtp/yatertp.h +@srcdir@/docs/api/index.html: @srcdir@/yateclass.h @srcdir@/yatengine.h \ + @srcdir@/yatephone.h @srcdir@/yatecbase.h \ + @srcdir@/contrib/ysip/yatesip.h @srcdir@/contrib/yrtp/yatertp.h $(MAKE) apidocs-build .PHONY: strip sex love war diff --git a/clients/Makefile.in b/clients/Makefile.in index 889b7c3f..5945ed4d 100644 --- a/clients/Makefile.in +++ b/clients/Makefile.in @@ -29,6 +29,10 @@ ifneq (@HAVE_QT@,no) PROGS := $(PROGS) yate-qt endif +ifneq (@HAVE_GTK2@,no) +PROGS := $(PROGS) yate-gtk2 +endif + LOCALFLAGS = LOCALLIBS = COMPILE = $(CXX) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS) @@ -86,9 +90,16 @@ yate-%: @srcdir@/main-%.cpp $(MKDEPS) ../libyate.so $(INCFILES) yate-gtk: LOCALFLAGS = @GTK_INC@ yate-gtk: LOCALLIBS = @GTK_LIB@ +yate-gtk2: ../contrib/gtk2/libgtk2client.a +yate-gtk2: LOCALFLAGS = @GTK2_INC@ +yate-gtk2: LOCALLIBS = ../contrib/gtk2/libgtk2client.a @GTK2_LIB@ + yate-qt: ../contrib/qt/libqtclientform.a yate-qt: LOCALFLAGS = -I@QT_INC@ yate-qt: LOCALLIBS = ../contrib/qt/libqtclientform.a -L@QT_DIR@/lib -lqt-mt ../contrib/qt/libqtclientform.a: $(MAKE) -C ../contrib/qt + +../contrib/gtk2/libgtk2client.a: + $(MAKE) -C ../contrib/gtk2 diff --git a/clients/main-gtk2.cpp b/clients/main-gtk2.cpp new file mode 100644 index 00000000..4f1b6639 --- /dev/null +++ b/clients/main-gtk2.cpp @@ -0,0 +1,43 @@ +/** + * main-gtk2.cpp + * This file is part of the YATE Project http://YATE.null.ro + * + * A Gtk-2 based universal telephony client + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004, 2005 Null Team + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include "../contrib/gtk2/gtk2client.h" + +using namespace TelEngine; + +static GTKDriver gtkdriver; + +extern "C" int main(int argc, const char** argv, const char** environ) +{ + g_thread_init(NULL); + gdk_threads_init(); + bool fail = !gtk_init_check(&argc,(char ***)&argv); + if (fail) + g_warning("Cannot open display: '%s'",gdk_get_display()); + TelEngine::Engine::extraPath() = "gtk2"; + return TelEngine::Engine::main(argc,argv,environ,TelEngine::Engine::Client,fail); +} +/* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/clients/run-gtk2 b/clients/run-gtk2 new file mode 100755 index 00000000..b514f03f --- /dev/null +++ b/clients/run-gtk2 @@ -0,0 +1,11 @@ +#!/bin/sh + +# Script to run the Gtk2 client from the build directory + +if [ -x yate-gtk2 -a -x ../run ]; then + # Need to put the path to Mozilla libraries here + export LD_LIBRARY_PATH=/usr/lib/mozilla-1.6 + cd ..; exec ./run --executable clients/yate-gtk2 "$@" +else + echo "Could not find client executable or run script" >&2 +fi diff --git a/configure.in b/configure.in index 1b57b6d2..849354ef 100644 --- a/configure.in +++ b/configure.in @@ -203,6 +203,21 @@ fi AC_SUBST(HAVE_GSM) AC_SUBST(GSM_INC) +HAVE_ILBC=no +AC_ARG_ENABLE(ilbc,AC_HELP_STRING([--enable-ilbc],[Enable iLBC codec]),want_ilbc=$enableval,want_ilbc=yes) +if [[ "x$want_ilbc" = "xyes" ]]; then + HAVE_ILBC=yes +fi +AC_MSG_CHECKING([for iLBC in contrib]) + inci10="contrib/ilbc" + if [[ -f "$basedir/$inci10/iLBC_encode.h" ]]; then + HAVE_ILBC=yes + ILBC_INC="-I$basedir/$inci2" + ILBC_DEP="../$inci2/libiax.a" + fi +AC_MSG_RESULT([$HAVE_ILBC]) +AC_SUBST(HAVE_ILBC) + HAVE_PWLIB=no PWLIB_RTTI=none PWLIB_INC="" @@ -465,6 +480,49 @@ AC_SUBST(HAVE_GTK) AC_SUBST(GTK_INC) AC_SUBST(GTK_LIB) +HAVE_GTK2=no +GTK2_INC="" +GTK2_LIB="" +AC_ARG_WITH(libgtk2,AC_HELP_STRING([--with-libgtk2],[use Gtk for graphical clients (default)]),[ac_cv_use_libgtk2=$withval],[ac_cv_use_libgtk2=yes]) +if [[ "x$ac_cv_use_libgtk2" = "xyes" ]]; then + AC_MSG_CHECKING([for Gtk2 using pkg-config]) + vergt=`pkg-config --modversion gtk+-2.0 2>/dev/null` + incgt=`pkg-config --cflags gtk+-2.0 gthread-2.0 2>/dev/null` + libgt=`pkg-config --libs gtk+-2.0 gthread-2.0 2>/dev/null` + if [[ "x$incgt" != "x" -a "x$libgt" != "x" ]]; then + HAVE_GTK2=yes + GTK2_INC="$incgt" + GTK2_LIB="$libgt" + ac_cv_use_libgtk2="no" + fi + AC_MSG_RESULT([$vergt]) +fi +AC_SUBST(HAVE_GTK2) +AC_SUBST(GTK2_INC) +AC_SUBST(GTK2_LIB) + +HAVE_GMOZ=no +GMOZ_INC="" +GMOZ_LIB="" +AC_ARG_WITH(gtkmozilla,AC_HELP_STRING([--with-gtkmozilla],[use Mozilla embedded in Gtk clients (default)]),[ac_cv_use_gtkmozilla=$withval],[ac_cv_use_gtkmozilla=yes]) +if [[ "x$ac_cv_use_gtkmozilla" = "xyes" ]]; then + AC_MSG_CHECKING([for Gtk Mozilla embedding using pkg-config]) + vergmoz=`pkg-config --modversion mozilla-gtkmozembed 2>/dev/null` + incgmoz=`pkg-config --cflags mozilla-gtkmozembed 2>/dev/null` + libgmoz=`pkg-config --libs mozilla-gtkmozembed 2>/dev/null` + if [[ "x$incgmoz" != "x" -a "x$libgmoz" != "x" ]]; then + HAVE_GMOZ=yes + GMOZ_INC="$incgmoz" + GMOZ_LIB="$libgmoz" + ac_cv_use_gtkmozilla="no" + fi + AC_MSG_RESULT([$vergmoz]) +fi +AC_SUBST(HAVE_GMOZ) +AC_SUBST(GMOZ_INC) +AC_SUBST(GMOZ_LIB) + + HAVE_QT=no QT_DIR="" QT_INC="" @@ -552,6 +610,7 @@ AC_CONFIG_FILES([yate.spec contrib/ysip/Makefile contrib/yrtp/Makefile contrib/qt/Makefile + contrib/gtk2/Makefile test/Makefile]) AC_CONFIG_FILES([yate-config],[chmod +x yate-config]) AC_CONFIG_FILES([run],[chmod +x run]) diff --git a/contrib/gtk2/.cvsignore b/contrib/gtk2/.cvsignore new file mode 100644 index 00000000..cfebb73f --- /dev/null +++ b/contrib/gtk2/.cvsignore @@ -0,0 +1,7 @@ +Makefile +core* +*.o +*.a +*.orig +*~ +.*.swp diff --git a/contrib/gtk2/Makefile.in b/contrib/gtk2/Makefile.in new file mode 100644 index 00000000..f0e9d948 --- /dev/null +++ b/contrib/gtk2/Makefile.in @@ -0,0 +1,38 @@ +# Makefile +# This file holds the make rules for the Gtk2 client support + +CXX := @CXX@ -Wall +AR := ar +DEFS:= + +INCLUDES:=-I. -I@srcdir@ -I@top_srcdir@ @GTK2_INC@ +CXXFLAGS:=$(CXXFLAGS) @MODULE_CPPFLAGS@ @INLINE_FLAGS@ +LDFLAGS:= -L../.. -lyate +INCFILES := @top_srcdir@/yateclass.h @top_srcdir@/yatecbase.h @srcdir@/gtk2client.h +MODFLAGS:= @MODULE_LDFLAGS@ +MODSTRIP:= @MODULE_SYMBOLS@ + +PROJECT = libgtk2client.a +SOURCES = gtk2client.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +COMPILE = $(CXX) $(DEFS) $(INCLUDES) $(CXXFLAGS) + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +.PHONY: all clean + +all: $(PROJECT) + +clean: + @-$(RM) $(PROJECT) $(OBJECTS) + +$(PROJECT): $(OBJECTS) + $(AR) rcs $@ $^ + +%.o: @srcdir@/%.cpp $(INCFILES) + $(COMPILE) -c $< + +Makefile: @srcdir@/Makefile.in ../../config.status + cd ../.. && ./config.status diff --git a/contrib/gtk2/gtk2client.cpp b/contrib/gtk2/gtk2client.cpp new file mode 100644 index 00000000..9e4bbf48 --- /dev/null +++ b/contrib/gtk2/gtk2client.cpp @@ -0,0 +1,1000 @@ +/** + * gtk2client.cpp + * This file is part of the YATE Project http://YATE.null.ro + * + * A Gtk based universal telephony client + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004, 2005 Null Team + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include "gtk2client.h" + +using namespace TelEngine; + +static int s_shown = 0; +static GtkWidget* s_moving = 0; +static Configuration s_cfg; +static Configuration s_save; +static ObjList s_factories; + +#define INVALID_POS (-1000000) +#define MAX_CONTAINER_DEPTH 20 + +// Internal class used to recursively find a widget +class WidgetFinder { +public: + inline WidgetFinder(const String& name) + : m_name(name), m_widget(0) + { } + GtkWidget* find(GtkContainer* container); +private: + static void findCb(GtkWidget* wid, gpointer dat); + const String& m_name; + GtkWidget* m_widget; +}; + +void WidgetFinder::findCb(GtkWidget* wid, gpointer dat) +{ + WidgetFinder* f = static_cast(dat); + if ((!f) || f->m_widget) + return; + const gchar* name = gtk_widget_get_name(wid); + if (f->m_name == name) { + f->m_widget = wid; + return; + } + if GTK_IS_CONTAINER(wid) + gtk_container_foreach(GTK_CONTAINER(wid),findCb,dat); +} + +GtkWidget* WidgetFinder::find(GtkContainer* container) +{ + gtk_container_foreach(container,findCb,this); + return m_widget; +} + +bool Widget::setText(const String& text) + { return GTKWindow::setText(m_widget,text); } +bool Widget::setCheck(bool checked) + { return GTKWindow::setCheck(m_widget,checked); } +bool Widget::setSelect(const String& item) + { return GTKWindow::setSelect(m_widget,item); } +bool Widget::addOption(const String& item, bool atStart) + { return GTKWindow::addOption(m_widget,item,atStart); } +bool Widget::delOption(const String& item) + { return GTKWindow::delOption(m_widget,item); } +bool Widget::getText(String& text) + { return GTKWindow::getText(m_widget,text); } +bool Widget::getCheck(bool& checked) + { return GTKWindow::getCheck(m_widget,checked); } + +typedef GtkWidget* (*GBuilder) (const gchar *label); + +// Internal class used to build widgets +class WidgetMaker +{ +public: + const char* name; + GBuilder builder; + const gchar* sig; + GCallback cb; +}; + +static Widget* getWidget(GtkWidget* wid) +{ + if (!wid) + return 0; + return static_cast(g_object_get_data((GObject*)wid,"Yate::Widget")); +} + +static GTKWindow* getWidgetWindow(GtkWidget* wid) +{ + if (!wid) + return 0; + GtkWidget* top = gtk_widget_get_toplevel(wid); + if (!top) + return 0; + return static_cast(g_object_get_data((GObject*)top,"Yate::Window")); +} + +static gboolean widgetCbAction(GtkWidget* wid, gpointer dat) +{ + Debug(GTKDriver::self(),DebugAll,"widgetCbAction data %p",dat); + if (GTKClient::changing()) + return FALSE; + GTKWindow* wnd = getWidgetWindow(wid); + return wnd && wnd->action(wid); +} + +static gboolean widgetCbToggle(GtkToggleButton* btn, gpointer dat) +{ + Debug(GTKDriver::self(),DebugAll,"widgetCbToggle data %p",dat); + if (GTKClient::changing()) + return FALSE; + GTKWindow* wnd = getWidgetWindow((GtkWidget*)btn); + return wnd && wnd->toggle(btn,gtk_toggle_button_get_active(btn)); +} + +static gboolean widgetCbSelected(GtkOptionMenu* opt, gpointer dat) +{ + Debug(GTKDriver::self(),DebugAll,"widgetCbChanged data %p",dat); + if (GTKClient::changing()) + return FALSE; + GTKWindow* wnd = getWidgetWindow((GtkWidget*)opt); + return wnd && wnd->select(opt,gtk_option_menu_get_history(opt)); +} + +static gboolean widgetCbMinimize(GtkWidget* wid, gpointer dat) +{ + DDebug(GTKDriver::self(),DebugAll,"widgetCbMinimize data %p",dat); + GtkWidget* top = gtk_widget_get_toplevel(wid); + if (!top) + return FALSE; + gtk_window_iconify((GtkWindow*)top); + return TRUE; +} + +static gboolean widgetCbMaximize(GtkWidget* wid, gpointer dat) +{ + DDebug(GTKDriver::self(),DebugAll,"widgetCbMaximize data %p",dat); + GtkWidget* top = gtk_widget_get_toplevel(wid); + if (!top) + return FALSE; + GTKWindow* wnd = getWidgetWindow(wid); + if (wnd && (wnd->state() & GDK_WINDOW_STATE_MAXIMIZED)) + gtk_window_unmaximize((GtkWindow*)top); + else + gtk_window_maximize((GtkWindow*)top); + return TRUE; +} + +static gboolean widgetCbHide(GtkWidget* wid, gpointer dat) +{ + DDebug(GTKDriver::self(),DebugAll,"widgetCbHide data %p",dat); + if (GTKClient::changing()) + return FALSE; + GTKWindow* wnd = getWidgetWindow(wid); + if (wnd) { + wnd->hide(); + return TRUE; + } + return FALSE; +} + +static gboolean widgetCbShow(GtkWidget* wid, gpointer dat) +{ + const gchar* name = gtk_widget_get_name(wid); + Debug(GTKDriver::self(),DebugAll,"widgetCbShow '%s'",name); + return GTKClient::setVisible(name); +} + +static GtkWidget* gtkLeftLabelNew(const gchar* text) +{ + GtkWidget* lbl = gtk_label_new(text); + if (lbl) + gtk_misc_set_alignment((GtkMisc*)lbl,0,0); + return lbl; +} + +static GtkWidget* gtkEntryNewWithText(const gchar* text) +{ + GtkWidget* ent = gtk_entry_new(); + if (text) + gtk_entry_set_text((GtkEntry*)ent,text); + return ent; +} + +static GtkWidget* gtkComboNewWithText(const gchar* text) +{ + GtkWidget* combo = gtk_combo_new(); + if (combo) { + GtkWidget* ent = GTK_COMBO(combo)->entry; + if (ent) + gtk_entry_set_text((GtkEntry*)ent,text); + } + return combo; +} + +static GtkWidget* gtkOptionMenuNew(const gchar* text) +{ + GtkWidget* opt = gtk_option_menu_new(); + if (opt) { + GtkWidget* mnu = gtk_menu_new(); + if (mnu) { + String tmp(text); + ObjList* l = tmp.split(','); + for (ObjList* i = l; i; i = i->next()) { + String* s = static_cast(i->get()); + if (s && *s) { + GtkWidget* item = gtk_menu_item_new_with_label(s->c_str()); + gtk_menu_shell_append(GTK_MENU_SHELL(mnu),item); + } + } + if (l) + l->destruct(); + gtk_option_menu_set_menu(GTK_OPTION_MENU(opt),mnu); + } + } + return opt; +} + +static WidgetMaker s_widgetMakers[] = { + { "label", gtkLeftLabelNew, 0, 0 }, + { "editor", gtkEntryNewWithText, "activate", G_CALLBACK(widgetCbAction) }, + { "button", gtk_button_new_with_label, "clicked", G_CALLBACK(widgetCbAction) }, + { "toggle", gtk_toggle_button_new_with_label, "toggled", G_CALLBACK(widgetCbToggle) }, + { "check", gtk_check_button_new_with_label, "toggled", G_CALLBACK(widgetCbToggle) }, + { "combo", gtkComboNewWithText, 0, 0 }, + { "option", gtkOptionMenuNew, "changed", G_CALLBACK(widgetCbSelected) }, + { "frame", gtk_frame_new, 0, 0 }, + { "image", gtk_image_new_from_file, 0, 0 }, + { "hseparator", (GBuilder)gtk_hseparator_new, 0, 0 }, + { "vseparator", (GBuilder)gtk_vseparator_new, 0, 0 }, + { "button_show", gtk_button_new_with_label, "clicked", G_CALLBACK(widgetCbShow) }, + { "button_icon", gtk_button_new_with_label, "clicked", G_CALLBACK(widgetCbMinimize) }, + { "button_hide", gtk_button_new_with_label, "clicked", G_CALLBACK(widgetCbHide) }, + { "button_max", gtk_button_new_with_label, "clicked", G_CALLBACK(widgetCbMaximize) }, + { 0, 0, 0, 0 }, +}; +// { "", gtk__new, "", }, + +static gboolean windowCbState(GtkWidget* wid, GdkEventWindowState* evt, gpointer dat) +{ + DDebug(GTKDriver::self(),DebugAll,"windowCbState data %p",dat); + GTKWindow* wnd = static_cast(dat); + if (wnd && evt) + wnd->state(evt->new_window_state); + return FALSE; +} + +static gboolean windowCbConfig(GtkWidget* wid, GdkEventConfigure* evt, gpointer dat) +{ + XDebug(GTKDriver::self(),DebugAll,"windowCbConfig data %p",dat); + if (wid != s_moving) + return FALSE; + GTKWindow* wnd = static_cast(dat); + if (wnd) + wnd->geometry(evt->x,evt->y,evt->width,evt->height); + return FALSE; +} + +static gboolean windowCbClose(GtkWidget* wid, GdkEvent* evt, gpointer dat) +{ + DDebug(GTKDriver::self(),DebugAll,"windowCbClose event %d data %p",evt->type,dat); + GTKWindow* wnd = static_cast(dat); + if (wnd) { + wnd->hide(); + return TRUE; + } + return FALSE; +} + +static gboolean windowCbInfo(GtkWidget* wid) +{ + gchar* wp = NULL; + gchar* wcp = NULL; + gtk_widget_path(wid,NULL,&wp,NULL); + gtk_widget_class_path(wid,NULL,&wcp,NULL); + Debug(GTKDriver::self(),DebugAll,"windowCbInfo widget %p path '%s' class path '%s'", + wid,wp,wcp); + delete wp; + delete wcp; + return FALSE; +} + +static gboolean windowCbClick(GtkWidget* wid, GdkEventButton* evt, gpointer dat) +{ + DDebug(GTKDriver::self(),DebugAll,"windowCbClick event %d data %p",evt->type,dat); + GTKWindow* wnd = static_cast(dat); + if (evt->type != GDK_BUTTON_PRESS) + return FALSE; + if (wnd && (evt->button == 3)) { + wnd->menu((int)evt->x_root,(int)evt->y_root); + return TRUE; + } + if (evt->button != 1) + return FALSE; + GtkWidget* top = gtk_widget_get_toplevel(wid); + if (top) { + s_moving = top; + if (wnd) + wnd->prepare(); + gtk_window_begin_move_drag((GtkWindow*)top,evt->button,(int)evt->x_root,(int)evt->y_root,evt->time); + return TRUE; + } + return FALSE; +} + +#ifdef XDEBUG +static gboolean windowCbEvent(GtkWidget* wid, GdkEvent* evt, gpointer dat) +{ + Debug(GTKDriver::self(),DebugAll,"windowCbEvent widget %p event %d data %p",wid,evt->type,dat); + return FALSE; +} +#endif + +static TokenDict s_layoutNames[] = { + { "fixed", GTKWindow::Fixed }, + { "table", GTKWindow::Table }, + { "infinite", GTKWindow::Infinite }, + { "hbox", GTKWindow::HBox }, + { "vbox", GTKWindow::VBox }, + { "boxed", GTKWindow::Boxed }, + { "tabbed", GTKWindow::Tabbed }, + { "framed", GTKWindow::Framed }, + { 0, 0 }, +}; + +Widget::Widget() + : m_widget(0) +{ + Debug(GTKDriver::self(),DebugAll,"Widget::Widget() [%p]",this); +} + +Widget::~Widget() +{ + Debug(GTKDriver::self(),DebugAll,"Widget::~Widget() [%p]",this); + widget(0); +} + +void Widget::widget(GtkWidget* wid) +{ + if (wid == m_widget) + return; + if (m_widget) { + g_object_set_data((GObject*)m_widget,"Yate::Widget",0); + m_widget = 0; + } + if (wid) { + g_object_set_data((GObject*)wid,"Yate::Widget",this); + g_signal_connect(G_OBJECT(wid),"destroy",G_CALLBACK(destroyCb),this); + m_widget = wid; + } +} + +void Widget::destroyed() +{ + m_widget = 0; + delete this; +} + +void Widget::destroyCb(GtkObject* obj, gpointer dat) +{ + Debug(GTKDriver::self(),DebugAll,"widgetCbDestroy object %p data %p",obj,dat); + Widget* w = static_cast(dat); + if (w) + w->destroyed(); +} + + +GTKWindow::GTKWindow(const char* id, Layout layout) + : Window(id), m_widget(0), m_filler(0), m_layout(layout), m_state(0), + m_posX(INVALID_POS), m_posY(INVALID_POS), m_sizeW(0), m_sizeH(0) +{ + m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_object_set_data((GObject*)m_widget,"Yate::Window",this); + gtk_window_set_role((GtkWindow*)m_widget,id); +// gtk_window_set_type_hint((GtkWindow*)m_widget,GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_decorated((GtkWindow*)m_widget,FALSE); +// gtk_window_set_resizable((GtkWindow*)m_widget,FALSE); + gtk_widget_add_events(m_widget,GDK_BUTTON_PRESS_MASK); + gtk_widget_add_events(m_widget,GDK_BUTTON_RELEASE_MASK); + g_signal_connect(G_OBJECT(m_widget),"button_press_event",G_CALLBACK(windowCbClick),this); + g_signal_connect(G_OBJECT(m_widget),"delete_event",G_CALLBACK(windowCbClose),this); + g_signal_connect(G_OBJECT(m_widget),"configure_event",G_CALLBACK(windowCbConfig),this); + g_signal_connect(G_OBJECT(m_widget),"window_state_event",G_CALLBACK(windowCbState),this); +#ifdef XDEBUG + g_signal_connect(G_OBJECT(m_widget),"event",G_CALLBACK(windowCbEvent),this); +#endif +} + +GTKWindow::~GTKWindow() +{ + prepare(); + m_widget = 0; + if ((m_posX != INVALID_POS) && (m_posY != INVALID_POS)) { + Debug(GTKDriver::self(),DebugAll,"saving '%s' %d,%d",m_id.c_str(),m_posX,m_posY); + s_save.setValue(m_id,"x",m_posX); + s_save.setValue(m_id,"y",m_posY); + s_save.setValue(m_id,"w",m_sizeW); + s_save.setValue(m_id,"h",m_sizeH); + } +} + +GtkWidget* GTKWindow::find(const String& name) const +{ + if (!(m_filler && name)) + return 0; + WidgetFinder wf(name); + return wf.find(GTK_CONTAINER(m_filler)); +} + +GtkWidget* GTKWindow::container(Layout layout) const +{ + DDebug(GTKDriver::self(),DebugAll,"Creating container type %s (%d)", + lookup(layout,s_layoutNames,"unknown"),layout); + switch (layout) { + case Fixed: + return gtk_fixed_new(); + case Table: + return gtk_table_new(100,100,FALSE); + case Infinite: + return gtk_layout_new(NULL,NULL); + case HBox: + return gtk_hbox_new(FALSE,0); + case VBox: + return gtk_vbox_new(FALSE,0); + case Boxed: + return gtk_event_box_new(); + case Tabbed: + return gtk_notebook_new(); + case Framed: + return gtk_frame_new(NULL); + default: + break; + } + return 0; +} + +GtkWidget* GTKWindow::container(const String& layout) const +{ + return container((Layout)layout.toInteger(s_layoutNames,Unknown)); +} + +GtkWidget* GTKWindow::filler() +{ + if (!m_filler) { + m_filler = container(m_layout); + if (!m_filler) + m_filler = container(HBox); + if (m_filler) + gtk_container_add(GTK_CONTAINER(m_widget),m_filler); + } + return m_filler; +} + +void GTKWindow::insert(GtkWidget* wid, int x, int y, int w, int h) +{ + Debug(GTKDriver::self(),DebugAll,"Inserting %dx%d widget at %d,%d (%p in %p)", + w,h,x,y,wid,filler()); + gtk_widget_set_size_request(wid,w,h); + if (GTK_IS_FIXED(filler())) + gtk_fixed_put(GTK_FIXED(filler()),wid,x,y); + else if (GTK_IS_LAYOUT(filler())) + gtk_layout_put(GTK_LAYOUT(filler()),wid,x,y); + else if (GTK_IS_BOX(filler())) + gtk_box_pack_start(GTK_BOX(filler()),wid,(x > 0),true,y); + else + gtk_container_add(GTK_CONTAINER(filler()),wid); +} + +GtkWidget* GTKWindow::build(const String& type, const String& text) +{ + WidgetMaker* def = s_widgetMakers; + for (; def->name; def++) { + if (type == def->name) { + GtkWidget* wid = def->builder(text.safe()); + if (def->sig && def->cb) + g_signal_connect(G_OBJECT(wid),def->sig,def->cb,0); + return wid; + } + } + GenObject* o = s_factories[type]; + WidgetFactory* f = YOBJECT(WidgetFactory,o); + Widget* w = f ? f->build(text) : 0; + return w ? w->widget() : 0; +} + +void GTKWindow::populate() +{ + gtk_widget_set_name(m_widget,m_id); + NamedList* sect = s_cfg.getSection(m_id); + if (!sect) + return; + GtkWidget* containerStack[MAX_CONTAINER_DEPTH]; + int depth = 0; + if (m_layout == Unknown) + m_layout = (Layout)sect->getIntValue("layout",s_layoutNames,GTKWindow::Unknown); + gtk_widget_set_size_request(filler(),sect->getIntValue("width",-1),sect->getIntValue("height",-1)); + int n = sect->length(); + for (int i = 0; i < n; i++) { + NamedString* p = sect->getParam(i); + if (!p) + continue; + int x = 0; + int y = 0; + int w = -1; + int h = -1; + String s(*p); + s >> x >> "," >> y >> "," >> w >> "," >> h >> ","; + String act; + int pos = s.find(','); + if (pos >= 0) { + act = s.substr(0,pos); + s = s.substr(pos+1); + } + GtkWidget* wid = build(p->name(),s.safe()); + if (wid) { +#ifdef DEBUG + g_signal_connect(G_OBJECT(wid),"button_press_event",G_CALLBACK(windowCbInfo),0); +#endif + if (act) + gtk_widget_set_name(wid,act); + insert(wid,x,y,w,h); + continue; + } + if (p->name() == "leave") { + if (depth > 0) { + Debug(GTKDriver::self(),DebugAll,"Popping container off stack of depth %d",depth); + depth--; + m_filler = containerStack[depth]; + } + continue; + } + if (depth >= MAX_CONTAINER_DEPTH) + continue; + wid = container(p->name()); + if (wid) { +#ifdef DEBUG + g_signal_connect(G_OBJECT(wid),"button_press_event",G_CALLBACK(windowCbInfo),0); +#endif + if (act) + gtk_widget_set_name(wid,act); + insert(wid,x,y,w,h); + containerStack[depth] = m_filler; + depth++; + m_filler = wid; + Debug(GTKDriver::self(),DebugAll,"Pushed container %p on stack of depth %d",wid,depth); + } + } +} + +void GTKWindow::init() +{ + gtk_window_set_title((GtkWindow*)m_widget,s_cfg.getValue(m_id,"title",m_id)); + m_master = s_cfg.getBoolValue(m_id,"master"); + if (!m_master) + gtk_window_set_type_hint((GtkWindow*)m_widget,GDK_WINDOW_TYPE_HINT_TOOLBAR); + m_posX = s_save.getIntValue(m_id,"x",m_posX); + m_posY = s_save.getIntValue(m_id,"y",m_posY); + m_sizeW = s_save.getIntValue(m_id,"w",m_sizeW); + m_sizeH = s_save.getIntValue(m_id,"h",m_sizeH); + restore(); + gtk_widget_show_all(m_widget); + m_visible = true; + ++s_shown; + if (GTKClient::self()) + GTKClient::self()->setCheck(m_id,true); +} + +void GTKWindow::show() +{ + Debug(GTKDriver::self(),DebugAll,"Window::show() '%s'",m_id.c_str()); + if (m_visible) + return; + ++s_shown; + gtk_widget_show(m_widget); + m_visible = true; + restore(); + if (GTKClient::self()) + GTKClient::self()->setCheck(m_id,true); +} + +void GTKWindow::hide() +{ + Debug(GTKDriver::self(),DebugAll,"Window::hide() '%s'",m_id.c_str()); + if (!m_visible) + return; + prepare(); + gtk_widget_hide(m_widget); + m_visible = false; + --s_shown; + if (GTKClient::self()) { + GTKClient::self()->setCheck(m_id,false); + if (!s_shown) + GTKClient::self()->allHidden(); + } +} + +void GTKWindow::size(int width, int height) +{ + if (!(width && height)) + return; + m_sizeW = width; + m_sizeH = height; + if (m_widget) + gtk_window_resize((GtkWindow*)m_widget,m_sizeW,m_sizeH); +} + +void GTKWindow::move(int x, int y) +{ + m_posX = x; + m_posY = y; + if (m_widget) + gtk_window_move((GtkWindow*)m_widget,m_posX,m_posY); +} + +void GTKWindow::moveRel(int dx, int dy) +{ + if ((m_posX == INVALID_POS) || (m_posY == INVALID_POS)) + return; + move(m_posX+dx,m_posY+dy); +} + +void GTKWindow::geometry(int x, int y, int w, int h) +{ + if ((m_posX == INVALID_POS) || (m_posY == INVALID_POS)) + return; + int dx = x - m_posX; + int dy = y - m_posY; + m_posX = x; + m_posY = y; + m_sizeW = w; + m_sizeH = h; + if (!m_visible) + return; + XDebug(GTKDriver::self(),DebugAll,"geometry '%s' %d,%d %dx%d moved %d,%d", + m_id.c_str(),x,y,w,h,dx,dy); + if (GTKClient::self() && (dx || dy) && m_master) + GTKClient::self()->moveRelated(this,dx,dy); +} + +bool GTKWindow::prepare() +{ + if (!(m_widget && m_visible)) + return false; + gtk_window_get_position((GtkWindow*)m_widget,&m_posX,&m_posY); + gtk_window_get_size((GtkWindow*)m_widget,&m_sizeW,&m_sizeH); + return true; +} + +bool GTKWindow::restore() +{ + if (!m_widget) + return false; + if ((m_posX == INVALID_POS) || (m_posY == INVALID_POS)) + return false; + move(m_posX,m_posY); + size(m_sizeW,m_sizeH); + return true; +} + +bool GTKWindow::action(GtkWidget* wid) +{ + const gchar* name = gtk_widget_get_name(wid); + Debug(GTKDriver::self(),DebugAll,"action '%s' wid=%p [%p]", + name,wid,this); + return GTKClient::self() && GTKClient::self()->action(this,name); +} + +bool GTKWindow::toggle(GtkToggleButton* btn, gboolean active) +{ + const gchar* name = gtk_widget_get_name((GtkWidget*)btn); + Debug(GTKDriver::self(),DebugAll,"toggle '%s' btn=%p active=%s [%p]", + name,btn,String::boolText(active),this); + return GTKClient::self() && GTKClient::self()->toggle(this,name,active); +} + +bool GTKWindow::select(GtkOptionMenu* opt, gint selected) +{ + const gchar* name = gtk_widget_get_name((GtkWidget*)opt); + Debug(GTKDriver::self(),DebugAll,"select '%s' opt=%p item=%d [%p]", + name,opt,selected,this); + // FIXME + String item(selected); + return GTKClient::self() && GTKClient::self()->select(this,name,item); +} + +bool GTKWindow::setShow(const String& name, bool visible) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + if (visible) + gtk_widget_show(wid); + else + gtk_widget_hide(wid); + return true; +} + +bool GTKWindow::setActive(const String& name, bool active) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + gtk_widget_set_sensitive(wid,active); + return true; +} + +bool GTKWindow::setText(const String& name, const String& text) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + Widget* yw = getWidget(wid); + return yw ? yw->setText(text) : setText(wid,text); +} + +bool GTKWindow::setText(GtkWidget* wid, const String& text) +{ + if (GTK_IS_LABEL(wid)) { + gtk_label_set_text(GTK_LABEL(wid),text.safe()); + return true; + } + if (GTK_IS_BUTTON(wid)) { + gtk_button_set_label(GTK_BUTTON(wid),text.safe()); + return true; + } + if (GTK_IS_ENTRY(wid)) { + gtk_entry_set_text(GTK_ENTRY(wid),text.safe()); + return true; + } + if (GTK_IS_COMBO(wid)) { + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(wid)->entry),text.safe()); + return true; + } + return false; +} + +bool GTKWindow::setCheck(const String& name, bool checked) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + Widget* yw = getWidget(wid); + return yw ? yw->setCheck(checked) : setCheck(wid,checked); +} + +bool GTKWindow::setCheck(GtkWidget* wid, bool checked) +{ + if (GTK_IS_TOGGLE_BUTTON(wid)) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wid),checked); + return true; + } + if (GTK_IS_CHECK_MENU_ITEM(wid)) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(wid),checked); + return true; + } + return false; +} + +bool GTKWindow::setSelect(const String& name, const String& item) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + Widget* yw = getWidget(wid); + return yw ? yw->setSelect(item) : setSelect(wid,item); +} + +bool GTKWindow::setSelect(GtkWidget* wid, const String& item) +{ + // FIXME + Debug(DebugMild,"Need to implement GTKWindow::setSelect()"); + return false; +} + +bool GTKWindow::addOption(const String& name, const String& item, bool atStart) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + Widget* yw = getWidget(wid); + return yw ? yw->addOption(item,atStart) : addOption(wid,item,atStart); +} + +bool GTKWindow::addOption(GtkWidget* wid, const String& item, bool atStart) +{ + // FIXME + Debug(DebugMild,"Need to implement GTKWindow::addOption()"); + return false; +} + +bool GTKWindow::delOption(const String& name, const String& item) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + Widget* yw = getWidget(wid); + return yw ? yw->delOption(item) : delOption(wid,item); +} + +bool GTKWindow::delOption(GtkWidget* wid, const String& item) +{ + // FIXME + Debug(DebugMild,"Need to implement GTKWindow::delOption()"); + return false; +} + +bool GTKWindow::getText(const String& name, String& text) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + Widget* yw = getWidget(wid); + return yw ? yw->getText(text) : getText(wid,text); +} + +bool GTKWindow::getText(GtkWidget* wid, String& text) +{ + if (GTK_IS_LABEL(wid)) { + text = gtk_label_get_text(GTK_LABEL(wid)); + return true; + } + if (GTK_IS_ENTRY(wid)) { + text = gtk_entry_get_text(GTK_ENTRY(wid)); + return true; + } + if (GTK_IS_COMBO(wid)) { + text = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(wid)->entry)); + return true; + } + return false; +} + +bool GTKWindow::getCheck(const String& name, bool& checked) +{ + GtkWidget* wid = find(name); + if (!wid) + return false; + Widget* yw = getWidget(wid); + return yw ? yw->getCheck(checked) : getCheck(wid,checked); +} + +bool GTKWindow::getCheck(GtkWidget* wid, bool& checked) +{ + if (GTK_IS_TOGGLE_BUTTON(wid)) { + checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid)); + return true; + } + if (GTK_IS_CHECK_MENU_ITEM(wid)) { + checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(wid)); + return true; + } + return false; +} + +void GTKWindow::menu(int x, int y) +{ + GtkWidget* mnu = gtk_menu_new(); + GtkWidget* item = gtk_check_menu_item_new_with_label("123"); + gtk_menu_shell_append((GtkMenuShell*)mnu,item); + gtk_menu_popup((GtkMenu*)mnu,NULL,NULL,NULL,NULL,3,gtk_get_current_event_time()); +} + + +GTKClient::GTKClient() + : Client("GTKClient") +{ + s_cfg = Engine::configPath() + Engine::pathSeparator() + "gtk2client.ui"; + s_cfg.load(); + s_save = Engine::configFile("gtk2client"); + s_save.load(); +} + +GTKClient::~GTKClient() +{ + m_windows.clear(); + s_save.save(); +} + +void GTKClient::lock() +{ + XDebug(GTKDriver::self(),DebugAll,"GTKClient::lock()"); + gdk_threads_enter(); +} + +void GTKClient::unlock() +{ + XDebug(GTKDriver::self(),DebugAll,"GTKClient::unlock()"); + gdk_flush(); + gdk_threads_leave(); +} + +void GTKClient::main() +{ + lock(); + gtk_main(); + unlock(); +} + +void GTKClient::allHidden() +{ + Debug(GTKDriver::self(),DebugInfo,"All %u windows hidden",m_windows.count()); + gtk_main_quit(); +} + +bool GTKClient::createWindow(const String& name) +{ + Window* w = 0; + GenObject* o = s_factories[name]; + const WindowFactory* f = YOBJECT(WindowFactory,o); + if (f) + w = f->build(); + else + w = new GTKWindow(name); + if (!w) { + Debug(GTKDriver::self(),DebugGoOn,"Could not create window '%s'",name.c_str()); + return false; + } + w->populate(); + m_windows.append(w); + return true; +} + +void GTKClient::loadWindows() +{ + gtk_rc_parse(Engine::configPath() + Engine::pathSeparator() + "gtk2client.rc"); + unsigned int n = s_cfg.sections(); + for (unsigned int i = 0; i < n; i++) { + NamedList* l = s_cfg.getSection(i); + if (l && l->getBoolValue("enabled",true)) + createWindow(*l); + } +} + + +GTKDriver::GTKDriver() +{ +} + +GTKDriver::~GTKDriver() +{ +} + +void GTKDriver::initialize() +{ + Output("Initializing module GTK2 client"); + s_device = Engine::config().getValue("client","device","oss//dev/dsp"); + if (!GTKClient::self()) + { + debugCopy(); + new GTKClient; + GTKClient::self()->startup(); + } + setup(); + installRelay(Halt); +} + +bool GTKDriver::factory(UIFactory* factory, const char* type) +{ + if (!type) { + s_factories.remove(factory,false); + return true; + } + String tmp(type); + if (tmp == "gtk2") { + s_factories.append(factory)->setDelete(false); + return true; + } + return false; +} + +WindowFactory::WindowFactory(const char* type, const char* name) + : UIFactory(type,name) +{ +} + + +WidgetFactory::WidgetFactory(const char* type, const char* name) + : UIFactory(type,name) +{ +} + +/* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/contrib/gtk2/gtk2client.h b/contrib/gtk2/gtk2client.h new file mode 100644 index 00000000..d9ce5853 --- /dev/null +++ b/contrib/gtk2/gtk2client.h @@ -0,0 +1,205 @@ +/** + * gtk2client.h + * This file is part of the YATE Project http://YATE.null.ro + * + * A Gtk based universal telephony client + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004, 2005 Null Team + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#ifdef _WINDOWS + +#ifdef LIBYGTK2_EXPORTS +#define YGTK2_API __declspec(dllexport) +#else +#ifndef LIBYGTK2_STATIC +#define YGTK2_API __declspec(dllimport) +#endif +#endif + +#endif /* _WINDOWS */ + +#ifndef YGTK2_API +#define YGTK2_API +#endif + +#include +#include +#include + +#include + +namespace TelEngine { + +class YGTK2_API GTKClient : public Client +{ + friend class GTKWindow; +public: + GTKClient(); + virtual ~GTKClient(); + virtual void main(); + virtual void lock(); + virtual void unlock(); + virtual void allHidden(); + virtual bool createWindow(const String& name); +protected: + virtual void loadWindows(); +}; + +class YGTK2_API GTKDriver : public ClientDriver +{ +public: + GTKDriver(); + virtual ~GTKDriver(); + virtual void initialize(); + virtual bool factory(UIFactory* factory, const char* type = 0); +}; + +class YGTK2_API Widget +{ +public: + Widget(); + virtual ~Widget(); + inline GtkWidget* widget() const + { return m_widget; } + virtual bool setText(const String& text); + virtual bool setCheck(bool checked); + virtual bool setSelect(const String& item); + virtual bool addOption(const String& item, bool atStart = false); + virtual bool delOption(const String& item); + virtual bool getText(String& text); + virtual bool getCheck(bool& checked); +protected: + void widget(GtkWidget* wid); +private: + GtkWidget* m_widget; + void destroyed(); + static void destroyCb(GtkObject* obj, gpointer dat); +}; + +class YGTK2_API GTKWindow : public Window +{ + friend class GTKClient; +public: + enum Layout { + Unknown = 0, + Fixed, + Table, + Infinite, + HBox, + VBox, + Boxed, + Tabbed, + Framed, + }; + GTKWindow(const char* id = 0, Layout layout = Unknown); + virtual ~GTKWindow(); + virtual bool setActive(const String& name, bool active); + virtual bool setShow(const String& name, bool visible); + virtual bool setText(const String& name, const String& text); + virtual bool setCheck(const String& name, bool checked); + virtual bool setSelect(const String& name, const String& item); + virtual bool addOption(const String& name, const String& item, bool atStart = false); + virtual bool delOption(const String& name, const String& item); + virtual bool getText(const String& name, String& text); + virtual bool getCheck(const String& name, bool& checked); + virtual void populate(); + virtual void init(); + virtual void show(); + virtual void hide(); + virtual void size(int width, int height); + virtual void move(int x, int y); + virtual void moveRel(int dx, int dy); + virtual void geometry(int x, int y, int w, int h); + virtual GtkWidget* filler(); + virtual GtkWidget* container(Layout layout) const; + virtual GtkWidget* container(const String& layout) const; + virtual GtkWidget* build(const String& type, const String& text); + GtkWidget* find(const String& name) const; + virtual void insert(GtkWidget* wid, int x = 0, int y = 0, int w = -1, int h = -1); + virtual bool action(GtkWidget* wid); + virtual bool toggle(GtkToggleButton* btn, gboolean active); + virtual bool select(GtkOptionMenu* opt, gint selected); + virtual void menu(int x, int y); + inline GtkWidget* widget() const + { return m_widget; } + inline int state() const + { return m_state; } + inline void state(int gdkState) + { m_state = gdkState; } + bool prepare(); + bool restore(); + static bool setText(GtkWidget* wid, const String& text); + static bool setCheck(GtkWidget* wid, bool checked); + static bool setSelect(GtkWidget* wid, const String& item); + static bool addOption(GtkWidget* wid, const String& item, bool atStart = false); + static bool delOption(GtkWidget* wid, const String& item); + static bool getText(GtkWidget* wid, String& text); + static bool getCheck(GtkWidget* wid, bool& checked); +protected: + GtkWidget* m_widget; + GtkWidget* m_filler; + int m_layout; + int m_state; + gint m_posX; + gint m_posY; + gint m_sizeW; + gint m_sizeH; +}; + +/** + * Each instance of WindowFactory creates special windows by name + * @short A static window creator + */ +class YGTK2_API WindowFactory : public UIFactory +{ + YCLASS(WindowFactory,UIFactory) +public: + WindowFactory(const char* type, const char* name); + virtual Window* build() const = 0; +}; + +#define WINDOW_FACTORY(type,name,cls) \ +class cls##__Factory : public WindowFactory \ +{ public: cls##__Factory() : WindowFactory(type,name) {} \ +Window* build() const { return new cls(name); } }; \ +cls##__Factory cls##__FactoryInstance; + +/** + * Each instance of WidgetFactory creates special widgets by name + * @short A static widget creator + */ +class YGTK2_API WidgetFactory : public UIFactory +{ + YCLASS(WidgetFactory,UIFactory) +public: + WidgetFactory(const char* type, const char* name); + virtual Widget* build(const String& text) const = 0; +}; + +#define WIDGET_FACTORY(type,name,cls) \ +class cls##__Factory : public WidgetFactory \ +{ public: cls##__Factory() : WidgetFactory(type,name) {} \ +Widget* build(const String& text) const { return new cls(text); } }; \ +cls##__Factory cls##__FactoryInstance; + +}; // namespace TelEngine + +/* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/engine/Client.cpp b/engine/Client.cpp new file mode 100644 index 00000000..2951f066 --- /dev/null +++ b/engine/Client.cpp @@ -0,0 +1,681 @@ +/** + * Client.cpp + * This file is part of the YATE Project http://YATE.null.ro + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004, 2005 Null Team + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include "yatecbase.h" + +using namespace TelEngine; + +class UIHandler : public MessageHandler +{ +public: + UIHandler() + : MessageHandler("ui.action",150) + { } + virtual bool received(Message &msg); +}; + + +Window::Window(const char* id) + : m_id(id), m_visible(false), m_master(false) +{ +} + +Window::~Window() +{ + if (Client::self()) + Client::self()->m_windows.remove(this,false); +} + +const String& Window::toString() const +{ + return m_id; +} + +bool Window::related(const Window* wnd) const +{ + if ((wnd == this) || !wnd || wnd->master()) + return false; + return true; +} + + +UIFactory::UIFactory(const char* type, const char* name) + : String(name) +{ + if (ClientDriver::self() && ClientDriver::self()->factory(this,type)) + return; + Debug(ClientDriver::self(),DebugGoOn,"Could not register '%s' factory type '%s'", + name,type); +} + +UIFactory::~UIFactory() +{ + if (ClientDriver::self()) + ClientDriver::self()->factory(this,0); +} + + +Client* Client::s_client = 0; +int Client::s_changing = 0; + +Client::Client(const char *name) + : Thread(name), m_line(0) +{ + s_client = this; + Engine::install(new UIHandler); +} + +Client::~Client() +{ + m_windows.clear(); + s_client = 0; + Engine::halt(0); +} + +void Client::run() +{ + loadWindows(); + Message msg("ui.event"); + msg.setParam("event","load"); + Engine::dispatch(msg); + initWindows(); + setStatus(""); + msg.setParam("event","init"); + Engine::dispatch(msg); + main(); +} + +Window* Client::getWindow(const String& name) +{ + if (!s_client) + return 0; + ObjList* l = s_client->m_windows.find(name); + return static_cast(l ? l->get() : 0); +} + +bool Client::setVisible(const String& name, bool show) +{ + Window* w = getWindow(name); + if (!w) + return false; + w->visible(show); + return true; +} + +bool Client::getVisible(const String& name) +{ + Window* w = getWindow(name); + return w && w->visible(); +} + +void Client::initWindows() +{ + ObjList* l = &m_windows; + for (; l; l = l->next()) { + Window* w = static_cast(l->get()); + if (w) + w->init(); + } +} + +void Client::moveRelated(const Window* wnd, int dx, int dy) +{ + if (!wnd) + return; + ObjList* l = &m_windows; + for (; l; l = l->next()) { + Window* w = static_cast(l->get()); + if (w && (w != wnd) && wnd->related(w)) + w->moveRel(dx,dy); + } +} + +bool Client::setShow(const String& name, bool visible, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->setShow(name,visible); + ++s_changing; + bool ok = false; + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip)) + ok = wnd->setShow(name,visible) || ok; + } + --s_changing; + return ok; +} + +bool Client::setActive(const String& name, bool active, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->setActive(name,active); + ++s_changing; + bool ok = false; + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip)) + ok = wnd->setActive(name,active) || ok; + } + --s_changing; + return ok; +} + +bool Client::setText(const String& name, const String& text, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->setText(name,text); + ++s_changing; + bool ok = false; + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip)) + ok = wnd->setText(name,text) || ok; + } + --s_changing; + return ok; +} + +bool Client::setCheck(const String& name, bool checked, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->setCheck(name,checked); + ++s_changing; + bool ok = false; + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip)) + ok = wnd->setCheck(name,checked) || ok; + } + --s_changing; + return ok; +} + +bool Client::setSelect(const String& name, const String& item, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->setSelect(name,item); + ++s_changing; + bool ok = false; + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip)) + ok = wnd->setSelect(name,item) || ok; + } + --s_changing; + return ok; +} + +bool Client::addOption(const String& name, const String& item, bool atStart, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->addOption(name,item,atStart); + ++s_changing; + bool ok = false; + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip)) + ok = wnd->addOption(name,item,atStart) || ok; + } + --s_changing; + return ok; +} + +bool Client::delOption(const String& name, const String& item, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->delOption(name,item); + ++s_changing; + bool ok = false; + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip)) + ok = wnd->delOption(name,item) || ok; + } + --s_changing; + return ok; +} + +bool Client::getText(const String& name, String& text, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->getText(name,text); + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip) && wnd->getText(name,text)) + return true; + } + return false; +} + +bool Client::getCheck(const String& name, bool& checked, Window* wnd, Window* skip) +{ + if (wnd) + return wnd->getCheck(name,checked); + ObjList* l = &m_windows; + for (; l; l = l->next()) { + wnd = static_cast(l->get()); + if (wnd && (wnd != skip) && wnd->getCheck(name,checked)) + return true; + } + return false; +} + +bool Client::getSelect(const String& name, String& item, Window* wnd, Window* skip) +{ + return getText(name,item,wnd,skip); +} + +bool Client::setStatus(const String& text, Window* wnd) +{ + Debug(ClientDriver::self(),DebugInfo,"Status '%s' in window %p",text.c_str(),wnd); + return setText("status",text,wnd); +} + +bool Client::setStatusLocked(const String& text, Window* wnd) +{ + lock(); + bool ok = setStatus(text,wnd); + unlock(); + return ok; +} + +bool Client::action(Window* wnd, const String& name) +{ + DDebug(ClientDriver::self(),DebugInfo,"Action '%s' in %p",name.c_str(),wnd); + if (name == "call" || name == "callto") { + String target; + getText("callto",target,wnd); + String line; + getText("line",line,wnd); + String proto; + getText("proto",proto,wnd); + return callStart(target,line,proto); + } + else if (name.startsWith("callto:")) + return callStart(name.substr(7)); + else if (name == "accept") { + callAccept(m_incoming); + return true; + } + else if (name.startsWith("accept:")) { + callAccept(name.substr(7)); + return true; + } + else if (name == "reject") { + callReject(m_incoming); + return true; + } + else if (name.startsWith("reject:")) { + callReject(name.substr(7)); + return true; + } + else if (name == "hangup") { + callHangup(m_incoming); + return true; + } + else if (name.startsWith("hangup:")) { + callHangup(name.substr(7)); + return true; + } + else if (name.startsWith("digit:")) { + emitDigit(name.at(6)); + return true; + } + else if (name.startsWith("line:")) { + int l = name.substr(5).toInteger(-1); + if (l >= 0) { + line(l); + return true; + } + } + Message* m = new Message("ui.event"); + m->addParam("event","action"); + m->addParam("name",name); + Engine::enqueue(m); + return false; +} + +bool Client::toggle(Window* wnd, const String& name, bool active) +{ + DDebug(ClientDriver::self(),DebugInfo,"Toggle '%s' %s in %p", + name.c_str(),String::boolText(active),wnd); + if (setVisible(name,active)) + return true; + setCheck(name,active,0,wnd); + Message* m = new Message("ui.event"); + m->addParam("event","toggle"); + m->addParam("name",name); + m->addParam("active",String::boolText(active)); + Engine::enqueue(m); + return false; +} + +bool Client::select(Window* wnd, const String& name, const String& item) +{ + DDebug(ClientDriver::self(),DebugInfo,"Select '%s' '%s' in %p", + name.c_str(),item.c_str(),wnd); + setSelect(name,item,0,wnd); + Message* m = new Message("ui.event"); + m->addParam("event","select"); + m->addParam("name",name); + m->addParam("item",item); + Engine::enqueue(m); + return false; +} + +void Client::line(int newLine) +{ + Debug(ClientDriver::self(),DebugInfo,"line(%d)",newLine); + m_line = newLine; +} + +void Client::callAccept(const char* callId) +{ + Debug(ClientDriver::self(),DebugInfo,"callAccept('%s')",callId); + ClientChannel* cc = static_cast(ClientDriver::self()->find(callId)); + if (cc) { + cc->openMedia(); + Engine::enqueue(cc->message("call.answered",false,true)); + } +} + +void Client::callReject(const char* callId) +{ + Debug(ClientDriver::self(),DebugInfo,"callReject('%s')",callId); + if (!ClientDriver::self()) + return; + Message* m = new Message("call.drop"); + m->addParam("id",callId ? callId : ClientDriver::self()->name().c_str()); + m->addParam("error","rejected"); + m->addParam("reason","Refused"); + Engine::enqueue(m); +} + +void Client::callHangup(const char* callId) +{ + Debug(ClientDriver::self(),DebugInfo,"callHangup('%s')",callId); + if (!ClientDriver::self()) + return; + Message* m = new Message("call.drop"); + m->addParam("id",callId ? callId : ClientDriver::self()->name().c_str()); + m->addParam("reason","User hangup"); + Engine::enqueue(m); +} + +bool Client::callStart(const String& target, const String& line, const String& proto) +{ + Debug(ClientDriver::self(),DebugInfo,"callStart('%s','%s','%s')", + target.c_str(),line.c_str(),proto.c_str()); + if (target.null()) + return false; + ClientChannel* cc = new ClientChannel(); + Message* m = cc->message("call.route"); + Regexp r("^[a-z]\\+/"); + if (r.matches(target.safe())) + m->setParam("callto",target); + else + m->setParam("called",target); + if (line) + m->setParam("line",line); + if (proto) + m->setParam("protocol",proto); + return cc->startRouter(m); +} + +bool Client::emitDigit(char digit) +{ + Debug(ClientDriver::self(),DebugInfo,"emitDigit('%c')",digit); + return false; +} + +bool Client::callIncoming(const String& caller, const String& dest, Message* msg) +{ + Debug(ClientDriver::self(),DebugAll,"callIncoming [%p]",this); + if (msg && msg->userData()) { + CallEndpoint* ch = static_cast(msg->userData()); + ClientChannel* cc = new ClientChannel(ch->id()); + if (cc->connect(ch)) { + m_incoming = cc->id(); + msg->setParam("peerid",m_incoming); + msg->setParam("targetid",m_incoming); + Engine::enqueue(cc->message("call.ringing",false,true)); + cc->deref(); + // notify the UI about the call + String tmp("Call from:"); + tmp << " " << caller; + lock(); + setStatus(tmp); + setText("incoming",tmp); + setVisible("incoming"); + unlock(); + return true; + } + } + return false; +} + +void Client::clearIncoming(const String& id) +{ + if (id == m_incoming) + m_incoming.clear(); +} + +bool UIHandler::received(Message &msg) +{ + if (!Client::self()) + return false; + String action(msg.getValue("action")); + if (action.null()) + return false; + Window* wnd = Client::getWindow(msg.getValue("window")); + if (action == "set_status") + return Client::self()->setStatusLocked(msg.getValue("status"),wnd); + String name(msg.getValue("name")); + if (name.null()) + return false; + DDebug(ClientDriver::self(),DebugAll,"UI action '%s' on '%s' in %p", + action.c_str(),name.c_str(),wnd); + bool ok = false; + Client::self()->lock(); + if (action == "set_text") + ok = Client::self()->setText(name,msg.getValue("text"),wnd); + else if (action == "set_toggle") + ok = Client::self()->setCheck(name,msg.getBoolValue("active"),wnd); + else if (action == "set_select") + ok = Client::self()->setSelect(name,msg.getValue("item"),wnd); + else if (action == "set_active") + ok = Client::self()->setActive(name,msg.getBoolValue("active"),wnd); + else if (action == "set_visible") + ok = Client::self()->setShow(name,msg.getBoolValue("visible"),wnd); + else if (action == "add_option") + ok = Client::self()->addOption(name,msg.getValue("item"),msg.getBoolValue("insert"),wnd); + else if (action == "del_option") + ok = Client::self()->delOption(name,msg.getValue("item"),wnd); + else if (action == "get_text") { + String text; + ok = Client::self()->getText(name,text,wnd); + if (ok) + msg.retValue() = text; + } + else if (action == "get_toggle") { + bool check; + ok = Client::self()->getCheck(name,check,wnd); + if (ok) + msg.retValue() = check; + } + else if (action == "get_select") { + String item; + ok = Client::self()->getSelect(name,item,wnd); + if (ok) + msg.retValue() = item; + } + else if (action == "window_show") + ok = Client::setVisible(name,true); + else if (action == "window_hide") + ok = Client::setVisible(name,false); + Client::self()->unlock(); + return ok; +} + +// IMPORTANT: having a target means "from inside Yate to the user" +// An user initiated call must be incoming (no target) +ClientChannel::ClientChannel(const char* target) + : Channel(ClientDriver::self(),0,target), m_line(0) +{ + m_targetid = target; + Engine::enqueue(message("chan.startup")); +} + +ClientChannel::~ClientChannel() +{ + closeMedia(); + String tmp("Hung up:"); + tmp << " " << (address() ? address() : id()); + if (Client::self()) { + Client::self()->clearIncoming(id()); + Client::self()->setStatusLocked(tmp); + } + Engine::enqueue(message("chan.hangup")); +} + +bool ClientChannel::openMedia() +{ + String dev = ClientDriver::device(); + if (dev.null()) + return false; + Message m("chan.attach"); + complete(m,true); + m.setParam("source",dev); + m.setParam("consumer",dev); + m.userData(this); + return Engine::dispatch(m); +} + +void ClientChannel::closeMedia() +{ + setSource(); + setConsumer(); +} + +void ClientChannel::line(int newLine) +{ + m_line = newLine; + m_address.clear(); + if (m_line > 0) + m_address << "line/" << m_line; +} + +bool ClientChannel::callRouted(Message& msg) +{ + String tmp("Calling:"); + tmp << " " << msg.retValue(); + Client::self()->setStatusLocked(tmp); + return true; +} + +void ClientChannel::callAccept(Message& msg) +{ + Debug(ClientDriver::self(),DebugAll,"ClientChannel::callAccept() [%p]",this); + Client::self()->setStatusLocked("Call connected"); + Channel::callAccept(msg); +} + +void ClientChannel::callReject(const char* error, const char* reason) +{ + Debug(ClientDriver::self(),DebugAll,"ClientChannel::callReject('%s','%s') [%p]", + error,reason,this); + if (!reason) + reason = error; + if (!reason) + reason = "Unknown reason"; + String tmp("Call failed:"); + tmp << " " << reason; + if (Client::self()) + Client::self()->setStatusLocked(tmp); + Channel::callReject(error,reason); +} + +bool ClientChannel::msgRinging(Message& msg) +{ + Debug(ClientDriver::self(),DebugAll,"ClientChannel::msgRinging() [%p]",this); + Client::self()->setStatusLocked("Call ringing"); + return Channel::msgRinging(msg); +} + +bool ClientChannel::msgAnswered(Message& msg) +{ + Debug(ClientDriver::self(),DebugAll,"ClientChannel::msgAnswered() [%p]",this); + Client::self()->setStatusLocked("Call answered"); + openMedia(); + return Channel::msgAnswered(msg); +} + + +ClientDriver* ClientDriver::s_driver = 0; +String ClientDriver::s_device; + +ClientDriver::ClientDriver() + : Driver("client","misc") +{ + s_driver = this; +} + +ClientDriver::~ClientDriver() +{ + s_driver = 0; +} + +bool ClientDriver::factory(UIFactory* factory, const char* type) +{ + return false; +} + +bool ClientDriver::msgExecute(Message& msg, String& dest) +{ + Debug(this,DebugInfo,"msgExecute() '%s'",dest.c_str()); + return (Client::self()) && (Client::self()->callIncoming(msg.getValue("caller"),dest,&msg)); +} + +ClientChannel* ClientDriver::findLine(int line) +{ + if (line < 1) + return 0; + Lock mylock(this); + ObjList* l = &channels(); + for (; l; l = l->next()) { + ClientChannel* cc = static_cast(l->get()); + if (cc && (cc->line() == line)) + return cc; + } + return 0; +} + +/* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/engine/Makefile.in b/engine/Makefile.in index ad1ded72..6e135563 100644 --- a/engine/Makefile.in +++ b/engine/Makefile.in @@ -18,14 +18,16 @@ MKDEPS := ../config.status YLIB:= libyate.so.@PACKAGE_VERSION@ CINC := @top_srcdir@/yateclass.h EINC := $(CINC) @top_srcdir@/yatengine.h -PINC := $(PINC) @top_srcdir@/yatephone.h +PINC := $(EINC) @top_srcdir@/yatephone.h +CLINC:= $(PINC) @top_srcdir@/yatecbase.h LIBS := CLSOBJS := TelEngine.o ObjList.o String.o DataBlock.o NamedList.o YMD5.o \ Mutex.o Thread.o Socket.o ENGOBJS := Configuration.o Message.o Plugin.o Engine.o TELOBJS := DataFormat.o Channel.o +CLIOBJS := Client.o -LIBOBJS := $(CLSOBJS) $(ENGOBJS) $(TELOBJS) +LIBOBJS := $(CLSOBJS) $(ENGOBJS) $(TELOBJS) $(CLIOBJS) CLEANS = $(LIBOBJS) core COMPILE = $(CXX) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS) LINK = $(CXX) $(LDFLAGS) @@ -68,6 +70,9 @@ Mutex.o: @srcdir@/Mutex.cpp $(MKDEPS) $(CINC) Thread.o: @srcdir@/Thread.cpp $(MKDEPS) $(CINC) $(COMPILE) @THREAD_KILL@ -c $< +Client.o: @srcdir@/Client.cpp $(MKDEPS) $(CLINC) + $(COMPILE) -c $< + %.o: @srcdir@/%.cpp $(MKDEPS) $(EINC) $(COMPILE) -c $< diff --git a/engine/ObjList.cpp b/engine/ObjList.cpp index 36da0254..ba6faf39 100644 --- a/engine/ObjList.cpp +++ b/engine/ObjList.cpp @@ -120,6 +120,12 @@ GenObject* ObjList::operator[](int index) const return obj ? obj->get() : 0; } +GenObject* ObjList::operator[](const String& str) const +{ + ObjList *obj = find(str); + return obj ? obj->get() : 0; +} + ObjList* ObjList::find(const GenObject* obj) const { XDebug(DebugAll,"ObjList::find(%p) [%p]",obj,this); diff --git a/modules/Makefile.in b/modules/Makefile.in index d4e24546..1d0e8acd 100644 --- a/modules/Makefile.in +++ b/modules/Makefile.in @@ -17,7 +17,7 @@ MODFLAGS:= @MODULE_LDFLAGS@ MODSTRIP:= @MODULE_SYMBOLS@ INCFILES := @top_srcdir@/yatengine.h @top_srcdir@/yatephone.h ../yateversn.h -SUBDIRS := +SUBDIRS := gtk2 MKDEPS := ../config.status PROGS := cdrbuild.yate cdrfile.yate \ regexroute.yate regfile.yate accfile.yate \ @@ -55,6 +55,10 @@ ifneq (@HAVE_GSM@,no) PROGS := $(PROGS) gsmcodec.yate endif +ifeq (@HAVE_GTK2@_@HAVE_GMOZ@,yes_yes) +PROGS := $(PROGS) gtk2/gtk2mozilla.yate +endif + LOCALFLAGS = LOCALLIBS = COMPILE = $(CXX) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS) @@ -80,14 +84,21 @@ clean: do-clean .PHONY: install install: all do-install @mkdir -p "$(DESTDIR)$(moddir)/" && \ - install $(PROGS) "$(DESTDIR)$(moddir)/" + for i in $(PROGS) ; do \ + install -D "$$i" "$(DESTDIR)$(moddir)/$$i" ; \ + done; .PHONY: uninstall uninstall: do-uninstall @-for i in $(PROGS) ; do \ rm "$(DESTDIR)$(moddir)/$$i" ; \ done; \ - rmdir "$(DESTDIR)$(moddir)" + $(if $(SUBDIRS),\ + for i in $(SUBDIRS) ; do \ + rmdir "$(DESTDIR)$(moddir)/$$i" ; \ + done; \ + ) + @-rmdir "$(DESTDIR)$(moddir)" %.o: @srcdir@/%.cpp $(MKDEPS) $(INCFILES) $(COMPILE) -c $< @@ -142,6 +153,10 @@ yrtpchan.yate: ../contrib/yrtp/libyatertp.a yrtpchan.yate: LOCALFLAGS = -I@top_srcdir@/contrib/yrtp yrtpchan.yate: LOCALLIBS = ../contrib/yrtp/libyatertp.a +gtk2/gtk2mozilla.yate: @top_srcdir@/contrib/gtk2/gtk2client.h +gtk2/gtk2mozilla.yate: LOCALFLAGS = @GTK2_INC@ @GMOZ_INC@ -I@top_srcdir@/contrib/gtk2 +gtk2/gtk2mozilla.yate: LOCALLIBS = @GMOZ_LIB@ + ../contrib/iax/libiax.a: $(MAKE) -C ../contrib/iax diff --git a/modules/gtk2/.cvsignore b/modules/gtk2/.cvsignore new file mode 100644 index 00000000..20aaabb7 --- /dev/null +++ b/modules/gtk2/.cvsignore @@ -0,0 +1,9 @@ +Makefile +core* +*.o +*.a +*.so +*.yate +*.orig +*~ +.*.swp diff --git a/modules/gtk2/gtk2mozilla.cpp b/modules/gtk2/gtk2mozilla.cpp new file mode 100644 index 00000000..3b3aeff5 --- /dev/null +++ b/modules/gtk2/gtk2mozilla.cpp @@ -0,0 +1,101 @@ +/** + * gtk2mozilla.cpp + * This file is part of the YATE Project http://YATE.null.ro + * + * Mozilla embedded widget for the Gtk2 based universal telephony client + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004, 2005 Null Team + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include "gtk2client.h" +#include + +using namespace TelEngine; + +// Embedded Mozilla widget +class MozWidget : public Widget +{ +public: + MozWidget(const String& text); + virtual ~MozWidget(); + virtual bool setText(const String& text); + virtual bool getText(String& text); + void setTextAsync(); +protected: + String m_url; +}; + +WIDGET_FACTORY("gtk2","mozilla",MozWidget) + +static Mutex s_mutex; + +static gboolean mozIntervalCb(gpointer dat) +{ + if (dat) + static_cast(dat)->setTextAsync(); + return false; +} + +MozWidget::MozWidget(const String& text) +{ + DDebug(ClientDriver::self(),DebugAll,"MozWidget::MozWidget()"); + widget(gtk_moz_embed_new()); + setText(text); +} + +MozWidget::~MozWidget() +{ + DDebug(ClientDriver::self(),DebugAll,"MozWidget::~MozWidget()"); +} + +bool MozWidget::setText(const String& text) +{ + if (widget() && text) { + s_mutex.lock(); + m_url = text; + gtk_timeout_add(1,mozIntervalCb,this); + s_mutex.unlock(); + return true; + } + return false; +} + +bool MozWidget::getText(String& text) +{ + if (widget()) { + char* url = gtk_moz_embed_get_location(GTK_MOZ_EMBED(widget())); + text = url; + delete url; + return true; + } + return false; +} + +void MozWidget::setTextAsync() +{ + s_mutex.lock(); + if (widget() && m_url) { + Debug(ClientDriver::self(),DebugAll,"MozWidget async url='%s'",m_url.c_str()); + gtk_moz_embed_load_url(GTK_MOZ_EMBED(widget()),m_url.c_str()); + m_url.clear(); + } + s_mutex.unlock(); +} + +/* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/modules/skin/default/gtk2client.rc b/modules/skin/default/gtk2client.rc new file mode 100644 index 00000000..793665ee --- /dev/null +++ b/modules/skin/default/gtk2client.rc @@ -0,0 +1,37 @@ +pixmap_path "/usr/src/yate09-gtk2/clients" + +style "tux" { + bg_pixmap[NORMAL] = "tux.xpm" +} + +style "brown" { + bg[NORMAL] = { 0.80, 0.75, 0.25 } +} + +style "red" { + bg[NORMAL] = { 1.0, 0.5, 0.5 } +} + +style "green" { + bg[NORMAL] = { 0.5, 1.0, 0.5 } +} + +style "blue" { + bg[NORMAL] = { 0.5, 0.5, 1.0 } + GtkButton::default_border = { 5, 0, 5, 0 } +} + +style "sky" { + bg[NORMAL] = { 0.85, 0.85, 1.0 } +} + +widget_class "default" style "tux" +#class "GtkWindow" style "brown" +#widget "default" style "brown" +widget "browser" style "sky" +widget "*.call" style "green" +widget "*.accept" style "green" +widget "*.reject" style "red" +widget "*.hangup" style "red" +widget "*.caption" style "sky" +widget "*.decoration" style "blue" diff --git a/modules/skin/default/gtk2client.ui b/modules/skin/default/gtk2client.ui new file mode 100644 index 00000000..5bdd3183 --- /dev/null +++ b/modules/skin/default/gtk2client.ui @@ -0,0 +1,72 @@ +[default] +enabled=on +title=Yate VoIP Client +master=yes +layout=fixed +width=220 +height=130 +label=10,5,100,20,Yate +label=10,30,150,20,Call address or number +combo=10,50,150,22,callto, +label=170,30,40,20,Line +option=170,48,40,30,line,l 1,l 2,l 3 +button=10,80,100,28,call,Call +button=110,80,100,28,hangup,Hangup +label=10,110,200,20,status,Initializing... +check=75,5,20,20,ctest,Test +toggle=135,5,20,20,browser, +toggle=155,5,20,20,tools, +button_icon=175,5,20,20,decoration, +button_hide=195,5,20,20,decoration, +x=489 +y=368 +[tools] +enabled=off +title=Tools +label=Tools Window +check=160,5,20,20,ctest,Test +button_hide=0,0,20,20,decoration, +x=608 +y=314 +[browser] +enabled=on +title=Browser +master=yes +layout=framed +vbox=> +hbox=,,0,-1 +label=1,1,,,Browser window +button_icon=,,20,20,decoration, +button_max=,,20,20,decoration, +button_hide=,,20,20,decoration, +leave=< +mozilla=1,1,300,400,browser,file:///usr/share/gtk-doc/html/gtk/GtkWidget.html#gtk-widget-set-size-request +editor=,1,200,22,callto, +hbox=,,0,-1 +button=1,,100,30,call,Call +button=1,,100,30,hangup,Hangup +button=1,,100,30,mumu,Mumu +leave=< +label=,,,,status,Initializing... +leave=< +x=688 +y=40 +[incoming] +enabled=on +title=Incoming Call +layout=framed +vbox=> +boxed=0,0,-1,-1,caption,> +hbox=,,0,-1 +label=1,1,,,Incoming call +button_hide=,,20,20,decoration, +leave=< +leave=< +label=,,,,incoming,No call +hbox= +button=1,,75,30,accept,Accept +button=1,,75,30,reject,Reject +leave=< +leave=< +x=688 +y=536 diff --git a/yatecbase.h b/yatecbase.h new file mode 100644 index 00000000..a307b6e0 --- /dev/null +++ b/yatecbase.h @@ -0,0 +1,202 @@ +/** + * yatecbase.h + * This file is part of the YATE Project http://YATE.null.ro + * + * Common base classes for all telephony clients + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004, 2005 Null Team + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __YATECBASE_H +#define __YATECBASE_H + +#ifndef __cplusplus +#error C++ is required +#endif + +#include + +/** + * Holds all Telephony Engine related classes. + */ +namespace TelEngine { + +/** + * A window is the basic user interface element. + * Everything inside is implementation specific functionality. + * @short An abstract user interface window + */ +class YATE_API Window : public GenObject +{ + friend class Client; +public: + Window(const char* id = 0); + virtual ~Window(); + virtual const String& toString() const; + virtual bool setActive(const String& name, bool active) = 0; + virtual bool setShow(const String& name, bool visible) = 0; + virtual bool setText(const String& name, const String& text) = 0; + virtual bool setCheck(const String& name, bool checked) = 0; + virtual bool setSelect(const String& name, const String& item) = 0; + virtual bool addOption(const String& name, const String& item, bool atStart = false) = 0; + virtual bool delOption(const String& name, const String& item) = 0; + virtual bool getText(const String& name, String& text) = 0; + virtual bool getCheck(const String& name, bool& checked) = 0; + virtual void populate() = 0; + virtual void init() = 0; + virtual void show() = 0; + virtual void hide() = 0; + virtual void size(int width, int height) = 0; + virtual void move(int x, int y) = 0; + virtual void moveRel(int dx, int dy) = 0; + virtual bool related(const Window* wnd) const; + virtual void menu(int x, int y) = 0; + inline const String& id() const + { return m_id; } + inline bool visible() const + { return m_visible; } + inline void visible(bool yes) + { if (yes) show(); else hide(); } + inline bool master() const + { return m_master; } +protected: + String m_id; + bool m_visible; + bool m_master; +}; + +/** + * Each instance of UIFactory creates special user interface elements by name + * @short A static user interface creator + */ +class YATE_API UIFactory : public String +{ +public: + UIFactory(const char* type, const char* name); + virtual ~UIFactory(); +}; + +/** + * Singleton class that holds the User Interface's main thread and methods + * @short Thread that runs the User Interface + */ +class YATE_API Client : public Thread +{ + friend class Window; +public: + Client(const char *name = 0); + virtual ~Client(); + virtual void run(); + virtual void main() = 0; + virtual void lock() = 0; + virtual void unlock() = 0; + virtual void allHidden() = 0; + virtual bool createWindow(const String& name) = 0; + virtual bool setStatus(const String& text, Window* wnd = 0); + bool setStatusLocked(const String& text, Window* wnd = 0); + virtual bool action(Window* wnd, const String& name); + virtual bool toggle(Window* wnd, const String& name, bool active); + virtual bool select(Window* wnd, const String& name, const String& item); + virtual bool callIncoming(const String& caller, const String& dest = String::empty(), Message* msg = 0); + void clearIncoming(const String& id); + void callAccept(const char* callId = 0); + void callReject(const char* callId = 0); + void callHangup(const char* callId = 0); + bool callStart(const String& target, const String& line = String::empty(), const String& proto = String::empty()); + bool emitDigit(char digit); + inline int line() const + { return m_line; } + void line(int newLine); + bool setActive(const String& name, bool active, Window* wnd = 0, Window* skip = 0); + bool setShow(const String& name, bool visible, Window* wnd = 0, Window* skip = 0); + bool setText(const String& name, const String& text, Window* wnd = 0, Window* skip = 0); + bool setCheck(const String& name, bool checked, Window* wnd = 0, Window* skip = 0); + bool setSelect(const String& name, const String& item, Window* wnd = 0, Window* skip = 0); + bool addOption(const String& name, const String& item, bool atStart, Window* wnd = 0, Window* skip = 0); + bool delOption(const String& name, const String& item, Window* wnd = 0, Window* skip = 0); + bool getText(const String& name, String& text, Window* wnd = 0, Window* skip = 0); + bool getCheck(const String& name, bool& checked, Window* wnd = 0, Window* skip = 0); + bool getSelect(const String& name, String& item, Window* wnd = 0, Window* skip = 0); + void moveRelated(const Window* wnd, int dx, int dy); + inline static Client* self() + { return s_client; } + inline static bool changing() + { return (s_changing > 0); } + static Window* getWindow(const String& name); + static bool setVisible(const String& name, bool show = true); + static bool getVisible(const String& name); +protected: + virtual void loadWindows() = 0; + virtual void initWindows(); + ObjList m_windows; + String m_incoming; + int m_line; + static Client* s_client; + static int s_changing; +}; + +/** + * This class implements a Channel used by client programs + * @short Channel used by client programs + */ +class YATE_API ClientChannel : public Channel +{ +public: + ClientChannel(const char* target = 0); + virtual ~ClientChannel(); + virtual bool msgRinging(Message& msg); + virtual bool msgAnswered(Message& msg); + virtual bool callRouted(Message& msg); + virtual void callAccept(Message& msg); + virtual void callReject(const char* error, const char* reason); + bool openMedia(); + void closeMedia(); + inline int line() const + { return m_line; } + void line(int newLine); +protected: + int m_line; +}; + +/** + * Abstract client Driver that implements some of the specific functionality + * @short Base Driver with client specific functions + */ +class YATE_API ClientDriver : public Driver +{ +public: + ClientDriver(); + virtual ~ClientDriver(); + virtual void initialize() = 0; + virtual bool factory(UIFactory* factory, const char* type); + virtual bool msgExecute(Message& msg, String& dest); + ClientChannel* findLine(int line); + inline static ClientDriver* self() + { return s_driver; } + inline static const String& device() + { return s_device; } +protected: + static ClientDriver* s_driver; + static String s_device; +}; + +}; // namespace TelEngine + +#endif /* __YATECBASE_H */ + +/* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/yateclass.h b/yateclass.h index e2f603b3..bdfa3eaa 100644 --- a/yateclass.h +++ b/yateclass.h @@ -779,6 +779,13 @@ public: */ GenObject* operator[](int index) const; + /** + * Array-like indexing operator + * @param str String value of the object to locate + * @return Pointer to the object or NULL + */ + GenObject* operator[](const String& str) const; + /** * Get the item in the list that holds an object * @param obj Pointer to the object to search for