1 /* Demonstration of a UI driven publisher.
2  * This example (publisher-ui.c) is in the public domain.
3  */
4 #include <glib/gstdio.h>
5 #include <gtk/gtk.h>
6 
7 #include <libepc/publisher.h>
8 #include <libepc-ui/progress-window.h>
9 
10 #define GET_WIDGET(builder, name)       (GTK_WIDGET (gtk_builder_get_object ((builder), (name))))
11 #define EPC_TYPE_DEMO_CONTENTS          (epc_demo_item_get_type ())
12 
13 typedef struct _EpcDemoItem EpcDemoItem;
14 
15 typedef void (*UpdateItemFunc)  (EpcDemoItem *item,
16                                  const gchar *text);
17 typedef void (*ForeachItemFunc) (EpcDemoItem *item,
18                                  gpointer     data);
19 
20 struct _EpcDemoItem
21 {
22   volatile gint ref_count;
23 
24   gchar *name;
25   gchar *text;
26   gchar *username;
27   gchar *password;
28   gchar *bookmark;
29 };
30 
31 static EpcPublisher *publisher = NULL;
32 static gboolean updating_selection = FALSE;
33 static GtkTextBuffer *text_buffer = NULL;
34 static GtkListStore *item_list = NULL;
35 static guint auto_save_id = 0;
36 
37 static GtkBuilder *builder = NULL;
38 static GtkWidget *item_view = NULL;
39 
40 void about_button_clicked_cb (GtkWidget *widget);
41 void add_item_clicked_cb (void);
42 void remove_item_clicked_cb (void);
43 void username_changed_cb (GtkEntry *entry);
44 void password_changed_cb (GtkEntry *entry);
45 void bookmark_changed_cb (GtkEntry *entry);
46 void service_name_changed_cb (GtkEntry *entry);
47 void publish_state_toggled_cb (GtkToggleToolButton *button);
48 void encryption_state_toggled_cb (GtkToggleToolButton *button);
49 void main_window_destroy_cb (void);
50 
51 static EpcDemoItem*
epc_demo_item_new(const gchar * name,const gchar * text)52 epc_demo_item_new (const gchar *name,
53                    const gchar *text)
54 {
55   EpcDemoItem *self = g_slice_new0 (EpcDemoItem);
56 
57   self->ref_count = 1;
58 
59   if (name)
60     self->name = g_strdup (name);
61   if (text)
62     self->text = g_strdup (text);
63 
64   return self;
65 }
66 
67 static gpointer
epc_demo_item_ref(gpointer boxed)68 epc_demo_item_ref (gpointer boxed)
69 {
70   EpcDemoItem *self = boxed;
71 
72   g_atomic_int_inc (&self->ref_count);
73 
74   return self;
75 }
76 
77 static void
epc_demo_item_unref(gpointer boxed)78 epc_demo_item_unref (gpointer boxed)
79 {
80   EpcDemoItem *self = boxed;
81 
82   if (g_atomic_int_dec_and_test (&self->ref_count))
83     {
84       g_free (self->name);
85       g_free (self->text);
86       g_free (self->username);
87       g_free (self->password);
88       g_free (self->bookmark);
89 
90       g_slice_free (EpcDemoItem, self);
91     }
92 }
93 
94 static EpcDemoItem*
epc_demo_item_load(GKeyFile * settings,const gchar * group)95 epc_demo_item_load (GKeyFile    *settings,
96                     const gchar *group)
97 {
98   EpcDemoItem *self = NULL;
99 
100   if (g_key_file_has_group (settings, group))
101     {
102       self = epc_demo_item_new (NULL, NULL);
103 
104       self->name = g_key_file_get_string (settings, group, "Name", NULL);
105       self->text = g_key_file_get_string (settings, group, "Text", NULL);
106 
107       self->username = g_key_file_get_string (settings, group, "Username", NULL);
108       self->password = g_key_file_get_string (settings, group, "Password", NULL);
109       self->bookmark = g_key_file_get_string (settings, group, "Bookmark", NULL);
110     }
111 
112   return self;
113 }
114 
115 static void
epc_demo_item_save(EpcDemoItem * self,GKeyFile * settings)116 epc_demo_item_save (EpcDemoItem *self,
117                     GKeyFile    *settings)
118 {
119   gchar *group = g_strdup_printf ("Item: %s", self->name);
120 
121   g_key_file_set_string (settings, group, "Name", self->name);
122   g_key_file_set_string (settings, group, "Text", self->text ? self->text : "");
123 
124   if (self->username)
125     g_key_file_set_string (settings, group, "Username", self->username);
126   if (self->password)
127     g_key_file_set_string (settings, group, "Password", self->password);
128   if (self->bookmark)
129     g_key_file_set_string (settings, group, "Bookmark", self->bookmark);
130 
131   g_free (group);
132 }
133 
134 static void
epc_demo_item_set_text(EpcDemoItem * self,const gchar * text)135 epc_demo_item_set_text (EpcDemoItem *self,
136                         const gchar *text)
137 {
138   g_return_if_fail (self);
139 
140   g_free (self->text);
141   self->text = (text && *text ? g_strdup (text) : NULL);
142 }
143 
144 static void
epc_demo_item_set_username(EpcDemoItem * self,const gchar * text)145 epc_demo_item_set_username (EpcDemoItem *self,
146                             const gchar *text)
147 {
148   g_return_if_fail (self);
149 
150   g_free (self->username);
151   self->username = (text && *text ? g_strdup (text) : NULL);
152 }
153 
154 static void
epc_demo_item_set_password(EpcDemoItem * self,const gchar * text)155 epc_demo_item_set_password (EpcDemoItem *self,
156                             const gchar *text)
157 {
158   g_return_if_fail (self);
159 
160   g_free (self->password);
161   self->password = (text && *text ? g_strdup (text) : NULL);
162 }
163 
164 static void
epc_demo_item_set_bookmark(EpcDemoItem * self,const gchar * text)165 epc_demo_item_set_bookmark (EpcDemoItem *self,
166                             const gchar *text)
167 {
168   g_return_if_fail (self);
169 
170   g_free (self->bookmark);
171   self->bookmark = (text && *text ? g_strdup (text) : NULL);
172 }
173 
174 static GType
epc_demo_item_get_type(void)175 epc_demo_item_get_type (void)
176 {
177   static GType type = G_TYPE_INVALID;
178 
179   if (G_UNLIKELY (!type))
180     type = g_boxed_type_register_static ("EpcDemoItem",
181                                          epc_demo_item_ref,
182                                          epc_demo_item_unref);
183 
184   return type;
185 }
186 
187 static void
show_error(GtkWidget * parent,const gchar * title,GError * error)188 show_error (GtkWidget   *parent,
189             const gchar *title,
190             GError      *error)
191 {
192   GtkWidget *dialog;
193 
194   if (parent)
195     parent = gtk_widget_get_toplevel (parent);
196 
197   dialog = gtk_message_dialog_new_with_markup (parent ? GTK_WINDOW (parent) : NULL,
198                                                GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
199                                                GTK_MESSAGE_ERROR,
200                                                GTK_BUTTONS_CLOSE,
201                                                "<big><b>%s</b></big>\n\n%s.",
202                                                title, error->message);
203 
204   gtk_dialog_run (GTK_DIALOG (dialog));
205   gtk_widget_destroy (dialog);
206 }
207 
208 static void
load_ui(const gchar * path)209 load_ui (const gchar *path)
210 {
211   gchar *dirname = g_path_get_dirname (path);
212 
213   if (!g_str_has_suffix (dirname, ".libs"))
214     {
215       GError *error = NULL;
216       gchar *filename;
217 
218       filename = g_build_filename (dirname, "publisher.ui", NULL);
219       gtk_builder_add_from_file (builder, filename, &error);
220 
221       if (error)
222         {
223           show_error (NULL, "Cannot load user interface.", error);
224           g_error_free (error);
225         }
226 
227       g_free (filename);
228     }
229   else
230     load_ui (dirname);
231 
232   g_free (dirname);
233 }
234 
235 static gchar*
get_settings_filename(void)236 get_settings_filename (void)
237 {
238   return g_build_filename (g_get_user_config_dir (), "libepc",
239                            g_get_prgname (), "settings", NULL);
240 }
241 
242 static void
load_settings(void)243 load_settings (void)
244 {
245   gchar *filename = get_settings_filename ();
246   GKeyFile *settings = g_key_file_new ();
247   gchar **groups = NULL;
248   gchar *text;
249   gint i;
250 
251   g_key_file_load_from_file (settings, filename, 0, NULL);
252   groups = g_key_file_get_groups (settings, NULL);
253 
254   if (!item_list)
255     item_list = gtk_list_store_new (2, G_TYPE_STRING, EPC_TYPE_DEMO_CONTENTS);
256   else
257     gtk_list_store_clear (item_list);
258 
259   gtk_toggle_tool_button_set_active (
260     GTK_TOGGLE_TOOL_BUTTON (GET_WIDGET (builder, "encryption-state")),
261     g_key_file_get_boolean (settings, "Settings", "Encrypted", NULL));
262 
263   text = g_key_file_get_string (settings, "Settings", "ServiceName", NULL);
264 
265   if (text)
266     gtk_entry_set_text (GTK_ENTRY (GET_WIDGET (builder, "service-name")), text);
267 
268   g_free (text);
269 
270   for (i = 0; groups[i]; ++i)
271     if (g_str_has_prefix (groups[i], "Item: "))
272       {
273         EpcDemoItem *item = epc_demo_item_load (settings, groups[i]);
274         GtkTreeIter iter;
275 
276         if (!item)
277           continue;
278 
279         gtk_list_store_append (item_list, &iter);
280         gtk_list_store_set (item_list, &iter, 0, item->name, 1, item, -1);
281 
282         epc_demo_item_unref (item);
283       }
284 
285   g_key_file_free (settings);
286   g_strfreev (groups);
287   g_free (filename);
288 }
289 
290 static void
foreach_item(ForeachItemFunc callback,gpointer user_data)291 foreach_item (ForeachItemFunc callback,
292               gpointer        user_data)
293 {
294   GtkTreeModel *model = GTK_TREE_MODEL (item_list);
295   GtkTreeIter iter;
296 
297   if (gtk_tree_model_get_iter_first (model, &iter))
298     do
299       {
300         EpcDemoItem *item = NULL;
301         gtk_tree_model_get (model, &iter, 1, &item, -1);
302 
303         if (item)
304           {
305             callback (item, user_data);
306             epc_demo_item_unref (item);
307           }
308       }
309     while (gtk_tree_model_iter_next (model, &iter));
310 }
311 
312 static gboolean
auto_save_cb(gpointer data G_GNUC_UNUSED)313 auto_save_cb (gpointer data G_GNUC_UNUSED)
314 {
315   gchar *filename = get_settings_filename ();
316   GKeyFile *settings = g_key_file_new ();
317   gchar **groups = NULL;
318   GtkWidget *widget;
319   gint i;
320 
321   gchar *settings_data;
322   gsize settings_len;
323 
324   g_key_file_load_from_file (settings, filename, 0, NULL);
325   groups = g_key_file_get_groups (settings, NULL);
326 
327   widget = GET_WIDGET (builder, "encryption-state");
328   g_key_file_set_boolean (settings, "Settings", "Encrypted",
329                           gtk_toggle_tool_button_get_active (
330                           GTK_TOGGLE_TOOL_BUTTON (widget)));
331 
332   widget = GET_WIDGET (builder, "service-name");
333   g_key_file_set_string (settings, "Settings", "ServiceName",
334                          gtk_entry_get_text (GTK_ENTRY (widget)));
335 
336   for (i = 0; groups[i]; ++i)
337     if (g_str_has_prefix (groups[i], "Item: "))
338       g_key_file_remove_group (settings, groups[i], NULL);
339 
340   foreach_item ((ForeachItemFunc) epc_demo_item_save, settings);
341   settings_data = g_key_file_to_data (settings, &settings_len, NULL);
342 
343   if (settings_data)
344     {
345       g_mkdir_with_parents (filename, 0700);
346       g_rmdir (filename);
347 
348       g_debug ("%s: filename=%s", G_STRFUNC, filename);
349       g_file_set_contents (filename, settings_data, settings_len, NULL);
350       g_free (settings_data);
351     }
352 
353   g_key_file_free (settings);
354   g_strfreev (groups);
355   g_free (filename);
356 
357   auto_save_id = 0;
358 
359   return FALSE;
360 }
361 
362 static void
schedule_auto_save(void)363 schedule_auto_save (void)
364 {
365   if (0 == auto_save_id)
366     auto_save_id = g_timeout_add (5 * 1000, auto_save_cb, NULL);
367 }
368 
369 void
about_button_clicked_cb(GtkWidget * widget)370 about_button_clicked_cb (GtkWidget *widget)
371 {
372   GtkWidget *dialog = GET_WIDGET (builder, "about-dialog");
373   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (gtk_widget_get_toplevel (widget)));
374   gtk_dialog_run (GTK_DIALOG (dialog));
375   gtk_widget_hide (dialog);
376 }
377 
378 void
add_item_clicked_cb(void)379 add_item_clicked_cb (void)
380 {
381   GtkTreeSelection *selection;
382   GtkTreeIter list_iter, iter;
383   GtkTreeViewColumn *column;
384   GtkTreeModel *model;
385   GtkTreePath *path;
386 
387   gtk_widget_grab_focus (item_view);
388   column = gtk_tree_view_get_column (GTK_TREE_VIEW (item_view), 0);
389   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (item_view));
390 
391   if (gtk_tree_selection_get_selected (selection, &model, &iter))
392     {
393       path = gtk_tree_model_get_path (model, &iter);
394       gtk_tree_view_set_cursor (GTK_TREE_VIEW (item_view), path, NULL, FALSE);
395       gtk_tree_path_free (path);
396     }
397 
398   gtk_list_store_append (item_list, &list_iter);
399   gtk_list_store_set (item_list, &list_iter, 0, "New Item", -1);
400 
401   gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (model),
402                                                   &iter, &list_iter);
403 
404   path = gtk_tree_model_get_path (model, &iter);
405   gtk_tree_view_set_cursor (GTK_TREE_VIEW (item_view), path, column, TRUE);
406   gtk_tree_path_free (path);
407 }
408 
409 static void
remove_item(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreeIter * iter)410 remove_item (GtkTreeSelection *selection,
411              GtkTreeModel     *model,
412              GtkTreeIter      *iter)
413 {
414   EpcDemoItem *item = NULL;
415   GtkTreePath *path = NULL;
416   GtkTreeIter  child_iter;
417 
418   gtk_tree_model_get (model, iter, 1, &item, -1);
419 
420   if (item)
421     {
422       epc_publisher_remove (publisher, item->name);
423       epc_demo_item_unref (item);
424     }
425 
426   path = gtk_tree_model_get_path (model, iter);
427   gtk_tree_view_set_cursor (GTK_TREE_VIEW (item_view), path, NULL, FALSE);
428 
429   if (GTK_IS_TREE_MODEL_SORT (model))
430     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model),
431                                                     &child_iter, iter);
432   else
433     child_iter = *iter;
434 
435   gtk_list_store_remove (item_list, &child_iter);
436 
437   if (gtk_tree_model_get_iter (model, iter, path) ||
438      (gtk_tree_path_prev (path) && gtk_tree_model_get_iter (model, iter, path)))
439     gtk_tree_selection_select_iter (selection, iter);
440 
441   gtk_tree_path_free (path);
442 }
443 
444 void
remove_item_clicked_cb(void)445 remove_item_clicked_cb (void)
446 {
447   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (item_view));
448   GtkTreeModel *model = NULL;
449   GtkTreeIter iter;
450 
451   if (gtk_tree_selection_get_selected (selection, &model, &iter))
452     remove_item (selection, model, &iter);
453 }
454 
455 static EpcDemoItem*
get_selected_item(GtkTreeSelection * selection)456 get_selected_item (GtkTreeSelection *selection)
457 {
458   GtkTreeModel *model = NULL;
459   EpcDemoItem *item = NULL;
460   GtkTreeIter iter;
461 
462   if (updating_selection)
463     return NULL;
464 
465   if (!selection)
466     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (item_view));
467   if (gtk_tree_selection_get_selected (selection, &model, &iter))
468     gtk_tree_model_get (model, &iter, 1, &item, -1);
469 
470   return item;
471 }
472 
473 static void
selection_changed_cb(GtkTreeSelection * selection)474 selection_changed_cb (GtkTreeSelection *selection)
475 {
476   EpcDemoItem *item;
477 
478   g_return_if_fail (!updating_selection);
479   item = get_selected_item (selection);
480   updating_selection = TRUE;
481 
482   if (item)
483     {
484       gtk_text_buffer_set_text (text_buffer,
485                                 item->text ? item->text : "",
486                                 -1);
487 
488       gtk_entry_set_text (GTK_ENTRY (GET_WIDGET (builder, "username")),
489                           item->username ? item->username : "");
490       gtk_entry_set_text (GTK_ENTRY (GET_WIDGET (builder, "password")),
491                           item->password ? item->password : "");
492       gtk_entry_set_text (GTK_ENTRY (GET_WIDGET (builder, "bookmark")),
493                           item->bookmark ? item->bookmark: "");
494 
495       epc_demo_item_unref (item);
496     }
497 
498   gtk_widget_set_sensitive (GET_WIDGET (builder, "item-vbox"), NULL != item);
499   gtk_widget_set_sensitive (GET_WIDGET (builder, "remove-item"), NULL != item);
500 
501   updating_selection = FALSE;
502 }
503 
504 static void
update_item(UpdateItemFunc update,const gchar * text)505 update_item (UpdateItemFunc update,
506              const gchar   *text)
507 {
508   EpcDemoItem *item = get_selected_item (NULL);
509 
510   if (item)
511     {
512       update (item, text);
513       epc_demo_item_unref (item);
514       schedule_auto_save ();
515     }
516 }
517 
518 void
username_changed_cb(GtkEntry * entry)519 username_changed_cb (GtkEntry *entry)
520 {
521   update_item (epc_demo_item_set_username, gtk_entry_get_text (entry));
522 }
523 
524 void
password_changed_cb(GtkEntry * entry)525 password_changed_cb (GtkEntry *entry)
526 {
527   update_item (epc_demo_item_set_password, gtk_entry_get_text (entry));
528 }
529 
530 void
bookmark_changed_cb(GtkEntry * entry)531 bookmark_changed_cb (GtkEntry *entry)
532 {
533   update_item (epc_demo_item_set_bookmark, gtk_entry_get_text (entry));
534 }
535 
536 static void
text_buffer_changed_cb(GtkTextBuffer * buffer)537 text_buffer_changed_cb (GtkTextBuffer *buffer)
538 {
539   GtkTextIter start, end;
540   gchar *text;
541 
542   gtk_text_buffer_get_bounds (buffer, &start, &end);
543   text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
544   update_item (epc_demo_item_set_text, text);
545   g_free (text);
546 }
547 
548 static gchar*
get_service_name(GtkEntry * entry)549 get_service_name (GtkEntry *entry)
550 {
551   const gchar *pattern = gtk_entry_get_text (entry);
552   GError *error = NULL;
553   gchar *name;
554 
555   name = epc_publisher_expand_name (pattern, &error);
556 
557   if (!name)
558     show_error (GTK_WIDGET (entry), "Cannot change service name", error);
559 
560   g_clear_error (&error);
561 
562   return name;
563 }
564 
565 static EpcProtocol
get_protocol(GtkToggleToolButton * button)566 get_protocol (GtkToggleToolButton *button)
567 {
568   gboolean encryption = gtk_toggle_tool_button_get_active (button);
569   return (encryption ? EPC_PROTOCOL_HTTPS : EPC_PROTOCOL_HTTP);
570 }
571 
572 void
service_name_changed_cb(GtkEntry * entry)573 service_name_changed_cb (GtkEntry *entry)
574 {
575   if (publisher)
576     {
577       gchar *name = get_service_name (entry);
578 
579       if (name)
580         epc_publisher_set_service_name (publisher, name);
581 
582       g_free (name);
583     }
584 
585   schedule_auto_save ();
586 }
587 
588 static EpcContents*
item_contents_handler(EpcPublisher * publisher G_GNUC_UNUSED,const gchar * key G_GNUC_UNUSED,gpointer data)589 item_contents_handler (EpcPublisher *publisher G_GNUC_UNUSED,
590                        const gchar  *key G_GNUC_UNUSED,
591                        gpointer      data)
592 {
593   EpcDemoItem *item = data;
594   const gchar *type = "text/plain";
595   const gchar *text = item->text ? item->text : "";
596 
597   if (g_str_has_prefix (text, "<html>"))
598     type = "text/html";
599 
600   return epc_contents_new_dup (type, text, -1);
601 }
602 
603 static gboolean
auth_handler(EpcAuthContext * context,const gchar * username,gpointer data G_GNUC_UNUSED)604 auth_handler (EpcAuthContext *context,
605               const gchar    *username,
606               gpointer        data G_GNUC_UNUSED)
607 {
608   EpcPublisher *publisher = epc_auth_context_get_publisher (context);
609   const gchar *key = epc_auth_context_get_key (context);
610   EpcDemoItem *item = epc_publisher_lookup (publisher, key);
611 
612   g_return_val_if_fail (NULL != item, FALSE);
613 
614   if (!item->username || !*item->username)
615     return TRUE;
616 
617   return
618     NULL != username &&
619     g_str_equal (username, item->username) &&
620     epc_auth_context_check_password (context, item->password);
621 }
622 
623 static void
publish_item_cb(EpcDemoItem * item,gpointer data G_GNUC_UNUSED)624 publish_item_cb (EpcDemoItem *item,
625                  gpointer     data G_GNUC_UNUSED)
626 {
627   epc_publisher_add_handler (publisher, item->name, item_contents_handler,
628                              epc_demo_item_ref (item),
629                              epc_demo_item_unref);
630   epc_publisher_set_auth_handler (publisher, item->name,
631                                   auth_handler, NULL, NULL);
632 }
633 
634 void
publish_state_toggled_cb(GtkToggleToolButton * button)635 publish_state_toggled_cb (GtkToggleToolButton *button)
636 {
637   gboolean publish = gtk_toggle_tool_button_get_active (button);
638   GError *error = NULL;
639 
640   if (!publish)
641     epc_publisher_quit (publisher);
642   else if (!epc_publisher_run_async (publisher, &error))
643     show_error (GTK_WIDGET (button), "Cannot start publisher", error);
644 
645   g_clear_error (&error);
646 }
647 
648 void
encryption_state_toggled_cb(GtkToggleToolButton * button)649 encryption_state_toggled_cb (GtkToggleToolButton *button)
650 {
651   if (publisher)
652     {
653       gboolean was_running;
654       GError *error = NULL;
655 
656       was_running = epc_publisher_quit (publisher);
657       epc_publisher_set_protocol (publisher, get_protocol (button));
658 
659       if (was_running && !epc_publisher_run_async (publisher, &error))
660         show_error (GTK_WIDGET (button), "Cannot restart publisher", error);
661 
662       g_clear_error (&error);
663     }
664 
665   schedule_auto_save ();
666 }
667 
668 static void
cell_editing_canceled_cb(void)669 cell_editing_canceled_cb (void)
670 {
671   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (item_view));
672   GtkTreeModel *model = NULL;
673   EpcDemoItem *item = NULL;
674   GtkTreeIter iter;
675 
676   if (gtk_tree_selection_get_selected (selection, &model, &iter))
677     gtk_tree_model_get (model, &iter, 1, &item, -1);
678 
679   if (!item)
680     remove_item (selection, model, &iter);
681   else
682     epc_demo_item_unref (item);
683 }
684 
685 static void
cell_item_edited_cb(GtkCellRendererText * cell G_GNUC_UNUSED,const gchar * path,const gchar * text)686 cell_item_edited_cb (GtkCellRendererText *cell G_GNUC_UNUSED,
687                      const gchar         *path,
688                      const gchar         *text)
689 {
690   GtkTreeIter iter, child_iter;
691   GtkTreeModel *model;
692   EpcDemoItem *item;
693 
694   model = gtk_tree_view_get_model (GTK_TREE_VIEW (item_view));
695 
696   if (gtk_tree_model_get_iter_from_string (model, &iter, path))
697     {
698       const gchar *base_name = text;
699       gchar *unique_name = NULL;
700       gint unique_index = 1;
701 
702       gtk_tree_model_get (model, &iter, 1, &item, -1);
703 
704       if (item && item->name)
705         epc_publisher_remove (publisher, item->name);
706 
707       while (epc_publisher_has_key (publisher, text))
708         {
709           g_free (unique_name);
710 
711           unique_name = g_strdup_printf ("%s #%d", base_name, unique_index);
712           unique_index += 1;
713 
714           text = unique_name;
715         }
716 
717       if (item)
718         {
719           g_free (item->name);
720           item->name = g_strdup (text);
721         }
722       else
723         item = epc_demo_item_new (text, NULL);
724 
725       publish_item_cb (item, NULL);
726 
727       gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model),
728                                                       &child_iter, &iter);
729       gtk_list_store_set (item_list, &child_iter, 0, text, 1, item, -1);
730 
731       selection_changed_cb (NULL);
732       epc_demo_item_unref (item);
733       schedule_auto_save ();
734     }
735 }
736 
737 void
main_window_destroy_cb(void)738 main_window_destroy_cb (void)
739 {
740   if (auto_save_id)
741     {
742       g_source_remove (auto_save_id);
743       auto_save_cb (NULL);
744     }
745 
746   gtk_main_quit ();
747 }
748 
749 static void
create_publisher(void)750 create_publisher (void)
751 {
752   GtkToggleToolButton *encryption = NULL;
753   GtkEntry *service_name_entry = NULL;
754   gchar *service_name = NULL;
755 
756   encryption = GTK_TOGGLE_TOOL_BUTTON (GET_WIDGET (builder, "encryption-state"));
757   service_name_entry = GTK_ENTRY (GET_WIDGET (builder, "service-name"));
758   service_name = get_service_name (service_name_entry);
759 
760   g_assert (NULL == publisher);
761   publisher = epc_publisher_new (service_name, "test-publisher", NULL);
762   epc_publisher_set_protocol (publisher, get_protocol (encryption));
763   foreach_item (publish_item_cb, NULL);
764 
765   g_free (service_name);
766 }
767 
768 int
main(int argc,char * argv[])769 main (int   argc,
770       char *argv[])
771 {
772   GtkCellRenderer *cell;
773   GtkTreeModel *sorted;
774   GtkTreeSelection *selection;
775 
776   GtkWidget *text_view;
777   GtkWidget *main_window;
778 
779   g_set_application_name ("Easy Publisher Example");
780 
781   gtk_init (&argc, &argv);
782 
783   builder = gtk_builder_new ();
784   load_ui (argv[0]);
785   load_settings ();
786 
787   create_publisher ();
788   gtk_builder_connect_signals (builder, NULL);
789 
790   item_view = GET_WIDGET (builder, "item-view");
791   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (item_view));
792   g_signal_connect (selection, "changed", G_CALLBACK (selection_changed_cb), NULL);
793 
794   cell = gtk_cell_renderer_text_new ();
795   g_object_set (cell, "editable", TRUE, NULL);
796 
797   g_object_connect (cell,
798                     "signal::editing-canceled", cell_editing_canceled_cb, NULL,
799                     "signal::edited", cell_item_edited_cb, NULL,
800                     NULL);
801 
802   gtk_tree_view_append_column (GTK_TREE_VIEW (item_view),
803                                gtk_tree_view_column_new_with_attributes (
804                                NULL, cell, "text", 0, NULL));
805 
806   sorted = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (item_list));
807   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sorted), 0, GTK_SORT_ASCENDING);
808   gtk_tree_view_set_model (GTK_TREE_VIEW (item_view), sorted);
809 
810   text_view = GET_WIDGET (builder, "text-view");
811   text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
812   g_signal_connect (text_buffer, "changed", G_CALLBACK (text_buffer_changed_cb), NULL);
813 
814   main_window = GET_WIDGET (builder, "main-window");
815   epc_progress_window_install (GTK_WINDOW (main_window));
816   gtk_widget_show (main_window);
817 
818   gtk_main ();
819 
820   if (publisher)
821     g_object_unref (publisher);
822 
823   g_object_unref (builder);
824 
825   return 0;
826 }
827