blob: b41038c2597cc56cb2ebc40f00eab3c32a4f018b [file] [log] [blame]
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -04001/*
2 * Copyright (C) 2015 Savoir-faire Linux Inc.
3 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "contactpopover.h"
32
33#include <contactmethod.h>
34#include "choosecontactview.h"
35#include "editcontactview.h"
36
37struct _ContactPopover
38{
39#if GTK_CHECK_VERSION(3,12,0)
40 GtkPopover parent;
41#else
42 GtkWindow parent;
43#endif
44};
45
46struct _ContactPopoverClass
47{
48#if GTK_CHECK_VERSION(3,12,0)
49 GtkPopoverClass parent_class;
50#else
51 GtkWindowClass parent_class;
52#endif
53};
54
55typedef struct _ContactPopoverPrivate ContactPopoverPrivate;
56
57struct _ContactPopoverPrivate
58{
59 GtkWidget *choosecontactview;
60 GtkWidget *editcontactview;
61
62 ContactMethod *cm;
63};
64
65#if GTK_CHECK_VERSION(3,12,0)
66 G_DEFINE_TYPE_WITH_PRIVATE(ContactPopover, contact_popover, GTK_TYPE_POPOVER);
67#else
68 G_DEFINE_TYPE_WITH_PRIVATE(ContactPopover, contact_popover, GTK_TYPE_WINDOW);
69#endif
70
71
72#define CONTACT_POPOVER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CONTACT_POPOVER_TYPE, ContactPopoverPrivate))
73
74#if !GTK_CHECK_VERSION(3,12,0)
75static gboolean
76contact_popover_button_release(GtkWidget *self, GdkEventButton *event)
77{
78 auto child = gtk_bin_get_child(GTK_BIN(self));
79
80 auto event_widget = gtk_get_event_widget((GdkEvent *) event);
81
82 GtkAllocation child_alloc;
83
84 gtk_widget_get_allocation(child, &child_alloc);
85
86 if (event->x < child_alloc.x ||
87 event->x > child_alloc.x + child_alloc.width ||
88 event->y < child_alloc.y ||
89 event->y > child_alloc.y + child_alloc.height)
90 gtk_widget_destroy(self);
91 else if (!gtk_widget_is_ancestor(event_widget, self))
92 gtk_widget_destroy(self);
93
94 return GDK_EVENT_PROPAGATE;
95}
96
97static gboolean
98contact_popover_key_press(GtkWidget *self, GdkEventKey *event)
99{
100 if (event->keyval == GDK_KEY_Escape) {
101 gtk_widget_destroy(self);
102 return GDK_EVENT_STOP;
103 }
104
105 return GDK_EVENT_PROPAGATE;
106}
107#endif
108
109static void
110contact_popover_init(ContactPopover *self)
111{
112#if GTK_CHECK_VERSION(3,12,0)
113 /* for now, destroy the popover on close, as we will construct a new one
114 * each time we need it */
115 g_signal_connect(self, "closed", G_CALLBACK(gtk_widget_destroy), NULL);
116#else
117 /* destroy the window on ESC, or when the user clicks outside of it */
118 g_signal_connect(self, "button_release_event", G_CALLBACK(contact_popover_button_release), NULL);
119 g_signal_connect(self, "key_press_event", G_CALLBACK(contact_popover_key_press), NULL);
120#endif
121}
122
123static void
124contact_popover_dispose(GObject *object)
125{
126 G_OBJECT_CLASS(contact_popover_parent_class)->dispose(object);
127}
128
129static void
130contact_popover_finalize(GObject *object)
131{
132 G_OBJECT_CLASS(contact_popover_parent_class)->finalize(object);
133}
134static void
135contact_popover_class_init(ContactPopoverClass *klass)
136{
137 G_OBJECT_CLASS(klass)->finalize = contact_popover_finalize;
138 G_OBJECT_CLASS(klass)->dispose = contact_popover_dispose;
139}
140
141static void
142construct_edit_contact_view(ContactPopover *self, Person *p)
143{
144 g_return_if_fail(IS_CONTACT_POPOVER(self));
145 ContactPopoverPrivate *priv = CONTACT_POPOVER_GET_PRIVATE(self);
146
147 priv->editcontactview = edit_contact_view_new(priv->cm, p);
148 g_object_add_weak_pointer(G_OBJECT(priv->editcontactview), (gpointer *)&priv->editcontactview);
149
150 gtk_container_remove(GTK_CONTAINER(self), priv->choosecontactview);
151 gtk_container_add(GTK_CONTAINER(self), priv->editcontactview);
152
153#if !GTK_CHECK_VERSION(3,12,0)
154 /* resize the window to shrink to the new view */
155 gtk_window_resize(GTK_WINDOW(self), 1, 1);
156#endif
157
158 /* destroy this popover when the contact is saved */
159 g_signal_connect_swapped(priv->editcontactview, "person-saved", G_CALLBACK(gtk_widget_destroy), self);
160}
161
162static void
163new_person_clicked(ContactPopover *self)
164{
165 g_return_if_fail(IS_CONTACT_POPOVER(self));
166 construct_edit_contact_view(self, NULL);
167}
168
169static void
170person_selected(ContactPopover *self, Person *p)
171{
172 g_return_if_fail(IS_CONTACT_POPOVER(self));
173 construct_edit_contact_view(self, p);
174}
175
176/**
177 * For gtk+ >= 3.12 this will create a GtkPopover pointing to the parent and if
178 * given, the GdkRectangle. Otherwise, this will create an undecorated GtkWindow
179 * which will be centered on the toplevel window of the given parent.
180 * This is to ensure cmpatibility with gtk+3.10.
181 */
182GtkWidget *
183contact_popover_new(ContactMethod *cm, GtkWidget *parent,
184#if !GTK_CHECK_VERSION(3,12,0)
185 G_GNUC_UNUSED
186#endif
187 GdkRectangle *rect)
188{
189 g_return_val_if_fail(cm, NULL);
190
191#if GTK_CHECK_VERSION(3,12,0)
192 gpointer self = g_object_new(CONTACT_POPOVER_TYPE,
193 "relative-to", parent,
194 "position", GTK_POS_RIGHT,
195 NULL);
196
197 if (rect)
198 gtk_popover_set_pointing_to(GTK_POPOVER(self), rect);
199#else
200 /* get the toplevel parent and try to center on it */
201 if (parent && GTK_IS_WIDGET(parent)) {
202 parent = gtk_widget_get_toplevel(GTK_WIDGET(parent));
203 if (!gtk_widget_is_toplevel(parent)) {
204 parent = NULL;
205 g_debug("could not get top level parent");
206 }
207 }
208
209 gpointer self = g_object_new(CONTACT_POPOVER_TYPE,
210 "modal", TRUE,
211 "transient-for", parent,
212 "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
213 "decorated", FALSE,
214 "resizable", FALSE,
215 NULL);
216#endif
217
218 ContactPopoverPrivate *priv = CONTACT_POPOVER_GET_PRIVATE(self);
219 priv->cm = cm;
220
221 priv->choosecontactview = choose_contact_view_new(cm);
222 gtk_container_add(GTK_CONTAINER(self), priv->choosecontactview);
223 g_object_add_weak_pointer(G_OBJECT(priv->choosecontactview), (gpointer *)&priv->choosecontactview);
224
225 g_signal_connect_swapped(priv->choosecontactview, "new-person-clicked", G_CALLBACK(new_person_clicked), self);
226 g_signal_connect_swapped(priv->choosecontactview, "person-selected", G_CALLBACK(person_selected), self);
227
228 return (GtkWidget *)self;
229}