/* * File: form.cc * * Copyright 2008 Jorge Arellano Cid * * 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 3 of the License, or * (at your option) any later version. */ #include "form.hh" #include "html_common.hh" #include #include #include "lout/misc.hh" #include "dw/core.hh" #include "dw/textblock.hh" #include "misc.h" #include "msg.h" #include "prefs.h" #include "uicmd.hh" using namespace lout; using namespace dw; using namespace dw::core; using namespace dw::core::style; using namespace dw::core::ui; /* * Forward declarations */ class DilloHtmlReceiver; class DilloHtmlSelect; class DilloHtmlOption; static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize); static void Html_option_finish(DilloHtml *html); /* * Typedefs */ typedef enum { DILLO_HTML_INPUT_UNKNOWN, DILLO_HTML_INPUT_TEXT, DILLO_HTML_INPUT_PASSWORD, DILLO_HTML_INPUT_CHECKBOX, DILLO_HTML_INPUT_RADIO, DILLO_HTML_INPUT_IMAGE, DILLO_HTML_INPUT_FILE, DILLO_HTML_INPUT_BUTTON, DILLO_HTML_INPUT_HIDDEN, DILLO_HTML_INPUT_SUBMIT, DILLO_HTML_INPUT_RESET, DILLO_HTML_INPUT_BUTTON_SUBMIT, DILLO_HTML_INPUT_BUTTON_RESET, DILLO_HTML_INPUT_SELECT, DILLO_HTML_INPUT_SEL_LIST, DILLO_HTML_INPUT_TEXTAREA, DILLO_HTML_INPUT_INDEX } DilloHtmlInputType; /* * Class declarations */ class DilloHtmlForm { friend class DilloHtmlReceiver; friend class DilloHtmlInput; DilloHtml *html; bool showing_hiddens; bool enabled; void eventHandler(Resource *resource, EventButton *event); DilloUrl *buildQueryUrl(DilloHtmlInput *active_input); Dstr *buildQueryData(DilloHtmlInput *active_submit); char *makeMultipartBoundary(iconv_t char_encoder, DilloHtmlInput *active_submit); Dstr *encodeText(iconv_t char_encoder, Dstr **input); void strUrlencodeAppend(Dstr *dstr, const char *str); void inputUrlencodeAppend(Dstr *data, const char *name, const char *value); void inputMultipartAppend(Dstr *data, const char *boundary, const char *name, const char *value); void filesInputMultipartAppend(Dstr* data, const char *boundary, const char *name, Dstr *file, const char *filename); void imageInputUrlencodeAppend(Dstr *data, Dstr *name, Dstr *x, Dstr *y); void imageInputMultipartAppend(Dstr *data, const char *boundary, Dstr *name, Dstr *x, Dstr *y); public: //BUG: for now everything is public DilloHtmlMethod method; DilloUrl *action; DilloHtmlEnc content_type; char *submit_charset; lout::misc::SimpleVector *inputs; int num_entry_fields; DilloHtmlReceiver *form_receiver; public: DilloHtmlForm (DilloHtml *html, DilloHtmlMethod method, const DilloUrl *action, DilloHtmlEnc content_type, const char *charset, bool enabled); ~DilloHtmlForm (); DilloHtmlInput *getInput (Resource *resource); DilloHtmlInput *getRadioInput (const char *name); void submit(DilloHtmlInput *active_input, EventButton *event); void reset (); void display_hiddens(bool display); void addInput(DilloHtmlInput *input, DilloHtmlInputType type); void setEnabled(bool enabled); }; class DilloHtmlReceiver: public Resource::ActivateReceiver, public Resource::ClickedReceiver { friend class DilloHtmlForm; DilloHtmlForm* form; DilloHtmlReceiver (DilloHtmlForm* form2) { form = form2; } ~DilloHtmlReceiver () { } void activate (Resource *resource); void enter (Resource *resource); void leave (Resource *resource); void clicked (Resource *resource, EventButton *event); }; class DilloHtmlInput { // DilloHtmlForm::addInput() calls connectTo() friend class DilloHtmlForm; public: //BUG: for now everything is public DilloHtmlInputType type; Embed *embed; /* May be NULL (think: hidden input) */ char *name; char *init_str; /* note: some overloading - for buttons, init_str is simply the value of the button; for text entries, it is the initial value */ DilloHtmlSelect *select; bool init_val; /* only meaningful for buttons */ Dstr *file_data; /* only meaningful for file inputs. TODO: may become a list... */ private: void connectTo(DilloHtmlReceiver *form_receiver); void activate(DilloHtmlForm *form, int num_entry_fields,EventButton *event); void readFile(BrowserWindow *bw); public: DilloHtmlInput (DilloHtmlInputType type, Embed *embed, const char *name, const char *init_str, bool init_val); ~DilloHtmlInput (); void appendValuesTo(Dlist *values, bool is_active_submit); void reset(); void setEnabled(bool enabled) {if (embed) embed->setEnabled(enabled); }; }; class DilloHtmlSelect { friend class DilloHtmlInput; private: lout::misc::SimpleVector *options; DilloHtmlSelect (); ~DilloHtmlSelect (); public: DilloHtmlOption *getCurrentOption (); void addOption (char *value, bool selected, bool enabled); void ensureSelection (); void addOptionsTo (SelectionResource *res); void appendValuesTo (Dlist *values, SelectionResource *res); }; class DilloHtmlOption { friend class DilloHtmlSelect; public: char *value, *content; bool selected, enabled; private: DilloHtmlOption (char *value, bool selected, bool enabled); ~DilloHtmlOption (); }; /* * Form API */ DilloHtmlForm *a_Html_form_new (DilloHtml *html, DilloHtmlMethod method, const DilloUrl *action, DilloHtmlEnc content_type, const char *charset, bool enabled) { return new DilloHtmlForm (html, method, action, content_type, charset, enabled); } void a_Html_form_delete (DilloHtmlForm *form) { delete form; } void a_Html_input_delete (DilloHtmlInput *input) { delete input; } void a_Html_form_submit2(void *vform) { ((DilloHtmlForm *)vform)->submit(NULL, NULL); } void a_Html_form_reset2(void *vform) { ((DilloHtmlForm *)vform)->reset(); } void a_Html_form_display_hiddens2(void *vform, bool display) { ((DilloHtmlForm *)vform)->display_hiddens(display); } /* * Form parsing functions */ /* * Add an HTML control */ static void Html_add_input(DilloHtml *html, DilloHtmlInputType type, Embed *embed, const char *name, const char *init_str, bool init_val) { _MSG("name=[%s] init_str=[%s] init_val=[%d]\n", name, init_str, init_val); DilloHtmlInput *input = new DilloHtmlInput(type, embed, name, init_str, init_val); if (html->InFlags & IN_FORM) { html->getCurrentForm()->addInput(input, type); } else { int ni = html->inputs_outside_form->size(); html->inputs_outside_form->increase(); html->inputs_outside_form->set(ni, input); input->setEnabled(false); } } /* * Find radio input by name */ static DilloHtmlInput *Html_get_radio_input(DilloHtml *html, const char *name) { if (name) { lout::misc::SimpleVector* inputs; if (html->InFlags & IN_FORM) inputs = html->getCurrentForm()->inputs; else inputs = html->inputs_outside_form; for (int idx = 0; idx < inputs->size(); idx++) { DilloHtmlInput *input = inputs->get(idx); if (input->type == DILLO_HTML_INPUT_RADIO && input->name && !dStrcasecmp(input->name, name)) return input; } } return NULL; } /* * Get the current input. * Note that this _assumes_ that there _is_ a current input. */ static DilloHtmlInput *Html_get_current_input(DilloHtml *html) { lout::misc::SimpleVector* inputs; if (html->InFlags & IN_FORM) inputs = html->getCurrentForm()->inputs; else inputs = html->inputs_outside_form; return inputs->get (inputs->size() - 1); } /* * Handle
tag */ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize) { DilloUrl *action; DilloHtmlMethod method; DilloHtmlEnc content_type; char *charset, *first; const char *attrbuf; HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); if (html->InFlags & IN_FORM) { BUG_MSG("nested forms\n"); return; } html->InFlags |= IN_FORM; html->InFlags &= ~IN_SELECT; html->InFlags &= ~IN_OPTION; html->InFlags &= ~IN_TEXTAREA; method = DILLO_HTML_METHOD_GET; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "method"))) { if (!dStrcasecmp(attrbuf, "post")) { method = DILLO_HTML_METHOD_POST; } else if (dStrcasecmp(attrbuf, "get")) { BUG_MSG("Unknown form submission method \"%s\"\n", attrbuf); } } if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "action"))) action = a_Html_url_new(html, attrbuf, NULL, 0); else { BUG_MSG("action attribute required for \n"); action = a_Url_dup(html->base_url); } content_type = DILLO_HTML_ENC_URLENCODED; if ((method == DILLO_HTML_METHOD_POST) && ((attrbuf = a_Html_get_attr(html, tag, tagsize, "enctype")))) { if (!dStrcasecmp(attrbuf, "multipart/form-data")) content_type = DILLO_HTML_ENC_MULTIPART; } charset = NULL; first = NULL; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "accept-charset"))) { /* a list of acceptable charsets, separated by commas or spaces */ char *ptr = first = dStrdup(attrbuf); while (ptr && !charset) { char *curr = dStrsep(&ptr, " ,"); if (!dStrcasecmp(curr, "utf-8")) { charset = curr; } else if (!dStrcasecmp(curr, "UNKNOWN")) { /* defined to be whatever encoding the document is in */ charset = html->charset; } } if (!charset) charset = first; } if (!charset) charset = html->charset; html->formNew(method, action, content_type, charset); dFree(first); a_Url_free(action); } void Html_tag_close_form(DilloHtml *html, int TagIdx) { // DilloHtmlForm *form; // int i; // // if (html->InFlags & IN_FORM) { // form = html->getCurrentForm (); // // /* Make buttons sensitive again */ // for (i = 0; i < form->inputs->size(); i++) { // input_i = form->inputs->get(i); // /* Check for tricky HTML (e.g. ) */ // if (!input_i->widget) // continue; // if (input_i->type == DILLO_HTML_INPUT_SUBMIT || // input_i->type == DILLO_HTML_INPUT_RESET) { // gtk_widget_set_sensitive(input_i->widget, TRUE); // } else if (input_i->type == DILLO_HTML_INPUT_IMAGE || // input_i->type == DILLO_HTML_INPUT_BUTTON_SUBMIT || // input_i->type == DILLO_HTML_INPUT_BUTTON_RESET) { // a_Dw_button_set_sensitive(DW_BUTTON(input_i->widget), TRUE); // } // } // } html->InFlags &= ~IN_FORM; html->InFlags &= ~IN_SELECT; html->InFlags &= ~IN_OPTION; html->InFlags &= ~IN_TEXTAREA; } /* * get size, restrict it to reasonable value */ static int Html_input_get_size(DilloHtml *html, const char *attrbuf) { const int MAX_SIZE = 1024; int size = 20; if (attrbuf) { size = strtol(attrbuf, NULL, 10); if (size < 1 || size > MAX_SIZE) { int badSize = size; size = (size < 1 ? 20 : MAX_SIZE); BUG_MSG("input size=%d, using size=%d instead\n", badSize, size); } } return size; } /* * Add a new input to current form */ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) { DilloHtmlInputType inp_type; Resource *resource = NULL; Embed *embed = NULL; char *value, *name, *type, *init_str; const char *attrbuf, *label; bool init_val = false; ResourceFactory *factory; if (html->InFlags & IN_SELECT) { BUG_MSG(" element inside element inside