diff --git a/Makefile.am b/Makefile.am index 1838afe5..6dc7df12 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,12 @@ include $(top_srcdir)/gtest.mk +if HAVE_GTK +SUBDIRS=man src mug contrib +else SUBDIRS=man src contrib +endif + # so we can say 'make test' check: test cleanupnote diff --git a/configure.ac b/configure.ac index e2af3eb0..ffef7024 100644 --- a/configure.ac +++ b/configure.ac @@ -116,6 +116,29 @@ AS_IF([test "x$have_gmime" = "xno"],[ ]) ]) +# do we have gtk+ installed? if not, don't build 'mug' +PKG_CHECK_MODULES(GTK,gtk+-2.0,[have_gtk=yes],[have_gtk=no]) +AC_SUBST(GTK_CFLAGS) +AC_SUBST(GTK_LIBS) +AS_IF([test "x$have_gtk" = "xno"],[ + AC_MSG_WARN([ + *** gtk+-2.0 not found, mug will not be built + ]) +]) +AM_CONDITIONAL(HAVE_GTK,test "x$have_gtk" = "xyes") + +# do we have webkit/gtk installed? if not, don't try to use it for 'mug' +PKG_CHECK_MODULES(WEBKIT,webkit-1.0,[have_webkit=yes],[have_webkit=no]) +AC_SUBST(WEBKIT_CFLAGS) +AC_SUBST(WEBKIT_LIBS) +AS_IF([test "x$have_webkit" = "xno"],[ + AC_MSG_WARN([ + *** webkit-1.0 not found, mug will not use webkit + ]) +]) +AC_DEFINE(HAVE_WEBKIT,[1],[Whether we have Webkit/Gtk]) + + # xapian? AC_CHECK_PROG(XAPIAN,xapian-config,xapian-config,no) AM_CONDITIONAL(HAVE_XAPIAN,test "x$XAPIAN" != "xno") @@ -130,9 +153,7 @@ AS_IF([test "x$XAPIAN" = "xno"],[ XAPIAN_CXXFLAGS=`$XAPIAN --cxxflags` XAPIAN_LIBS=`$XAPIAN --libs` have_xapian="yes" - AC_DEFINE(HAVE_XAPIAN,[1],[Whether we have Xapian]) -]) - +]) AC_SUBST(XAPIAN_CXXFLAGS) AC_SUBST(XAPIAN_LIBS) @@ -140,6 +161,7 @@ AC_CONFIG_FILES([ Makefile src/Makefile src/tests/Makefile +mug/Makefile man/Makefile contrib/Makefile ]) diff --git a/mug/Makefile.am b/mug/Makefile.am new file mode 100644 index 00000000..79d7a9d6 --- /dev/null +++ b/mug/Makefile.am @@ -0,0 +1,51 @@ +## 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 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= . + +#if HAVE_GTEST +#SUBDIRS += tests +#endif + +INCLUDES=-I${top_srcdir}/src $(GTK_CFLAGS) + +# 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 + +bin_PROGRAMS= \ + mug + +# note, mu.cc is only '.cc' and not '.c' because libmu must explicitly +# be linked as c++, not c. +mug_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_LDADD= \ + ${top_srcdir}/src/libmu.la \ + ${GTK_LIBS} + diff --git a/mug/TODO b/mug/TODO new file mode 100644 index 00000000..1b2e082f --- /dev/null +++ b/mug/TODO @@ -0,0 +1,18 @@ +* todo for mug + +** TODO column sizing +** TODO query in status bar +** TODO tool bar working +** TODO pretty-print addresses +** TODO pretty-print dates +** TODO up/down arrows => need msg-list-view support +** TODO unread mails => bold, important mails => red +** TODO detail info about current msg +** TODO guess maildir +** TODO properties dialog +** TODO bookmarks +** TODO icon / .desktop file +** TODO clear msg when starting new query + + + diff --git a/mug/mug-msg-list-view.c b/mug/mug-msg-list-view.c new file mode 100644 index 00000000..13516bf5 --- /dev/null +++ b/mug/mug-msg-list-view.c @@ -0,0 +1,282 @@ +/* +** 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. +** +*/ + +#include "mug-msg-list-view.h" +#include "mu-query.h" +#include "mu-msg-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, + LAST_SIGNAL +}; + +enum { + MUG_COL_DATE, + MUG_COL_FROM, + MUG_COL_TO, + MUG_COL_SUBJECT, + MUG_COL_PATH, + + MUG_N_COLS +}; + +typedef struct _MugMsgListViewPrivate MugMsgListViewPrivate; +struct _MugMsgListViewPrivate { + GtkListStore *_store; + char *_xpath; +}; +#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}; + +GType +mug_msg_list_view_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof(MugMsgListViewClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) mug_msg_list_view_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof(MugMsgListView), + 0, /* n_preallocs, ignored since 2.10 */ + (GInstanceInitFunc) mug_msg_list_view_init, + NULL + }; + my_type = g_type_register_static (GTK_TYPE_TREE_VIEW, + "MugMsgListView", + &my_info, 0); + } + return my_type; +} + +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); +} + + +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 GtkTreeViewColumn * +get_col (const char* label, int colidx) +{ + 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); + + //g_object_unref (renderer); + + return col; +} + + + +static void +mug_msg_list_view_init (MugMsgListView *obj) +{ + GtkTreeViewColumn *col; + MugMsgListViewPrivate *priv; + + priv = MUG_MSG_LIST_VIEW_GET_PRIVATE(obj); + + priv->_xpath = NULL; + + priv->_store = gtk_list_store_new (MUG_N_COLS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (obj), + 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); + + + col = get_col ("Date", MUG_COL_DATE); + gtk_tree_view_append_column (GTK_TREE_VIEW (obj), col); + + col = get_col ("From", MUG_COL_FROM); + gtk_tree_view_append_column (GTK_TREE_VIEW (obj), col); + + col = get_col ("To", MUG_COL_TO); + gtk_tree_view_append_column (GTK_TREE_VIEW (obj), col); + + col = get_col ("Subject", MUG_COL_SUBJECT); + gtk_tree_view_append_column (GTK_TREE_VIEW (obj), col); + + 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_OBJECT_CLASS(parent_class)->finalize (obj); +} + +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 int +update_model (GtkListStore *store, const char *xpath, const char *query) +{ + MuQuery *xapian; + MuMsgIter *iter; + int count; + + xapian = mu_query_new (xpath); + if (!xapian) { + g_printerr ("Failed to create a Xapian query\n"); + return -1; + } + + iter = mu_query_run (xapian, query, NULL, TRUE, 0); + if (!iter) { + g_warning ("error: running query failed\n"); + mu_query_destroy (xapian); + return -1; + } + + for (count = 0; !mu_msg_iter_is_done (iter); + mu_msg_iter_next (iter), ++count) { + + GtkTreeIter treeiter; + const gchar *date, *from, *subject, *path, *to; + + date = mu_msg_str_date_s (mu_msg_iter_get_date (iter)); + from = mu_msg_iter_get_from(iter); + to = mu_msg_iter_get_to (iter); + subject = mu_msg_iter_get_subject (iter); + path = mu_msg_iter_get_path (iter); + + gtk_list_store_append (store, &treeiter); + gtk_list_store_set (store, &treeiter, + MUG_COL_DATE, date, + MUG_COL_FROM, from, + MUG_COL_TO, to, + MUG_COL_SUBJECT, subject, + MUG_COL_PATH, path, + -1); + } + mu_query_destroy (xapian); + + return count; +} + + +int +mug_msg_list_view_query (MugMsgListView *self, const char *query) +{ + MugMsgListViewPrivate *priv; + 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); + + if (!query) + return TRUE; + + return update_model (priv->_store, priv->_xpath, query); +} diff --git a/mug/mug-msg-list-view.h b/mug/mug-msg-list-view.h new file mode 100644 index 00000000..aaf5cd78 --- /dev/null +++ b/mug/mug-msg-list-view.h @@ -0,0 +1,62 @@ +/* +** 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 +/* other include files */ + +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 */ +}; + +struct _MugMsgListViewClass { + GtkTreeViewClass parent_class; + /* insert signal callback declarations, e.g. */ + void (* msg_selected) (MugMsgListView* obj, const char* msgpath); +}; + +/* 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); + +G_END_DECLS + +#endif /* __MUG_MSG_LIST_VIEW_H__ */ + diff --git a/mug/mug-msg-view.c b/mug/mug-msg-view.c new file mode 100644 index 00000000..a8340166 --- /dev/null +++ b/mug/mug-msg-view.c @@ -0,0 +1,144 @@ +/* +** 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. +** +*/ + + +#include "mug-msg-view.h" +#include "mu-msg.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 +}; + +typedef struct _MugMsgViewPrivate MugMsgViewPrivate; +struct _MugMsgViewPrivate { + 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}; */ + +GType +mug_msg_view_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof(MugMsgViewClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) mug_msg_view_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof(MugMsgView), + 0, /* n_preallocs, ignored since 2.10 */ + (GInstanceInitFunc) mug_msg_view_init, + NULL + }; + my_type = g_type_register_static (GTK_TYPE_VBOX, + "MugMsgView", + &my_info, 0); + } + return my_type; +} + +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 void +mug_msg_view_init (MugMsgView *obj) +{ + MugMsgViewPrivate *priv; + + priv = MUG_MSG_VIEW_GET_PRIVATE(obj); + + 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); + + gtk_box_pack_start (GTK_BOX(obj), priv->_view, 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)); +} + + +gboolean +mug_msg_view_set_msg (MugMsgView *self, const char* msgpath) +{ + MugMsgViewPrivate *priv; + MuMsg* msg; + const char *txt; + GtkTextBuffer *buf; + + g_return_val_if_fail (MUG_IS_MSG_VIEW(self), FALSE); + g_return_val_if_fail (msgpath, FALSE); + + priv = MUG_MSG_VIEW_GET_PRIVATE(self); + + msg = mu_msg_new (msgpath, NULL); + if (!msg) + return FALSE; + + txt = mu_msg_get_body_text (msg); + buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW(priv->_view)); + + gtk_text_buffer_set_text (buf, txt ? txt : "", -1); + + return TRUE; +} + diff --git a/mug/mug-msg-view.h b/mug/mug-msg-view.h new file mode 100644 index 00000000..3be2e00c --- /dev/null +++ b/mug/mug-msg-view.h @@ -0,0 +1,62 @@ +/* +** 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/mug/mug-query-bar.c b/mug/mug-query-bar.c new file mode 100644 index 00000000..0516fe62 --- /dev/null +++ b/mug/mug-query-bar.c @@ -0,0 +1,120 @@ +/* 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 (GtkEntry *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(w)); +} + +static void +mug_query_bar_init (MugQueryBar *obj) +{ + MugQueryBarPrivate *priv; + + priv = MUG_QUERY_BAR_GET_PRIVATE(obj); + + priv->_entry = gtk_entry_new (); + /* gtk_entry_set_max_length (GTK_ENTRY(priv->_entry), 32); */ + + 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)); +} + +/* following: other function implementations */ +/* such as mug_query_bar_do_something, or mug_query_bar_has_foo */ + diff --git a/mug/mug-query-bar.h b/mug/mug-query-bar.h new file mode 100644 index 00000000..c04a5097 --- /dev/null +++ b/mug/mug-query-bar.h @@ -0,0 +1,45 @@ +/* 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); + + +G_END_DECLS + +#endif /* __MUG_QUERY_BAR_H__ */ + diff --git a/mug/mug.cc b/mug/mug.cc new file mode 100644 index 00000000..fe0d7e0a --- /dev/null +++ b/mug/mug.cc @@ -0,0 +1,268 @@ +/* +** 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. +** +*/ + +#if HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ + +#include + +#include "mu-config.h" +#include "mu-log.h" +#include "mu-util.h" + +#include "mug-msg-list-view.h" +#include "mug-query-bar.h" +#include "mug-msg-view.h" + +struct _MugData { + GtkWidget *statusbar; + GtkWidget *mlist; + GtkWidget *toolbar; + GtkWidget *msg; +}; +typedef struct _MugData MugData; + + +static GtkWidget* +mug_menu (void) +{ + GtkWidget *menu; + + menu = gtk_menu_bar_new (); + + return menu; +} + +enum _ToolAction { + ACTION_PREV_MSG = 1, + ACTION_NEXT_MSG, + ACTION_SHOW_PREFS, + ACTION_DO_QUIT +}; +typedef enum _ToolAction ToolAction; + +static void +on_tool_button_clicked (GtkToolButton *btn, MugData *data) +{ + 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; + default: + g_print ("%u\n", action); + } +} + +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}, + {GTK_STOCK_PREFERENCES, ACTION_SHOW_PREFS}, + {GTK_STOCK_QUIT, ACTION_DO_QUIT} + }; + + for (toolbar = gtk_toolbar_new(), i = 0; i != G_N_ELEMENTS(tools); ++i) { + GtkToolItem *btn; + btn = gtk_tool_button_new_from_stock (tools[i].stock_id); + g_object_set_data (G_OBJECT(btn), "action", GUINT_TO_POINTER(tools[i].action)); + g_signal_connect (G_OBJECT(btn), "clicked", + G_CALLBACK(on_tool_button_clicked), + mugdata); + gtk_toolbar_insert (GTK_TOOLBAR(toolbar), btn, i); + } + + return toolbar; +} + +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; + + 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", count, + count > 1 ? "s" : ""); + gtk_statusbar_push (GTK_STATUSBAR(mugdata->statusbar), 0, msg); + g_free (msg); + } +} + + +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->msg), mpath); +} + + + +static GtkWidget* +mug_querybar (void) +{ + GtkWidget *querybar; + + querybar = mug_query_bar_new (); + + return querybar; +} + +#if 0 +static GtkWidget* +mug_shortcuts (void) +{ + GtkWidget *shortcuts; + + shortcuts = gtk_vbutton_box_new (); + + gtk_box_pack_start (GTK_BOX(shortcuts), + gtk_button_new_with_label ("Inbox"), FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX(shortcuts), + gtk_button_new_with_label ("Archive"), FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX(shortcuts), + gtk_button_new_with_label ("Sent"), FALSE, FALSE, 2); + + gtk_button_box_set_layout (GTK_BUTTON_BOX(shortcuts), GTK_BUTTONBOX_START); + + return shortcuts; +} +#endif + + +static GtkWidget* +mug_query_area (MugData *mugdata) +{ + GtkWidget *queryarea; + GtkWidget *paned, *querybar; + GtkWidget *scrolled; + + queryarea = gtk_vbox_new (FALSE, 2); + + paned = gtk_vpaned_new (); + mugdata->mlist = mug_msg_list_view_new("/home/dbinnema/.mu/xapian/"); + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER(scrolled), mugdata->mlist); + gtk_paned_add1 (GTK_PANED (paned), scrolled); + + mugdata->msg = mug_msg_view_new (); + g_signal_connect (G_OBJECT(mugdata->mlist), "msg-selected", + G_CALLBACK(on_msg_selected), mugdata); + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled), + mugdata->msg); + gtk_paned_add2 (GTK_PANED (paned), scrolled); + + querybar = mug_querybar(); + g_signal_connect (G_OBJECT(querybar), "query_changed", + G_CALLBACK(on_query_changed), + mugdata); + + gtk_box_pack_start (GTK_BOX(queryarea), + querybar, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX(queryarea), + paned, TRUE, TRUE, 2); + + return queryarea; +} + + +static GtkWidget* +mug_main_area (MugData *mugdata) +{ + GtkWidget *mainarea; + + mainarea = gtk_hpaned_new (); + //gtk_paned_add1 (GTK_PANED (mainarea), mug_shortcuts()); + gtk_paned_add2 (GTK_PANED (mainarea), mug_query_area (mugdata)); + + return mainarea; +} + + + +GtkWidget* +mug_shell (MugData *mugdata) +{ + GtkWidget *win, *vbox; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW(win), "mu"); + + vbox = gtk_vbox_new (FALSE, 2); + + gtk_box_pack_start (GTK_BOX(vbox), mug_menu(), FALSE, 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(win), vbox); + gtk_widget_show_all (vbox); + + gtk_window_set_default_size (GTK_WINDOW(win), 500, 500); + gtk_window_set_resizable (GTK_WINDOW(win), TRUE); + + return win; +} + +int +main (int argc, char *argv[]) +{ + MugData mugdata; + GtkWidget *mugshell; + + gtk_init (&argc, &argv); + + mugshell = mug_shell (&mugdata); + g_signal_connect(G_OBJECT(mugshell), "destroy", + G_CALLBACK(gtk_main_quit), NULL); + + gtk_widget_show (mugshell); + gtk_main (); + + return 0; +}