1 /*
2  * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of Zrythm
5  *
6  * Zrythm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Zrythm is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "gui/widgets/foldable_notebook.h"
21 #include "gui/widgets/main_window.h"
22 #include "utils/gtk.h"
23 #include "utils/ui.h"
24 #include "zrythm_app.h"
25 
G_DEFINE_TYPE(FoldableNotebookWidget,foldable_notebook_widget,GTK_TYPE_NOTEBOOK)26 G_DEFINE_TYPE (
27   FoldableNotebookWidget,
28   foldable_notebook_widget,
29   GTK_TYPE_NOTEBOOK)
30 
31 static void
32 on_switch_page (
33   GtkNotebook * notebook,
34   GtkWidget * page,
35   guint       page_num,
36   FoldableNotebookWidget * self)
37 {
38   int num_pages =
39     gtk_notebook_get_n_pages (notebook);
40   GtkWidget * widget;
41   for (int i = 0; i < num_pages; i++)
42     {
43       /* set the child visible */
44       widget =
45         foldable_notebook_widget_get_widget_at_page (
46           self, i);
47       gtk_widget_set_visible (
48         widget, (guint) i == page_num ? 1 : 0);
49     }
50 }
51 
52 /**
53  * Sets the folded space visible or not.
54  */
55 void
foldable_notebook_widget_set_visibility(FoldableNotebookWidget * self,bool new_visibility)56 foldable_notebook_widget_set_visibility (
57   FoldableNotebookWidget * self,
58   bool                     new_visibility)
59 {
60   /* toggle visibility of all box children.
61    * do this on the children because toggling
62    * the visibility of the box causes gtk to
63    * automatically hide the tab too */
64   int num_pages =
65     gtk_notebook_get_n_pages (
66       GTK_NOTEBOOK (self));
67   for (int i = 0; i < num_pages; i++)
68     {
69       GtkWidget * widget =
70         foldable_notebook_widget_get_widget_at_page (
71           self, i);
72       gtk_widget_set_visible (
73         widget, new_visibility);
74     }
75 
76   if (new_visibility)
77     {
78       if (self->paned && self->prev_pos > 0)
79         {
80           gtk_paned_set_position (
81             self->paned, self->prev_pos);
82         }
83     }
84   else
85     {
86       if (self->paned)
87         {
88           /* remember position before hiding */
89           self->prev_pos =
90             gtk_paned_get_position (self->paned);
91 
92           /* hide */
93           int position;
94           if (self->pos_in_paned == GTK_POS_BOTTOM)
95             {
96               position =
97                 gtk_widget_get_allocated_height (
98                   GTK_WIDGET (self->paned));
99             }
100           else if (self->pos_in_paned ==
101                      GTK_POS_RIGHT)
102             {
103               position =
104                 gtk_widget_get_allocated_width (
105                   GTK_WIDGET (self->paned));
106             }
107           else
108             {
109               position = 0;
110             }
111           gtk_paned_set_position (
112             self->paned, position);
113         }
114     }
115 }
116 
117 /**
118  * Returns if the content of the foldable notebook
119  * is visible.
120  */
121 int
foldable_notebook_widget_is_content_visible(FoldableNotebookWidget * self)122 foldable_notebook_widget_is_content_visible (
123   FoldableNotebookWidget * self)
124 {
125   GtkWidget * widget =
126     foldable_notebook_widget_get_current_widget (
127       self);
128   return
129     gtk_widget_get_visible (widget);
130 }
131 
132 /**
133  * Get the widget currently visible.
134  */
135 GtkWidget *
foldable_notebook_widget_get_current_widget(FoldableNotebookWidget * self)136 foldable_notebook_widget_get_current_widget (
137   FoldableNotebookWidget * self)
138 {
139   GtkContainer * current_box =
140     GTK_CONTAINER (
141       z_gtk_notebook_get_current_page_widget (
142         GTK_NOTEBOOK (self)));
143   GtkWidget * widget =
144     z_gtk_container_get_single_child (
145       GTK_CONTAINER (current_box));
146   return widget;
147 }
148 
149 /**
150  * Combines the above.
151  */
152 void
foldable_notebook_widget_toggle_visibility(FoldableNotebookWidget * self)153 foldable_notebook_widget_toggle_visibility (
154   FoldableNotebookWidget * self)
155 {
156   g_return_if_fail (self);
157   foldable_notebook_widget_set_visibility (
158     self,
159     !foldable_notebook_widget_is_content_visible (
160        self));
161 }
162 
163 GtkWidget *
foldable_notebook_widget_get_widget_at_page(FoldableNotebookWidget * self,int page)164 foldable_notebook_widget_get_widget_at_page (
165   FoldableNotebookWidget * self,
166   int                      page)
167 {
168   GtkContainer * container =
169     GTK_CONTAINER (
170       gtk_notebook_get_nth_page (
171         GTK_NOTEBOOK (self), page));
172   GtkWidget * widget =
173     z_gtk_container_get_single_child (
174       GTK_CONTAINER (container));
175   return widget;
176 }
177 
178 /**
179  * Tab release callback.
180  */
181 static void
on_multipress_released(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,FoldableNotebookWidget * self)182 on_multipress_released (
183   GtkGestureMultiPress *   gesture,
184   gint                     n_press,
185   gdouble                  x,
186   gdouble                  y,
187   FoldableNotebookWidget * self)
188 {
189   bool hit =
190     ui_is_child_hit (
191       GTK_WIDGET (self),
192       self->tab_during_press,
193       1, 1, x, y,  16, 3);
194   if (hit)
195     {
196 
197       int new_visibility =
198         !foldable_notebook_widget_is_content_visible (
199            self);
200 
201       foldable_notebook_widget_set_visibility (
202         self, new_visibility);
203     }
204 }
205 
206 /**
207  * Tab press callback.
208  */
209 static void
on_multipress_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,FoldableNotebookWidget * self)210 on_multipress_pressed (
211   GtkGestureMultiPress *gesture,
212   gint                  n_press,
213   gdouble               x,
214   gdouble               y,
215   FoldableNotebookWidget * self)
216 {
217   self->tab_during_press =
218     z_gtk_notebook_get_current_tab_label_widget (
219       GTK_NOTEBOOK (self));
220 }
221 
222 FoldableNotebookWidget *
foldable_notebook_widget_new()223 foldable_notebook_widget_new ()
224 {
225   FoldableNotebookWidget * self =
226     g_object_new (FOLDABLE_NOTEBOOK_WIDGET_TYPE,
227                   NULL);
228 
229   return self;
230 }
231 
232 static void
foldable_notebook_widget_finalize(FoldableNotebookWidget * self)233 foldable_notebook_widget_finalize (
234   FoldableNotebookWidget * self)
235 {
236   G_OBJECT_CLASS (
237     foldable_notebook_widget_parent_class)->
238       finalize (G_OBJECT (self));
239 }
240 
241 /**
242  * Sets up an existing FoldableNotebookWidget.
243  */
244 void
foldable_notebook_widget_setup(FoldableNotebookWidget * self,GtkPaned * paned,GtkPositionType pos_in_paned)245 foldable_notebook_widget_setup (
246   FoldableNotebookWidget * self,
247   GtkPaned *               paned,
248   GtkPositionType          pos_in_paned)
249 {
250   self->paned = paned;
251   self->pos_in_paned = pos_in_paned;
252 
253   /* add events */
254   gtk_widget_add_events (
255     GTK_WIDGET (self),
256     GDK_ALL_EVENTS_MASK);
257 
258   /* make detachable */
259   z_gtk_notebook_make_detachable (
260     GTK_NOTEBOOK (self), GTK_WINDOW (MAIN_WINDOW));
261 
262   /* add signals */
263   self->mp =
264     GTK_GESTURE_MULTI_PRESS (
265       gtk_gesture_multi_press_new (
266         GTK_WIDGET (self)));
267   gtk_event_controller_set_propagation_phase (
268     GTK_EVENT_CONTROLLER (self->mp),
269     GTK_PHASE_CAPTURE);
270   g_signal_connect (
271     G_OBJECT (self->mp), "pressed",
272     G_CALLBACK (on_multipress_pressed), self);
273   g_signal_connect (
274     G_OBJECT (self->mp), "released",
275     G_CALLBACK (on_multipress_released), self);
276   g_signal_connect (
277     G_OBJECT (self), "switch-page",
278     G_CALLBACK (on_switch_page), self);
279 }
280 
281 static void
foldable_notebook_widget_class_init(FoldableNotebookWidgetClass * klass)282 foldable_notebook_widget_class_init (
283   FoldableNotebookWidgetClass * klass)
284 {
285   GObjectClass * oklass = G_OBJECT_CLASS (klass);
286   oklass->finalize =
287     (GObjectFinalizeFunc)
288     foldable_notebook_widget_finalize;
289 }
290 
291 static void
foldable_notebook_widget_init(FoldableNotebookWidget * self)292 foldable_notebook_widget_init (
293   FoldableNotebookWidget * self)
294 {
295 }
296