Commit 34dc0b375065d839dea1e24f8e0f18cdfe34b48d
1 parent
8eb32755
Exists in
master
and in
4 other branches
Buttons on the header bar are now configurable.
Showing
6 changed files
with
220 additions
and
138 deletions
Show diff stats
src/include/pw3270/application.h
... | ... | @@ -72,6 +72,10 @@ |
72 | 72 | gboolean pw3270_settings_set_int(const gchar *key, gint value); |
73 | 73 | |
74 | 74 | |
75 | + // Tools | |
76 | + GtkBuilder * pw3270_application_get_builder(const gchar *name); | |
77 | + void gtk_container_remove_all(GtkContainer *container); | |
78 | + | |
75 | 79 | // Actions |
76 | 80 | void pw3270_application_print_copy_activated(GAction *action, GVariant *parameter, GtkWidget *terminal); |
77 | 81 | void pw3270_application_save_copy_activated(GAction *action, GVariant *parameter, GtkWidget *terminal); | ... | ... |
src/main/tools.c
... | ... | @@ -38,4 +38,28 @@ |
38 | 38 | |
39 | 39 | /*---[ Implement ]----------------------------------------------------------------------------------*/ |
40 | 40 | |
41 | + GtkBuilder * pw3270_application_get_builder(const gchar *name) { | |
42 | + | |
43 | +#ifdef DEBUG | |
44 | + g_autofree gchar * filename = g_build_filename("ui",name,NULL); | |
45 | +#else | |
46 | + lib3270_autoptr(char) filename = lib3270_build_data_filename("ui",name,NULL); | |
47 | +#endif // DEBUG | |
48 | + | |
49 | + return gtk_builder_new_from_file(filename); | |
50 | + } | |
51 | + | |
52 | + void gtk_container_remove_all(GtkContainer *container) { | |
53 | + | |
54 | + GList * children = gtk_container_get_children(container); | |
55 | + GList * item; | |
56 | + | |
57 | + for(item = children;item;item = g_list_next(item)) { | |
58 | + gtk_container_remove(container,GTK_WIDGET(item->data)); | |
59 | + } | |
60 | + | |
61 | + g_list_free(children); | |
62 | + | |
63 | + } | |
64 | + | |
41 | 65 | ... | ... |
src/objects/toolbar/toolbar.c
... | ... | @@ -91,7 +91,6 @@ |
91 | 91 | PROP_ACTION_NAMES, |
92 | 92 | }; |
93 | 93 | |
94 | - | |
95 | 94 | struct _pw3270ToolBar { |
96 | 95 | GtkToolbar parent; |
97 | 96 | |
... | ... | @@ -375,24 +374,12 @@ |
375 | 374 | |
376 | 375 | } |
377 | 376 | |
378 | - void pw3270_toolbar_clear(GtkWidget *toolbar) { | |
379 | - | |
380 | - GList * children = gtk_container_get_children(GTK_CONTAINER(toolbar)); | |
381 | - GList * item; | |
382 | - | |
383 | - for(item = children;item;item = g_list_next(item)) { | |
384 | - gtk_container_remove(GTK_CONTAINER(toolbar),GTK_WIDGET(item->data)); | |
385 | - } | |
386 | - | |
387 | - g_list_free(children); | |
388 | - } | |
389 | - | |
390 | 377 | void pw3270_toolbar_set_actions(GtkWidget *toolbar, const gchar *action_names) { |
391 | 378 | |
392 | 379 | gchar ** actions = g_strsplit(action_names,",",-1); |
393 | 380 | size_t ix; |
394 | 381 | |
395 | - pw3270_toolbar_clear(toolbar); | |
382 | + gtk_container_remove_all(GTK_CONTAINER(toolbar)); | |
396 | 383 | |
397 | 384 | for(ix = 0; actions[ix]; ix++) { |
398 | 385 | pw3270_toolbar_insert_action(toolbar,actions[ix],-1); |
... | ... | @@ -426,6 +413,5 @@ |
426 | 413 | |
427 | 414 | g_list_free(children); |
428 | 415 | |
429 | - | |
430 | 416 | return g_string_free(str,FALSE); |
431 | 417 | } | ... | ... |
src/objects/window/header.c
... | ... | @@ -33,6 +33,53 @@ |
33 | 33 | #include <pw3270/application.h> |
34 | 34 | #include <pw3270/actions.h> |
35 | 35 | |
36 | + void pw3270_window_set_header_action_names(GtkWidget *window, const gchar *action_names) { | |
37 | + | |
38 | + GtkWidget * header = gtk_window_get_titlebar(GTK_WINDOW(window)); | |
39 | + | |
40 | + if(!(header && GTK_IS_HEADER_BAR(header))) | |
41 | + return; | |
42 | + | |
43 | + gtk_container_remove_all(GTK_CONTAINER(header)); | |
44 | + | |
45 | + if(action_names && *action_names) { | |
46 | + | |
47 | + size_t ix; | |
48 | + gchar ** header_blocks = g_strsplit(action_names,":",-1); | |
49 | + | |
50 | + g_autoptr(GtkBuilder) builder = pw3270_application_get_builder("window.xml"); | |
51 | + | |
52 | + if(g_strv_length(header_blocks) >= 2) { | |
53 | + | |
54 | + gchar ** elements; | |
55 | + GtkWidget * button; | |
56 | + | |
57 | + // First the left side actions. | |
58 | + elements = g_strsplit(header_blocks[0],",",-1); | |
59 | + for(ix=0;elements[ix];ix++) { | |
60 | + button = pw3270_header_button_new_from_builder(GTK_WIDGET(window),builder,elements[ix]); | |
61 | + g_object_set_data(G_OBJECT(button),"header-position-id",GINT_TO_POINTER(0)); | |
62 | + gtk_header_bar_pack_start(GTK_HEADER_BAR(header), button); | |
63 | + } | |
64 | + g_strfreev(elements); | |
65 | + | |
66 | + // And then, the right side actions; | |
67 | + elements = g_strsplit(header_blocks[1],",",-1); | |
68 | + for(ix=0;elements[ix];ix++) { | |
69 | + button = pw3270_header_button_new_from_builder(GTK_WIDGET(window),builder,elements[ix]); | |
70 | + g_object_set_data(G_OBJECT(button),"header-position-id",GINT_TO_POINTER(1)); | |
71 | + gtk_header_bar_pack_end(GTK_HEADER_BAR(header), button); | |
72 | + } | |
73 | + g_strfreev(elements); | |
74 | + | |
75 | + } | |
76 | + | |
77 | + g_strfreev(header_blocks); | |
78 | + | |
79 | + } | |
80 | + | |
81 | + } | |
82 | + | |
36 | 83 | static void on_sensitive(GtkWidget *button, GParamSpec *spec, GtkWidget *widget) { |
37 | 84 | |
38 | 85 | gboolean sensitive; |
... | ... | @@ -73,8 +120,53 @@ |
73 | 120 | gtk_widget_set_focus_on_click(button,FALSE); |
74 | 121 | gtk_widget_set_can_focus(button,FALSE); |
75 | 122 | gtk_widget_set_can_default(button,FALSE); |
123 | + gtk_widget_set_name(button,action_name); | |
76 | 124 | |
77 | 125 | } |
78 | 126 | |
79 | 127 | return button; |
80 | 128 | } |
129 | + | |
130 | + gchar * pw3270_window_get_action_names(GtkWidget *window) { | |
131 | + | |
132 | + GtkWidget * header = gtk_window_get_titlebar(GTK_WINDOW(window)); | |
133 | + | |
134 | + if(!(header && GTK_IS_HEADER_BAR(header))) | |
135 | + return g_strdup("win.disconnect,win.reconnect,win.file.transfer,win.print:menu.open-menu"); | |
136 | + | |
137 | + GString * str = g_string_new(""); | |
138 | + | |
139 | + GList * children = gtk_container_get_children(GTK_CONTAINER(header)); | |
140 | + GList * item; | |
141 | + | |
142 | + int id; | |
143 | + for(id = 0; id < 2; id++) { | |
144 | + | |
145 | + gboolean sep = FALSE; | |
146 | + | |
147 | + for(item = children;item;item = g_list_next(item)) { | |
148 | + | |
149 | + if(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item->data),"header-position-id") != id)) | |
150 | + continue; | |
151 | + | |
152 | + if(sep) | |
153 | + g_string_append(str,","); | |
154 | + | |
155 | + if(GTK_IS_MENU_BUTTON(item->data)) { | |
156 | + g_string_append(str,gtk_widget_get_name(GTK_WIDGET(item->data))); | |
157 | + } else if(GTK_IS_ACTIONABLE(item->data)) { | |
158 | + g_string_append(str,gtk_actionable_get_action_name(GTK_ACTIONABLE(item->data))); | |
159 | + } | |
160 | + | |
161 | + sep = TRUE; | |
162 | + } | |
163 | + | |
164 | + if(!id) | |
165 | + g_string_append(str,":"); | |
166 | + | |
167 | + } | |
168 | + | |
169 | + g_list_free(children); | |
170 | + | |
171 | + return g_string_free(str,FALSE); | |
172 | + } | ... | ... |
src/objects/window/private.h
... | ... | @@ -77,14 +77,18 @@ |
77 | 77 | G_GNUC_INTERNAL GtkWidget * pw3270_setup_image_button(GtkWidget *button, const gchar *image_name); |
78 | 78 | |
79 | 79 | // Actions |
80 | - GAction * pw3270_action_host_properties_new(void); | |
81 | - GAction * pw3270_set_color_action_new(void); | |
82 | - GAction * pw3270_file_transfer_action_new(void); | |
80 | + G_GNUC_INTERNAL GAction * pw3270_action_host_properties_new(void); | |
81 | + G_GNUC_INTERNAL GAction * pw3270_set_color_action_new(void); | |
82 | + G_GNUC_INTERNAL GAction * pw3270_file_transfer_action_new(void); | |
83 | 83 | |
84 | 84 | GAction * pw3270_action_session_properties_new(void); |
85 | 85 | |
86 | + // Header bar | |
87 | + G_GNUC_INTERNAL void pw3270_window_set_header_action_names(GtkWidget *window, const gchar *action_names); | |
88 | + G_GNUC_INTERNAL gchar * pw3270_window_get_action_names(GtkWidget *window); | |
89 | + | |
86 | 90 | // Terminal actions. |
87 | - GAction * pw3270_model_number_action_new(GtkWidget *terminal); | |
91 | + G_GNUC_INTERNAL GAction * pw3270_model_number_action_new(GtkWidget *terminal); | |
88 | 92 | |
89 | 93 | G_GNUC_INTERNAL void pw3270_window_open_activated(GSimpleAction * action, GVariant *parameter, gpointer window); |
90 | 94 | G_GNUC_INTERNAL void pw3270_window_close_activated(GSimpleAction * action, GVariant *parameter, gpointer window); | ... | ... |
src/objects/window/window.c
... | ... | @@ -33,8 +33,16 @@ |
33 | 33 | #include <pw3270/application.h> |
34 | 34 | #include <pw3270/actions.h> |
35 | 35 | |
36 | + static void get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); | |
37 | + static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); | |
38 | + | |
36 | 39 | G_DEFINE_TYPE(pw3270ApplicationWindow, pw3270ApplicationWindow, GTK_TYPE_APPLICATION_WINDOW); |
37 | 40 | |
41 | + enum { | |
42 | + PROP_NONE, | |
43 | + PROP_ACTION_NAMES, | |
44 | + }; | |
45 | + | |
38 | 46 | static void destroy(GtkWidget *widget) { |
39 | 47 | |
40 | 48 | size_t ix; |
... | ... | @@ -70,9 +78,39 @@ |
70 | 78 | |
71 | 79 | static void pw3270ApplicationWindow_class_init(pw3270ApplicationWindowClass *klass) { |
72 | 80 | |
73 | - GtkWidgetClass * widget = GTK_WIDGET_CLASS(klass); | |
74 | - widget->destroy = destroy; | |
81 | + GTK_WIDGET_CLASS(klass)->destroy = destroy; | |
82 | + | |
83 | + GObjectClass *object_class = G_OBJECT_CLASS(klass); | |
84 | + | |
85 | + object_class->set_property = set_property; | |
86 | + object_class->get_property = get_property; | |
87 | + | |
88 | + g_object_class_install_property( | |
89 | + object_class, | |
90 | + PROP_ACTION_NAMES, | |
91 | + g_param_spec_string ("action-names", | |
92 | + N_("Action Names"), | |
93 | + N_("The name of the actions in the header bar"), | |
94 | + NULL, | |
95 | + G_PARAM_WRITABLE|G_PARAM_READABLE) | |
96 | + ); | |
97 | + | |
98 | + | |
99 | + } | |
100 | + | |
101 | + void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec G_GNUC_UNUSED(*pspec)) { | |
102 | + | |
103 | + if(prop_id == PROP_ACTION_NAMES) { | |
104 | + pw3270_window_set_header_action_names(GTK_WIDGET(object), g_value_get_string(value)); | |
105 | + } | |
106 | + | |
107 | + } | |
108 | + | |
109 | + void get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { | |
75 | 110 | |
111 | + if(prop_id == PROP_ACTION_NAMES) { | |
112 | + g_value_take_string(value,pw3270_window_get_action_names(GTK_WIDGET(object))); | |
113 | + } | |
76 | 114 | |
77 | 115 | } |
78 | 116 | |
... | ... | @@ -178,8 +216,11 @@ |
178 | 216 | |
179 | 217 | GtkWidget * pw3270_application_window_new(GtkApplication * application) { |
180 | 218 | |
219 | + gchar *title = _( "IBM 3270 Terminal emulator" ); | |
220 | + | |
221 | + g_return_val_if_fail(PW3270_IS_APPLICATION(application),NULL); | |
222 | + | |
181 | 223 | size_t ix; |
182 | - const gchar * title = _( "IBM 3270 Terminal emulator" ); | |
183 | 224 | |
184 | 225 | g_autoptr(GSettings) settings = pw3270_application_get_settings(G_APPLICATION(application)); |
185 | 226 | |
... | ... | @@ -190,138 +231,69 @@ |
190 | 231 | "application", application, |
191 | 232 | NULL); |
192 | 233 | |
193 | - if(PW3270_IS_APPLICATION(gtk_window_get_application(GTK_WINDOW(window)))) { | |
194 | - | |
195 | - GtkBuilder * builder; | |
196 | -#ifdef DEBUG | |
197 | - builder = gtk_builder_new_from_file("ui/window.xml"); | |
198 | -#else | |
199 | - { | |
200 | - lib3270_autoptr(char) build_file = lib3270_build_data_filename("ui","window.xml",NULL); | |
201 | - builder = gtk_builder_new_from_file(build_file); | |
202 | - } | |
203 | -#endif // DEBUG | |
204 | - | |
205 | - switch(pw3270_application_get_ui_style(G_APPLICATION(application))) { | |
206 | - case PW3270_UI_STYLE_CLASSICAL: | |
207 | - { | |
208 | - gtk_window_set_title(GTK_WINDOW(window), title); | |
209 | - | |
210 | - } | |
211 | - break; | |
212 | - | |
213 | - case PW3270_UI_STYLE_GNOME: | |
214 | - { | |
215 | - // Create header bar | |
216 | - GtkHeaderBar * header = GTK_HEADER_BAR(gtk_header_bar_new()); | |
217 | - gtk_window_set_titlebar(GTK_WINDOW(window), GTK_WIDGET(header)); | |
218 | - gtk_header_bar_set_show_close_button(header,TRUE); | |
219 | - | |
220 | - gtk_header_bar_set_title(header,title); | |
221 | - if(settings) | |
222 | - g_settings_bind(settings, "has-subtitle", header, "has-subtitle", G_SETTINGS_BIND_DEFAULT); | |
223 | - else | |
224 | - gtk_header_bar_set_has_subtitle(header,TRUE); | |
225 | - | |
226 | - // Show the new header | |
227 | - gtk_widget_show_all(GTK_WIDGET(header)); | |
228 | - | |
229 | - // Create header's action buttons | |
230 | - // https://wiki.gnome.org/Initiatives/GnomeGoals/GearIcons | |
231 | - { | |
232 | - g_autofree gchar * header_actions = g_settings_get_string(settings, "header-action-names"); | |
233 | - gchar ** header_blocks = g_strsplit(header_actions,":",-1); | |
234 | - | |
235 | - if(g_strv_length(header_blocks) >= 2) { | |
236 | - | |
237 | - gchar ** elements; | |
238 | - | |
239 | - // First the left side actions. | |
240 | - elements = g_strsplit(header_blocks[0],",",-1); | |
241 | - for(ix=0;elements[ix];ix++) { | |
242 | - gtk_header_bar_pack_start(header, pw3270_header_button_new_from_builder(GTK_WIDGET(window),builder,elements[ix])); | |
243 | - } | |
244 | - g_strfreev(elements); | |
245 | - | |
246 | - // And then, the right side actions; | |
247 | - elements = g_strsplit(header_blocks[1],",",-1); | |
248 | - for(ix=0;elements[ix];ix++) { | |
249 | - gtk_header_bar_pack_end(header, pw3270_header_button_new_from_builder(GTK_WIDGET(window),builder,elements[ix])); | |
250 | - } | |
251 | - g_strfreev(elements); | |
252 | - | |
253 | - } | |
254 | - | |
255 | - g_strfreev(header_blocks); | |
256 | - | |
257 | - } | |
234 | + // | |
235 | + // Get builder | |
236 | + // | |
237 | + g_autoptr(GtkBuilder) builder = pw3270_application_get_builder("window.xml"); | |
258 | 238 | |
259 | - /* | |
260 | - { | |
261 | - g_autofree gchar * left = g_settings_get_string(settings, "header-start-action-names"); | |
262 | - g_autofree gchar * right = g_settings_get_string(settings, "header-end-action-names"); | |
239 | + // Load popup menus. | |
240 | + const gchar * popup_menus[G_N_ELEMENTS(window->popups)] = { | |
241 | + "popup-over-selected-area", | |
242 | + "popup-over-unselected-area", | |
243 | + "popup-when-offline" | |
244 | + }; | |
263 | 245 | |
246 | + for(ix = 0; ix < G_N_ELEMENTS(popup_menus); ix++) { | |
264 | 247 | |
265 | - } | |
266 | - */ | |
248 | + GObject * model = gtk_builder_get_object(builder, popup_menus[ix]); | |
249 | + if(model) { | |
250 | + window->popups[ix] = gtk_menu_new_from_model(G_MENU_MODEL(model)); | |
251 | + gtk_menu_attach_to_widget(GTK_MENU(window->popups[ix]),GTK_WIDGET(window),NULL); | |
252 | + } | |
267 | 253 | |
268 | - /* | |
269 | - static const gchar * end_actions[] = { | |
270 | - "menu.open-menu", | |
271 | - }; | |
254 | + } | |
272 | 255 | |
273 | - for(ix = 0; ix < G_N_ELEMENTS(end_actions); ix++) { | |
274 | - gtk_header_bar_pack_end(header, pw3270_header_button_new_from_builder(GTK_WIDGET(window),builder,end_actions[ix])); | |
275 | - } | |
256 | + if(pw3270_application_get_ui_style(G_APPLICATION(application)) == PW3270_UI_STYLE_GNOME) { | |
276 | 257 | |
258 | + // Create header bar | |
277 | 259 | |
278 | - static const gchar * start_actions[] = { | |
279 | - "win.disconnect", | |
280 | - "win.reconnect", | |
281 | - "win.file.transfer", | |
282 | - "win.print" | |
283 | - }; | |
260 | + GtkHeaderBar * header = GTK_HEADER_BAR(gtk_header_bar_new()); | |
261 | + gtk_window_set_titlebar(GTK_WINDOW(window), GTK_WIDGET(header)); | |
262 | + gtk_header_bar_set_show_close_button(header,TRUE); | |
284 | 263 | |
285 | - for(ix = 0; ix < G_N_ELEMENTS(start_actions); ix++) { | |
286 | - gtk_header_bar_pack_start(header, pw3270_header_button_new_from_builder(GTK_WIDGET(window),builder,start_actions[ix])); | |
287 | - } | |
288 | - */ | |
264 | + gtk_header_bar_set_title(header,title); | |
265 | + g_settings_bind( | |
266 | + settings, | |
267 | + "has-subtitle", | |
268 | + header, | |
269 | + "has-subtitle", | |
270 | + G_SETTINGS_BIND_DEFAULT | |
271 | + ); | |
289 | 272 | |
273 | + // Show the new header | |
274 | + gtk_widget_show_all(GTK_WIDGET(header)); | |
290 | 275 | |
291 | - /* | |
292 | - // Create "new tab" bar | |
293 | - GtkWidget * new_tab_button = pw3270_setup_image_button(gtk_button_new(),"tab-new-symbolic"); | |
294 | - gtk_actionable_set_action_name(GTK_ACTIONABLE(new_tab_button),"app.new.tab"); | |
295 | - gtk_header_bar_pack_start(header, new_tab_button); | |
296 | - gtk_widget_show(new_tab_button); | |
297 | - */ | |
276 | + // g_autofree gchar * header_actions = g_settings_get_string(settings, "header-action-names"); | |
277 | + // pw3270_window_set_header_action_names(GTK_WIDGET(window), header_actions); | |
298 | 278 | |
299 | - } | |
300 | - break; | |
279 | + g_settings_bind( | |
280 | + settings, | |
281 | + "header-action-names", | |
282 | + window, | |
283 | + "action-names", | |
284 | + G_SETTINGS_BIND_DEFAULT | |
285 | + ); | |
301 | 286 | |
302 | - default: | |
303 | - g_warning("Unexpected UI"); | |
287 | +#ifdef DEBUG | |
288 | + { | |
289 | + g_autofree gchar * an = pw3270_window_get_action_names(window); | |
304 | 290 | |
305 | 291 | } |
292 | +#endif // DEBUG | |
306 | 293 | |
307 | - // Load popup menus. | |
308 | - const gchar * popup_menus[G_N_ELEMENTS(window->popups)] = { | |
309 | - "popup-over-selected-area", | |
310 | - "popup-over-unselected-area", | |
311 | - "popup-when-offline" | |
312 | - }; | |
313 | - | |
314 | - for(ix = 0; ix < G_N_ELEMENTS(popup_menus); ix++) { | |
315 | - | |
316 | - GObject * model = gtk_builder_get_object(builder, popup_menus[ix]); | |
317 | - if(model) { | |
318 | - window->popups[ix] = gtk_menu_new_from_model(G_MENU_MODEL(model)); | |
319 | - gtk_menu_attach_to_widget(GTK_MENU(window->popups[ix]),GTK_WIDGET(window),NULL); | |
320 | - } | |
321 | - | |
322 | - } | |
294 | + } else { | |
323 | 295 | |
324 | - g_object_unref(builder); | |
296 | + gtk_window_set_title(GTK_WINDOW(window), title); | |
325 | 297 | |
326 | 298 | } |
327 | 299 | ... | ... |