diff --git a/Makefile.am b/Makefile.am index 336678c3..0f493405 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,14 @@ include $(top_srcdir)/gtest.mk -SUBDIRS=m4 man src contrib toys +# only build widgets if we have webkit +if HAVE_WEBKIT +widgets=widgets +else +widgets= +endif + +SUBDIRS=m4 man src $(widgets) contrib toys ACLOCAL_AMFLAGS=-I m4 diff --git a/configure.ac b/configure.ac index f7768421..0ad62615 100644 --- a/configure.ac +++ b/configure.ac @@ -199,9 +199,10 @@ AC_CONFIG_FILES([ Makefile src/Makefile src/tests/Makefile +widgets/Makefile toys/Makefile toys/mug/Makefile -toys/mug/mug.desktop +toys/mug2/Makefile man/Makefile m4/Makefile contrib/Makefile diff --git a/src/mu-query.cc b/src/mu-query.cc index ac46d326..a6b69a57 100644 --- a/src/mu-query.cc +++ b/src/mu-query.cc @@ -312,7 +312,6 @@ mu_query_new (const char* xpath, GError **err) "'%s' is not a readable xapian dir", xpath); return NULL; } - if (!mu_util_db_version_up_to_date (xpath)) { g_set_error (err, 0, MU_ERROR_XAPIAN_NOT_UPTODATE, diff --git a/toys/mug2/Makefile.am b/toys/mug2/Makefile.am new file mode 100644 index 00000000..0f16cbc1 --- /dev/null +++ b/toys/mug2/Makefile.am @@ -0,0 +1,53 @@ +## Copyright (C) 2008-2011 Dirk-Jan C. Binnema +## +## 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 +## t he Free Software Foundation; either version 3 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 $(top_srcdir)/gtest.mk + +# enforce compiling this dir first before decending into tests/ +SUBDIRS= . + +INCLUDES=-I${top_srcdir}/src $(GTK_CFLAGS) $(WEBKIT_CFLAGS) -DICONDIR='"$(icondir)"' \ + -DGTK_DISABLE_DEPRECATED -DGSEAL_ENABLE + +# don't use -Werror, as it might break on other compilers +# use -Wno-unused-parameters, because some callbacks may not +# really need all the params they get +AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement +AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter + +noinst_PROGRAMS= \ + mug2 + +# note, mug.cc is '.cc' only because libmu must explicitly +# be linked as c++, not c. +mug2_SOURCES= \ + mug.cc \ + mug-msg-list-view.c \ + mug-msg-list-view.h \ + mug-msg-view.h \ + mug-msg-view.c \ + mug-query-bar.h \ + mug-query-bar.c \ + mug-shortcuts.c \ + mug-shortcuts.h + +mug2_LDADD= \ + ${top_builddir}/src/libmu.la \ + ${top_builddir}/widgets/libmuwidgets.la \ + ${GTK_LIBS} + + + diff --git a/toys/mug2/mug-msg-list-view.c b/toys/mug2/mug-msg-list-view.c new file mode 100644 index 00000000..2a11b909 --- /dev/null +++ b/toys/mug2/mug-msg-list-view.c @@ -0,0 +1,434 @@ +/* +** Copyright (C) 2008-2011 Dirk-Jan C. Binnema +** +** 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, 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 "mug-msg-list-view.h" +#include "mu-query.h" +#include "mu-str.h" +/* include other impl specific header files */ + +/* 'private'/'protected' functions */ +static void mug_msg_list_view_class_init (MugMsgListViewClass * klass); +static void mug_msg_list_view_init (MugMsgListView * obj); +static void mug_msg_list_view_finalize (GObject * obj); + +/* list my signals */ +enum { + MUG_MSG_SELECTED, + MUG_ERROR_OCCURED, + LAST_SIGNAL +}; + +enum { + MUG_COL_DATESTR, + MUG_COL_MAILDIR, + MUG_COL_FLAGSSTR, + MUG_COL_FROM, + MUG_COL_TO, + MUG_COL_SUBJECT, + MUG_COL_PATH, + MUG_COL_PRIO, + MUG_COL_FLAGS, + MUG_COL_TIME, + MUG_N_COLS +}; + +typedef struct _MugMsgListViewPrivate MugMsgListViewPrivate; +struct _MugMsgListViewPrivate { + GtkListStore *_store; + char *_xpath; + char *_query; +}; +#define MUG_MSG_LIST_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MUG_TYPE_MSG_LIST_VIEW, \ + MugMsgListViewPrivate)) +/* globals */ +static GtkTreeViewClass *parent_class = NULL; + +/* uncomment the following if you have defined any signals */ +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (MugMsgListView, mug_msg_list_view, GTK_TYPE_TREE_VIEW); + +static void +mug_msg_list_view_class_init (MugMsgListViewClass * klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mug_msg_list_view_finalize; + + g_type_class_add_private (gobject_class, + sizeof (MugMsgListViewPrivate)); + + signals[MUG_MSG_SELECTED] = + g_signal_new ("msg-selected", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MugMsgListViewClass, + msg_selected), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + signals[MUG_ERROR_OCCURED] = + g_signal_new ("error-occured", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MugMsgListViewClass, + error_occured), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); +} + +static void +on_cursor_changed (GtkTreeView * tview, MugMsgListView * lst) +{ + GtkTreeSelection *sel; + GtkTreeIter iter; + MugMsgListViewPrivate *priv; + + priv = MUG_MSG_LIST_VIEW_GET_PRIVATE (tview); + + sel = gtk_tree_view_get_selection (tview); + if (!sel) + return; /* hmmm */ + if (gtk_tree_selection_get_selected (sel, NULL, &iter)) { + char *path; + gtk_tree_model_get (GTK_TREE_MODEL (priv->_store), &iter, + MUG_COL_PATH, &path, -1); + g_signal_emit (G_OBJECT (lst), + signals[MUG_MSG_SELECTED], 0, path); + g_free (path); + } +} + +static void +treecell_func (GtkTreeViewColumn * tree_column, GtkCellRenderer * renderer, + GtkTreeModel * tree_model, GtkTreeIter * iter, gpointer data) +{ + MuMsgFlags flags; + MuMsgPrio prio; + + gtk_tree_model_get (tree_model, iter, + MUG_COL_FLAGS, &flags, MUG_COL_PRIO, &prio, -1); + + g_object_set (G_OBJECT (renderer), + "weight", (flags & MU_MSG_FLAG_NEW) ? 800 : 400, + "weight", (flags & MU_MSG_FLAG_SEEN) ? 400 : 800, + "foreground", prio == MU_MSG_PRIO_HIGH ? "red" : NULL, + NULL); +} + +/* sortcolidx == -1 means 'sortcolidx = colidx' */ +static void +append_col (GtkTreeView * treeview, const char *label, int colidx, + int sortcolidx, gint maxwidth) +{ + GtkTreeViewColumn *col; + GtkCellRenderer *renderer; + + renderer = gtk_cell_renderer_text_new (); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + + col = gtk_tree_view_column_new_with_attributes (label, renderer, "text", + colidx, NULL); + g_object_set (G_OBJECT (col), "resizable", TRUE, NULL); + + gtk_tree_view_column_set_sort_indicator (col, TRUE); + + if (sortcolidx == -1) + sortcolidx = colidx; + gtk_tree_view_column_set_sort_column_id (col, sortcolidx); + + if (maxwidth) { + gtk_tree_view_column_set_sizing (col, + GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (col, maxwidth); + gtk_tree_view_column_set_expand (col, FALSE); + } else + gtk_tree_view_column_set_expand (col, TRUE); + + gtk_tree_view_column_set_cell_data_func (col, renderer, + (GtkTreeCellDataFunc) + treecell_func, NULL, NULL); + + gtk_tree_view_append_column (treeview, col); +} + +static void +mug_msg_list_view_init (MugMsgListView * obj) +{ + MugMsgListViewPrivate *priv; + GtkTreeView *tview; + + priv = MUG_MSG_LIST_VIEW_GET_PRIVATE (obj); + + priv->_xpath = priv->_query = NULL; + priv->_store = gtk_list_store_new (MUG_N_COLS, G_TYPE_STRING, /* date */ + G_TYPE_STRING, /* folder */ + G_TYPE_STRING, /* flagstr */ + G_TYPE_STRING, /* from */ + G_TYPE_STRING, /* to */ + G_TYPE_STRING, /* subject */ + G_TYPE_STRING, /* path */ + G_TYPE_UINT, /* prio */ + G_TYPE_UINT, /* flags */ + G_TYPE_INT); /* timeval */ + + tview = GTK_TREE_VIEW (obj); + gtk_tree_view_set_model (tview, GTK_TREE_MODEL (priv->_store)); + gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (obj), TRUE); + gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (obj), + GTK_TREE_VIEW_GRID_LINES_VERTICAL); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (obj), TRUE); + + append_col (tview, "Date", MUG_COL_DATESTR, MUG_COL_TIME, 80); + append_col (tview, "Folder", MUG_COL_MAILDIR, -1, 60); + append_col (tview, "F", MUG_COL_FLAGSSTR, -1, 25); + append_col (tview, "From", MUG_COL_FROM, -1, 0); + append_col (tview, "To", MUG_COL_TO, -1, 0); + append_col (tview, "Subject", MUG_COL_SUBJECT, -1, 0); + + g_signal_connect (G_OBJECT (obj), "cursor-changed", + G_CALLBACK (on_cursor_changed), obj); +} + +static void +mug_msg_list_view_finalize (GObject * obj) +{ + MugMsgListViewPrivate *priv; + priv = MUG_MSG_LIST_VIEW_GET_PRIVATE (obj); + + if (priv->_store) + g_object_unref (priv->_store); + + g_free (priv->_xpath); + g_free (priv->_query); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +void +mug_msg_list_view_move_first (MugMsgListView * self) +{ + GtkTreePath *path; + + g_return_if_fail (MUG_IS_MSG_LIST_VIEW (self)); + + path = gtk_tree_path_new_first (); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE); + + gtk_tree_path_free (path); +} + +static gboolean +msg_list_view_move (MugMsgListView * self, gboolean next) +{ + GtkTreePath *path; + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (self), &path, NULL); + if (!path) + return FALSE; + + if (next) + gtk_tree_path_next (path); + else + gtk_tree_path_prev (path); + + gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE); + gtk_tree_path_free (path); + + return TRUE; +} + +gboolean +mug_msg_list_view_move_next (MugMsgListView * self) +{ + g_return_val_if_fail (MUG_IS_MSG_LIST_VIEW (self), FALSE); + + return msg_list_view_move (self, TRUE); +} + +gboolean +mug_msg_list_view_move_prev (MugMsgListView * self) +{ + g_return_val_if_fail (MUG_IS_MSG_LIST_VIEW (self), FALSE); + + return msg_list_view_move (self, FALSE); +} + +GtkWidget * +mug_msg_list_view_new (const char *xpath) +{ + GtkWidget *w; + MugMsgListViewPrivate *priv; + + g_return_val_if_fail (xpath, NULL); + + w = GTK_WIDGET (g_object_new (MUG_TYPE_MSG_LIST_VIEW, NULL)); + + priv = MUG_MSG_LIST_VIEW_GET_PRIVATE (w); + priv->_xpath = g_strdup (xpath); + + return w; +} + +static gchar * +empty_or_display_contact (const gchar * str) +{ + if (!str || *str == '\0') + return g_strdup ("-"); + else + return mu_str_display_contact (str); + +} + +static MugError +mu_result_to_mug_error (MuResult r) +{ + switch (r) { + case MU_ERROR_XAPIAN_DIR: + return MUG_ERROR_XAPIAN_DIR; + case MU_ERROR_XAPIAN_NOT_UPTODATE: + return MUG_ERROR_XAPIAN_NOT_UPTODATE; + case MU_ERROR_QUERY: + return MUG_ERROR_QUERY; + default: + return MUG_ERROR_OTHER; + } +} + +static MuMsgIter * +run_query (const char *xpath, const char *query, MugMsgListView * self) +{ + GError *err; + MuQuery *xapian; + MuMsgIter *iter; + + err = NULL; + xapian = mu_query_new (xpath, &err); + if (!xapian) { + g_warning ("Error: %s", err->message); + g_signal_emit (G_OBJECT (self), + signals[MUG_ERROR_OCCURED], 0, + mu_result_to_mug_error (err->code)); + g_error_free (err); + return NULL; + } + + iter = + mu_query_run (xapian, query, MU_MSG_FIELD_ID_DATE, TRUE, 0, &err); + mu_query_destroy (xapian); + if (!iter) { + g_warning ("Error: %s", err->message); + g_signal_emit (G_OBJECT (self), + signals[MUG_ERROR_OCCURED], 0, + mu_result_to_mug_error (err->code)); + g_error_free (err); + return NULL; + } + + return iter; +} + +static void +add_row (GtkListStore * store, MuMsgIter * iter) +{ + GtkTreeIter treeiter; + const gchar *datestr, *flagstr; + gchar *from, *to; + time_t timeval; + + timeval = mu_msg_iter_get_date (iter); + datestr = timeval == 0 ? "-" : mu_str_display_date_s (timeval); + from = empty_or_display_contact (mu_msg_iter_get_from (iter)); + to = empty_or_display_contact (mu_msg_iter_get_to (iter)); + flagstr = mu_msg_flags_str_s (mu_msg_iter_get_flags (iter)); + + gtk_list_store_append (store, &treeiter); + gtk_list_store_set (store, &treeiter, + MUG_COL_DATESTR, datestr, + MUG_COL_MAILDIR, mu_msg_iter_get_maildir (iter), + MUG_COL_FLAGSSTR, flagstr, + MUG_COL_FROM, from, + MUG_COL_TO, to, + MUG_COL_SUBJECT, mu_msg_iter_get_subject (iter), + MUG_COL_PATH, mu_msg_iter_get_path (iter), + MUG_COL_PRIO, mu_msg_iter_get_prio (iter), + MUG_COL_FLAGS, mu_msg_iter_get_flags (iter), + MUG_COL_TIME, timeval, -1); + g_free (from); + g_free (to); +} + +static int +update_model (GtkListStore * store, const char *xpath, const char *query, + MugMsgListView * self) +{ + MuMsgIter *iter; + int count; + + iter = run_query (xpath, query, self); + if (!iter) { + g_warning ("error: running query failed\n"); + return -1; + } + + for (count = 0; !mu_msg_iter_is_done (iter); + mu_msg_iter_next (iter), ++count) + add_row (store, iter); + + mu_msg_iter_destroy (iter); + + return count; +} + +int +mug_msg_list_view_query (MugMsgListView * self, const char *query) +{ + MugMsgListViewPrivate *priv; + gboolean rv; + + g_return_val_if_fail (MUG_IS_MSG_LIST_VIEW (self), FALSE); + + priv = MUG_MSG_LIST_VIEW_GET_PRIVATE (self); + gtk_list_store_clear (priv->_store); + + g_free (priv->_query); + priv->_query = query ? g_strdup (query) : NULL; + + if (!query) + return TRUE; + + rv = update_model (priv->_store, priv->_xpath, query, self); + + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (self)); + + return rv; +} + +const gchar * +mug_msg_list_view_get_query (MugMsgListView * self) +{ + g_return_val_if_fail (MUG_IS_MSG_LIST_VIEW (self), NULL); + + return MUG_MSG_LIST_VIEW_GET_PRIVATE (self)->_query; +} diff --git a/toys/mug2/mug-msg-list-view.h b/toys/mug2/mug-msg-list-view.h new file mode 100644 index 00000000..db2a247d --- /dev/null +++ b/toys/mug2/mug-msg-list-view.h @@ -0,0 +1,82 @@ +/* +** Copyright (C) 2008-2010 Dirk-Jan C. Binnema +** +** 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, 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. +** +*/ + +#ifndef __MUG_MSG_LIST_VIEW_H__ +#define __MUG_MSG_LIST_VIEW_H__ + +#include +#include + +G_BEGIN_DECLS +/* convenience macros */ +#define MUG_TYPE_MSG_LIST_VIEW (mug_msg_list_view_get_type()) +#define MUG_MSG_LIST_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MUG_TYPE_MSG_LIST_VIEW,MugMsgListView)) +#define MUG_MSG_LIST_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MUG_TYPE_MSG_LIST_VIEW,MugMsgListViewClass)) +#define MUG_IS_MSG_LIST_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MUG_TYPE_MSG_LIST_VIEW)) +#define MUG_IS_MSG_LIST_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MUG_TYPE_MSG_LIST_VIEW)) +#define MUG_MSG_LIST_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MUG_TYPE_MSG_LIST_VIEW,MugMsgListViewClass)) +typedef struct _MugMsgListView MugMsgListView; +typedef struct _MugMsgListViewClass MugMsgListViewClass; + +struct _MugMsgListView { + GtkTreeView parent; + /* insert public members, if any */ +}; + +enum _MugError { + MUG_ERROR_XAPIAN_NOT_UPTODATE, + MUG_ERROR_XAPIAN_DIR, + MUG_ERROR_QUERY, + MUG_ERROR_OTHER +}; +typedef enum _MugError MugError; + +struct _MugMsgListViewClass { + GtkTreeViewClass parent_class; + /* insert signal callback declarations, e.g. */ + void (*msg_selected) (MugMsgListView * obj, const char *msgpath); + void (*error_occured) (MugMsgListView * obj, MugError err); +}; + +/* member functions */ +GType +mug_msg_list_view_get_type (void) + G_GNUC_CONST; + +/* parameter-less _new function (constructor) */ +/* if this is a kind of GtkWidget, it should probably return at GtkWidget* */ +GtkWidget * +mug_msg_list_view_new (const char *xpath); + +int +mug_msg_list_view_query (MugMsgListView * self, const char *query); + +void +mug_msg_list_view_move_first (MugMsgListView * self); + +gboolean +mug_msg_list_view_move_prev (MugMsgListView * self); +gboolean +mug_msg_list_view_move_next (MugMsgListView * self); + +const gchar * +mug_msg_list_view_get_query (MugMsgListView * self); + +G_END_DECLS +#endif /* __MUG_MSG_LIST_VIEW_H__ */ diff --git a/toys/mug2/mug-msg-view.c b/toys/mug2/mug-msg-view.c new file mode 100644 index 00000000..ec50cbe5 --- /dev/null +++ b/toys/mug2/mug-msg-view.c @@ -0,0 +1,274 @@ +/* +** Copyright (C) 2008-2010 Dirk-Jan C. Binnema +** +** 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, 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. +** +*/ +#ifdef HAVE_CONFIG +#include "config.h" +#endif /*HAVE_CONFIG*/ + +#include + +#include "mug-msg-view.h" +#include "mu-msg.h" +#include "mu-str.h" + +/* 'private'/'protected' functions */ +static void mug_msg_view_class_init (MugMsgViewClass * klass); +static void mug_msg_view_init (MugMsgView * obj); +static void mug_msg_view_finalize (GObject * obj); + +/* list my signals */ +enum { + /* MY_SIGNAL_1, */ + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +enum _HeaderRow { + HEADER_ROW_FROM, + HEADER_ROW_TO, + HEADER_ROW_SUBJECT, + HEADER_ROW_CC, + HEADER_ROW_DATE, + HEADER_ROW_PATH, + HEADER_ROW_MSGID, + HEADER_ROW_SIZE, + HEADER_ROW_NUM +}; +typedef enum _HeaderRow HeaderRow; + +struct _HeaderInfo { + HeaderRow row; + const char *title; +}; +typedef struct _HeaderInfo HeaderInfo; + +static const HeaderInfo HEADER_INFO[] = { + {HEADER_ROW_CC, "Cc"}, + {HEADER_ROW_SUBJECT, "Subject"}, + {HEADER_ROW_DATE, "Date"} +}; + +static const HeaderInfo HEADER_INFO_EXPANDER[] = { + {HEADER_ROW_FROM, "From"}, + {HEADER_ROW_TO, "To"}, + {HEADER_ROW_PATH, "Path"}, + {HEADER_ROW_MSGID, "Message-Id"}, + {HEADER_ROW_SIZE, "Size"} +}; + +typedef struct _MugMsgViewPrivate MugMsgViewPrivate; +struct _MugMsgViewPrivate { + + GtkWidget *_headers_area; + GtkWidget *_tablemain, *_tableexpander; + GtkWidget *_headervals[HEADER_ROW_NUM]; + + GtkWidget *_expander_header, *_expander; + GtkWidget *_view; +}; +#define MUG_MSG_VIEW_GET_PRIVATE(o)(G_TYPE_INSTANCE_GET_PRIVATE((o),MUG_TYPE_MSG_VIEW, MugMsgViewPrivate)) +/* globals */ +static GtkVBoxClass *parent_class = NULL; + +/* uncomment the following if you have defined any signals */ +/* static guint signals[LAST_SIGNAL] = {0}; */ + +G_DEFINE_TYPE (MugMsgView, mug_msg_view, GTK_TYPE_VBOX); + +static void +mug_msg_view_class_init (MugMsgViewClass * klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mug_msg_view_finalize; + + g_type_class_add_private (gobject_class, sizeof (MugMsgViewPrivate)); + + /* signal definitions go here, e.g.: */ +/* signals[MY_SIGNAL_1] = */ +/* g_signal_new ("my_signal_1",....); */ +/* signals[MY_SIGNAL_2] = */ +/* g_signal_new ("my_signal_2",....); */ +/* etc. */ +} + +static GtkWidget * +create_table (MugMsgViewPrivate * priv, const HeaderInfo * hinfo, guint num) +{ + guint i; + GtkWidget *table; + + table = gtk_table_new (num, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 5); + + for (i = 0; i < num; ++i) { + + char *str; + GtkWidget *l, *al; + + l = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5); + + gtk_label_set_justify (GTK_LABEL (l), GTK_JUSTIFY_LEFT); + str = g_strdup_printf ("%s:", hinfo[i].title); + gtk_label_set_markup (GTK_LABEL (l), str); + g_free (str); + + al = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); + gtk_container_add (GTK_CONTAINER (al), l); + gtk_table_attach (GTK_TABLE (table), al, 0, 1, i, i + 1, + GTK_FILL, 0, 0, 0); + + l = priv->_headervals[hinfo[i].row] = gtk_label_new (NULL); + al = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); + gtk_label_set_selectable (GTK_LABEL (l), TRUE); + + gtk_container_add (GTK_CONTAINER (al), l); + gtk_label_set_justify (GTK_LABEL (l), GTK_JUSTIFY_LEFT); + gtk_label_set_line_wrap_mode (GTK_LABEL (l), + PANGO_WRAP_WORD_CHAR); + gtk_label_set_line_wrap (GTK_LABEL (l), FALSE); + gtk_table_attach (GTK_TABLE (table), al, + 1, 2, i, i + 1, GTK_FILL, 0, 0, 0); + } + + return table; +} + +static GtkWidget * +headers_area (MugMsgViewPrivate * priv) +{ + GtkWidget *scrolled, *vbox; + + priv->_tablemain = create_table (priv, HEADER_INFO, + G_N_ELEMENTS (HEADER_INFO)); + priv->_tableexpander = create_table + (priv, HEADER_INFO_EXPANDER, G_N_ELEMENTS (HEADER_INFO_EXPANDER)); + priv->_expander = gtk_expander_new ("Details"); + gtk_container_add (GTK_CONTAINER (priv->_expander), + priv->_tableexpander); + + vbox = gtk_vbox_new (FALSE, FALSE); + gtk_box_pack_start (GTK_BOX (vbox), priv->_tablemain, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), priv->_expander, FALSE, FALSE, 0); + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), + vbox); + + return priv->_headers_area = scrolled; +} + +static void +mug_msg_view_init (MugMsgView * obj) +{ + MugMsgViewPrivate *priv; + GtkWidget *scrolled; + + priv = MUG_MSG_VIEW_GET_PRIVATE (obj); + + priv->_view = mu_msg_view_new (); + /* priv->_view = gtk_text_view_new (); */ + /* gtk_text_view_set_editable (GTK_TEXT_VIEW (priv->_view), FALSE); */ + /* gtk_text_view_set_left_margin (GTK_TEXT_VIEW (priv->_view), 10); */ + /* gtk_text_view_set_right_margin (GTK_TEXT_VIEW (priv->_view), 10); */ + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), + priv->_view); + + gtk_box_pack_start (GTK_BOX (obj), headers_area (priv), FALSE, FALSE, + 0); + gtk_box_pack_start (GTK_BOX (obj), scrolled, TRUE, TRUE, 0); +} + +static void +mug_msg_view_finalize (GObject * obj) +{ +/* free/unref instance resources here */ + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +GtkWidget * +mug_msg_view_new (void) +{ + return GTK_WIDGET (g_object_new (MUG_TYPE_MSG_VIEW, NULL)); +} + + +static void +fill_header (MugMsgViewPrivate * priv, MuMsg * msg) +{ + int i; + + for (i = 0; i != HEADER_ROW_NUM; ++i) { + const gchar *val; + switch (i) { + case HEADER_ROW_FROM: val = mu_msg_get_from (msg); break; + case HEADER_ROW_TO: val = mu_msg_get_to (msg); break; + case HEADER_ROW_SUBJECT: val = mu_msg_get_subject (msg); break; + case HEADER_ROW_MSGID: val = mu_msg_get_msgid (msg);break; + case HEADER_ROW_CC: val = mu_msg_get_cc (msg); break; + case HEADER_ROW_PATH: val = mu_msg_get_path (msg); break; + case HEADER_ROW_DATE: + val = mu_str_date_s ("%c", mu_msg_get_date (msg)); + break; + case HEADER_ROW_SIZE: + val = mu_str_size_s (mu_msg_get_size (msg)); + break; + default: + val = NULL; + } + + { + gchar *str; + str = g_markup_escape_text (val ? val : "", -1); + gtk_label_set_markup + (GTK_LABEL (priv->_headervals[i]), str); + g_free (str); + } + } +} + +gboolean +mug_msg_view_set_msg (MugMsgView * self, const char *msgpath) +{ + MugMsgViewPrivate *priv; + g_return_val_if_fail (MUG_IS_MSG_VIEW (self), FALSE); + + priv = MUG_MSG_VIEW_GET_PRIVATE (self); + + if (!msgpath) + mu_msg_view_set_message (MU_MSG_VIEW(priv->_view), NULL); + else { + MuMsg *msg = mu_msg_new (msgpath, NULL, NULL); + mu_msg_view_set_message (MU_MSG_VIEW(priv->_view), msg); + fill_header (priv, msg); + mu_msg_destroy (msg); + } + + return TRUE; +} diff --git a/toys/mug2/mug-msg-view.h b/toys/mug2/mug-msg-view.h new file mode 100644 index 00000000..695604c7 --- /dev/null +++ b/toys/mug2/mug-msg-view.h @@ -0,0 +1,57 @@ +/* +** Copyright (C) 2010 Dirk-Jan C. Binnema +** +** 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, 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. +** +*/ + +#ifndef __MUG_MSG_VIEW_H__ +#define __MUG_MSG_VIEW_H__ + +#include +/* other include files */ + +G_BEGIN_DECLS +/* convenience macros */ +#define MUG_TYPE_MSG_VIEW (mug_msg_view_get_type()) +#define MUG_MSG_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MUG_TYPE_MSG_VIEW,MugMsgView)) +#define MUG_MSG_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MUG_TYPE_MSG_VIEW,MugMsgViewClass)) +#define MUG_IS_MSG_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MUG_TYPE_MSG_VIEW)) +#define MUG_IS_MSG_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MUG_TYPE_MSG_VIEW)) +#define MUG_MSG_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MUG_TYPE_MSG_VIEW,MugMsgViewClass)) +typedef struct _MugMsgView MugMsgView; +typedef struct _MugMsgViewClass MugMsgViewClass; + +struct _MugMsgView { + GtkVBox parent; + /* insert public members, if any */ +}; + +struct _MugMsgViewClass { + GtkVBoxClass parent_class; + /* insert signal callback declarations, e.g. */ + /* void (* my_event) (MugMsg* obj); */ +}; + +/* member functions */ +GType mug_msg_view_get_type (void) G_GNUC_CONST; + +/* parameter-less _new function (constructor) */ +/* if this is a kind of GtkWidget, it should probably return at GtkWidget* */ +GtkWidget* mug_msg_view_new (void); +gboolean mug_msg_view_set_msg (MugMsgView * self, const char *msgpath); + +G_END_DECLS +#endif /* __MUG_MSG_VIEW_H__ */ diff --git a/toys/mug2/mug-query-bar.c b/toys/mug2/mug-query-bar.c new file mode 100644 index 00000000..66666c22 --- /dev/null +++ b/toys/mug2/mug-query-bar.c @@ -0,0 +1,136 @@ +/* mug-query-bar.c */ + +/* insert (c)/licensing information) */ + +#include "mug-query-bar.h" +/* include other impl specific header files */ + +/* 'private'/'protected' functions */ +static void mug_query_bar_class_init (MugQueryBarClass * klass); +static void mug_query_bar_init (MugQueryBar * obj); +static void mug_query_bar_finalize (GObject * obj); + +/* list my signals */ +enum { + MUG_QUERY_CHANGED, + LAST_SIGNAL +}; + +typedef struct _MugQueryBarPrivate MugQueryBarPrivate; +struct _MugQueryBarPrivate { + GtkWidget *_entry; +}; +#define MUG_QUERY_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MUG_TYPE_QUERY_BAR, \ + MugQueryBarPrivate)) +/* globals */ +static GtkContainerClass *parent_class = NULL; + +static guint signals[LAST_SIGNAL] = { 0 }; + +GType +mug_query_bar_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof (MugQueryBarClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) mug_query_bar_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (MugQueryBar), + 0, /* n_preallocs, ignored since 2.10 */ + (GInstanceInitFunc) mug_query_bar_init, + NULL + }; + my_type = g_type_register_static (GTK_TYPE_HBOX, + "MugQueryBar", &my_info, 0); + } + return my_type; +} + +static void +mug_query_bar_class_init (MugQueryBarClass * klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mug_query_bar_finalize; + + g_type_class_add_private (gobject_class, sizeof (MugQueryBarPrivate)); + + /* signal definitions go here, e.g.: */ + signals[MUG_QUERY_CHANGED] = + g_signal_new ("query_changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MugQueryBarClass, query_changed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + +static void +on_entry_activated (GtkWidget * w, MugQueryBar * bar) +{ + MugQueryBarPrivate *priv; + + priv = MUG_QUERY_BAR_GET_PRIVATE (bar); + + g_signal_emit (G_OBJECT (bar), signals[MUG_QUERY_CHANGED], 0, + gtk_entry_get_text (GTK_ENTRY (w))); +} + +static void +mug_query_bar_init (MugQueryBar * obj) +{ + MugQueryBarPrivate *priv; + + priv = MUG_QUERY_BAR_GET_PRIVATE (obj); + + priv->_entry = gtk_entry_new (); + + g_signal_connect (priv->_entry, "activate", + G_CALLBACK (on_entry_activated), obj); + + gtk_box_pack_start (GTK_BOX (obj), priv->_entry, TRUE, TRUE, 0); +} + +static void +mug_query_bar_finalize (GObject * obj) +{ +/* free/unref instance resources here */ + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +GtkWidget * +mug_query_bar_new (void) +{ + return GTK_WIDGET (g_object_new (MUG_TYPE_QUERY_BAR, NULL)); +} + +void +mug_query_bar_set_query (MugQueryBar * self, const char *query, gboolean run) +{ + MugQueryBarPrivate *priv; + + g_return_if_fail (MUG_IS_QUERY_BAR (self)); + priv = MUG_QUERY_BAR_GET_PRIVATE (self); + + gtk_entry_set_text (GTK_ENTRY (priv->_entry), query ? query : ""); + + if (run) + on_entry_activated (priv->_entry, self); +} + +void +mug_query_bar_grab_focus (MugQueryBar * self) +{ + g_return_if_fail (MUG_IS_QUERY_BAR (self)); + + gtk_widget_grab_focus + (GTK_WIDGET (MUG_QUERY_BAR_GET_PRIVATE (self)->_entry)); +} diff --git a/toys/mug2/mug-query-bar.h b/toys/mug2/mug-query-bar.h new file mode 100644 index 00000000..97cdd2a5 --- /dev/null +++ b/toys/mug2/mug-query-bar.h @@ -0,0 +1,49 @@ +/* mug-query-bar.h */ +/* insert (c)/licensing information) */ + +#ifndef __MUG_QUERY_BAR_H__ +#define __MUG_QUERY_BAR_H__ + +#include +/* other include files */ + +G_BEGIN_DECLS +/* convenience macros */ +#define MUG_TYPE_QUERY_BAR (mug_query_bar_get_type()) +#define MUG_QUERY_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MUG_TYPE_QUERY_BAR,MugQueryBar)) +#define MUG_QUERY_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MUG_TYPE_QUERY_BAR,MugQueryBarClass)) +#define MUG_IS_QUERY_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MUG_TYPE_QUERY_BAR)) +#define MUG_IS_QUERY_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MUG_TYPE_QUERY_BAR)) +#define MUG_QUERY_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MUG_TYPE_QUERY_BAR,MugQueryBarClass)) +typedef struct _MugQueryBar MugQueryBar; +typedef struct _MugQueryBarClass MugQueryBarClass; + +struct _MugQueryBar { + GtkHBox parent; + /* insert public members, if any */ +}; + +struct _MugQueryBarClass { + GtkHBoxClass parent_class; + /* insert signal callback declarations, e.g. */ + void (*query_changed) (MugQueryBar * obj, const char *query); +}; + +/* member functions */ +GType +mug_query_bar_get_type (void) + G_GNUC_CONST; + +/* parameter-less _new function (constructor) */ +/* if this is a kind of GtkWidget, it should probably return at GtkWidget* */ +GtkWidget * +mug_query_bar_new (void); + +void +mug_query_bar_grab_focus (MugQueryBar * self); + +void +mug_query_bar_set_query (MugQueryBar * self, const char *query, gboolean run); + +G_END_DECLS +#endif /* __MUG_QUERY_BAR_H__ */ diff --git a/toys/mug2/mug-shortcuts.c b/toys/mug2/mug-shortcuts.c new file mode 100644 index 00000000..0b39832f --- /dev/null +++ b/toys/mug2/mug-shortcuts.c @@ -0,0 +1,152 @@ +/* +** Copyright (C) 2010 Dirk-Jan C. Binnema +** +** 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, 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 "mug-shortcuts.h" +#include "mu-bookmarks.h" + +/* include other impl specific header files */ + +/* 'private'/'protected' functions */ +static void mug_shortcuts_class_init (MugShortcutsClass * klass); +static void mug_shortcuts_init (MugShortcuts * obj); +static void mug_shortcuts_finalize (GObject * obj); + +#define MUG_SHORTCUT_BOOKMARK "bookmark" + +/* list my signals */ +enum { + SHORTCUT_CLICKED, + /* MY_SIGNAL_1, */ + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +struct _MugShortcutsPrivate { + GtkWidget *_bbox; + +}; +#define MUG_SHORTCUTS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MUG_TYPE_SHORTCUTS, \ + MugShortcutsPrivate)) +/* globals */ +static GtkVBoxClass *parent_class = NULL; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (MugShortcuts, mug_shortcuts, GTK_TYPE_VBOX); + +static void +mug_shortcuts_class_init (MugShortcutsClass * klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mug_shortcuts_finalize; + + g_type_class_add_private (gobject_class, sizeof (MugShortcutsPrivate)); + + /* signal definitions go here, e.g.: */ + signals[SHORTCUT_CLICKED] = + g_signal_new ("clicked", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MugShortcutsClass, clicked), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + +/* signals[MY_SIGNAL_2] = */ +/* g_signal_new ("my_signal_2",....); */ +/* etc. */ +} + +static void +mug_shortcuts_init (MugShortcuts * obj) +{ + obj->_priv = MUG_SHORTCUTS_GET_PRIVATE (obj); + + obj->_priv->_bbox = gtk_vbutton_box_new (); + gtk_button_box_set_layout (GTK_BUTTON_BOX (obj->_priv->_bbox), + GTK_BUTTONBOX_START); + gtk_box_pack_start (GTK_BOX (obj), obj->_priv->_bbox, TRUE, TRUE, 0); +} + +static void +mug_shortcuts_finalize (GObject * obj) +{ +/* free/unref instance resources here */ + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +on_button_clicked (GtkWidget * button, MugShortcuts * self) +{ + g_signal_emit (G_OBJECT (self), + signals[SHORTCUT_CLICKED], 0, + (const gchar *)g_object_get_data (G_OBJECT (button), + MUG_SHORTCUT_BOOKMARK)); +} + +static void +each_bookmark (const char *key, const char *val, MugShortcuts * self) +{ + GtkWidget *button; + + button = gtk_button_new_with_label (key); + g_object_set_data_full (G_OBJECT (button), MUG_SHORTCUT_BOOKMARK, + g_strdup (val), g_free); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (on_button_clicked), self); + + gtk_container_add (GTK_CONTAINER (self->_priv->_bbox), button); +} + +static gboolean +init_shortcuts (MugShortcuts * self, const char *bmpath) +{ + MuBookmarks *bookmarks; + + bookmarks = mu_bookmarks_new (bmpath); + if (!bookmarks) + return TRUE; + + mu_bookmarks_foreach (bookmarks, (MuBookmarksForeachFunc) each_bookmark, + self); + + mu_bookmarks_destroy (bookmarks); + return TRUE; +} + +GtkWidget * +mug_shortcuts_new (const char *bmpath) +{ + MugShortcuts *self; + + self = MUG_SHORTCUTS (g_object_new (MUG_TYPE_SHORTCUTS, NULL)); + if (!init_shortcuts (self, bmpath)) { + g_object_unref (self); + return NULL; + } + + return GTK_WIDGET (self); +} + +/* following: other function implementations */ +/* such as mug_shortcuts_do_something, or mug_shortcuts_has_foo */ diff --git a/toys/mug2/mug-shortcuts.h b/toys/mug2/mug-shortcuts.h new file mode 100644 index 00000000..d98b0aa2 --- /dev/null +++ b/toys/mug2/mug-shortcuts.h @@ -0,0 +1,66 @@ +/* +** Copyright (C) 2010 Dirk-Jan C. Binnema +** +** 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, 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. +** +*/ + +#ifndef __MUG_SHORTCUTS_H__ +#define __MUG_SHORTCUTS_H__ + +#include +/* other include files */ + +G_BEGIN_DECLS +/* convenience macros */ +#define MUG_TYPE_SHORTCUTS (mug_shortcuts_get_type()) +#define MUG_SHORTCUTS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MUG_TYPE_SHORTCUTS,MugShortcuts)) +#define MUG_SHORTCUTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MUG_TYPE_SHORTCUTS,MugShortcutsClass)) +#define MUG_IS_SHORTCUTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MUG_TYPE_SHORTCUTS)) +#define MUG_IS_SHORTCUTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MUG_TYPE_SHORTCUTS)) +#define MUG_SHORTCUTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MUG_TYPE_SHORTCUTS,MugShortcutsClass)) +typedef struct _MugShortcuts MugShortcuts; +typedef struct _MugShortcutsClass MugShortcutsClass; +typedef struct _MugShortcutsPrivate MugShortcutsPrivate; + +struct _MugShortcuts { + GtkVBox parent; + /* insert public members, if any */ + + /* private */ + MugShortcutsPrivate *_priv; +}; + +struct _MugShortcutsClass { + GtkVBoxClass parent_class; + void (*clicked) (MugShortcuts * obj, const char *query); +}; + +/* member functions */ +GType +mug_shortcuts_get_type (void) + G_GNUC_CONST; + +/* parameter-less _new function (constructor) */ +/* if this is a kind of GtkWidget, it should probably return at GtkWidget* */ +GtkWidget * +mug_shortcuts_new (const char *bmpath); + +/* fill in other public functions, e.g.: */ +/* void mug_shortcuts_do_something (MugShortcuts *self, const gchar* param); */ +/* gboolean mug_shortcuts_has_foo (MugShortcuts *self, gint value); */ + +G_END_DECLS +#endif /* __MUG_SHORTCUTS_H__ */ diff --git a/toys/mug2/mug.cc b/toys/mug2/mug.cc new file mode 100644 index 00000000..579b44b6 --- /dev/null +++ b/toys/mug2/mug.cc @@ -0,0 +1,384 @@ +/* +** Copyright (C) 2010-2011 Dirk-Jan C. Binnema +** +** 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, 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. +** +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif /*HAVE_CONFIG*/ + +#include +#include /* for memset */ + +#include "mu-util.h" +#include "mu-runtime.h" + +#include "mug-msg-list-view.h" +#include "mug-query-bar.h" +#include "mug-msg-view.h" +#include "mug-shortcuts.h" + +struct _MugData { + GtkWidget *win; + GtkWidget *statusbar; + GtkWidget *mlist; + GtkWidget *toolbar; + GtkWidget *msgview; + GtkWidget *querybar; + GtkWidget *shortcuts; + gchar *muhome; +}; +typedef struct _MugData MugData; + +static void +about_mug (MugData * mugdata) +{ + GtkWidget *about; + about = gtk_message_dialog_new + (GTK_WINDOW (mugdata->win), GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, GTK_BUTTONS_OK, + "Mug version %s\n" + "A graphical frontend to the 'mu' e-mail search engine\n\n" + "(c) 2010-2011 Dirk-Jan C. Binnema\n" + "Released under the terms of the GPLv3+", VERSION); + + gtk_dialog_run (GTK_DIALOG (about)); + gtk_widget_destroy (about); +} + +enum _ToolAction { + ACTION_PREV_MSG = 1, + ACTION_NEXT_MSG, + ACTION_DO_QUIT, + ACTION_ABOUT, + ACTION_SEPARATOR /* pseudo action */ +}; +typedef enum _ToolAction ToolAction; + +static void +on_tool_button_clicked (GtkToolButton * btn, MugData * mugdata) +{ + ToolAction action; + action = (ToolAction) + GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (btn), "action")); + switch (action) { + + case ACTION_DO_QUIT: + gtk_main_quit (); + break; + case ACTION_NEXT_MSG: + mug_msg_list_view_move_next (MUG_MSG_LIST_VIEW + (mugdata->mlist)); + break; + case ACTION_PREV_MSG: + mug_msg_list_view_move_prev (MUG_MSG_LIST_VIEW + (mugdata->mlist)); + break; + case ACTION_ABOUT: + about_mug (mugdata); + break; + default: + g_print ("%u\n", action); + } +} + +static GtkToolItem* +get_connected_tool_button (const char* stock_id, ToolAction action, + MugData *mugdata) +{ + GtkToolItem *btn; + + btn = gtk_tool_button_new_from_stock (stock_id); + g_object_set_data (G_OBJECT (btn), "action", + GUINT_TO_POINTER (action)); + g_signal_connect (G_OBJECT (btn), "clicked", + G_CALLBACK (on_tool_button_clicked), + mugdata); + return btn; +} + +static GtkWidget * +mug_toolbar (MugData * mugdata) +{ + GtkWidget *toolbar; + int i; + struct { + const char *stock_id; + ToolAction action; + } tools[] = { + {GTK_STOCK_GO_UP, ACTION_PREV_MSG}, + {GTK_STOCK_GO_DOWN, ACTION_NEXT_MSG}, + {NULL, ACTION_SEPARATOR}, + {GTK_STOCK_ABOUT, ACTION_ABOUT}, + {NULL, ACTION_SEPARATOR}, + {GTK_STOCK_QUIT, ACTION_DO_QUIT}}; + + toolbar = gtk_toolbar_new (); + for (i = 0; i != G_N_ELEMENTS (tools); ++i) { + if (tools[i].action == ACTION_SEPARATOR) { /* separator? */ + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), + gtk_separator_tool_item_new (), i); + continue; + } else /* nope: a real item */ + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), + get_connected_tool_button + (tools[i].stock_id, tools[i].action, + mugdata), i); + } + + return toolbar; +} + +static void +on_shortcut_clicked (GtkWidget * w, const gchar * query, MugData * mdata) +{ + mug_query_bar_set_query (MUG_QUERY_BAR (mdata->querybar), query, TRUE); +} + +static GtkWidget * +mug_shortcuts_bar (MugData * data) +{ + data->shortcuts = mug_shortcuts_new (mu_runtime_bookmarks_file ()); + + g_signal_connect (G_OBJECT (data->shortcuts), "clicked", + G_CALLBACK (on_shortcut_clicked), data); + + return data->shortcuts; +} + +static GtkWidget * +mug_statusbar (void) +{ + GtkWidget *statusbar; + + statusbar = gtk_statusbar_new (); + + return statusbar; +} + +static void +on_query_changed (MugQueryBar * bar, const char *query, MugData * mugdata) +{ + int count; + + /* clear the old message */ + //mug_msg_view_set_text (MUG_MSG_VIEW(mugdata->msgview), NULL); + mug_msg_view_set_msg (MUG_MSG_VIEW (mugdata->msgview), NULL); + + count = mug_msg_list_view_query (MUG_MSG_LIST_VIEW (mugdata->mlist), + query); + if (count >= 0) { + gchar *msg = + g_strdup_printf ("%d message%s found matching '%s'", + count, + count > 1 ? "s" : "", + mug_msg_list_view_get_query + (MUG_MSG_LIST_VIEW (mugdata->mlist))); + gtk_statusbar_push (GTK_STATUSBAR (mugdata->statusbar), 0, msg); + g_free (msg); + + mug_msg_list_view_move_first (MUG_MSG_LIST_VIEW + (mugdata->mlist)); + gtk_widget_grab_focus (GTK_WIDGET (mugdata->mlist)); + } + + if (count == 0) /* nothing found */ + mug_query_bar_grab_focus (MUG_QUERY_BAR (bar)); +} + +static void +on_msg_selected (MugMsgListView * mlist, const char *mpath, MugData * mugdata) +{ + // g_warning ("msg selected: %s", mpath); + mug_msg_view_set_msg (MUG_MSG_VIEW (mugdata->msgview), mpath); +} + +static void +on_list_view_error (MugMsgListView * mlist, MugError err, MugData * mugdata) +{ + GtkWidget *errdialog; + const char *msg; + + switch (err) { + case MUG_ERROR_XAPIAN_NOT_UPTODATE: + msg = "The Xapian Database has the wrong version\n" + "Please run 'mu index --rebuild'"; + break; + case MUG_ERROR_XAPIAN_DIR: + msg = "Cannot find the Xapian database dir\n" + "Please restart mug with --muhome=... pointing\n" + "to your mu home directory"; + break; + case MUG_ERROR_QUERY: + msg = "Error in query"; + break; + default: + msg = "Some error occured"; + break; + } + + errdialog = gtk_message_dialog_new + (GTK_WINDOW (mugdata->win), GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg); + + gtk_dialog_run (GTK_DIALOG (errdialog)); + gtk_widget_destroy (errdialog); + + if (err == MUG_ERROR_QUERY) { + mug_query_bar_grab_focus (MUG_QUERY_BAR (mugdata->querybar)); + } +} + +static GtkWidget * +mug_querybar (void) +{ + GtkWidget *querybar; + + querybar = mug_query_bar_new (); + + return querybar; +} + +static GtkWidget * +mug_query_area (MugData * mugdata) +{ + GtkWidget *queryarea; + GtkWidget *paned; + GtkWidget *scrolled; + + queryarea = gtk_vbox_new (FALSE, 2); + paned = gtk_vpaned_new (); + + mugdata->mlist = mug_msg_list_view_new (mu_runtime_xapian_dir ()); + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + gtk_container_add (GTK_CONTAINER (scrolled), mugdata->mlist); + gtk_paned_add1 (GTK_PANED (paned), scrolled); + + mugdata->msgview = mug_msg_view_new (); + + mug_msg_view_set_msg (MUG_MSG_VIEW (mugdata->msgview), NULL); + g_signal_connect (G_OBJECT (mugdata->mlist), "msg-selected", + G_CALLBACK (on_msg_selected), mugdata); + g_signal_connect (G_OBJECT (mugdata->mlist), "error-occured", + G_CALLBACK (on_list_view_error), mugdata); + gtk_paned_add2 (GTK_PANED (paned), mugdata->msgview); + + mugdata->querybar = mug_querybar (); + g_signal_connect (G_OBJECT (mugdata->querybar), "query-changed", + G_CALLBACK (on_query_changed), mugdata); + + gtk_box_pack_start (GTK_BOX (queryarea), + mugdata->querybar, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (queryarea), paned, TRUE, TRUE, 2); + + gtk_widget_show_all (queryarea); + return queryarea; +} + +static GtkWidget * +mug_main_area (MugData * mugdata) +{ + GtkWidget *mainarea, *w; + mainarea = gtk_hbox_new (FALSE, 5); + + w = mug_shortcuts_bar (mugdata); + gtk_box_pack_start (GTK_BOX (mainarea), w, FALSE, FALSE, 0); + gtk_widget_show (w); + + w = mug_query_area (mugdata); + gtk_box_pack_start (GTK_BOX (mainarea), w, TRUE, TRUE, 0); + gtk_widget_show (w); + + return mainarea; +} + +GtkWidget * +mug_shell (MugData * mugdata) +{ + GtkWidget *vbox; + + mugdata->win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (mugdata->win), "Mug Mail Search"); + + vbox = gtk_vbox_new (FALSE, 2); + mugdata->toolbar = mug_toolbar (mugdata); + gtk_box_pack_start (GTK_BOX (vbox), mugdata->toolbar, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (vbox), mug_main_area (mugdata), TRUE, + TRUE, 2); + + mugdata->statusbar = mug_statusbar (); + gtk_box_pack_start (GTK_BOX (vbox), mugdata->statusbar, FALSE, FALSE, + 2); + + gtk_container_add (GTK_CONTAINER (mugdata->win), vbox); + gtk_widget_show_all (vbox); + + gtk_window_set_default_size (GTK_WINDOW (mugdata->win), 700, 500); + gtk_window_set_resizable (GTK_WINDOW (mugdata->win), TRUE); + + // { + // gchar *icon; + // icon = g_strdup_printf ("%s%cmug.svg", ICONDIR, G_DIR_SEPARATOR); + // gtk_window_set_icon_from_file (GTK_WINDOW (mugdata->win), icon, NULL); + // g_free (icon); + + return mugdata->win; +} + +int +main (int argc, char *argv[]) +{ + MugData mugdata; + GtkWidget *mugshell; + GOptionContext *octx; + GOptionEntry entries[] = { + {"muhome", 0, 0, G_OPTION_ARG_FILENAME, &mugdata.muhome, + "specify an alternative mu directory", NULL}, + {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL} /* sentinel */ + }; + + gtk_init (&argc, &argv); + + octx = g_option_context_new ("- mug options"); + g_option_context_add_main_entries (octx, entries, "Mug"); + + memset (&mugdata, 0, sizeof (MugData)); + if (!g_option_context_parse (octx, &argc, &argv, NULL)) { + g_printerr ("mug: error in options\n"); + return 1; + } + + mu_runtime_init (mugdata.muhome); + + mugshell = mug_shell (&mugdata); + g_signal_connect (G_OBJECT (mugshell), "destroy", + G_CALLBACK (gtk_main_quit), NULL); + + gtk_widget_show (mugshell); + mug_query_bar_grab_focus (MUG_QUERY_BAR (mugdata.querybar)); + + gtk_main (); + g_free (mugdata.muhome); + + mu_runtime_uninit (); + + return 0; +} diff --git a/widgets/Makefile.am b/widgets/Makefile.am new file mode 100644 index 00000000..5bc6a98a --- /dev/null +++ b/widgets/Makefile.am @@ -0,0 +1,43 @@ +## Copyright (C) 2011 Dirk-Jan C. Binnema +## +## 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 +## t he Free Software Foundation; either version 3 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 $(top_srcdir)/gtest.mk + +# enforce compiling this dir first before decending into tests/ +SUBDIRS= . +INCLUDES=-I${top_srcdir}/src $(GTK_CFLAGS) $(WEBKIT_CFLAGS) -DICONDIR='"$(icondir)"' \ + -DGTK_DISABLE_DEPRECATED -DGSEAL_ENABLE + +# don't use -Werror, as it might break on other compilers +# use -Wno-unused-parameters, because some callbacks may not +# really need all the params they get +AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement +AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter + +noinst_LTLIBRARIES= \ + libmuwidgets.la + +libmuwidgets_la_SOURCES= \ + mu-msg-body-view.c \ + mu-msg-body-view.h \ + mu-msg-view.h \ + mu-msg-view.c + +libmuwidgets_la_LIBADD= \ + ${top_builddir}/src/libmu.la \ + ${GTK_LIBS} \ + ${WEBKIT_LIBS} + diff --git a/widgets/mu-msg-body-view.c b/widgets/mu-msg-body-view.c new file mode 100644 index 00000000..180bd5ff --- /dev/null +++ b/widgets/mu-msg-body-view.c @@ -0,0 +1,128 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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, 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 +#include "mu-msg-body-view.h" + +/* include other impl specific header files */ + +/* 'private'/'protected' functions */ +static void mu_msg_body_view_class_init (MuMsgBodyViewClass *klass); +static void mu_msg_body_view_init (MuMsgBodyView *obj); +static void mu_msg_body_view_finalize (GObject *obj); + +/* list my signals */ +enum { + /* MY_SIGNAL_1, */ + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +struct _MuMsgBodyViewPrivate { + WebKitWebSettings *_settings; +}; +#define MU_MSG_BODY_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MU_TYPE_MSG_BODY_VIEW, \ + MuMsgBodyViewPrivate)) +/* globals */ +static WebKitWebViewClass *parent_class = NULL; + +/* uncomment the following if you have defined any signals */ +/* static guint signals[LAST_SIGNAL] = {0}; */ + +G_DEFINE_TYPE (MuMsgBodyView, mu_msg_body_view, WEBKIT_TYPE_WEB_VIEW); + +static void +mu_msg_body_view_class_init (MuMsgBodyViewClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mu_msg_body_view_finalize; + + g_type_class_add_private (gobject_class, sizeof(MuMsgBodyViewPrivate)); + + /* signal definitions go here, e.g.: */ +/* signals[MY_SIGNAL_1] = */ +/* g_signal_new ("my_signal_1",....); */ +/* signals[MY_SIGNAL_2] = */ +/* g_signal_new ("my_signal_2",....); */ +/* etc. */ +} + +static void +mu_msg_body_view_init (MuMsgBodyView *obj) +{ + obj->_priv = MU_MSG_BODY_VIEW_GET_PRIVATE(obj); + + obj->_priv->_settings = webkit_web_settings_new (); + g_object_set (G_OBJECT(obj->_priv->_settings), + "enable-scripts", FALSE, + "auto-load-images", TRUE, + "enable-plugins", FALSE, + NULL); + + webkit_web_view_set_settings (WEBKIT_WEB_VIEW(obj), + obj->_priv->_settings); + webkit_web_view_set_editable (WEBKIT_WEB_VIEW(obj), FALSE); + /* other settings */ +} + +static void +mu_msg_body_view_finalize (GObject *obj) +{ + MuMsgBodyViewPrivate *priv; + + priv = MU_MSG_BODY_VIEW_GET_PRIVATE(obj); + if (priv && priv->_settings) + g_object_unref (priv->_settings); + + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +GtkWidget* +mu_msg_body_view_new (void) +{ + return GTK_WIDGET(g_object_new(MU_TYPE_MSG_BODY_VIEW, NULL)); +} + + +void +mu_msg_body_view_set_html (MuMsgBodyView *self, const char* html) +{ + g_return_if_fail (MU_IS_MSG_BODY_VIEW(self)); + + webkit_web_view_load_string (WEBKIT_WEB_VIEW(self), + html ? html : "", + "text/html", + "utf-8", + ""); +} + +void +mu_msg_body_view_set_text (MuMsgBodyView *self, const char* txt) +{ + g_return_if_fail (MU_IS_MSG_BODY_VIEW(self)); + + webkit_web_view_load_string (WEBKIT_WEB_VIEW(self), + txt ? txt : "", + "text/plain", + "utf-8", + ""); +} diff --git a/widgets/mu-msg-body-view.h b/widgets/mu-msg-body-view.h new file mode 100644 index 00000000..0d717f3c --- /dev/null +++ b/widgets/mu-msg-body-view.h @@ -0,0 +1,66 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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, 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. +** +*/ + +#ifndef __MU_MSG_BODY_VIEW_H__ +#define __MU_MSG_BODY_VIEW_H__ + +#include + +G_BEGIN_DECLS + +/* convenience macros */ +#define MU_TYPE_MSG_BODY_VIEW (mu_msg_body_view_get_type()) +#define MU_MSG_BODY_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MU_TYPE_MSG_BODY_VIEW,MuMsgBodyView)) +#define MU_MSG_BODY_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MU_TYPE_MSG_BODY_VIEW,MuMsgBodyViewClass)) +#define MU_IS_MSG_BODY_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MU_TYPE_MSG_BODY_VIEW)) +#define MU_IS_MSG_BODY_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MU_TYPE_MSG_BODY_VIEW)) +#define MU_MSG_BODY_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MU_TYPE_MSG_BODY_VIEW,MuMsgBodyViewClass)) + +typedef struct _MuMsgBodyView MuMsgBodyView; +typedef struct _MuMsgBodyViewClass MuMsgBodyViewClass; +typedef struct _MuMsgBodyViewPrivate MuMsgBodyViewPrivate; + +struct _MuMsgBodyView { + WebKitWebView parent; + /* insert public members, if any */ + + /* private */ + MuMsgBodyViewPrivate *_priv; +}; + +struct _MuMsgBodyViewClass { + WebKitWebViewClass parent_class; + /* insert signal callback declarations, e.g. */ + /* void (* my_event) (MuMsgBodyView* obj); */ +}; + +/* member functions */ +GType mu_msg_body_view_get_type (void) G_GNUC_CONST; + +/* parameter-less _new function (constructor) */ +/* if this is a kind of GtkWidget, it should probably return at GtkWidget* */ +GtkWidget* mu_msg_body_view_new (void); + +void mu_msg_body_view_set_html (MuMsgBodyView *self, const char* html); +void mu_msg_body_view_set_text (MuMsgBodyView *self, const char* html); + +G_END_DECLS + +#endif /* __MU_MSG_BODY_VIEW_H__ */ + diff --git a/widgets/mu-msg-view.c b/widgets/mu-msg-view.c new file mode 100644 index 00000000..fbfe0b88 --- /dev/null +++ b/widgets/mu-msg-view.c @@ -0,0 +1,122 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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, 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 "mu-msg-view.h" +#include "mu-msg-body-view.h" +#include "mu-msg.h" + +/* 'private'/'protected' functions */ +static void mu_msg_view_class_init (MuMsgViewClass *klass); +static void mu_msg_view_init (MuMsgView *obj); +static void mu_msg_view_finalize (GObject *obj); + +/* list my signals */ +enum { + /* MY_SIGNAL_1, */ + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +struct _MuMsgViewPrivate { + GtkWidget *_body; +}; +#define MU_MSG_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MU_TYPE_MSG_VIEW, \ + MuMsgViewPrivate)) +/* globals */ +static GtkVBoxClass *parent_class = NULL; + +/* uncomment the following if you have defined any signals */ +/* static guint signals[LAST_SIGNAL] = {0}; */ + +G_DEFINE_TYPE (MuMsgView, mu_msg_view, GTK_TYPE_VBOX); + +static void +mu_msg_view_class_init (MuMsgViewClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mu_msg_view_finalize; + + g_type_class_add_private (gobject_class, sizeof(MuMsgViewPrivate)); + + /* signal definitions go here, e.g.: */ +/* signals[MY_SIGNAL_1] = */ +/* g_signal_new ("my_signal_1",....); */ +/* signals[MY_SIGNAL_2] = */ +/* g_signal_new ("my_signal_2",....); */ +/* etc. */ +} + +static void +mu_msg_view_init (MuMsgView *obj) +{ + GtkWidget *scrolledwin; + + obj->_priv = MU_MSG_VIEW_GET_PRIVATE(obj); + + obj->_priv->_body = mu_msg_body_view_new (); + scrolledwin = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER(scrolledwin), + obj->_priv->_body); + + gtk_box_pack_start (GTK_BOX(obj), scrolledwin, + TRUE, TRUE, 2); + + /* to init any of the private data, do e.g: */ +/* obj->_priv->_frobnicate_mode = FALSE; */ +} + +static void +mu_msg_view_finalize (GObject *obj) +{ +/* free/unref instance resources here */ + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +GtkWidget* +mu_msg_view_new (void) +{ + return GTK_WIDGET(g_object_new(MU_TYPE_MSG_VIEW, NULL)); +} + +void +mu_msg_view_set_message (MuMsgView *self, MuMsg *msg) +{ + const char *data; + + g_return_if_fail (MU_IS_MSG_VIEW(self)); + + if (!msg) { /* clear */ + mu_msg_body_view_set_text + (MU_MSG_BODY_VIEW(self->_priv->_body),""); + return; + } + + data = mu_msg_get_body_html (msg); + if (data) + mu_msg_body_view_set_html (MU_MSG_BODY_VIEW(self->_priv->_body), + data); + else + mu_msg_body_view_set_text (MU_MSG_BODY_VIEW(self->_priv->_body), + mu_msg_get_body_text (msg)); +} + diff --git a/widgets/mu-msg-view.h b/widgets/mu-msg-view.h new file mode 100644 index 00000000..e3581b6c --- /dev/null +++ b/widgets/mu-msg-view.h @@ -0,0 +1,66 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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, 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. +** +*/ + +#ifndef __MU_MSG_VIEW_H__ +#define __MU_MSG_VIEW_H__ + +#include +#include + +G_BEGIN_DECLS + +/* convenience macros */ +#define MU_TYPE_MSG_VIEW (mu_msg_view_get_type()) +#define MU_MSG_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MU_TYPE_MSG_VIEW,MuMsgView)) +#define MU_MSG_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MU_TYPE_MSG_VIEW,MuMsgViewClass)) +#define MU_IS_MSG_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MU_TYPE_MSG_VIEW)) +#define MU_IS_MSG_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MU_TYPE_MSG_VIEW)) +#define MU_MSG_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MU_TYPE_MSG_VIEW,MuMsgViewClass)) + +typedef struct _MuMsgView MuMsgView; +typedef struct _MuMsgViewClass MuMsgViewClass; +typedef struct _MuMsgViewPrivate MuMsgViewPrivate; + +struct _MuMsgView { + GtkVBox parent; + /* insert public members, if any */ + + /* private */ + MuMsgViewPrivate *_priv; +}; + +struct _MuMsgViewClass { + GtkVBoxClass parent_class; + /* insert signal callback declarations, e.g. */ + /* void (* my_event) (MuMsgView* obj); */ +}; + +/* member functions */ +GType mu_msg_view_get_type (void) G_GNUC_CONST; + +/* parameter-less _new function (constructor) */ +/* if this is a kind of GtkWidget, it should probably return at GtkWidget* */ +GtkWidget* mu_msg_view_new (void); + +void mu_msg_view_set_message (MuMsgView *self, MuMsg *msg); + +G_END_DECLS + +#endif /* __MU_MSG_VIEW_H__ */ +