1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
11 #include "pollcreator.h"
16 typedef struct _PollDlg PollDlg
;
20 GtkDialog dlg
; /* Parent widget */
21 GtkWidget
*pollname
, *viewers
, *voters
;
22 JamReorderable questions
;
26 #define POLLDLG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), polldlg_get_type(), PollDlg))
29 /* macro to generate prototypes for type and instance init functions, as
30 * well as the actual type function, in one swell foop. */
31 #define MAKETYPE(fname, newtypeclass, classinitf, instanceinitf, parenttype, parentclass) \
32 static void instanceinitf(GtkWidget *w); \
33 static GType fname (void) G_GNUC_CONST; \
36 static GType new_type = 0; \
38 const GTypeInfo new_info = { \
39 sizeof (parentclass), \
42 (GClassInitFunc)classinitf, \
45 sizeof (newtypeclass), \
47 (GInstanceInitFunc) instanceinitf, \
49 new_type = g_type_register_static(parenttype, \
50 #newtypeclass, &new_info, 0); \
55 #define MENU_ADD(menu, mnemonic) gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_menu_item_new_with_mnemonic(mnemonic));
58 /* Poll Creator container */
59 MAKETYPE(polldlg_get_type
, PollDlg
, NULL
, polldlg_init
, GTK_TYPE_DIALOG
, GtkDialogClass
)
62 static GtkWidget
*make_pollmeta (PollDlg
*pdlg
) {
63 GtkWidget
*hbox
, *vbox
, *menu
;
65 vbox
= gtk_vbox_new(FALSE
, 6);
67 pdlg
->pollname
= gtk_entry_new();
68 gtk_box_pack_start(GTK_BOX(vbox
), labelled_box_new(_("Poll _name: "), pdlg
->pollname
), FALSE
, FALSE
, 0);
70 hbox
= gtk_hbox_new(FALSE
, 18);
72 pdlg
->voters
= gtk_option_menu_new();
73 menu
= gtk_menu_new();
74 MENU_ADD(menu
, _("All users"));
75 MENU_ADD(menu
, _("Friends"));
76 gtk_option_menu_set_menu(GTK_OPTION_MENU(pdlg
->voters
), menu
);
77 gtk_box_pack_start(GTK_BOX(hbox
), labelled_box_new(_("V_oters:"), pdlg
->voters
), FALSE
, FALSE
, 0);
79 pdlg
->viewers
= gtk_option_menu_new();
80 menu
= gtk_menu_new();
81 MENU_ADD(menu
, _("All users"));
82 MENU_ADD(menu
, _("Friends"));
83 MENU_ADD(menu
, _("Nobody"));
84 gtk_option_menu_set_menu(GTK_OPTION_MENU(pdlg
->viewers
), menu
);
85 gtk_box_pack_end(GTK_BOX(hbox
), labelled_box_new(_("_Results visible to:"), pdlg
->viewers
), FALSE
, FALSE
, 0);
87 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
93 static void add_question (PollDlg
*pdlg
, PollQuestion
*pq
) {
95 gtk_list_store_append(pdlg
->questions
.store
, &iter
);
96 gtk_list_store_set(pdlg
->questions
.store
, &iter
, 0, pq
->question
, 1, pq
, -1);
100 static void add_multi_cb (PollDlg
*pdlg
) {
102 pq
= pollmultidlg_run(GTK_WINDOW(pdlg
), NULL
);
103 if (pq
) add_question(pdlg
, pq
);
107 static void add_scale_cb (PollDlg
*pdlg
) {
109 pq
= pollscaledlg_run(GTK_WINDOW(pdlg
), NULL
);
110 if (pq
) add_question(pdlg
, pq
);
114 static void add_text_cb (PollDlg
*pdlg
) {
116 pq
= polltextdlg_run(GTK_WINDOW(pdlg
), NULL
);
117 if (pq
) add_question(pdlg
, pq
);
121 static void add_question_cb (PollDlg
*pdlg
) {
122 GtkWidget
*menu
, *item
;
124 menu
= gtk_menu_new();
126 item
= gtk_menu_item_new_with_label(_("Multiple Choice Question"));
127 g_signal_connect_swapped(G_OBJECT(item
), "activate", G_CALLBACK(add_multi_cb
), pdlg
);
128 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
130 item
= gtk_menu_item_new_with_label(_("Range Question"));
131 g_signal_connect_swapped(G_OBJECT(item
), "activate", G_CALLBACK(add_scale_cb
), pdlg
);
132 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
134 item
= gtk_menu_item_new_with_label(_("Text Question"));
135 g_signal_connect_swapped(G_OBJECT(item
), "activate", G_CALLBACK(add_text_cb
), pdlg
);
136 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
138 gtk_widget_show_all(menu
);
140 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
, 0 /* don't know button */ , gtk_get_current_event_time());
144 static void edit_question_cb (PollDlg
*pdlg
) {
145 GtkTreeSelection
*sel
;
150 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pdlg
->questions
.view
));
152 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
)) return;
153 gtk_tree_model_get(model
, &iter
, 1, &pq
, -1);
158 pq
= pollmultidlg_run(GTK_WINDOW(pdlg
), (PollQuestionMulti
*) pq
);
161 pq
= polltextdlg_run(GTK_WINDOW(pdlg
), (PollQuestionText
*) pq
);
164 pq
= pollscaledlg_run(GTK_WINDOW(pdlg
), (PollQuestionScale
*) pq
);
169 if (pq
) gtk_list_store_set(pdlg
->questions
.store
, &iter
, 0, pq
->question
, 1, pq
, -1);
173 static void remove_question_cb (PollDlg
*pdlg
) {
174 GtkTreeSelection
*sel
;
178 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pdlg
->questions
.view
));
179 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
)) return;
180 gtk_tree_model_get(model
, &iter
, 1, &pq
, -1);
181 poll_question_free(pq
);
182 gtk_list_store_remove(pdlg
->questions
.store
, &iter
);
186 static void make_list (PollDlg
*pdlg
) {
187 GtkCellRenderer
*renderer
;
188 GtkTreeViewColumn
*column
;
190 pdlg
->questions
.store
= gtk_list_store_new(2, G_TYPE_STRING
, G_TYPE_POINTER
);
191 jam_reorderable_make(&pdlg
->questions
);
192 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pdlg
->questions
.view
), FALSE
);
193 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(pdlg
->questions
.view
), TRUE
);
194 g_object_unref(pdlg
->questions
.store
);
196 renderer
= gtk_cell_renderer_text_new();
197 column
= gtk_tree_view_column_new();
198 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
199 gtk_tree_view_column_add_attribute(column
, renderer
, "text", 0);
200 gtk_tree_view_append_column(GTK_TREE_VIEW(pdlg
->questions
.view
), column
);
202 g_signal_connect_data(G_OBJECT(pdlg
->questions
.add
), "clicked", G_CALLBACK(add_question_cb
), pdlg
, NULL
, G_CONNECT_AFTER
|G_CONNECT_SWAPPED
);
203 g_signal_connect_swapped(G_OBJECT(pdlg
->questions
.edit
), "clicked", G_CALLBACK(edit_question_cb
), pdlg
);
204 g_signal_connect_swapped(G_OBJECT(pdlg
->questions
.remove
), "clicked", G_CALLBACK(remove_question_cb
), pdlg
);
208 static GtkWidget
*make_content (PollDlg
*pdlg
) {
212 vbox
= gtk_vbox_new(FALSE
, 6);
213 label
= gtk_label_new_with_mnemonic(_("Poll _Questions:"));
214 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
215 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
218 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), pdlg
->questions
.view
);
219 gtk_box_pack_start(GTK_BOX(vbox
), pdlg
->questions
.box
, TRUE
, TRUE
, 0);
225 static void polldlg_init (GtkWidget
*w
) {
226 PollDlg
*pdlg
= POLLDLG(w
);
228 gtk_window_set_title(GTK_WINDOW(pdlg
), _("Poll Creator"));
229 vbox
= gtk_vbox_new(FALSE
, 12);
230 gtk_box_pack_start(GTK_BOX(vbox
), make_pollmeta(pdlg
), FALSE
, FALSE
, 0);
231 gtk_box_pack_start(GTK_BOX(vbox
), make_content(pdlg
), TRUE
, TRUE
, 0);
232 gtk_dialog_add_buttons(GTK_DIALOG(pdlg
), GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, _("_Generate"), GTK_RESPONSE_OK
, NULL
);
233 jam_dialog_set_contents(GTK_DIALOG(pdlg
), vbox
);
237 static GtkWidget
*polldlg_new (GtkWidget
*parent
) {
238 PollDlg
*pdlg
= POLLDLG(g_object_new(polldlg_get_type(), NULL
));
239 gtk_window_set_transient_for(GTK_WINDOW(pdlg
), GTK_WINDOW(parent
));
240 return GTK_WIDGET(pdlg
);
244 static void generate_multi (PollQuestion
*pq
, GString
*ptext
) {
245 static const char *qtype_to_string
[] = {
250 PollQuestionMulti
*pqm
= (PollQuestionMulti
*)pq
;
252 g_string_append_printf(ptext
, "<lj-pq type=\"%s\">\n", qtype_to_string
[pq
->type
]);
253 g_string_append_printf(ptext
, "%s\n", pq
->question
);
254 for (l
= pqm
->answers
; l
; l
= l
->next
) g_string_append_printf(ptext
, "<lj-pi>%s</lj-pi>\n", (char *)l
->data
);
255 g_string_append(ptext
, "</lj-pq>\n\n");
259 static void generate_text (PollQuestion
*pq
, GString
*ptext
) {
260 PollQuestionText
*pqt
= (PollQuestionText
*) pq
;
261 g_string_append_printf(ptext
, "<lj-pq type=\"text\"");
262 if (pqt
->size
) g_string_append_printf(ptext
, " size=\"%d\"", pqt
->size
);
263 if (pqt
->width
) g_string_append_printf(ptext
, " maxlength=\"%d\"", pqt
->width
);
264 g_string_append_printf(ptext
, ">\n%s\n</lj-pq>\n\n", pq
->question
);
268 static void generate_scale (PollQuestion
*pq
, GString
*ptext
) {
269 PollQuestionScale
*pqs
= (PollQuestionScale
*) pq
;
270 g_string_append_printf(ptext
, "<lj-pq type=\"scale\" from=\"%d\" to=\"%d\" by=\"%d\">\n%s\n</lj-pq>\n\n", pqs
->from
, pqs
->to
, pqs
->by
, pq
->question
);
274 static GString
*poll_to_text (Poll
*poll
) {
275 static const char *psec_to_string
[] = {
282 ptext
= g_string_sized_new(2048);
284 g_string_append(ptext
, "<lj-poll ");
285 if (poll
->name
) g_string_append_printf(ptext
, "name=\"%s\" ", poll
->name
);
286 /* ...with metadata */
287 g_string_append_printf(ptext
, "whoview=\"%s\" ", psec_to_string
[poll
->viewers
]);
288 g_string_append_printf(ptext
, "whovote=\"%s\">\n\n", psec_to_string
[poll
->voters
]);
290 for (l
= poll
->questions
; l
; l
= l
->next
) {
291 PollQuestion
*pq
= l
->data
;
296 generate_multi(pq
, ptext
);
299 generate_text(pq
, ptext
);
302 generate_scale(pq
, ptext
);
306 g_string_append(ptext
, "</lj-poll>\n");
311 static Poll
*poll_read (PollDlg
*pdlg
) {
313 GtkTreeModel
*model
= GTK_TREE_MODEL(pdlg
->questions
.store
);
315 poll
= g_new0(Poll
, 1);
316 poll
->name
= gtk_editable_get_chars(GTK_EDITABLE(pdlg
->pollname
), 0, -1);
317 if (poll
->name
&& !poll
->name
[0]) string_replace(&poll
->name
, NULL
);
318 poll
->viewers
= gtk_option_menu_get_history(GTK_OPTION_MENU(pdlg
->viewers
));
319 poll
->voters
= gtk_option_menu_get_history(GTK_OPTION_MENU(pdlg
->voters
));
320 if (gtk_tree_model_get_iter_first(model
, &iter
)) {
321 /* this is probably O(n^2) or something like that.
322 * but there hopefully won't be that many answers. */
325 gtk_tree_model_get(model
, &iter
, 1, &pq
, -1);
326 poll
->questions
= g_slist_append(poll
->questions
, pq
);
327 } while (gtk_tree_model_iter_next(model
, &iter
));
333 static void poll_free (Poll
*poll
) {
335 g_slist_foreach(poll
->questions
, (GFunc
) poll_question_free
, NULL
);
336 g_slist_free(poll
->questions
);
341 static GString
*polltext_generate (PollDlg
*pdlg
) {
344 poll
= poll_read(pdlg
);
345 str
= poll_to_text(poll
);
351 /* The only public function in this module.
353 * Run the Poll Creator dialog. If a poll is made, paste it
354 * into the main LogJam entry at the current location. */
355 void run_poll_creator_dlg (JamWin
*jw
) {
356 GString
*polltext
= NULL
;
357 PollDlg
*pdlg
= POLLDLG(polldlg_new(GTK_WIDGET(jw
)));
358 GtkTextBuffer
*buffer
= jam_doc_get_text_buffer(jw
->doc
);
359 if (gtk_dialog_run(GTK_DIALOG(pdlg
)) == GTK_RESPONSE_OK
) {
360 polltext
= polltext_generate(pdlg
);
361 gtk_text_buffer_delete_selection(buffer
, FALSE
, FALSE
);
362 gtk_text_buffer_insert_at_cursor(buffer
, polltext
->str
, polltext
->len
);
363 g_string_free(polltext
, TRUE
);
365 gtk_widget_destroy(GTK_WIDGET(pdlg
));