adg-demo: refactored UI handling
[adg-lua.git] / adg-demo.lua.in
blob51877899e20044b6d7c5f3dc542a5ef92c1fc8d6
1 --[[
3 This file is part of adg-lua.
4 Copyright (C) 2012-2013 Nicola Fontana <ntd at entidi.it>
6 adg-lua is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License, or (at your option) any later version.
11 adg-lua is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General
17 Public License along with adg-lua; if not, write to the Free
18 Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
24 local lgi = require 'lgi'
25 local GLib = lgi.require 'GLib'
26 local Gtk = lgi.require 'Gtk'
27 local Adg = lgi.require 'Adg'
28 local Piston = require 'piston'
31 -- Command line parsing
32 -----------------------------------------------------------------
34 if arg[1] == '--version' or arg[1] == '-V' then
35 print('adg-demo @VERSION@')
36 return 0
37 elseif arg[1] == '--help' or arg[1] == '-h' then
38 print([[
39 Usage:
40 ]] .. arg[0] .. [[ [OPTION...] - ADG demonstration program
42 -h, --help Show help options
43 -V, --version Display version information
44 -E, --show-extents Show the boundary boxes of every entity
45 ]])
46 return 0
47 elseif arg[1] == '--show-extents' or arg[1] == '-E' then
48 Adg.switch_extents(true)
49 elseif arg[1] then
50 error('Invalid argument (' .. arg[1] .. ')')
51 return 1
52 end
55 -- Customize error handler
56 -----------------------------------------------------------------
58 error = function (message)
59 local dialog = Gtk.MessageDialog {
60 message_type = Gtk.MessageType.ERROR,
61 buttons = Gtk.ButtonsType.CLOSE,
62 text = message,
63 title = 'Error from adg-demo',
65 dialog:run()
66 dialog:destroy()
67 end
70 -- Part definition
71 -----------------------------------------------------------------
73 local piston = Piston.new {
74 A = 55,
75 B = 20.6,
76 C = 2,
77 DHOLE = 2,
78 LHOLE = 3,
79 D1 = 9.3,
80 D2 = 6.5,
81 D3 = 13.8,
82 D4 = 6.5,
83 D5 = 4.5,
84 D6 = 7.2,
85 D7 = 2,
86 RD34 = 1,
87 RD56 = 0.2,
88 LD2 = 7,
89 LD3 = 3.5,
90 LD5 = 5,
91 LD6 = 1,
92 LD7 = 0.5,
93 GROOVE = false,
94 ZGROOVE = 16,
95 DGROOVE = 8.3,
96 LGROOVE = 1,
97 CHAMFER = 0.3,
99 -- Metadata
100 TITLE = 'SAMPLE DRAWING',
101 DRAWING = 'PISTON',
102 AUTHOR = 'adg-demo',
103 DATE = os.date('%d/%m/%Y'),
107 -- GtkBuilder initialization
108 -----------------------------------------------------------------
110 local ui
113 -- Path where the ADG library is installed: used for accessing
114 -- shared resources such as icons and GtkBuilder XML files.
115 local adg_data_dir = '@ADGDATADIR@'
117 -- This hack checks if ADGDATADIR has been expanded by
118 -- configure and provides a fallback otherwise. It allows to
119 -- directly run adg-demo.lua.in if ADG is installed in the
120 -- /usr prefix (as it happens to be on my system ;).
121 if adg_data_dir == '@ADGDATADIR' .. '@' then
122 adg_data_dir = '/usr/share/adg'
125 Adg.gtk_use_default_icons(adg_data_dir)
127 local builder = Gtk.Builder()
128 builder:add_from_file(adg_data_dir .. '/adg-demo.ui')
129 ui = builder.objects
132 local function opener(button, dialog)
133 dialog:set_transient_for(ui.wndMain)
135 button.on_clicked = function ()
136 dialog:run()
137 Adg.gtk_window_hide_here(dialog)
142 -- Canvas settings
143 -----------------------------------------------------------------
145 local canvas = piston.view.detailed
146 canvas:set_paper('iso_a4', Gtk.PageOrientation.LANDSCAPE)
147 canvas:autoscale()
149 local area = ui.mainCanvas
150 area:set_canvas(canvas)
151 area.on_button_press_event = function (area, event)
152 if event.button == 1 then
153 -- Restore the original zoom
154 area:reset()
155 elseif event.button == 3 then
156 canvas:autoscale()
157 area:queue_draw()
160 return false
164 -- Help dialog
165 -----------------------------------------------------------------
167 opener(ui.mainHelp, ui.wndHelp)
170 -- About dialog
171 -----------------------------------------------------------------
174 local dialog = ui.wndAbout
175 opener(ui.mainAbout, dialog)
177 -- The last icon is supposed to be the largest one:
178 -- check adg_gtk_use_default_icons() implementation.
179 local icon_list = Gtk.Window.get_default_icon_list()
180 local last_icon = icon_list[#icon_list]
181 dialog:set_logo(last_icon)
186 -- Edit dialog
187 -----------------------------------------------------------------
190 local dialog = ui.wndEdit
191 opener(ui.mainEdit, dialog)
192 dialog:set_position(Gtk.WindowPosition.MOUSE)
194 local function entry_info(widget)
195 if Gtk.ToggleButton:is_type_of(widget) then
196 -- Boolean value
197 return 'get_active', 'set_active', 'on_toggled'
198 elseif Gtk.Entry:is_type_of(widget) then
199 -- String value
200 return 'get_text', 'set_text', 'on_changed'
201 elseif Gtk.SpinButton:is_type_of(widget) then
202 -- Number value
203 return 'get_value', 'set_value', 'on_changed'
204 else
205 -- Unhandled type
206 return
210 local function lock_ui(status)
211 local sensitiveness = status == false
212 ui.btnApply:set_sensitive(sensitiveness)
213 ui.btnReset:set_sensitive(sensitiveness)
216 local function regenerate()
217 -- Regenerate all the models
218 for _, model in pairs(piston.model) do
219 model:reset()
220 model:regenerate(piston)
221 model:changed()
224 -- Update the title block data
225 local title_block = canvas.title_block
226 for field in pairs(Adg.TitleBlock._property) do
227 local value = piston.data[field:upper()]
228 if value then title_block[field] = value end
232 for field in pairs(piston.data) do
233 local widget = ui['edit' .. field]
234 if widget then
235 -- Synchronize GtkSpinButton to GtkAdjustment, that is
236 -- initialize the widget to its default value
237 if Gtk.SpinButton:is_type_of(widget) then
238 local adj = widget:get_adjustment()
239 adj:value_changed()
242 -- Unlock the UI on every widget change
243 local getter, _, notification = entry_info(widget)
244 widget[notification] = function ()
245 lock_ui(false)
247 piston.data[field] = widget[getter](widget)
251 regenerate()
253 ui.editGROOVE.on_toggled = function (self)
254 local toggled = self:get_active()
255 ui.editZGROOVE:set_sensitive(toggled)
256 ui.editZGROOVELabel:set_sensitive(toggled)
257 ui.editDGROOVE:set_sensitive(toggled)
258 ui.editDGROOVELabel:set_sensitive(toggled)
259 ui.editLGROOVE:set_sensitive(toggled)
260 ui.editLGROOVELabel:set_sensitive(toggled)
263 ui.btnApply.on_clicked = function ()
264 -- Refresh the piston data using the widgets
265 for field in pairs(piston.data) do
266 local widget = ui['edit' .. field]
267 local getter = entry_info(widget)
268 if getter then
269 piston.data[field] = widget[getter](widget)
273 regenerate()
274 area:queue_draw()
275 lock_ui()
278 ui.btnReset.on_clicked = function ()
279 -- Update the widgets using the piston data
280 for field, value in pairs(piston.data) do
281 local widget = ui['edit' .. field]
282 local _, setter = entry_info(widget)
283 if setter then
284 widget[setter](widget, value)
288 lock_ui()
294 -- Save as dialog
295 -----------------------------------------------------------------
298 local dialog = ui.wndSaveAs
299 opener(ui.mainSaveAs, dialog)
300 dialog:set_current_name('adg-demo')
302 dialog.on_response = function (dialog, response)
303 if response ~= Gtk.ResponseType.OK then return end
305 -- Retrieve the file suffix (format type)
306 local suffix = ''
307 for _, radio in pairs(ui.saveAsPng:get_group()) do
308 if radio:get_active() then
309 suffix = radio:get_tooltip_markup()
310 break
314 -- Forcibly append the proper suffix, if not yet present
315 local file = dialog:get_filename()
316 if not GLib.str_has_suffix(file, suffix) then
317 file = file .. suffix
319 _, err = canvas:export(file)
320 if err then error(err) end
326 -- Print dialog
327 -----------------------------------------------------------------
330 local settings
332 ui.mainPrint.on_clicked = function ()
333 local operation = Gtk.PrintOperation.new()
335 operation:set_use_full_page(operation, false)
336 operation:set_unit(Gtk.Unit.POINTS)
338 if settings then
339 operation:set_print_settings(settings)
342 if Gtk.PrintOperation.set_embed_page_setup then
343 operation:set_embed_page_setup(true)
346 local page_setup = canvas:get_page_setup()
347 if page_setup then
348 operation:set_default_page_setup(page_setup)
351 operation.on_begin_print = function ()
352 operation:set_n_pages(1)
355 operation.on_draw_page = function (operation, context)
356 local cr = context:get_cairo_context()
358 local old_map = canvas:get_global_map()
359 canvas:set_global_map(Adg.matrix_identity())
360 canvas:render(cr)
361 canvas:set_global_map(old_map)
364 local result, err = operation:run(Gtk.PrintOperationAction.PRINT_DIALOG, ui.wndMain)
365 if result == Gtk.PrintOperationResult.APPLY then
366 settings = operation:get_print_settings()
367 elseif err then
368 error(err)
375 -- Quit command
376 -----------------------------------------------------------------
378 ui.mainQuit.on_clicked = Gtk.main_quit
381 -- Main
382 -----------------------------------------------------------------
384 local window = ui.wndMain
385 window.on_delete_event = Gtk.main_quit
386 window:show_all()
388 Gtk.main()