Logo Search packages:      
Sourcecode: verbiste version File versions

main-window.cpp

/*  $Id: main-window.cpp,v 1.12 2005/03/13 04:03:26 sarrazip Exp $
    main-window.cpp - Input and conjugation window

    verbiste - French conjugation system
    Copyright (C) 2003-2005 Pierre Sarrazin <http://sarrazip.com/>

    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., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA.
*/

#include "main-window.h"

#include "conjugation.h"
#include "util.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <libintl.h>
#define _(x) gettext(x)
#define N_(x) (x)

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <libgnomeui/libgnomeui.h>

#include <iostream>

using namespace std;
using namespace verbiste;


/*****************************************************************************/

gboolean hideOnDelete = TRUE;


static FrenchVerbDictionary *fvd = NULL;

static GtkWidget *resultWin = NULL;
static GtkWidget *verbEntry = NULL;
static GtkWidget *conjButton = NULL;
static GtkWidget *showPronounsCB = NULL;
static GtkWidget *resultNotebook = NULL;

static const gint SP = 2;  // default spacing for the GTK+ boxes


/*****************************************************************************/


class ResultPage
{
public:

    GtkWidget *notebookPage;
    GtkWidget *table;

    ResultPage();

    // No destructor because this object does not own the two GtkWidgets.
};


ResultPage::ResultPage()
  : notebookPage(gtk_vbox_new(FALSE, SP)),
    table(gtk_table_new(4, 4, FALSE))
{
    GtkWidget *scrolledWin = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(
                  GTK_SCROLLED_WINDOW(scrolledWin),
                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_add_with_viewport(
                  GTK_SCROLLED_WINDOW(scrolledWin), table);

    g_object_set_data(G_OBJECT(notebookPage), "ResultPage", this);
      // make the vbox widget point to the corresponding ResultPage object

    gtk_box_pack_start(GTK_BOX(notebookPage), scrolledWin, TRUE, TRUE, 0);
}


/*****************************************************************************/


static
gboolean
onKeyPressInResultWin(GtkWidget *, GdkEventKey *event, gpointer)
{
    g_return_val_if_fail(event != NULL, TRUE);

    if (event->keyval == GDK_w && (event->state & GDK_CONTROL_MASK) != 0)
    {
      if (hideOnDelete)
          gtk_widget_hide(resultWin);
      else
          gtk_main_quit();
      return TRUE;
    }

    return FALSE;
}


static
gboolean
onKeyPressInAbout(GtkWidget *about, GdkEventKey *event, gpointer)
{
    g_return_val_if_fail(event != NULL, TRUE);

    switch (event->keyval)
    {
      case GDK_Escape:
          gtk_dialog_response(GTK_DIALOG(about), GTK_RESPONSE_OK);
          return TRUE;

      default:
          return FALSE;
    }
}


void
showAbout()
{
    const gchar *authors[] =
    {
      "Pierre Sarrazin <http://sarrazip.com/>",
      NULL
    };

    string logoFilename = string(PIXMAPDIR) + "/" + PACKAGE ".png";

    GdkPixbuf *logo = gdk_pixbuf_new_from_file(logoFilename.c_str(), NULL);

    string copyright =
      string("Copyright (C) 2003-2005 Pierre Sarrazin <http://sarrazip.com/>\n")
      + _("Distributed under the GNU General Public License");

    GtkWidget *about = gnome_about_new(
            PACKAGE_FULL_NAME,
            VERSION,
            copyright.c_str(),
            _("A French conjugation system"),
            authors,
            NULL,
            NULL,
            logo);

    if (logo != NULL)
      gdk_pixbuf_unref(logo);

    g_signal_connect(G_OBJECT(about), "key-press-event",
                  G_CALLBACK(onKeyPressInAbout), NULL);

    set_window_icon_to_default(about);

    gtk_widget_show(about);
}


static
gboolean
onKeyPressInEntry(GtkWidget *entry, GdkEventKey *event, gpointer data)
{
    g_return_val_if_fail(event != NULL, TRUE);

    switch (event->keyval)
    {
      case GDK_Return:
      case GDK_KP_Enter:
          {
            const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
            processText(text);
          }
          return TRUE;

      default:
          return FALSE;
    }
}


static
void
onChangeInEntry(GtkEditable *, gpointer)
{
    const gchar *text = gtk_entry_get_text(GTK_ENTRY(verbEntry));
    gtk_widget_set_sensitive(GTK_WIDGET(conjButton), text[0] != '\0');
}


static
void
onConjugateButton(GtkWidget *, gpointer)
{
    const gchar *text = gtk_entry_get_text(GTK_ENTRY(verbEntry));
    processText(text);
}


static
GtkWidget *
newLabel(const string &markup, gboolean selectable)
{
    GtkWidget *label = gtk_label_new("");
    gtk_label_set_markup(GTK_LABEL(label), markup.c_str());
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
    gtk_label_set_selectable(GTK_LABEL(label), selectable);
    return label;
}


static
ResultPage *
appendResultPage(const string &utf8LabelText)
{
    ResultPage *rp = new ResultPage();
    GtkWidget *label = newLabel("<b>" + utf8LabelText + "</b>" , FALSE);

    gtk_notebook_append_page(GTK_NOTEBOOK(resultNotebook),
                        GTK_WIDGET(rp->notebookPage),
                        GTK_WIDGET(label));
    return rp;
}


static
void
onAboutButton(GtkWidget *, gpointer)
{
    showAbout();
}


static
void
showResultWin()
{
    if (resultWin == NULL)
    {
      resultWin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW(resultWin), PACKAGE_FULL_NAME);
      gtk_window_set_default_size(GTK_WINDOW(resultWin), 380, 525);
      gtk_container_set_border_width(GTK_CONTAINER(resultWin), 4);

      if (hideOnDelete)
      {
          /*
            When user clicks on title bar's close button, the window must
            only be hidden, not be destroyed.
          */
          g_signal_connect(G_OBJECT(resultWin), "delete_event",
                  G_CALLBACK(gtk_widget_hide_on_delete), NULL);
      }
      else
          g_signal_connect(G_OBJECT(resultWin), "delete_event",
                  G_CALLBACK(gtk_main_quit), NULL);

      /*
          Capture the key presses in order to hide or close the window
          on Ctrl-W.
      */
      g_signal_connect(G_OBJECT(resultWin), "key-press-event",
                  G_CALLBACK(onKeyPressInResultWin), NULL);


      /*
          Create a text field where the user can enter requests:
      */
      verbEntry = gtk_entry_new_with_max_length(255);
      g_signal_connect(G_OBJECT(verbEntry), "key-press-event",
                  G_CALLBACK(onKeyPressInEntry), NULL);
      g_signal_connect(G_OBJECT(verbEntry), "changed",
                  G_CALLBACK(onChangeInEntry), NULL);
      GtkWidget *prompt = gtk_label_new_with_mnemonic(_("_Verb:"));
      gtk_label_set_mnemonic_widget(GTK_LABEL(prompt), verbEntry);

      GtkWidget *promptBox = gtk_hbox_new(FALSE, SP);
      gtk_box_pack_start(GTK_BOX(promptBox), prompt, FALSE, FALSE, 0);
      gtk_box_pack_start(GTK_BOX(promptBox), verbEntry, TRUE, TRUE, 0);

      conjButton = gtk_button_new_with_mnemonic(_("_Conjugate"));
      gtk_widget_set_sensitive(GTK_WIDGET(conjButton), false);
      gtk_box_pack_start(GTK_BOX(promptBox), conjButton, FALSE, FALSE, 0);
      g_signal_connect(G_OBJECT(conjButton), "clicked",
                            G_CALLBACK(onConjugateButton), NULL);

      GtkWidget *aboutButton = gtk_button_new_with_mnemonic(_("_About"));
      gtk_box_pack_end(GTK_BOX(promptBox), aboutButton, FALSE, FALSE, 0);
      g_signal_connect(G_OBJECT(aboutButton), "clicked",
                            G_CALLBACK(onAboutButton), NULL);


      /*
          Create an options box.
      */
      GtkWidget *optionsBox = gtk_hbox_new(FALSE, SP);
      showPronounsCB = gtk_check_button_new_with_mnemonic(
                                          _("Show _Pronouns"));
      gtk_box_pack_start(GTK_BOX(optionsBox), showPronounsCB,
                                          FALSE, FALSE, 0);


      /*
          Create a notebook that receives the conjugations.
      */
      resultNotebook = gtk_notebook_new();
      gtk_notebook_set_scrollable(GTK_NOTEBOOK(resultNotebook), TRUE);


      /*
          Finish the window setup:
      */
      GtkWidget *vbox = gtk_vbox_new(FALSE, SP);
      gtk_box_pack_start(GTK_BOX(vbox), promptBox, FALSE, FALSE, 0);
      gtk_box_pack_start(GTK_BOX(vbox), optionsBox, FALSE, FALSE, 0);
      gtk_box_pack_start(GTK_BOX(vbox), resultNotebook, TRUE, TRUE, 0);
      gtk_container_add(GTK_CONTAINER(resultWin), vbox);
      set_window_icon_to_default(resultWin);
      gtk_widget_show_all(GTK_WIDGET(resultWin));
    }

    gtk_window_present(GTK_WINDOW(resultWin));
}


static
void
clearResultNotebook()
{
    GtkWidget *w;
    while ((w = gtk_notebook_get_nth_page(
                  GTK_NOTEBOOK(resultNotebook), 0)) != NULL)
    {
      ResultPage *rp = (ResultPage *) g_object_get_data(
                                    G_OBJECT(w), "ResultPage");
      if (rp == NULL)
          g_warning("clearResultNotebook: null ResultPage pointer");
      gtk_notebook_remove_page(GTK_NOTEBOOK(resultNotebook), 0);
      delete rp;
    }
}


static
string
tolowerUTF8(const string &s)
{
    gchar *down = g_utf8_strdown(s.data(), s.length());
    string result = down;
    g_free(down);
    return result;
}


static
GtkWidget *
createTableCell(const VVS &latin1Tense,
            const string &utf8TenseName,
            const string &utf8UserText,
            FrenchVerbDictionary *fvd)
{
    GtkWidget *vbox = gtk_vbox_new(FALSE, SP);
    GtkWidget *nameLabel = newLabel(
                      "<b><u>" + utf8TenseName + "</u></b>", TRUE);

    string latin1Persons = createTableCellText(
                        latin1Tense,
                        fvd->utf8ToLatin1(tolowerUTF8(utf8UserText)),
                        "<span foreground=\"red\">",
                        "</span>");

    string utf8Persons = fvd->latin1ToUTF8(latin1Persons);
    GtkWidget *personsLabel = newLabel(utf8Persons, TRUE);

    gtk_box_pack_start(GTK_BOX(vbox), nameLabel, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), personsLabel, FALSE, FALSE, 0);

    return vbox;
}


void
processText(const string &utf8UserText)
/*
    'utf8UserText' must be a UTF-8 string to be deconjugated.
    It must not contain a newline character('\n').
*/
{
    g_return_if_fail(fvd != NULL);

    showResultWin();

    gtk_entry_set_text(GTK_ENTRY(verbEntry), utf8UserText.c_str());
    gtk_editable_select_region(GTK_EDITABLE(verbEntry), 0, -1);

    clearResultNotebook();

    string lowerCaseUserText = fvd->utf8ToLatin1(tolowerUTF8(utf8UserText));

    bool includePronouns = gtk_toggle_button_get_active(
                                  GTK_TOGGLE_BUTTON(showPronounsCB));

    /*
      For each possible deconjugation, take the infinitive form and
      obtain its complete conjugation.
    */
    vector<InflectionDesc> v;
    fvd->deconjugate(lowerCaseUserText, v);

    string prevLatin1Infinitive;
    size_t numPages = 0;

    for (vector<InflectionDesc>::const_iterator it = v.begin();
                                  it != v.end(); it++)
    {
      const InflectionDesc &d = *it;

      VVVS latin1Conjug;
      getConjugation(*fvd, d.infinitive, latin1Conjug, includePronouns);

      if (latin1Conjug.size() == 0           // if no tenses
          || latin1Conjug[0].size() == 0     // if no infinitive tense
          || latin1Conjug[0][0].size() == 0  // if no person in inf. tense
          || latin1Conjug[0][0][0].empty())  // if infinitive string empty
      {
          continue;
      }

      string latin1Infinitive = latin1Conjug[0][0][0];

      if (latin1Infinitive == prevLatin1Infinitive)
          continue;

      ResultPage *rp = appendResultPage(fvd->latin1ToUTF8(latin1Infinitive));
      numPages++;

      int i = 0;
      for (VVVS::const_iterator t = latin1Conjug.begin();
                        t != latin1Conjug.end(); t++, i++)
      {
          const VVS &latin1Tense = *t;

          if (i == 1)
            i = 4;
          else if (i == 11)
            i = 12;
          assert(i >= 0 && i < 16);

          int row = i / 4;
          int col = i % 4;

          string utf8TenseName = getTenseNameForTableCell(row, col);
          assert(!utf8TenseName.empty());

          GtkWidget *cell = createTableCell(
                        latin1Tense, utf8TenseName, utf8UserText, fvd);
          gtk_table_attach(GTK_TABLE(rp->table), cell,
                        col, col + 1, row, row + 1,
                        GTK_FILL, GTK_FILL,
                        8, 8);
      }

      gtk_widget_show_all(GTK_WIDGET(rp->notebookPage));
            /* must be done here to show the elements added in the for() */

      prevLatin1Infinitive = latin1Infinitive;
    }

    if (numPages == 0 && !utf8UserText.empty())
    {
      ResultPage *rp = appendResultPage(
                              "<i>" + string(_("error")) + "</i>");
      GtkWidget *cell = newLabel(_("Unknown verb."), FALSE);
      gtk_table_attach(GTK_TABLE(rp->table), cell,
                        0, 1, 0, 1,
                        GTK_FILL, GTK_FILL,
                        8, 8);
      gtk_widget_show_all(GTK_WIDGET(rp->notebookPage));
    }

    gtk_notebook_set_current_page(GTK_NOTEBOOK(resultNotebook), 0);
}


void
showErrorDialog(const string &msg)
{
    GtkWidget *dlg = gtk_message_dialog_new(NULL,
                              GTK_DIALOG_MODAL,
                              GTK_MESSAGE_ERROR,
                              GTK_BUTTONS_CLOSE,
                              msg.c_str());
    gtk_dialog_run(GTK_DIALOG(dlg));
    gtk_widget_destroy(dlg);
}


void
initVerbDict() throw(logic_error)
{
    /*
      Create the French verb dictionary, which can conjugate and
      deconjugate French verbs.
    */
    const char *libdatadir = getenv("LIBDATADIR");
    if (libdatadir == NULL)
      libdatadir = LIBDATADIR;
    string conjFN  = libdatadir + string("/") + "conjugation-fr.xml";
    string verbsFN = libdatadir + string("/") + "verbs-fr.xml";
    fvd = new FrenchVerbDictionary(conjFN, verbsFN);  // may throw
}

Generated by  Doxygen 1.6.0   Back to index