blob: f0627f5a11c391811140addec472ed5109e3335d [file] [log] [blame]
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -04001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -04003 * 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.
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -040018 */
19
20#include "contactpopover.h"
21
22#include <contactmethod.h>
23#include "choosecontactview.h"
24#include "editcontactview.h"
25
26struct _ContactPopover
27{
28#if GTK_CHECK_VERSION(3,12,0)
29 GtkPopover parent;
30#else
31 GtkWindow parent;
32#endif
33};
34
35struct _ContactPopoverClass
36{
37#if GTK_CHECK_VERSION(3,12,0)
38 GtkPopoverClass parent_class;
39#else
40 GtkWindowClass parent_class;
41#endif
42};
43
44typedef struct _ContactPopoverPrivate ContactPopoverPrivate;
45
46struct _ContactPopoverPrivate
47{
48 GtkWidget *choosecontactview;
49 GtkWidget *editcontactview;
50
51 ContactMethod *cm;
52};
53
54#if GTK_CHECK_VERSION(3,12,0)
55 G_DEFINE_TYPE_WITH_PRIVATE(ContactPopover, contact_popover, GTK_TYPE_POPOVER);
56#else
57 G_DEFINE_TYPE_WITH_PRIVATE(ContactPopover, contact_popover, GTK_TYPE_WINDOW);
58#endif
59
60
61#define CONTACT_POPOVER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CONTACT_POPOVER_TYPE, ContactPopoverPrivate))
62
63#if !GTK_CHECK_VERSION(3,12,0)
64static gboolean
65contact_popover_button_release(GtkWidget *self, GdkEventButton *event)
66{
67 auto child = gtk_bin_get_child(GTK_BIN(self));
68
69 auto event_widget = gtk_get_event_widget((GdkEvent *) event);
70
71 GtkAllocation child_alloc;
72
73 gtk_widget_get_allocation(child, &child_alloc);
74
75 if (event->x < child_alloc.x ||
76 event->x > child_alloc.x + child_alloc.width ||
77 event->y < child_alloc.y ||
78 event->y > child_alloc.y + child_alloc.height)
79 gtk_widget_destroy(self);
80 else if (!gtk_widget_is_ancestor(event_widget, self))
81 gtk_widget_destroy(self);
82
83 return GDK_EVENT_PROPAGATE;
84}
85
86static gboolean
87contact_popover_key_press(GtkWidget *self, GdkEventKey *event)
88{
89 if (event->keyval == GDK_KEY_Escape) {
90 gtk_widget_destroy(self);
91 return GDK_EVENT_STOP;
92 }
93
94 return GDK_EVENT_PROPAGATE;
95}
96#endif
97
98static void
99contact_popover_init(ContactPopover *self)
100{
101#if GTK_CHECK_VERSION(3,12,0)
102 /* for now, destroy the popover on close, as we will construct a new one
103 * each time we need it */
104 g_signal_connect(self, "closed", G_CALLBACK(gtk_widget_destroy), NULL);
105#else
106 /* destroy the window on ESC, or when the user clicks outside of it */
107 g_signal_connect(self, "button_release_event", G_CALLBACK(contact_popover_button_release), NULL);
108 g_signal_connect(self, "key_press_event", G_CALLBACK(contact_popover_key_press), NULL);
109#endif
110}
111
112static void
113contact_popover_dispose(GObject *object)
114{
115 G_OBJECT_CLASS(contact_popover_parent_class)->dispose(object);
116}
117
118static void
119contact_popover_finalize(GObject *object)
120{
121 G_OBJECT_CLASS(contact_popover_parent_class)->finalize(object);
122}
123static void
124contact_popover_class_init(ContactPopoverClass *klass)
125{
126 G_OBJECT_CLASS(klass)->finalize = contact_popover_finalize;
127 G_OBJECT_CLASS(klass)->dispose = contact_popover_dispose;
128}
129
130static void
131construct_edit_contact_view(ContactPopover *self, Person *p)
132{
133 g_return_if_fail(IS_CONTACT_POPOVER(self));
134 ContactPopoverPrivate *priv = CONTACT_POPOVER_GET_PRIVATE(self);
135
136 priv->editcontactview = edit_contact_view_new(priv->cm, p);
137 g_object_add_weak_pointer(G_OBJECT(priv->editcontactview), (gpointer *)&priv->editcontactview);
138
139 gtk_container_remove(GTK_CONTAINER(self), priv->choosecontactview);
140 gtk_container_add(GTK_CONTAINER(self), priv->editcontactview);
141
142#if !GTK_CHECK_VERSION(3,12,0)
143 /* resize the window to shrink to the new view */
144 gtk_window_resize(GTK_WINDOW(self), 1, 1);
145#endif
146
147 /* destroy this popover when the contact is saved */
148 g_signal_connect_swapped(priv->editcontactview, "person-saved", G_CALLBACK(gtk_widget_destroy), self);
149}
150
151static void
152new_person_clicked(ContactPopover *self)
153{
154 g_return_if_fail(IS_CONTACT_POPOVER(self));
155 construct_edit_contact_view(self, NULL);
156}
157
158static void
159person_selected(ContactPopover *self, Person *p)
160{
161 g_return_if_fail(IS_CONTACT_POPOVER(self));
162 construct_edit_contact_view(self, p);
163}
164
165/**
166 * For gtk+ >= 3.12 this will create a GtkPopover pointing to the parent and if
167 * given, the GdkRectangle. Otherwise, this will create an undecorated GtkWindow
168 * which will be centered on the toplevel window of the given parent.
169 * This is to ensure cmpatibility with gtk+3.10.
170 */
171GtkWidget *
172contact_popover_new(ContactMethod *cm, GtkWidget *parent,
173#if !GTK_CHECK_VERSION(3,12,0)
174 G_GNUC_UNUSED
175#endif
176 GdkRectangle *rect)
177{
178 g_return_val_if_fail(cm, NULL);
179
180#if GTK_CHECK_VERSION(3,12,0)
181 gpointer self = g_object_new(CONTACT_POPOVER_TYPE,
182 "relative-to", parent,
183 "position", GTK_POS_RIGHT,
184 NULL);
185
186 if (rect)
187 gtk_popover_set_pointing_to(GTK_POPOVER(self), rect);
188#else
189 /* get the toplevel parent and try to center on it */
190 if (parent && GTK_IS_WIDGET(parent)) {
191 parent = gtk_widget_get_toplevel(GTK_WIDGET(parent));
192 if (!gtk_widget_is_toplevel(parent)) {
193 parent = NULL;
194 g_debug("could not get top level parent");
195 }
196 }
197
198 gpointer self = g_object_new(CONTACT_POPOVER_TYPE,
199 "modal", TRUE,
200 "transient-for", parent,
201 "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
202 "decorated", FALSE,
203 "resizable", FALSE,
204 NULL);
205#endif
206
207 ContactPopoverPrivate *priv = CONTACT_POPOVER_GET_PRIVATE(self);
208 priv->cm = cm;
209
210 priv->choosecontactview = choose_contact_view_new(cm);
211 gtk_container_add(GTK_CONTAINER(self), priv->choosecontactview);
212 g_object_add_weak_pointer(G_OBJECT(priv->choosecontactview), (gpointer *)&priv->choosecontactview);
213
214 g_signal_connect_swapped(priv->choosecontactview, "new-person-clicked", G_CALLBACK(new_person_clicked), self);
215 g_signal_connect_swapped(priv->choosecontactview, "person-selected", G_CALLBACK(person_selected), self);
216
217 return (GtkWidget *)self;
218}