blob: 5084b9c8b54e08125a614b0933f5115a70d5af4b [file] [log] [blame]
requires 2.0.0
%alltop{
/*
* Copyright © 2009 Red Hat, Inc. All rights reserved.
* Copyright © 2009 Ding-Yi Chen <dchen at redhat.com>
*
* This file is part of the ibus-chewing Project.
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <strings.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <locale.h>
%}
%headertop{
#ifndef MAKER_DIALOG_H_
#define MAKER_DIALOG_H_
%}
enum MAKER_DIALOG_PROPERTY_FLAG{
INVISIBLE =0x1,
INSENSITIVE =0x2,
INEDITABLE =0x4,
HAS_TRANSLATION =0x8,
TRANSLATION_WITH_CONTEXT =0x10,
} Maker:Dialog:Property:Flag;
%headertop{
typedef guint MakerDialogPropertyFlags;
typedef struct _PropertyContext PropertyContext;
typedef GValue *(* CallbackGetFunc)(PropertyContext *ctx);
typedef void(* CallbackSetFunc)(PropertyContext *ctx, GValue *value);
typedef struct{
GType valueType;
gchar key[30];
gchar pageName[50];
gchar label[200];
gchar defaultValue[100];
const gchar **validValues;
gchar *translationContext;
gint min;
gint max;
CallbackGetFunc getFunc;
CallbackSetFunc setFunc;
MakerDialogPropertyFlags propertyFlags;
gint width;
gint height;
const gchar *tooltip;
gpointer userData;
} PropertySpec;
struct _PropertyContext{
PropertySpec *spec;
gpointer userData; //<! User data to be used in callback.
};
#ifndef WIDGET_ID_PREFIX
#define WIDGET_ID_PREFIX "+"
#endif
#ifndef STRING_BUFFER_SIZE_DEFAULT
#define STRING_BUFFER_SIZE_DEFAULT 1000
#endif
%}
%{
void G_DEBUG_MSG(gint level, const char *format, ...);
#include "MakerDialog-def.c"
%}
class Maker:Dialog from Gtk:Dialog{
public gboolean vbox_homogeneous=TRUE;
public gint hbox_spacing=2;
public gint vbox_spacing=2;
public GtkWidget *dialog_notebook=NULL
destroywith gtk_widget_destroy;
private GHashTable *widgetTable={g_hash_table_new_full(g_str_hash,g_str_equal,NULL,NULL)}
destroywith g_hash_table_destroy;
private GHashTable *notebookContentTable={g_hash_table_new_full(g_str_hash,g_str_equal,NULL,NULL)}
destroywith g_hash_table_destroy;
private GStringChunk *widgetIds={g_string_chunk_new(STRING_BUFFER_SIZE_DEFAULT)}
destroywith g_string_chunk_free;
private GPtrArray *propList={g_ptr_array_new()}
destroy {
if (VAR){
g_ptr_array_foreach(VAR,propList_free_deep_callback,NULL);
g_ptr_array_free(VAR,TRUE);
}
};
init(self){
}
public MakerDialog *new (void) {
Self *self = GET_NEW;
return self;
}
public MakerDialog *new_full (const gchar *title,
int notebook_pages, const gchar **notebook_page_labels,
int button_num, const gchar **button_labels, GtkResponseType *button_responses) {
Self *self = GET_NEW;
GtkDialog *dialog=GTK_DIALOG(self);
gtk_window_set_title(GTK_WINDOW(dialog),title);
gtk_window_set_destroy_with_parent (GTK_WINDOW(dialog), TRUE);
gtk_window_set_type_hint (GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
int i;
for(i=0;i<button_num;i++){
GtkWidget *button=gtk_dialog_add_button(dialog,button_labels[i],button_responses[i]);
self_widget_register(self, button, button_labels[i], "button");
}
GtkWidget *dialog_vbox = GTK_DIALOG(dialog)->vbox;
gtk_widget_show (dialog_vbox);
if (notebook_pages>0){
self->dialog_notebook = gtk_notebook_new();
gtk_box_pack_start (GTK_BOX(dialog_vbox), self->dialog_notebook, TRUE, TRUE, 0);
for (i=0;i<notebook_pages;i++){
GtkWidget *label=gtk_label_new(_(notebook_page_labels[i]));
self_widget_register(self, label, notebook_page_labels[i], "label");
gtk_widget_show(label);
GtkWidget *vbox=gtk_vbox_new(self->vbox_homogeneous,self->vbox_spacing);
self_widget_register(self, vbox, notebook_page_labels[i], "vbox");
gtk_widget_show(vbox);
gtk_notebook_append_page (GTK_NOTEBOOK(self->dialog_notebook), vbox,label);
}
}
return self;
}
public void set_verbose_level(self,gint level){
verboseLevel=level;
}
/**
* add_property_no_gui:
* @self: A MakerDialog
* @pSpec: A property spec to be added.
* @user_data: user_data to be passed to callback functions.
* @returns: A newly allocated PropertyContext.
*
* Add a property without GUI automation involved.
*/
public PropertyContext *add_property_no_gui(self, PropertySpec *pSpec, gpointer user_data){
G_DEBUG_MSG(2,"::add_property_no_gui(%s,-)",pSpec->key);
PropertyContext *ctx=propertyContext_new(pSpec,user_data);
g_ptr_array_add(self->_priv->propList,ctx);
g_hash_table_insert(self->_priv->notebookContentTable, pSpec->key, pSpec->pageName);
return ctx;
}
/**
* add_property:
* @self: A MakerDialog
* @pSpec: A property spec to be added.
* @initValue: Initial value of the property.
* @user_data: user_data to be passed to callback functions.
* @returns: TRUE if the property adding succeed; FALSE otherwise.
*
* Add a property to the dialog and corresponding Gtk widget will also be
* set.
* If @initValue is not NULL, it will be the initial value of the widget.
*/
public gboolean add_property(self, PropertySpec *pSpec, const gchar *initValue, gpointer user_data){
G_DEBUG_MSG(2,"::add_property(%s,%s,-)",pSpec->key,initValue);
GtkWidget *vbox=self_get_base_vbox(self, pSpec->pageName);
g_assert(vbox);
GtkWidget *hbox=gtk_hbox_new(FALSE,self->hbox_spacing);
self_widget_register(self, hbox, pSpec->key, "hbox");
GtkWidget *label=gtk_label_new(_(pSpec->label));
self_widget_register(self, label, pSpec->key, "label");
if (pSpec->tooltip){
gtk_widget_set_tooltip_text (label,_(pSpec->tooltip));
}
gtk_widget_show(label);
gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
GtkWidget *widget=NULL;
GtkAdjustment *gAdjust=NULL;
PropertyContext *ctx=self_add_property_no_gui(self,pSpec,user_data);
if (!ctx){
return FALSE;
}
switch(pSpec->valueType){
case G_TYPE_BOOLEAN:
widget = gtk_check_button_new();
self_widget_register(self, widget, pSpec->key, NULL);
gboolean bValue=FALSE;
if(initValue){
bValue=atob(initValue);
}else if (pSpec->defaultValue){
bValue=atob(pSpec->defaultValue);
}
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),bValue);
if (pSpec->setFunc){
g_signal_connect(widget, "toggled", G_CALLBACK(on_toggleButton_toggled_wrap),ctx);
}
break;
case G_TYPE_UINT:
case G_TYPE_INT:
gAdjust = (GtkAdjustment *)gtk_adjustment_new (atoi(pSpec->defaultValue),
pSpec->min, pSpec->max, 1.0, 0.0, 0.0);
widget = gtk_spin_button_new (gAdjust, 1.0, 0);
self_widget_register(self, widget, pSpec->key, NULL);
if(initValue){
gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),atoi(initValue));
}else if (pSpec->defaultValue){
gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),atoi(pSpec->defaultValue));
}
if (pSpec->setFunc){
g_signal_connect(widget, "value-changed", G_CALLBACK(on_spinButton_value_changed_wrap),ctx);
}
break;
case G_TYPE_STRING:
if (pSpec->validValues){
GtkListStore *listStore=NULL;
if (pSpec->propertyFlags & MAKER_DIALOG_PROPERTY_FLAG_HAS_TRANSLATION){
listStore=gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
}else{
listStore=gtk_list_store_new (1, G_TYPE_STRING);
}
int i;
for(i=0;pSpec->validValues[i]!=NULL;i++){
/* Add new item*/
listStore_append(listStore, pSpec->validValues[i],
pSpec->translationContext,pSpec->propertyFlags);
}
int index=-1;
if (initValue){
index=listStore_find_string(listStore,initValue,
pSpec->translationContext,pSpec->propertyFlags);
}
if (index<0 && pSpec->defaultValue){
index=listStore_find_string(listStore,pSpec->defaultValue,
pSpec->translationContext,pSpec->propertyFlags);
}
if (pSpec->propertyFlags & MAKER_DIALOG_PROPERTY_FLAG_INEDITABLE){
widget=gtk_combo_box_new_with_model (GTK_TREE_MODEL(listStore));
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, FALSE);
if (pSpec->propertyFlags & MAKER_DIALOG_PROPERTY_FLAG_HAS_TRANSLATION){
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), renderer,
"text", 1,
NULL);
}else{
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), renderer,
"text", 0,
NULL);
}
}else{
widget=gtk_combo_box_entry_new_with_model (GTK_TREE_MODEL(listStore),
(pSpec->propertyFlags & MAKER_DIALOG_PROPERTY_FLAG_HAS_TRANSLATION)? 1:0);
}
self_widget_register(self, widget, pSpec->key, NULL);
gtk_combo_box_set_active(GTK_COMBO_BOX(widget),index);
if (pSpec->setFunc){
g_signal_connect(widget, "changed", G_CALLBACK(on_comboBox_changed_wrap),ctx);
}
}else{
widget=gtk_entry_new();
self_widget_register(self, widget, pSpec->key, NULL);
if (pSpec->max>=0){
gtk_entry_set_max_length(GTK_ENTRY(widget),pSpec->max);
}
const char *sValue=NULL;
if(initValue){
sValue=initValue;
}else if (pSpec->defaultValue){
sValue=pSpec->defaultValue;
}else{
sValue="";
}
gtk_entry_set_text(GTK_ENTRY(widget),sValue);
gtk_editable_set_editable (GTK_EDITABLE(widget),
!(pSpec->propertyFlags & MAKER_DIALOG_PROPERTY_FLAG_INEDITABLE));
if (pSpec->setFunc){
g_signal_connect(widget, "activate", G_CALLBACK(on_entry_activate_wrap),ctx);
}
}
break;
default:
break;
}
if (!widget){
return FALSE;
}
gtk_box_pack_start (GTK_BOX(hbox), widget, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
return TRUE;
}
/**
* align_labels:
* @self: A MakerDialog.
* @pageName: Name of notebook page to be apply, can be NULL.
* @xalign: x align of labels. 0.0f for LEFT, 0.5f for CENTER, 1.0f for RIGHT.
* @yalign: y align of labels. 0.0f for TOP, 0.5f for CENTER, 1.0f for BOTTOM.
*
* Align labels for better dialog appearance.
*
* Labels that associate with value setting widget are stretched into same width.
* If @pageName is not NULL, only the labels in the page are stretched,
* otherwise, all labels will be stretched.
*
* Moreover, the horizontal and vertical alignment can set by @xalign and @yalign, respectively.
*/
public void align_labels(self, const gchar *pageName, gfloat xalign, gfloat yalign){
WidgetAlignment wAlignment;
wAlignment.self=self;
wAlignment.currentMaxWidth=0;
wAlignment.pageName=pageName;
wAlignment.xalign=xalign;
wAlignment.yalign=yalign;
g_hash_table_foreach(self->_priv->notebookContentTable,
caculate_max_label_width_callback, (gpointer) &wAlignment);
g_hash_table_foreach(self->_priv->notebookContentTable,
set_label_width_callback, (gpointer) &wAlignment);
// Chromium OS Change:
// Do now show the dialog so that our window manager (chromiumos-wm)
// does not remove focus from the Chrome window.
//
// gtk_widget_show_all(GTK_WIDGET(self));
}
public void reload_property_value(self, const gchar *key){
gboolean boolValue;
gint intValue;
gint uintValue;
const gchar *stringValue;
PropertyContext *ctx=self_get_propertyContext_by_key(self,key);
PropertySpec *pSpec=ctx->spec;
GtkWidget *widget=self_get_widget_by_key(self,pSpec->key);
GValue *gValue;
if (pSpec->getFunc){
gValue=pSpec->getFunc(ctx);
switch(pSpec->valueType){
case G_TYPE_BOOLEAN:
boolValue=g_value_get_boolean(gValue);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),boolValue);
break;
case G_TYPE_INT:
intValue=g_value_get_int(gValue);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),(gdouble) intValue);
break;
case G_TYPE_UINT:
uintValue=g_value_get_uint(gValue);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),(gdouble) uintValue);
break;
case G_TYPE_STRING:
stringValue=g_value_get_string(gValue);
if (pSpec->validValues){
int index=-1;
if (stringValue){
index=combo_find_string_index(GTK_COMBO_BOX(widget),stringValue,
pSpec->translationContext,
ctx->spec->propertyFlags);
}
if (index<0 && pSpec->defaultValue){
index=combo_find_string_index(GTK_COMBO_BOX(widget),pSpec->defaultValue,
pSpec->translationContext,
ctx->spec->propertyFlags);
}
gtk_combo_box_set_active(GTK_COMBO_BOX(widget),index);
}else{
gtk_entry_set_text(GTK_ENTRY(widget),stringValue);
}
break;
default:
break;
}
}
}
/**
* write_gconf_schemas_file:
* @self: A MakerDialog.
* @filename: Filename for output.
* @owner: Owner of the schemas.
* @schemasHome: The "home direcory" of schemas.
* @locales: Supported locales, use ';' as delimiter.
* @returns: TRUE if succeed; FALSE otherwise.
*
* Output the parameters as GConf schemes file.
*/
public gboolean write_gconf_schemas_file(self, const gchar *filename,
const gchar * owner, const gchar *schemasHome, const gchar *locales){
G_DEBUG_MSG(2,"::output_gconf_schemes_file(%s)",filename);
FILE *outF=fopen(filename,"w");
if (outF==NULL){
G_DEBUG_MSG(1,"::output_gconf_schemes_file(%s) file %s cannot be written!",
filename,filename);
return FALSE;
}
xml_tags_write(outF,"gconfschemafile",XML_TAG_TYPE_BEGIN_ONLY,NULL,NULL);
xml_tags_write(outF,"schemalist",XML_TAG_TYPE_BEGIN_ONLY,NULL,NULL);
SchemasFileData sData;
sData.schemasHome=schemasHome;
sData.owner=owner;
sData.locales=locales;
sData.outF=outF;
g_ptr_array_foreach(self->_priv->propList,ctx_write_callback,&sData);
xml_tags_write(outF,"schemalist",XML_TAG_TYPE_END_ONLY,NULL,NULL);
xml_tags_write(outF,"gconfschemafile",XML_TAG_TYPE_END_ONLY,NULL,NULL);
if (fclose(outF))
return FALSE;
G_DEBUG_MSG(2,"::output_gconf_schemes_file(%s) ... done.",filename);
return TRUE;
}
/**
* apply_widget_value:
* @self: A MakerDialog.
* @key: The key of a property.
*
* Apply the value shown in widget to the property.
* Note that a set callback function is needed in PropertySpec to apply the value to property.
*
* @see_also: set_widget_value()
*/
public void apply_widget_value(self, const gchar *key){
G_DEBUG_MSG(2,"::apply_widget_value(%s)",key);
PropertyContext *ctx=self_get_propertyContext_by_key(self,key);
if (!ctx){
/* Not found */
g_warning("%s: no such PropertyContext",key);
return;
}
GValue gValue={0};
self_get_widget_value(self,key,&gValue);
if (ctx->spec->setFunc){
ctx->spec->setFunc(ctx,&gValue);
}
g_value_unset(&gValue);
}
public GValue *get_widget_value(self, const gchar *key, GValue *gValue){
G_DEBUG_MSG(2,"::get_widget_value(%s)",key);
PropertyContext *ctx=self_get_propertyContext_by_key(self,key);
if (!ctx){
/* Not found */
return NULL;
}
GtkWidget *widget=self_get_widget_by_key(self,key);
if (ctx->spec->valueType!=G_TYPE_STRING){
g_value_init(gValue,ctx->spec->valueType);
}
switch(ctx->spec->valueType){
case G_TYPE_BOOLEAN:
g_value_set_boolean(gValue, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)));
break;
case G_TYPE_INT:
g_value_set_int(gValue, (gint) gtk_spin_button_get_value (GTK_SPIN_BUTTON(widget)));
break;
case G_TYPE_UINT:
g_value_set_uint(gValue, (guint) gtk_spin_button_get_value (GTK_SPIN_BUTTON(widget)));
break;
case G_TYPE_STRING:
if (ctx->spec->validValues){
const char *str=combo_get_active_text (GTK_COMBO_BOX(widget),gValue);
G_DEBUG_MSG(3,"::get_widget_value(%s) %s",
key,str);
}else{
g_value_set_string(gValue, gtk_entry_get_text (GTK_ENTRY(widget)));
}
break;
default:
break;
}
return gValue;
}
/**
* set_widget_value:
* @self: A MakerDialog.
* @key: The key of a property.
* @value: Value to be shown in widget.
*
* Set value for widget display.
* However, the property value is not set.
* Use apply_widget_value() to do it.
* @see_also: apply_widget_value()
*/
public void set_widget_value(self, const gchar *key, GValue *value){
int index;
PropertyContext *ctx=self_get_propertyContext_by_key(self,key);
if (!ctx){
/* Not found */
return;
}
GtkWidget *widget=self_get_widget_by_key(self,key);
switch(ctx->spec->valueType){
case G_TYPE_BOOLEAN:
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget),g_value_get_boolean(value));
break;
case G_TYPE_INT:
gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),(gdouble) g_value_get_int(value));
break;
case G_TYPE_UINT:
gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),(gdouble) g_value_get_uint(value));
break;
case G_TYPE_STRING:
if (ctx->spec->validValues){
const gchar *str=g_value_get_string(value);
index=combo_find_string_index(GTK_COMBO_BOX(widget),str,
ctx->spec->translationContext,ctx->spec->propertyFlags);
gtk_combo_box_set_active (GTK_COMBO_BOX(widget),index);
}else{
g_value_set_string(value, gtk_entry_get_text (GTK_ENTRY(widget)));
}
break;
default:
break;
}
}
public GtkWidget *get_widget(self,
const gchar *widget_key, const gchar *widget_type){
gchar buf[STRING_BUFFER_SIZE_DEFAULT];
widget_get_id(buf, STRING_BUFFER_SIZE_DEFAULT, widget_key, widget_type);
return self_get_widget_by_id(self, buf);
}
public GtkWidget *get_widget_by_key(self, const gchar *key){
return self_get_widget(self,key,NULL);
}
public GtkWidget *get_widget_by_id(self, const gchar *id){
G_DEBUG_MSG(3,"*** get_widget_by_id(%s)",id);
return (GtkWidget *) g_hash_table_lookup (self->_priv->widgetTable, id);
}
public PropertyContext *get_propertyContext_by_key(self,const gchar *key){
int i=0;
PropertyContext *ctx=NULL;
for(i=0; i<self->_priv->propList->len;i++){
ctx=g_ptr_array_index(self->_priv->propList,i);
if (strcmp(ctx->spec->key,key)==0){
break;
}
}
if (i>=self->_priv->propList->len){
/* Not found */
return NULL;
}
return ctx;
}
protected GtkWidget *get_base_vbox(self, const gchar *notebook_page_label){
gchar buf[STRING_BUFFER_SIZE_DEFAULT];
GtkWidget *ret;
if (notebook_page_label){
widget_get_id(buf, STRING_BUFFER_SIZE_DEFAULT, notebook_page_label, "vbox");
ret=self_get_widget(self, notebook_page_label, "vbox");
}else{
ret=GTK_DIALOG(self)->vbox;
}
if (!ret){
ret=GTK_DIALOG(self)->vbox;
}
return ret;
}
private void widget_register(self, GtkWidget *widget,
const gchar *widget_label, const gchar *widget_type){
gchar buf[STRING_BUFFER_SIZE_DEFAULT];
widget_get_id(buf, STRING_BUFFER_SIZE_DEFAULT, widget_label, widget_type);
gchar *chunk_ret=g_string_chunk_insert(self->_priv->widgetIds,buf);
g_hash_table_insert(self->_priv->widgetTable, chunk_ret,widget);
G_DEBUG_MSG(3,"*** widget_register(-,%s,%s) widget registered as %s",
widget_label,(widget_type)? (widget_type) : "", chunk_ret);
}
}
%headertop{
#endif /* MAKER_DIALOG_H_ */
%}