/** $Id: gitkrlayout.c,v 1.5 2004/11/10 08:55:07 ensonic Exp $
*
* @file gitk-renderer-phone/gitkrlayout.c
* @author Steffen Ast <sast@users.sf.net>
* @date Mon Mar 29 20:08:16 2004
*
* @brief gitk renderer layout routines
* @ingroup gitkrendererphone
*
*/
#define GITK_RENDERER_C
#define GITKR_LAYOUT_C
#include "gitkrincludes.h"
/** @todo this is a hack to make gettext aware of these*/
#define giml_ctrl_text N_("Prev");
#undef giml_ctrl_text
#define giml_ctrl_text N_("Next");
#undef giml_ctrl_text
/** @todo Stefan fragen wegen LINES */
#define LINES 99
/** @brief generate a layout for a given dialog
* @internal
* @param dialog dialog which should be layouted. If there is old layoutdata there it gets freed.
*/
void gitkr_dialog_generate_layout(GitkDialogPtr dialog) {
GitkrPhoneLayoutPtr layout;
xmlNodePtr node;
gitk_log("gitkr_dialog_generate_layout() beg");
g_assert(dialog!=NULL);
//-- free old layout
gitkr_dialog_free_layout(dialog);
//-- and generate a new empty one
layout=gitkr_dialog_new_layout(dialog);
dialog->layout=(GitkLayoutPtr)layout;
if((node=gitk_xpath_get_node(dialog,"/"GITK_NS_PREFIX":giml/"GITK_NS_PREFIX":dialog",NULL))) {
/** @todo do this for every page */
gitkr_dialog_generate_dialog_widgets(dialog);
gitk_log("starting recursion ...");
/** @todo decide which layouting algorithm to use (XSLT) */
gitkr_dialog_generate_sequential_layout(dialog,node);
//gitkr_dialog_generate_hierarchical_layout(dialog,node);
gitk_log("finished recursion ...");
}
else { gitk_err("failed to get root widgetgroup"); }
}
/** @brief create a initialise a layout structure
* @internal
* @param dialog dialog handle
*/
GitkrPhoneLayoutPtr gitkr_dialog_new_layout(GitkDialogPtr dialog) {
GitkrPhoneLayoutPtr layout=g_new(GitkrPhoneLayout,1);
gitk_log("gitkr_dialog_new_layout() beg");
//-- set default layout parameter
layout->focus=GITK_FOCUS_TYPE_CTRL;
layout->currentPage=0;
layout->currentWidget=0; /** @todo currently the first gadget (even a label) will have the focus */
layout->maxWidgetsPerPage=LINES-GITK_LINE_OFFSET; /* title line + one spacer line + one spacer line + footer line */
layout->pages=NULL;
layout->title=gitkr_cxpath_get_string(dialog,xpath_get_dialog_name,NULL);
//-- allocate memory for the ctrl widgets
layout->ctrl.widgets=g_new0(GitkrPhoneWidget,1);
gitk_log("gitkr_dialog_new_layout() end");
return(layout);
}
/** @brief generate dialog meta widgets
* @internal
* @param dialog dialog handle
*/
void gitkr_dialog_generate_dialog_widgets(GitkDialogPtr dialog) {
GitkrPhoneLayoutPtr layout=(GitkrPhoneLayoutPtr)dialog->layout;
xmlNodePtr node;
gitk_log("gitkr_dialog_generate_dialog_widgets() beg");
if((node=gitk_xpath_get_node(dialog,"/"GITK_NS_PREFIX":giml/"GITK_NS_PREFIX":dialog/"GITK_NS_PREFIX":dialogwidgets/"GITK_NS_PREFIX":widget",NULL))) {
if(!xmlNodeIsText(node)) {
if(!strncasecmp(node->name,"widget",7)) {
GitkrPhoneWidgetPtr widget;
widget=&layout->ctrl.widgets[0];
gitkr_widget_optionchoice_new(widget,dialog,node);
//-- allocate and attach type specific widget data
//gitkr_widget_optionchoice_new(widget,"__ctrl",dialog,NULL);
gitk_log3(" dialogwidget 0x%08x <%s id=\"%s\">",(gint)widget,node->name,widget->id);
layout->ctrl.widgetsPerPage=1;
gitk_log1("layout->ctrl.widgetsPerPage => %d",layout->ctrl.widgetsPerPage);
}
else gitk_err1("expecting 'widget' node, got '%s'",node->name);
}
else gitk_err("unexpected textnode in dialog definition");
}
else gitk_err("failed to get dialog widgets");
}
/** @brief generate a layout with pure sequential order
* @internal
* @param dialog dialog handle
* @param root dialog node to start from
*/
void gitkr_dialog_generate_sequential_layout(GitkDialogPtr dialog,xmlNodePtr root) {
GitkrPhoneLayoutPtr layout;
xmlXPathCompExprPtr xpath_get_pages,xpath_get_widgets;
g_assert(dialog!=NULL);
g_assert(dialog->layout!=NULL);
gitk_log("gitkr_dialog_generate_sequential_layout() beg");
/** @todo Currently we do not yet handle arbitrary deep netsed widgetgroups (only level2).
We need to flatten the whole hierarchy.
*/
layout=(GitkrPhoneLayoutPtr)dialog->layout;
if((xpath_get_pages=xmlXPathCompile("./"GITK_NS_PREFIX":widgetgroup/"GITK_NS_PREFIX":*/parent::*"))
&& (xpath_get_widgets=xmlXPathCompile("./"GITK_NS_PREFIX":widget"))) {
//-- get number of pages (widgetgroups which are not empty)
xmlXPathObjectPtr pages_xpoptr;
if((pages_xpoptr=gitk_xpath_type_filter(
gitk_cxpath_get_object(dialog,xpath_get_pages,root),
XPATH_NODESET))) {
gint i,pageIx;
xmlNodeSetPtr pages=(xmlNodeSetPtr)pages_xpoptr->nodesetval;
gint pages_len=xmlXPathNodeSetGetLength(pages);
//-- count pages
layout->numberOfPages=gitkr_dialog_generate_sequential_layout_count_pages(dialog,root,pages,xpath_get_widgets);
//-- allocate memory for the widgets
layout->pages=g_new0(GitkrPhoneLayoutPage,layout->numberOfPages);
for(i=0;i<layout->numberOfPages;i++) {
layout->pages[i].widgets=g_new0(GitkrPhoneWidget,layout->maxWidgetsPerPage);
}
//-- build pages
pageIx=0;
pageIx=gitkr_dialog_generate_sequential_layout_build_page(dialog,root,xpath_get_widgets,pageIx);
for(i=0;i<pages_len;i++) {
pageIx=gitkr_dialog_generate_sequential_layout_build_page(dialog,xmlXPathNodeSetItem(pages,i),xpath_get_widgets,pageIx);
}
xmlXPathFreeObject(pages_xpoptr);
}
xmlXPathFreeCompExpr(xpath_get_pages);
xmlXPathFreeCompExpr(xpath_get_widgets);
gitk_log1("layout->maxWidgetsPerPage : %d",layout->maxWidgetsPerPage);
gitk_log1("layout->numberOfPages : %d",layout->numberOfPages);
gitk_log1("layout->title : \"%s\"",layout->title);
}
else { gitk_err("failed to compile xpath expression (get_pages, get_widgets)"); }
}
/** @brief helper function that calculates the number of page we will need
* @internal
* @param dialog dialog dialog
* @param root dialog node to start from
* @param pages node set pointer that contain the pages
* @param xpath_get_widgets precompiles xpath expression to get the widgets of one group
*/
gint gitkr_dialog_generate_sequential_layout_count_pages(GitkDialogPtr dialog,xmlNodePtr root,xmlNodeSetPtr pages,xmlXPathCompExprPtr xpath_get_widgets) {
GitkrPhoneLayoutPtr layout;
xmlXPathObjectPtr widgets_xpoptr;
gint pages_len=xmlXPathNodeSetGetLength(pages);
gint numberOfPages=pages_len;
gint i;
g_assert(dialog!=NULL);
g_assert(dialog->layout!=NULL);
gitk_log_intro();
layout=(GitkrPhoneLayoutPtr)dialog->layout;
gitk_log1(" initial numberOfPages %d",numberOfPages);
//-- first check root widgetgroup ...
if((widgets_xpoptr=gitk_xpath_type_filter(
gitk_cxpath_get_object(dialog,xpath_get_widgets,root),
XPATH_NODESET))) {
//-- if root page is not empty, add at least one page
if(xmlXPathNodeSetGetLength((xmlNodeSetPtr)widgets_xpoptr->nodesetval)) numberOfPages++;
//-- if number of widgets is larger than layout->maxWidgetsPerPages correct numberOfPages
numberOfPages+=xmlXPathNodeSetGetLength((xmlNodeSetPtr)widgets_xpoptr->nodesetval)/layout->maxWidgetsPerPage;
gitk_log1(" numberOfPages[r] %d",numberOfPages);
xmlXPathFreeObject(widgets_xpoptr);
}
//-- now check for each widgetgroup ...
for(i=0;i<pages_len;i++) {
if((widgets_xpoptr=gitk_xpath_type_filter(
gitk_cxpath_get_object(dialog,xpath_get_widgets,xmlXPathNodeSetItem(pages,i)),
XPATH_NODESET))) {
//-- if number of widgets is larger than layout->maxWidgetsPerPages correct numberOfPages
numberOfPages+=xmlXPathNodeSetGetLength((xmlNodeSetPtr)widgets_xpoptr->nodesetval)/layout->maxWidgetsPerPage;
gitk_log1(" numberOfPages[r] %d",numberOfPages);
xmlXPathFreeObject(widgets_xpoptr);
}
}
gitk_log1(" final numberOfPages %d",numberOfPages);
gitk_log_outro();
return(numberOfPages);
}
/** @brief build a single page
* @internal
* @param dialog dialog dialog
* @param root dialog node to start from
* @param xpath_get_widgets precompiles xpath expression to get the widgets of one group
* @param pageIx the current page index
*/
gint gitkr_dialog_generate_sequential_layout_build_page(GitkDialogPtr dialog,xmlNodePtr root,xmlXPathCompExprPtr xpath_get_widgets,gint pageIx) {
GitkrPhoneLayoutPtr layout;
xmlXPathObjectPtr widgets_xpoptr;
gchar *title;
gitk_log("gitkr_dialog_generate_sequential_layout_build_page() beg");
g_assert(dialog!=NULL);
g_assert(dialog->layout!=NULL);
layout=(GitkrPhoneLayoutPtr)dialog->layout;
g_assert(layout->pages!=NULL);
//-- get widgetgroup title
title=gitkr_cxpath_get_string(dialog,xpath_get_label,root);
gitk_log1("====> widgetgroup title \"%s\"",title);
if((widgets_xpoptr=gitk_xpath_type_filter(
gitk_cxpath_get_object(dialog,xpath_get_widgets,root),
XPATH_NODESET))) {
gint j;
xmlNodeSetPtr widgets=(xmlNodeSetPtr)widgets_xpoptr->nodesetval;
gint widgets_len=xmlXPathNodeSetGetLength(widgets);
xmlNodePtr node;
gint widgetIx;
GitkrPhoneWidgetPtr widget;
gchar *focus;
//-- do not add empty pages
if(widgets_len) {
//-- now add widgets
g_assert(layout->pages[pageIx].widgets!=NULL);
widgetIx=0;
gitk_log2("====> starting at page %2d with %2d widgets",pageIx,widgets_len);
for(j=0;j<widgets_len;j++) {
node=xmlXPathNodeSetItem(widgets,j);
//-- add new widget
if(!xmlNodeIsText(node)) {
//-- check if this widget has the focus
if(focus=xmlGetProp(node,"has_focus")) {
if(!strncmp(focus,"true\0",5)) {
layout->currentPage=pageIx;
layout->currentWidget=widgetIx;
layout->focus=GITK_FOCUS_TYPE_MAIN;
}
xmlFree(focus);
}
//-- get widget data
widget=&layout->pages[pageIx].widgets[widgetIx];
//-- allocate and attach type specific widget data
switch(gitk_widget_get_type(node)) {
case GITK_WIDGET_TYPE_ACTION:
gitkr_widget_action_new(widget,dialog,node);
break;
case GITK_WIDGET_TYPE_CHARACTERINPUT:
gitkr_widget_characterinput_new(widget,dialog,node);
/** @todo set initial value */
break;
case GITK_WIDGET_TYPE_CHARACTERINPUT_ALPHABETIC:
gitkr_widget_characterinput_alphabetic_new(widget,dialog,node);
/** @todo set initial value */
break;
case GITK_WIDGET_TYPE_OPTIONCHOICE:
case GITK_WIDGET_TYPE_OPTIONCHOICE_SINGLE:
case GITK_WIDGET_TYPE_OPTIONCHOICE_SINGLE_COMPACT:
/** @todo implement widget */
gitkr_widget_optionchoice_new(widget,dialog,node);
/** @todo set initial value, choices */
break;
case GITK_WIDGET_TYPE_OPTIONCHOICE_BOOLEAN:
gitkr_widget_optionchoice_boolean_new(widget,dialog,node);
break;
case GITK_WIDGET_TYPE_LABEL:
gitkr_widget_label_new(widget,dialog,node);
break;
case GITK_WIDGET_TYPE_UNDEF:
default:
gitk_log("widget type not yet implemented");
gitkr_widget_new(widget,dialog,node);
}
gitk_log1(" widget=0x%08lx",(unsigned long)widget);
gitk_log1(" id=\"%s\"" ,widget->id);
gitk_log1(" label=\"%s\"" ,widget->label);
widgetIx++;
}
else { gitk_err("unexpected textnode in dialog definition"); }
//-- if page is full do next page
if(widgetIx==layout->maxWidgetsPerPage){
layout->pages[pageIx].title=title;
layout->pages[pageIx].widgetsPerPage=widgetIx;
gitk_log2(" widgetsPerPages[%2d]=%2d",pageIx,layout->pages[pageIx].widgetsPerPage);
pageIx++;
g_assert(layout->pages[pageIx].widgets!=NULL);
widgetIx=0;
}
}
layout->pages[pageIx].title=title;
layout->pages[pageIx].widgetsPerPage=widgetIx;
gitk_log2(" widgetsPerPages[%2d]=%2d",pageIx,layout->pages[pageIx].widgetsPerPage);
pageIx++;
}
xmlXPathFreeObject(widgets_xpoptr);
}
gitk_log("gitkr_dialog_generate_sequential_layout_build_page() end");
return(pageIx);
}
/** @brief generate a layout with pure hierarchical order
* @internal
* @param dialog dialog handle
* @param root dialog node to start from
*/
void gitkr_dialog_generate_hierarchical_layout(GitkDialogPtr dialog,xmlNodePtr root) {
GitkrPhoneLayoutPtr layout;
//xmlXPathCompExprPtr xpath_get_pages;
g_assert(dialog!=NULL);
g_assert(dialog->layout!=NULL);
gitk_log("gitkr_dialog_generate_hierarchical_layout() beg");
layout=(GitkrPhoneLayoutPtr)dialog->layout;
/** @todo implement this part */
}
/** @brief display the layout of a dialog
* @internal
* @param dialog which dialogs layout should be displayed
*/
void gitkr_dialog_output_layout(GitkDialogPtr dialog) {
GitkrPhoneLayoutPtr layout;
GitkrPhoneWidgetPtr page;
g_assert(dialog!=NULL);
g_assert(dialog->layout!=NULL);
gitk_log("gitkr_layout_output() beg");
layout=(GitkrPhoneLayoutPtr)dialog->layout;
g_assert(layout->pages!=NULL);
g_assert((&layout->pages[layout->currentPage])!=NULL);
g_assert(layout->currentPage<layout->numberOfPages);
if((page=layout->pages[layout->currentPage].widgets)) {
GitkrPhoneWidgetPtr widget;
guint j;
//guint color_index=1,display_attrs;
gitk_log("starting output");
//capi_text_output(capiinfo, "G I T K application start", ftinfo);
//-- display the title line
if((layout->title) || (layout->pages[layout->currentPage].title)) {
gchar *title;
gboolean newtitle=FALSE;
if((layout->title) && (layout->pages[layout->currentPage].title)) {
guint len=strlen(IconApplication)+strlen(layout->title)+strlen(layout->pages[layout->currentPage].title)+strlen(IconPagename)+4;
title=g_new(gchar,len);
newtitle=TRUE;
gitk_log1("stringlnge Pagename %d", strlen(layout->pages[layout->currentPage].title));
gitk_log1("stringlnge Alles %d", len);
gitk_log1("string Pagename :%s:", layout->pages[layout->currentPage].title);
if(layout->pages[layout->currentPage].title != " " && layout->pages[layout->currentPage].title != "" && strlen(layout->pages[layout->currentPage].title) > 2)
snprintf(title,len,"%s %s %s %s ",IconApplication, layout->title, IconPagename, layout->pages[layout->currentPage].title);
else
snprintf(title,len,"%s %s ", IconApplication, layout->title);
}
else if(layout->title) {
guint len=strlen(IconApplication)+strlen(layout->title)+4;
snprintf(title,len,"%s %s ", IconApplication, layout->title);
}
else {
guint len=strlen(layout->pages[layout->currentPage].title)+strlen(IconPagename)+4;
snprintf(title,len,"%s %s ", IconPagename, layout->pages[layout->currentPage].title);
}
capi_text_output(capiinfo, title, ftinfo);
if(newtitle) g_free(title);
}
//-- display the widgets
for(j=0;j<layout->pages[layout->currentPage].widgetsPerPage;j++) {
widget=&page[j];
if(widget && widget->id && widget->output){
widget->output(widget,(layout->currentWidget==j));
}
}
//-- display the navigation line
if(layout->ctrl.widgetsPerPage==1) {
GitkrPhoneWidgetPtr widget=&layout->ctrl.widgets[0];
/* order is ["help"], "okay","cancel" */
/* order is ["help"], "apply","okay","cancel" */
/* order is ["help"], "try","revert","okay","cancel" */
if(widget && widget->id && widget->output){
gitk_log1("currentWidget = %d", layout->currentWidget);
capi_text_output(capiinfo, IconControl, ftinfo);
widget->output(widget,(layout->currentWidget==GITKR_TEXT_NAVIGATION));
}
//gitk_log("displaying dialogwidgets ----");
}
//-- now set the cursor to the active widget
}
}
/** @brief frees the layout data of a dialog
* @internal
* @param dialog dialog which contains the layout data to be freed
*/
void gitkr_dialog_free_layout(GitkDialogPtr dialog) {
g_assert(dialog!=NULL);
gitk_log("gitkr_dialog_layout_free() beg");
if(dialog->layout) {
GitkrPhoneLayoutPtr layout=(GitkrPhoneLayoutPtr)dialog->layout;
if(layout->pages) {
GitkrPhoneWidgetPtr page;
GitkrPhoneWidgetPtr widget;
gint i;
for(i=0;i<layout->numberOfPages;i++) {
if(layout->pages[i].widgets) {
gint j;
page=layout->pages[i].widgets;
for(j=0;j<layout->maxWidgetsPerPage;j++) {
widget=&page[j];
xmlFree(widget->id);
xmlFree(widget->label);
}
g_free(page);
}
}
g_free(layout->pages);
}
g_free(layout->ctrl.widgets);
xmlFree(layout->title);
g_free(layout);
dialog->layout=NULL;
}
}
/** @brief get the current active widget
* @internal
* @param layout pointer to active GitkrPhoneLayout
* @return pointer to GitkrPhoneWidget
*/
GitkrPhoneWidgetPtr gitkr_layout_get_current_widget(GitkrPhoneLayoutPtr layout) {
GitkrPhoneWidgetPtr page;
if((page=layout->pages[layout->currentPage].widgets)) {
return(&page[layout->currentWidget]);
}
return(NULL);
}
/** @brief get the line position of the current widget
* @internal
* @param layout pointer to active GitkrPhoneLayout
*/
gint gitkr_layout_get_current_widget_line(GitkrPhoneLayoutPtr layout) {
return(GITK_LINE_OFFSET+layout->currentWidget);
}