1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpsessioninfo-book.c
5  * Copyright (C) 2001-2007 Michael Natterer <mitch@gimp.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <gtk/gtk.h>
24 
25 #include "libgimpconfig/gimpconfig.h"
26 
27 #include "widgets-types.h"
28 
29 #include "gimpdialogfactory.h"
30 #include "gimpdock.h"
31 #include "gimpdockbook.h"
32 #include "gimpsessioninfo.h"
33 #include "gimpsessioninfo-book.h"
34 #include "gimpsessioninfo-dockable.h"
35 
36 
37 enum
38 {
39   SESSION_INFO_BOOK_POSITION,
40   SESSION_INFO_BOOK_CURRENT_PAGE,
41   SESSION_INFO_BOOK_DOCKABLE
42 };
43 
44 
45 /*  public functions  */
46 
47 GimpSessionInfoBook *
gimp_session_info_book_new(void)48 gimp_session_info_book_new (void)
49 {
50   return g_slice_new0 (GimpSessionInfoBook);
51 }
52 
53 void
gimp_session_info_book_free(GimpSessionInfoBook * info)54 gimp_session_info_book_free (GimpSessionInfoBook *info)
55 {
56   g_return_if_fail (info != NULL);
57 
58   if (info->dockables)
59     {
60       g_list_free_full (info->dockables,
61                         (GDestroyNotify) gimp_session_info_dockable_free);
62       info->dockables = NULL;
63     }
64 
65   g_slice_free (GimpSessionInfoBook, info);
66 }
67 
68 void
gimp_session_info_book_serialize(GimpConfigWriter * writer,GimpSessionInfoBook * info)69 gimp_session_info_book_serialize (GimpConfigWriter    *writer,
70                                   GimpSessionInfoBook *info)
71 {
72   GList *pages;
73 
74   g_return_if_fail (writer != NULL);
75   g_return_if_fail (info != NULL);
76 
77   gimp_config_writer_open (writer, "book");
78 
79   if (info->position != 0)
80     {
81       gint position;
82 
83       position = gimp_session_info_apply_position_accuracy (info->position);
84 
85       gimp_config_writer_open (writer, "position");
86       gimp_config_writer_printf (writer, "%d", position);
87       gimp_config_writer_close (writer);
88     }
89 
90   gimp_config_writer_open (writer, "current-page");
91   gimp_config_writer_printf (writer, "%d", info->current_page);
92   gimp_config_writer_close (writer);
93 
94   for (pages = info->dockables; pages; pages = g_list_next (pages))
95     gimp_session_info_dockable_serialize (writer, pages->data);
96 
97   gimp_config_writer_close (writer);
98 }
99 
100 GTokenType
gimp_session_info_book_deserialize(GScanner * scanner,gint scope,GimpSessionInfoBook ** book)101 gimp_session_info_book_deserialize (GScanner             *scanner,
102                                     gint                  scope,
103                                     GimpSessionInfoBook **book)
104 {
105   GimpSessionInfoBook *info;
106   GTokenType           token;
107 
108   g_return_val_if_fail (scanner != NULL, G_TOKEN_LEFT_PAREN);
109   g_return_val_if_fail (book != NULL, G_TOKEN_LEFT_PAREN);
110 
111   g_scanner_scope_add_symbol (scanner, scope, "position",
112                               GINT_TO_POINTER (SESSION_INFO_BOOK_POSITION));
113   g_scanner_scope_add_symbol (scanner, scope, "current-page",
114                               GINT_TO_POINTER (SESSION_INFO_BOOK_CURRENT_PAGE));
115   g_scanner_scope_add_symbol (scanner, scope, "dockable",
116                               GINT_TO_POINTER (SESSION_INFO_BOOK_DOCKABLE));
117 
118   info = gimp_session_info_book_new ();
119 
120   token = G_TOKEN_LEFT_PAREN;
121 
122   while (g_scanner_peek_next_token (scanner) == token)
123     {
124       token = g_scanner_get_next_token (scanner);
125 
126       switch (token)
127         {
128         case G_TOKEN_LEFT_PAREN:
129           token = G_TOKEN_SYMBOL;
130           break;
131 
132         case G_TOKEN_SYMBOL:
133           switch (GPOINTER_TO_INT (scanner->value.v_symbol))
134             {
135               GimpSessionInfoDockable *dockable;
136 
137             case SESSION_INFO_BOOK_POSITION:
138               token = G_TOKEN_INT;
139               if (! gimp_scanner_parse_int (scanner, &info->position))
140                 goto error;
141               break;
142 
143             case SESSION_INFO_BOOK_CURRENT_PAGE:
144               token = G_TOKEN_INT;
145               if (! gimp_scanner_parse_int (scanner, &info->current_page))
146                 goto error;
147               break;
148 
149             case SESSION_INFO_BOOK_DOCKABLE:
150               g_scanner_set_scope (scanner, scope + 1);
151               token = gimp_session_info_dockable_deserialize (scanner,
152                                                               scope + 1,
153                                                               &dockable);
154 
155               if (token == G_TOKEN_LEFT_PAREN)
156                 {
157                   info->dockables = g_list_append (info->dockables, dockable);
158                   g_scanner_set_scope (scanner, scope);
159                 }
160               else
161                 goto error;
162 
163               break;
164 
165             default:
166               goto error;
167             }
168           token = G_TOKEN_RIGHT_PAREN;
169           break;
170 
171         case G_TOKEN_RIGHT_PAREN:
172           token = G_TOKEN_LEFT_PAREN;
173           break;
174 
175         default:
176           break;
177         }
178     }
179 
180   *book = info;
181 
182   g_scanner_scope_remove_symbol (scanner, scope, "position");
183   g_scanner_scope_remove_symbol (scanner, scope, "current-page");
184   g_scanner_scope_remove_symbol (scanner, scope, "dockable");
185 
186   return token;
187 
188  error:
189   *book = NULL;
190 
191   gimp_session_info_book_free (info);
192 
193   return token;
194 }
195 
196 GimpSessionInfoBook *
gimp_session_info_book_from_widget(GimpDockbook * dockbook)197 gimp_session_info_book_from_widget (GimpDockbook *dockbook)
198 {
199   GimpSessionInfoBook *info;
200   GtkWidget           *parent;
201   GList               *children;
202   GList               *list;
203 
204   g_return_val_if_fail (GIMP_IS_DOCKBOOK (dockbook), NULL);
205 
206   info = gimp_session_info_book_new ();
207 
208   parent = gtk_widget_get_parent (GTK_WIDGET (dockbook));
209 
210   if (GTK_IS_PANED (parent))
211     {
212       GtkPaned *paned = GTK_PANED (parent);
213 
214       if (GTK_WIDGET (dockbook) == gtk_paned_get_child2 (paned))
215         info->position = gtk_paned_get_position (paned);
216     }
217 
218   info->current_page =
219     gtk_notebook_get_current_page (GTK_NOTEBOOK (dockbook));
220 
221   children = gtk_container_get_children (GTK_CONTAINER (dockbook));
222 
223   for (list = children; list; list = g_list_next (list))
224     {
225       GimpSessionInfoDockable *dockable;
226 
227       dockable = gimp_session_info_dockable_from_widget (list->data);
228 
229       info->dockables = g_list_prepend (info->dockables, dockable);
230     }
231 
232   info->dockables = g_list_reverse (info->dockables);
233 
234   g_list_free (children);
235 
236   return info;
237 }
238 
239 GimpDockbook *
gimp_session_info_book_restore(GimpSessionInfoBook * info,GimpDock * dock)240 gimp_session_info_book_restore (GimpSessionInfoBook *info,
241                                 GimpDock            *dock)
242 {
243   GimpDialogFactory *dialog_factory;
244   GimpMenuFactory   *menu_factory;
245   GtkWidget         *dockbook;
246   GList             *pages;
247   gint               n_dockables = 0;
248 
249   g_return_val_if_fail (info != NULL, NULL);
250   g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL);
251 
252   dialog_factory = gimp_dock_get_dialog_factory (dock);
253   menu_factory   = gimp_dialog_factory_get_menu_factory (dialog_factory);
254 
255   dockbook = gimp_dockbook_new (menu_factory);
256 
257   gimp_dock_add_book (dock, GIMP_DOCKBOOK (dockbook), -1);
258 
259   for (pages = info->dockables; pages; pages = g_list_next (pages))
260     {
261       GimpSessionInfoDockable *dockable_info = pages->data;
262       GimpDockable            *dockable;
263 
264       dockable = gimp_session_info_dockable_restore (dockable_info, dock);
265 
266       if (dockable)
267         {
268           gimp_dockbook_add (GIMP_DOCKBOOK (dockbook), dockable, -1);
269           n_dockables++;
270         }
271     }
272 
273   if (info->current_page <
274       gtk_notebook_get_n_pages (GTK_NOTEBOOK (dockbook)))
275     {
276       gtk_notebook_set_current_page (GTK_NOTEBOOK (dockbook),
277                                      info->current_page);
278     }
279   else if (n_dockables > 1)
280     {
281       gtk_notebook_set_current_page (GTK_NOTEBOOK (dockbook), 0);
282     }
283 
284   /*  Return the dockbook even if no dockable could be restored
285    *  (n_dockables == 0) because otherwise we would have to remove it
286    *  from the dock right here, which could implicitly destroy the
287    *  dock and make catching restore errors much harder on higher
288    *  levels. Instead, we check for restored empty dockbooks in our
289    *  caller.
290    */
291   return GIMP_DOCKBOOK (dockbook);
292 }
293