yate/contrib/gtk2/gtk2client.cpp

2046 lines
56 KiB
C++

/**
* 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-2006 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gtk2client.h"
#include <gdk/gdkkeysyms.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;
static bool s_clickInfo = false;
#define INVALID_POS (-1000000)
#define MAX_CONTAINER_DEPTH 20
#define MAX_COLUMNS_NUMBER 50
#ifdef _WINDOWS
#define BUGGY_IDLE
#define ONE_THREAD true
#define DEFAULT_DEVICE "dsound/*"
#else
#undef BUGGY_IDLE
#define ONE_THREAD false
#define DEFAULT_DEVICE "oss//dev/dsp"
#endif
// 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<WidgetFinder*>(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);
XDebug(GTKDriver::self(),DebugAll,"WidgetFinder::find '%s' found %p",m_name.c_str(),m_widget);
return m_widget;
}
// Default Widget behaviour is to use GTKWindow's static methods
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::setUrgent(bool urgent)
{ return GTKWindow::setUrgent(m_widget,urgent); }
bool Widget::hasOption(const String& item)
{ return GTKWindow::hasOption(m_widget,item); }
bool Widget::addOption(const String& item, bool atStart, const String& text)
{ return GTKWindow::addOption(m_widget,item,atStart,text); }
bool Widget::delOption(const String& item)
{ return GTKWindow::delOption(m_widget,item); }
bool Widget::addTableRow(const String& item, const NamedList* data, bool atStart)
{ return GTKWindow::addTableRow(m_widget,item,data,atStart); }
bool Widget::delTableRow(const String& item)
{ return GTKWindow::delTableRow(m_widget,item); }
bool Widget::setTableRow(const String& item, const NamedList* data)
{ return GTKWindow::setTableRow(m_widget,item,data); }
bool Widget::getTableRow(const String& item, NamedList* data)
{ return GTKWindow::getTableRow(m_widget,item,data); }
bool Widget::clearTable()
{ return GTKWindow::clearTable(m_widget); }
bool Widget::getText(String& text)
{ return GTKWindow::getText(m_widget,text); }
bool Widget::getCheck(bool& checked)
{ return GTKWindow::getCheck(m_widget,checked); }
bool Widget::getSelect(String& item)
{ return GTKWindow::getSelect(m_widget,item); }
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 bool validPos(int x, int y)
{
return (-10000 < x) && (x < 10000) && (-10000 < y) && (y < 10000);
}
static gboolean gtkIdleCb(gpointer dat)
{
if (dat) {
// idle and timeout callbacks are called from glib directly
// so gtk/gdk thread safety is not assured by default
gdk_threads_enter();
static_cast<GTKClient*>(dat)->idleActions();
gdk_threads_leave();
}
return true;
}
static gboolean debugCbInfo(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,"debugCbInfo widget %p path '%s' class path '%s'",
wid,wp,wcp);
g_free(wp);
g_free(wcp);
return FALSE;
}
static void attachDebug(GtkWidget* wid)
{
if (wid && s_clickInfo)
g_signal_connect(G_OBJECT(wid),"button_press_event",G_CALLBACK(debugCbInfo),0);
}
static Widget* getWidget(GtkWidget* wid)
{
if (!wid)
return 0;
return static_cast<Widget*>(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<GTKWindow*>(g_object_get_data((GObject*)top,"Yate::Window"));
}
static bool getOptionText(GtkOptionMenu* opt, gint index, String& text)
{
GtkWidget* menu = gtk_option_menu_get_menu(opt);
if (!menu)
return false;
GList* menuItems = gtk_container_get_children(GTK_CONTAINER(menu));
GList* l = menuItems;
while (index && l) {
index--;
l = g_list_next(l);
}
bool ok = false;
if (l && GTK_IS_MENU_ITEM(l->data)) {
GtkWidget* mnu = (GtkWidget*)(l->data);
GtkWidget* lbl = gtk_bin_get_child(GTK_BIN(mnu));
if (!lbl)
lbl = (GtkWidget*)g_object_get_data((GObject*)mnu,"Yate::Label");
if (GTK_IS_LABEL(lbl)) {
text = gtk_label_get_text(GTK_LABEL(lbl));
ok = true;
}
}
g_list_free(menuItems);
return ok;
}
static int getOptionIndex(GtkOptionMenu* opt, const String& item)
{
GtkWidget* menu = gtk_option_menu_get_menu(opt);
if (!menu)
return -1;
GList* menuItems = gtk_container_get_children(GTK_CONTAINER(menu));
GList* l = menuItems;
int index = 0;
int pos = -1;
while (l) {
if (GTK_IS_MENU_ITEM(l->data)) {
GtkWidget* mnu = (GtkWidget*)(l->data);
GtkWidget* lbl = gtk_bin_get_child(GTK_BIN(mnu));
if (!lbl)
lbl = (GtkWidget*)g_object_get_data((GObject*)mnu,"Yate::Label");
if (GTK_IS_LABEL(lbl) && (item == gtk_label_get_text(GTK_LABEL(lbl)))) {
pos = index;
break;
}
}
index++;
l = g_list_next(l);
}
g_list_free(menuItems);
return pos;
}
static GtkWidget* getOptionItem(GtkOptionMenu* opt, const String& item)
{
GtkWidget* menu = gtk_option_menu_get_menu(opt);
if (!menu)
return 0;
GList* menuItems = gtk_container_get_children(GTK_CONTAINER(menu));
GList* l = menuItems;
GtkWidget* ret = 0;
while (l) {
if (GTK_IS_MENU_ITEM(l->data)) {
GtkWidget* mnu = (GtkWidget*)(l->data);
GtkWidget* lbl = gtk_bin_get_child(GTK_BIN(mnu));
if (!lbl)
lbl = (GtkWidget*)g_object_get_data((GObject*)mnu,"Yate::Label");
if (GTK_IS_LABEL(lbl) && (item == gtk_label_get_text(GTK_LABEL(lbl)))) {
ret = mnu;
break;
}
}
l = g_list_next(l);
}
g_list_free(menuItems);
return ret;
}
static GtkWidget* getListItem(GtkList* lst, const String& item)
{
GList* listItems = gtk_container_get_children(GTK_CONTAINER(lst));
GList* l = listItems;
GtkWidget* ret = 0;
while (l) {
if (GTK_IS_LIST_ITEM(l->data)) {
GtkWidget* it = (GtkWidget*)(l->data);
GtkWidget* lbl = gtk_bin_get_child(GTK_BIN(it));
if (!lbl)
lbl = (GtkWidget*)g_object_get_data((GObject*)it,"Yate::Label");
if (GTK_IS_LABEL(lbl) && (item == gtk_widget_get_name(lbl))) {
ret = it;
break;
}
}
l = g_list_next(l);
}
g_list_free(listItems);
return ret;
}
static gboolean widgetCbAction(GtkWidget* wid, gpointer dat)
{
Debug(GTKDriver::self(),DebugAll,"widgetCbAction(%p,%p)",wid,dat);
if (GTKClient::changing())
return FALSE;
GTKWindow* wnd = getWidgetWindow(wid);
return wnd && wnd->action(wid);
}
static gboolean widgetCbToggle(GtkWidget* wid, gpointer dat)
{
Debug(GTKDriver::self(),DebugAll,"widgetCbToggle(%p,%p)",wid,dat);
if (GTKClient::changing())
return FALSE;
GTKWindow* wnd = getWidgetWindow(wid);
if (!wnd)
wnd = static_cast<GTKWindow*>(dat);
if (!wnd)
return FALSE;
gboolean active = FALSE;
if (GTK_IS_TOGGLE_BUTTON(wid))
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid));
else if (GTK_IS_CHECK_MENU_ITEM(wid))
active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(wid));
return wnd->toggle(wid,active);
}
static gboolean widgetCbSelected(GtkOptionMenu* opt, gpointer dat)
{
Debug(GTKDriver::self(),DebugAll,"widgetCbSelected(%p,%p)",opt,dat);
if (GTKClient::changing())
return FALSE;
GTKWindow* wnd = getWidgetWindow((GtkWidget*)opt);
return wnd && wnd->select(opt,gtk_option_menu_get_history(opt));
}
static gboolean widgetCbSelection(GtkList* lst, GtkListItem* item, gpointer dat)
{
Debug(GTKDriver::self(),DebugAll,"widgetCbSelection(%p,%p,%p)",lst,item,dat);
g_object_set_data((GObject*)lst,"Yate::ListItem",item);
if (GTKClient::changing())
return FALSE;
GTKWindow* wnd = getWidgetWindow((GtkWidget*)lst);
return wnd && wnd->select(lst,item);
}
static gboolean widgetCbCursorChanged(GtkTreeView* view, gpointer dat)
{
Debug(GTKDriver::self(),DebugAll,"widgetCbCursorChanged(%p,%p)",view,dat);
if (GTKClient::changing())
return FALSE;
GTKWindow* wnd = getWidgetWindow((GtkWidget*)view);
return wnd && wnd->select(view);
GtkTreePath* path = 0;
gtk_tree_view_get_cursor(view,&path,NULL);
if (!path)
return FALSE;
gtk_tree_path_free(path);
}
static gboolean widgetCbRowActivated(GtkTreeView* view, GtkTreePath* arg1, GtkTreeViewColumn* arg2, gpointer dat)
{
Debug(GTKDriver::self(),DebugAll,"widgetCbRowActivated(%p,%p,%p,%p)",view,arg1,arg2,dat);
if (GTKClient::changing())
return FALSE;
GTKWindow* wnd = getWidgetWindow((GtkWidget*)view);
return wnd && wnd->action((GtkWidget*)view);
}
static gboolean widgetCbMinimize(GtkWidget* wid, gpointer dat)
{
DDebug(GTKDriver::self(),DebugAll,"widgetCbMinimize(%p,%p)",wid,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(%p,%p)",wid,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(%p,%p)",wid,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(%p,%p) '%s'",wid,dat,name);
return GTKClient::setVisible(name);
}
static gboolean widgetCbChanged(GtkRange* range, gpointer dat)
{
const gchar* name = gtk_widget_get_name((GtkWidget*)range);
Debug(GTKDriver::self(),DebugAll,"widgetCbChanged(%p,%p) '%s'",range,dat,name);
return FALSE;
}
static gboolean widgetCbSwitch(GtkNotebook* nbk, GtkNotebookPage* page, guint page_num, gpointer dat)
{
const gchar* name = gtk_widget_get_name(GTK_WIDGET(nbk));
Debug(GTKDriver::self(),DebugAll,"widgetCbSwitch(%p,%p,%u,%p) '%s'",nbk,page,page_num,dat,name);
return FALSE;
}
static gboolean widgetCbHelp(GtkWidget* wid, GtkWidgetHelpType typ, gpointer dat)
{
Debug(GTKDriver::self(),DebugAll,"widgetCbHelp(%p,%d,%p)",wid,typ,dat);
return FALSE;
}
// Hopefully we'll have no threading issues.
static GtkRadioButton* s_radioGroup = 0;
static String s_skinPath;
static GtkWidget* gtkRadioButtonNew(const gchar* text)
{
GtkWidget* btn = 0;
if (s_radioGroup) {
if (null(text))
btn = gtk_radio_button_new_from_widget(s_radioGroup);
else
btn = gtk_radio_button_new_with_label_from_widget(s_radioGroup,text);
}
else {
if (null(text))
btn = gtk_radio_button_new(NULL);
else
btn = gtk_radio_button_new_with_label(NULL,text);
s_radioGroup = GTK_RADIO_BUTTON(btn);
}
return btn;
}
static GtkWidget* gtkCheckButtonNew(const gchar* text)
{
if (null(text))
return gtk_check_button_new();
else
return gtk_check_button_new_with_label(text);
}
static GtkWidget* populateIcon(const gchar* str)
{
if (null(str))
return 0;
String text(str);
String icon;
Regexp r("^\"\\([^\"]*\\)\" *\\(.*\\)$");
if (text.matches(r)) {
icon = s_skinPath + text.matchString(1);
text = text.matchString(2);
}
if (icon && text) {
GtkWidget* box = gtk_vbox_new(FALSE,1);
gtk_container_add(GTK_CONTAINER(box),gtk_image_new_from_file(icon.c_str()));
gtk_container_add(GTK_CONTAINER(box),gtk_label_new(text.c_str()));
gtk_widget_show_all(box);
return box;
}
else if (icon)
return gtk_image_new_from_file(icon.c_str());
else if (text)
return gtk_label_new(text.c_str());
return 0;
}
static GtkWidget* populateButton(GtkWidget* btn, const gchar* str)
{
if (null(str) || !btn)
return btn;
GtkWidget* icon = populateIcon(str);
if (icon)
gtk_container_add(GTK_CONTAINER(btn),icon);
return btn;
}
static GtkWidget* gtkButtonNew(const gchar* text)
{
return populateButton(gtk_button_new(),text);
}
static GtkWidget* gtkToggleButtonNew(const gchar* text)
{
return populateButton(gtk_toggle_button_new(),text);
}
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);
attachDebug(ent);
}
attachDebug(GTK_COMBO(combo)->list);
}
return combo;
}
static GtkWidget* gtkMenuItemNew(const gchar* name, const gchar* text = 0)
{
if (!text)
text = name;
// We don't use gtk_menu_item_new_with_label as we need to
// work around not getting the GtkLabel out of GtkMenuItem
GtkWidget* item = gtk_menu_item_new();
GtkWidget* label = gtk_label_new(text);
g_object_set_data((GObject*)item,"Yate::Label",label);
if (name)
gtk_widget_set_name(label,name);
gtk_container_add(GTK_CONTAINER(item),label);
attachDebug(item);
attachDebug(label);
return item;
}
static GtkWidget* gtkListItemNew(const gchar* name, const gchar* text = 0)
{
if (!text)
text = name;
GtkWidget* item = gtk_list_item_new();
GtkWidget* label = gtk_label_new(text);
gtk_misc_set_alignment((GtkMisc*)label,0,0);
g_object_set_data((GObject*)item,"Yate::Label",label);
if (name)
gtk_widget_set_name(label,name);
gtk_container_add(GTK_CONTAINER(item),label);
attachDebug(item);
attachDebug(label);
return item;
}
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<String*>(i->get());
if (s && *s)
gtk_menu_shell_append(GTK_MENU_SHELL(mnu),gtkMenuItemNew(s->c_str()));
}
if (l)
l->destruct();
gtk_option_menu_set_menu(GTK_OPTION_MENU(opt),mnu);
}
}
return opt;
}
static GtkWidget* gtkListNew(const gchar* text)
{
GtkWidget* lst = gtk_list_new();
if (lst) {
GList* list = 0;
String tmp(text);
ObjList* l = tmp.split(',');
for (ObjList* i = l; i; i = i->next()) {
String* s = static_cast<String*>(i->get());
if (s && *s)
list = g_list_append(list,gtkListItemNew(s->c_str()));
}
if (l)
l->destruct();
if (list)
gtk_list_append_items(GTK_LIST(lst),list);
}
return lst;
}
static GtkWidget* gtkTableNew(const gchar* text)
{
String tmp(text);
ObjList* lst = tmp.split(',',false);
if (!lst)
return 0;
int ncol = lst->count();
// data column number zero is reserved as row identification
GType types[MAX_COLUMNS_NUMBER+1];
if (ncol > MAX_COLUMNS_NUMBER)
ncol = MAX_COLUMNS_NUMBER;
int i;
for (i = 0; i <= ncol; i++)
types[i] = G_TYPE_STRING;
GtkTreeModel* store = GTK_TREE_MODEL(gtk_list_store_newv(ncol+1,types));
GtkWidget* table = gtk_tree_view_new_with_model(store);
for (i = 0; i < ncol; i++) {
String* col = static_cast<String*>((*lst)[i]);
if (!col)
continue;
GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
// although undocumented we MUST set the "text" attribute which says which data to render
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(col->safe(),renderer,
"text",i+1,
NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(table),column);
}
// we can now unref the store as the view will hold a reference on it
g_object_unref(G_OBJECT(store));
lst->destruct();
g_signal_connect(G_OBJECT(table),"row_activated",G_CALLBACK(widgetCbRowActivated),0);
return table;
}
static GtkWidget* gtkHscaleNew(const gchar* text)
{
return gtk_hscale_new_with_range(0,100,10);
}
static GtkWidget* gtkVscaleNew(const gchar* text)
{
return gtk_vscale_new_with_range(0,100,10);
}
static WidgetMaker s_widgetMakers[] = {
{ "label", gtkLeftLabelNew, 0, 0 },
{ "editor", gtkEntryNewWithText, "activate", G_CALLBACK(widgetCbAction) },
{ "button", gtkButtonNew, "clicked", G_CALLBACK(widgetCbAction) },
{ "toggle", gtkToggleButtonNew, "toggled", G_CALLBACK(widgetCbToggle) },
{ "check", gtkCheckButtonNew, "toggled", G_CALLBACK(widgetCbToggle) },
{ "radio", gtkRadioButtonNew, "toggled", G_CALLBACK(widgetCbToggle) },
{ "combo", gtkComboNewWithText, 0, 0 },
{ "option", gtkOptionMenuNew, "changed", G_CALLBACK(widgetCbSelected) },
{ "list", gtkListNew, "select-child", G_CALLBACK(widgetCbSelection) },
{ "table", gtkTableNew, "cursor-changed", G_CALLBACK(widgetCbCursorChanged) },
{ "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", gtkButtonNew, "clicked", G_CALLBACK(widgetCbShow) },
{ "button_icon", gtkButtonNew, "clicked", G_CALLBACK(widgetCbMinimize) },
{ "button_hide", gtkButtonNew, "clicked", G_CALLBACK(widgetCbHide) },
{ "button_max", gtkButtonNew, "clicked", G_CALLBACK(widgetCbMaximize) },
{ "hscale", gtkHscaleNew, "value-changed", G_CALLBACK(widgetCbChanged) },
{ "vscale", gtkVscaleNew, "value-changed", G_CALLBACK(widgetCbChanged) },
{ 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<GTKWindow*>(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<GTKWindow*>(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<GTKWindow*>(dat);
if (wnd) {
wnd->hide();
return TRUE;
}
return FALSE;
}
static gboolean windowCbClick(GtkWidget* wid, GdkEventButton* evt, gpointer dat)
{
DDebug(GTKDriver::self(),DebugAll,"windowCbClick event %d data %p",evt->type,dat);
if (wid && s_clickInfo)
debugCbInfo(wid);
GTKWindow* wnd = static_cast<GTKWindow*>(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;
if (wnd && !wnd->dragable())
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 },
{ "scroll", GTKWindow::Scroll },
{ 0, 0 },
};
static TokenDict s_directions[] = {
{ "left", GTK_POS_LEFT },
{ "right", GTK_POS_RIGHT },
{ "top", GTK_POS_TOP },
{ "bottom", GTK_POS_BOTTOM },
{ 0, 0 },
};
static TokenDict s_shadows[] = {
{ "none", GTK_SHADOW_NONE },
{ "in", GTK_SHADOW_IN },
{ "out", GTK_SHADOW_OUT },
{ "etched_in", GTK_SHADOW_ETCHED_IN },
{ "etched_out", GTK_SHADOW_ETCHED_OUT },
{ 0, 0 },
};
static TokenDict s_reliefs[] = {
{ "full", GTK_RELIEF_NORMAL },
{ "half", GTK_RELIEF_HALF },
{ "none", GTK_RELIEF_NONE },
{ 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<Widget*>(dat);
if (w)
w->destroyed();
}
GTKWindow::GTKWindow(const char* id, bool decorated, Layout layout)
: Window(id), m_decorated(decorated), m_dragable(false), m_layout(layout),
m_widget(0), m_filler(0), 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,decorated);
// 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);
g_signal_connect(G_OBJECT(m_widget),"show_help",G_CALLBACK(widgetCbHelp),this);
#ifdef XDEBUG
g_signal_connect(G_OBJECT(m_widget),"event",G_CALLBACK(windowCbEvent),this);
#endif
}
GTKWindow::~GTKWindow()
{
prepare();
m_widget = 0;
if (validPos(m_posX,m_posY)) {
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);
if (!m_master)
s_save.setValue(m_id,"visible",m_visible);
}
}
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:
{
GtkWidget* nbk = gtk_notebook_new();
g_signal_connect(G_OBJECT(nbk),"switch_page",G_CALLBACK(widgetCbSwitch),0);
return nbk;
}
case Framed:
return gtk_frame_new(NULL);
case Scroll:
return gtk_scrolled_window_new(NULL,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),(x > 1),y);
else if (GTK_IS_SCROLLED_WINDOW(filler()) && !GTK_IS_TREE_VIEW(wid))
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(filler()),wid);
else
gtk_container_add(GTK_CONTAINER(filler()),wid);
if (GTK_IS_NOTEBOOK(filler()) && m_tabName)
gtk_notebook_set_tab_label(GTK_NOTEBOOK(m_filler),wid,populateIcon(m_tabName));
m_tabName.clear();
}
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;
m_dragable = sect->getBoolValue("dragable",true);
s_radioGroup = 0;
GtkWidget* containerStack[MAX_CONTAINER_DEPTH];
GtkTooltips* tips = 0;
int depth = 0;
if (m_layout == Unknown)
m_layout = (Layout)sect->getIntValue("layout",s_layoutNames,GTKWindow::Unknown);
GtkWidget* lastWidget = filler();
gtk_widget_set_size_request(lastWidget,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) {
lastWidget = wid;
attachDebug(wid);
if (act)
gtk_widget_set_name(wid,act);
insert(wid,x,y,w,h);
continue;
}
if (p->name() == "leave") {
lastWidget = 0;
if (depth > 0) {
Debug(GTKDriver::self(),DebugAll,"Popping container off stack of depth %d",depth);
depth--;
m_filler = containerStack[depth];
}
continue;
}
else if (p->name() == "tabname") {
m_tabName = *p;
continue;
}
else if (p->name() == "newradio") {
s_radioGroup = 0;
continue;
}
else if (p->name() == "tooltip") {
if (*p && lastWidget) {
if (!tips)
tips = gtk_tooltips_new();
gtk_tooltips_set_tip(tips,lastWidget,p->c_str(),NULL);
}
else
Debug(GTKDriver::self(),DebugInfo,"Could not set tooltip '%s' on widget %p",
p->c_str(),lastWidget);
continue;
}
else if (p->name() == "accelerator") {
guint keyval = gdk_keyval_from_name(p->safe());
if (lastWidget && (keyval != GDK_VoidSymbol)) {
Debug(GTKDriver::self(),DebugInfo,"Seting accelerator '%s' (0x%06X) on widget %p",
p->c_str(),keyval,lastWidget);
gtk_window_add_mnemonic((GtkWindow*)m_widget,keyval,lastWidget);
}
else
Debug(GTKDriver::self(),DebugWarn,"Could not set accelerator '%s' on widget %p",
p->c_str(),lastWidget);
continue;
}
else if (p->name().startsWith("property:")) {
if (!lastWidget)
continue;
String tmp = p->name();
tmp >> "property:";
Debug(GTKDriver::self(),DebugAll,"Setting property '%s' to '%s' in %p",
tmp.c_str(),p->c_str(),lastWidget);
if (tmp.startSkip("int:",false) && tmp)
g_object_set(G_OBJECT(lastWidget),tmp.c_str(),p->toInteger(),NULL);
else if (tmp.startSkip("bool:",false) && tmp)
g_object_set(G_OBJECT(lastWidget),tmp.c_str(),p->toBoolean(),NULL);
else if (tmp.startSkip("str:",false) && tmp)
g_object_set(G_OBJECT(lastWidget),tmp.c_str(),p->safe(),NULL);
else if (tmp.startSkip("pos:",false) && tmp)
g_object_set(G_OBJECT(lastWidget),tmp.c_str(),p->toInteger(s_directions),NULL);
else if (tmp.startSkip("align:",false) && tmp)
g_object_set(G_OBJECT(lastWidget),tmp.c_str(),(gfloat)p->toInteger(50)*0.01,NULL);
else if (tmp.startSkip("relief:",false) && tmp)
g_object_set(G_OBJECT(lastWidget),tmp.c_str(),p->toInteger(s_reliefs),NULL);
else if (tmp.startSkip("shadow:",false) && tmp)
g_object_set(G_OBJECT(lastWidget),tmp.c_str(),p->toInteger(s_shadows),NULL);
}
if (depth >= MAX_CONTAINER_DEPTH)
continue;
wid = container(p->name());
if (wid) {
lastWidget = wid;
attachDebug(wid);
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);
}
}
s_radioGroup = 0;
}
void GTKWindow::title(const String& text)
{
Window::title(text);
gtk_window_set_title((GtkWindow*)m_widget,m_title.safe());
setText("title",text);
}
void GTKWindow::init()
{
title(s_cfg.getValue(m_id,"title",m_id));
m_master = s_cfg.getBoolValue(m_id,"master");
m_popup = s_cfg.getBoolValue(m_id,"popup");
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);
bool initial = s_cfg.getBoolValue(m_id,"visible",m_master);
initial = s_save.getBoolValue(m_id,"visible",initial);
restore();
// we realize the widget explicitely to avoid a gtk-win32 bug
gtk_widget_realize(m_widget);
// popup windows are not displayed initially
if (m_popup || !initial) {
gtk_widget_show_all(filler());
return;
}
gtk_widget_show_all(m_widget);
m_visible = true;
if (m_master)
++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;
if (m_master)
++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_window_set_modal(GTK_WINDOW(m_widget),FALSE);
gtk_window_set_transient_for(GTK_WINDOW(m_widget),NULL);
gtk_widget_hide(m_widget);
m_visible = false;
if (m_master)
--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)
{
if (!validPos(x,y))
return;
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 (!validPos(m_posX,m_posY))
return;
move(m_posX+dx,m_posY+dy);
}
void GTKWindow::geometry(int x, int y, int w, int h)
{
if (!validPos(m_posX,m_posY))
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 && !m_decorated)
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 (!validPos(m_posX,m_posY))
return false;
move(m_posX,m_posY);
size(m_sizeW,m_sizeH);
return true;
}
bool GTKWindow::setParams(const NamedList& params)
{
bool ok = Window::setParams(params);
if (params.getBoolValue("modal"))
gtk_window_set_modal(GTK_WINDOW(m_widget),TRUE);
return ok;
}
void GTKWindow::setOver(const Window* parent)
{
GTKWindow* gwnd = YOBJECT(GTKWindow,parent);
if (gwnd) {
gtk_window_set_transient_for(GTK_WINDOW(m_widget),GTK_WINDOW(gwnd->widget()));
if (gwnd->prepare() && validPos(gwnd->m_posX,gwnd->m_posY))
move(gwnd->m_posX + (gwnd->m_sizeW - m_sizeW) / 2,
gwnd->m_posY + (gwnd->m_sizeH - m_sizeH) / 2);
}
}
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(GtkWidget* wid, gboolean active)
{
const gchar* name = gtk_widget_get_name(wid);
Debug(GTKDriver::self(),DebugAll,"toggle '%s' wid=%p active=%s [%p]",
name,wid,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);
String item(name);
item += selected;
getOptionText(opt,selected,item);
return GTKClient::self() && GTKClient::self()->select(this,name,item);
}
bool GTKWindow::select(GtkList* lst, GtkListItem* item)
{
const gchar* name = gtk_widget_get_name((GtkWidget*)lst);
Debug(GTKDriver::self(),DebugAll,"select '%s' lst=%p item=%p [%p]",
name,lst,item,this);
GtkWidget* lbl = gtk_bin_get_child(GTK_BIN(item));
if (!lbl)
lbl = (GtkWidget*)g_object_get_data((GObject*)item,"Yate::Label");
if (GTK_IS_LABEL(lbl)) {
String item(gtk_widget_get_name(lbl));
String val(gtk_label_get_text(GTK_LABEL(lbl)));
return GTKClient::self() && GTKClient::self()->select(this,name,item,val);
}
return false;
}
bool GTKWindow::select(GtkTreeView* view)
{
const gchar* name = gtk_widget_get_name((GtkWidget*)view);
Debug(GTKDriver::self(),DebugAll,"select '%s' view=%p [%p]",
name,view,this);
GtkTreeModel* model = gtk_tree_view_get_model(view);
if (!model)
return false;
GtkTreePath* path = 0;
gtk_tree_view_get_cursor(view,&path,NULL);
if (!path)
return FALSE;
GtkTreeIter iter;
if (!gtk_tree_model_get_iter(model,&iter,path))
return false;
gtk_tree_path_free(path);
String item;
gchar* val = 0;
// column 0 is reserved for row/item name
gtk_tree_model_get(model,&iter,0,&val,-1);
item = val;
g_free(val);
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::hasElement(const String& name)
{
return (find(name) != 0);
}
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)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::setText(%p,'%s')",wid,text.safe());
if (GTK_IS_LABEL(wid)) {
if (text.startsWith("<markup>"))
gtk_label_set_markup(GTK_LABEL(wid),text.safe());
else
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;
}
if (GTK_IS_ADJUSTMENT(wid)) {
gtk_adjustment_set_value(GTK_ADJUSTMENT(wid),text.toDouble());
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)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::setCheck(%p,%d)",wid,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)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::setSelect(%p,'%s')",wid,item.safe());
if (GTK_IS_OPTION_MENU(wid)) {
GtkOptionMenu* opt = GTK_OPTION_MENU(wid);
int i = getOptionIndex(opt,item);
if (i >= 0) {
gtk_option_menu_set_history(opt,i);
return true;
}
return false;
}
return false;
}
bool GTKWindow::setUrgent(const String& name, bool urgent)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->setUrgent(urgent) : setUrgent(wid,urgent);
}
bool GTKWindow::setUrgent(GtkWidget* wid, bool urgent)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::setUrgent(%p,%d)",wid,urgent);
return false;
}
bool GTKWindow::hasOption(const String& name, const String& item)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->hasOption(item) : hasOption(wid,item);
}
bool GTKWindow::hasOption(GtkWidget* wid, const String& item)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::hasOption(%p,'%s')",wid,item.safe());
if (GTK_IS_OPTION_MENU(wid)) {
GtkOptionMenu* opt = GTK_OPTION_MENU(wid);
GtkWidget* it = getOptionItem(opt,item);
return (it != 0);
}
if (GTK_IS_LIST(wid)) {
GtkList* lst = GTK_LIST(wid);
GtkWidget* it = getListItem(lst,item);
return (it != 0);
}
return false;
}
bool GTKWindow::addOption(const String& name, const String& item, bool atStart, const String& text)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->addOption(item,atStart,text) : addOption(wid,item,atStart,text);
}
bool GTKWindow::addOption(GtkWidget* wid, const String& item, bool atStart, const String& text)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::addOption(%p,'%s')",wid,item.safe());
if (GTK_IS_OPTION_MENU(wid)) {
if (getOptionItem(GTK_OPTION_MENU(wid),item))
return true;
GtkWidget* mnu = gtk_option_menu_get_menu(GTK_OPTION_MENU(wid));
if (!GTK_IS_MENU(mnu))
return false;
GtkWidget* child = gtkMenuItemNew(item,text);
if (child) {
if (atStart)
gtk_menu_shell_prepend(GTK_MENU_SHELL(mnu),child);
else
gtk_menu_shell_append(GTK_MENU_SHELL(mnu),child);
gtk_widget_show_all(child);
return true;
}
return false;
}
if (GTK_IS_LIST(wid)) {
GtkWidget* li = gtkListItemNew(item,text);
if (!li)
return false;
GList* list = g_list_append(NULL,li);
if (list) {
if (atStart)
gtk_list_prepend_items(GTK_LIST(wid),list);
else
gtk_list_append_items(GTK_LIST(wid),list);
gtk_widget_show_all(li);
return true;
}
return false;
}
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)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::delOption(%p,'%s')",wid,item.safe());
if (GTK_IS_OPTION_MENU(wid)) {
GtkOptionMenu* opt = GTK_OPTION_MENU(wid);
GtkWidget* it = getOptionItem(opt,item);
if (it) {
gtk_widget_destroy(it);
return true;
}
return false;
}
if (GTK_IS_LIST(wid)) {
GtkList* lst = GTK_LIST(wid);
GtkWidget* sel = (GtkWidget*)g_object_get_data((GObject*)wid,"Yate::ListItem");
GtkWidget* it = getListItem(lst,item);
if (it) {
if (it == sel)
g_object_set_data((GObject*)wid,"Yate::ListItem",NULL);
GList* list = g_list_append(NULL,it);
gtk_list_remove_items(lst,list);
return true;
}
return false;
}
return false;
}
bool GTKWindow::addTableRow(const String& name, const String& item, const NamedList* data, bool atStart)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->addTableRow(item,data,atStart) : addTableRow(wid,item,data,atStart);
}
bool GTKWindow::delTableRow(const String& name, const String& item)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->delTableRow(item) : delTableRow(wid,item);
}
bool GTKWindow::setTableRow(const String& name, const String& item, const NamedList* data)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->setTableRow(item,data) : setTableRow(wid,item,data);
}
bool GTKWindow::getTableRow(const String& name, const String& item, NamedList* data)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->getTableRow(item,data) : getTableRow(wid,item,data);
}
bool GTKWindow::clearTable(const String& name)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->clearTable() : clearTable(wid);
}
bool GTKWindow::addTableRow(GtkWidget* wid, const String& item, const NamedList* data, bool atStart)
{
DDebug(GTKDriver::self(),DebugInfo,"GTKWindow::addTableRow(%p,'%s',%p,%s)",
wid,item.c_str(),data,String::boolText(atStart));
if (GTK_IS_TREE_VIEW(wid)) {
GtkTreeView* view = GTK_TREE_VIEW(wid);
GtkTreeModel* model = gtk_tree_view_get_model(view);
GtkListStore* store = GTK_LIST_STORE(model);
if (!store)
return false;
int ncol = gtk_tree_model_get_n_columns(model);
DDebug(GTKDriver::self(),DebugInfo,"adding %d columns row to store %p of view %p",
ncol,store,view);
GtkTreeIter iter;
if (atStart)
gtk_list_store_insert(store,&iter,0);
else
gtk_list_store_append(store,&iter);
if (!data)
return true;
// data column number zero is used as row identification
gtk_list_store_set(store,&iter,0,item.safe(),-1);
for (int i = 0; i < ncol; i++) {
GtkTreeViewColumn* column = gtk_tree_view_get_column(view,i);
if (!column)
continue;
String name = gtk_tree_view_column_get_title(column);
name.toLower();
const String* param = data->getParam(name);
if (param)
gtk_list_store_set(store,&iter,i+1,param->safe(),-1);
}
return true;
}
return false;
}
// Helper function to find a row by name and set a GetTreeIter to it
static bool findTableRow(GtkTreeModel* model, const String& item, GtkTreeIter* iter)
{
if (!gtk_tree_model_get_iter_first(model,iter))
return false;
for (;;) {
gchar* val = 0;
// column 0 is reserved for row/item name
gtk_tree_model_get(model,iter,0,&val,-1);
bool found = (item == val);
g_free(val);
if (found)
return true;
if (!gtk_tree_model_iter_next(model,iter))
break;
}
return false;
}
static bool findTableRow(GtkTreeView* view, const String& item, GtkTreeIter* iter)
{
GtkTreeModel* model = gtk_tree_view_get_model(view);
if (!model)
return false;
if (item.null()) {
// find currently selected table row
GtkTreePath* path = 0;
gtk_tree_view_get_cursor(view,&path,NULL);
if (!path)
return false;
bool ok = gtk_tree_model_get_iter(model,iter,path);
gtk_tree_path_free(path);
return ok;
}
return findTableRow(model,item,iter);
}
bool GTKWindow::delTableRow(GtkWidget* wid, const String& item)
{
DDebug(GTKDriver::self(),DebugInfo,"GTKWindow::delTableRow(%p,'%s')",
wid,item.c_str());
if (GTK_IS_TREE_VIEW(wid)) {
GtkTreeView* view = GTK_TREE_VIEW(wid);
GtkTreeModel* model = gtk_tree_view_get_model(view);
if (!model)
return false;
GtkListStore* store = GTK_LIST_STORE(model);
if (!store)
return false;
GtkTreeIter iter;
if (!findTableRow(model,item,&iter))
return false;
gtk_list_store_remove(store,&iter);
return true;
}
return false;
}
bool GTKWindow::setTableRow(GtkWidget* wid, const String& item, const NamedList* data)
{
DDebug(GTKDriver::self(),DebugInfo,"GTKWindow::setTableRow(%p,'%s',%p)",
wid,item.c_str(),data);
if (GTK_IS_TREE_VIEW(wid)) {
GtkTreeView* view = GTK_TREE_VIEW(wid);
GtkTreeModel* model = gtk_tree_view_get_model(view);
if (!model)
return false;
GtkListStore* store = GTK_LIST_STORE(model);
if (!store)
return false;
GtkTreeIter iter;
if (!findTableRow(view,item,&iter))
return false;
int ncol = gtk_tree_model_get_n_columns(model);
for (int i = 0; i < ncol; i++) {
GtkTreeViewColumn* column = gtk_tree_view_get_column(view,i);
if (!column)
continue;
String name = gtk_tree_view_column_get_title(column);
name.toLower();
const String* param = data->getParam(name);
if (param)
gtk_list_store_set(store,&iter,i+1,param->safe(),-1);
}
}
return false;
}
bool GTKWindow::getTableRow(GtkWidget* wid, const String& item, NamedList* data)
{
DDebug(GTKDriver::self(),DebugInfo,"GTKWindow::getTableRow(%p,'%s',%p)",
wid,item.c_str(),data);
if (GTK_IS_TREE_VIEW(wid)) {
GtkTreeView* view = GTK_TREE_VIEW(wid);
GtkTreeModel* model = gtk_tree_view_get_model(view);
if (!model)
return false;
GtkTreeIter iter;
if (!findTableRow(view,item,&iter)) {
Debug(GTKDriver::self(),DebugMild,"Could not find row '%s' in table %p",
item.c_str(),wid);
return false;
}
if (data) {
int ncol = gtk_tree_model_get_n_columns(model);
for (int i = 0; i < ncol; i++) {
GtkTreeViewColumn* column = gtk_tree_view_get_column(view,i);
if (!column)
continue;
String name = gtk_tree_view_column_get_title(column);
name.toLower();
gchar* val = 0;
// read past column 0 which is reserved for row/item name
gtk_tree_model_get(model,&iter,i+1,&val,-1);
data->setParam(name,val);
g_free(val);
}
}
return true;
}
return false;
}
bool GTKWindow::clearTable(GtkWidget* wid)
{
DDebug(GTKDriver::self(),DebugInfo,"GTKWindow::clearTable(%p)",wid);
if (GTK_IS_TREE_VIEW(wid)) {
GtkTreeView* view = GTK_TREE_VIEW(wid);
GtkTreeModel* model = gtk_tree_view_get_model(view);
if (!model)
return false;
GtkListStore* store = GTK_LIST_STORE(model);
if (!store)
return false;
gtk_list_store_clear(store);
return true;
}
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)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::getText(%p)",wid);
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;
}
if (GTK_IS_OPTION_MENU(wid)) {
GtkOptionMenu* opt = GTK_OPTION_MENU(wid);
return getOptionText(opt,gtk_option_menu_get_history(opt),text);
}
if (GTK_IS_LIST(wid)) {
GtkWidget* it = (GtkWidget*)g_object_get_data((GObject*)wid,"Yate::ListItem");
if (it) {
GtkWidget* lbl = gtk_bin_get_child(GTK_BIN(it));
if (!lbl)
lbl = (GtkWidget*)g_object_get_data((GObject*)it,"Yate::Label");
if (GTK_IS_LABEL(lbl)) {
text = gtk_label_get_text(GTK_LABEL(lbl));
return true;
}
}
return false;
}
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)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::getCheck(%p)",wid);
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;
}
bool GTKWindow::getSelect(const String& name, String& item)
{
GtkWidget* wid = find(name);
if (!wid)
return false;
Widget* yw = getWidget(wid);
return yw ? yw->getSelect(item) : getSelect(wid,item);
}
bool GTKWindow::getSelect(GtkWidget* wid, String& item)
{
XDebug(GTKDriver::self(),DebugAll,"GTKWindow::getSelect(%p)",wid);
if (GTK_IS_OPTION_MENU(wid)) {
GtkOptionMenu* opt = GTK_OPTION_MENU(wid);
return getOptionText(opt,gtk_option_menu_get_history(opt),item);
}
if (GTK_IS_LIST(wid)) {
GtkWidget* it = (GtkWidget*)g_object_get_data((GObject*)wid,"Yate::ListItem");
if (it) {
GtkWidget* lbl = gtk_bin_get_child(GTK_BIN(it));
if (!lbl)
lbl = (GtkWidget*)g_object_get_data((GObject*)it,"Yate::Label");
if (GTK_IS_LABEL(lbl)) {
item = gtk_widget_get_name(lbl);
return true;
}
}
return false;
}
if (GTK_IS_TREE_VIEW(wid)) {
GtkTreeView* view = GTK_TREE_VIEW(wid);
GtkTreeModel* model = gtk_tree_view_get_model(view);
if (!model)
return false;
GtkTreePath* path = 0;
gtk_tree_view_get_cursor(view,&path,NULL);
if (!path)
return false;
GtkTreeIter iter;
if (!gtk_tree_model_get_iter(model,&iter,path))
return false;
gtk_tree_path_free(path);
gchar* val = 0;
// column 0 is reserved for row/item name
gtk_tree_model_get(model,&iter,0,&val,-1);
item = val;
g_free(val);
return true;
}
return false;
}
void GTKWindow::menu(int x, int y)
{
GtkWidget* mnu = 0;
ObjList* wnds = GTKClient::listWindows();
for (ObjList* l = wnds; l; l = l->next()) {
String* s = static_cast<String*>(l->get());
if (!s || s->null())
continue;
Window* w = GTKClient::getWindow(*s);
if (!w)
continue;
if (!Engine::config().getBoolValue("client","fullmenu")) {
if (w->master() || w->popup())
continue;
}
if (!mnu)
mnu = gtk_menu_new();
GtkWidget* item = gtk_check_menu_item_new_with_label(w->title().safe());
gtk_widget_set_name(item,s->c_str());
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),w->visible());
g_signal_connect(G_OBJECT(item),"toggled",G_CALLBACK(widgetCbToggle),this);
gtk_menu_shell_append((GtkMenuShell*)mnu,item);
}
delete wnds;
if (!mnu)
return;
gtk_widget_show_all(mnu);
gtk_menu_popup((GtkMenu*)mnu,NULL,NULL,NULL,NULL,3,gtk_get_current_event_time());
}
GTKClient::GTKClient()
: Client("GTKClient")
{
m_oneThread = Engine::config().getBoolValue("client","onethread",ONE_THREAD);
s_skinPath = Engine::config().getValue("client","skinbase");
if (s_skinPath.null())
s_skinPath << Engine::modulePath() << Engine::pathSeparator() << "skin";
if (!s_skinPath.endsWith(Engine::pathSeparator()))
s_skinPath << Engine::pathSeparator();
String skin(Engine::config().getValue("client","skin","default"));
if (skin)
s_skinPath << skin;
if (!s_skinPath.endsWith(Engine::pathSeparator()))
s_skinPath << Engine::pathSeparator();
s_cfg = s_skinPath + "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()
{
if (!m_windows.count()) {
Debug(DebugGoOn,"Gtk Client refusing to start with no windows loaded!");
Engine::halt(1);
}
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,s_cfg.getBoolValue(name,"decorated"));
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(s_skinPath + "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);
}
#ifdef BUGGY_IDLE
// don't use gtk_idle_add - it hogs the CPU on Windows
g_timeout_add(1,gtkIdleCb,this);
#else
// but on Linux the 1ms timeout makes the UI crawl...
gtk_idle_add(gtkIdleCb,this);
#endif
}
GTKDriver::GTKDriver()
{
}
GTKDriver::~GTKDriver()
{
}
void GTKDriver::initialize()
{
Output("Initializing module GTK2 client");
s_device = Engine::config().getValue("client","device",DEFAULT_DEVICE);
if (!GTKClient::self())
{
s_clickInfo = Engine::config().getBoolValue("client","clickinfo");
debugCopy();
new GTKClient;
GTKClient::self()->startup();
}
setup();
}
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: */