1/** 2 * Copyright (c) 2017-2020 Alecaddd (https://alecaddd.com) 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public 15 * License along with this program; if not, write to the 16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301 USA 18 * 19 * Authored by: Francisco Altoe 20 * Authored by: Alessandro "Alecaddd" Castellani <castellani.ale@gmail.com> 21 */ 22 23/** 24 * Dialog subclass used to prompt the user to provide the required query parameters 25 */ 26public class Sequeler.Widgets.QueryParamsDialog : Gtk.Dialog { 27 public weak Sequeler.Window window { get; construct; } 28 29 public Sequeler.Layouts.Views.Query parent_view { get; construct; } 30 public string query { get; construct; } 31 public Gda.Statement statement { get; construct; } 32 public Gda.Set params { get; construct; } 33 private Gee.ArrayList<Gtk.Widget> entries; 34 35 private Sequeler.Partials.ResponseMessage response_msg; 36 37 enum Action { 38 RUN_QUERY, 39 CANCEL 40 } 41 42 public QueryParamsDialog ( 43 Sequeler.Window? parent, 44 Sequeler.Layouts.Views.Query view, 45 string query, 46 Gda.Statement statement, 47 Gda.Set? @params 48 ) { 49 Object ( 50 border_width: 6, 51 deletable: false, 52 resizable: true, 53 title: _("Query parameters"), 54 transient_for: parent, 55 window: parent, 56 parent_view: view, 57 query: query, 58 statement: statement, 59 params: @params 60 ); 61 } 62 63 construct { 64 build_content (); 65 response.connect (on_response); 66 } 67 68 private void build_content () { 69 default_width = 500; 70 var content = get_content_area (); 71 72 var form_grid = new Gtk.Grid (); 73 form_grid.margin = 6; 74 form_grid.row_spacing = 12; 75 form_grid.column_spacing = 12; 76 77 entries = new Gee.ArrayList<Gtk.Widget> (); 78 79 for (int i = 0; ; i++) { 80 Gda.Holder? holder = params.get_nth_holder (i); 81 if (holder == null) { 82 break; 83 } 84 var holder_id = holder.get_id (); 85 86 var label = new Gtk.Label (holder_id + ":"); 87 form_grid.attach (label, 0, i, 1, 1); 88 var entry = entry_for_holder (holder); 89 form_grid.attach (entry, 1, i, 1, 1); 90 entries.add (entry); 91 } 92 93 var scrolled_window = new Gtk.ScrolledWindow (null, null); 94 scrolled_window.add (form_grid); 95 96 int main_window_width, main_window_height; 97 parent_view.window.get_size (out main_window_width, out main_window_height); 98 99 // Prevent the scrolled window from growing bigger than the main window itself. 100 scrolled_window.set_max_content_height (main_window_height / 2); 101 scrolled_window.set_max_content_width (main_window_width); 102 scrolled_window.set_propagate_natural_width (true); 103 scrolled_window.set_propagate_natural_height (true); 104 105 response_msg = new Sequeler.Partials.ResponseMessage (); 106 107 content.add (scrolled_window); 108 content.add (response_msg); 109 110 var cancel_button = new Gtk.Button.with_label (_("Cancel")); 111 add_action_widget (cancel_button, Action.CANCEL); 112 113 var run_button = new Sequeler.Partials.RunQueryButton (); 114 add_action_widget (run_button, Action.RUN_QUERY); 115 } 116 117 private Gtk.Widget entry_for_holder (Gda.Holder holder) { 118 Type holder_g_type = holder.get_g_type (); 119 switch (holder_g_type) { 120 case Type.BOOLEAN: 121 return new Gtk.Switch (); 122 case Type.INT: 123 case Type.UINT: 124 var widget = new Partials.ParamEntry (this, Gtk.InputPurpose.DIGITS); 125 return widget; 126 case Type.FLOAT: 127 case Type.DOUBLE: 128 var widget = new Partials.ParamEntry (this, Gtk.InputPurpose.NUMBER); 129 return widget; 130 default: 131 return new Partials.ParamEntry (this); 132 } 133 } 134 135 /** 136 * Takes the parse result and update the widgets style and the holder value. 137 */ 138 private bool store_parsed_value ( 139 bool parse_result, 140 Value parsed_value, 141 Gda.Holder holder, 142 Gtk.Entry entry 143 ) { 144 entry.get_style_context ().remove_class ("error"); 145 146 if (!parse_result) { 147 entry.get_style_context ().add_class ("error"); 148 return false; 149 } 150 151 try { 152 holder.set_value (parsed_value); 153 } catch (Error e) { 154 write_response (e.message); 155 entry.get_style_context ().add_class ("error"); 156 return false; 157 } 158 159 return true; 160 } 161 162 private bool set_value_for_holder (Gda.Holder holder, Gtk.Widget widget) { 163 Type holder_g_type = holder.get_g_type (); 164 if (holder_g_type == Type.BOOLEAN) { 165 Gtk.Switch switch = widget as Gtk.Switch; 166 if (switch == null) { 167 return false; 168 } 169 170 try { 171 holder.set_value (switch.get_active ()); 172 } catch (Error ex) { 173 return false; 174 } 175 176 return true; 177 } else { 178 Gtk.Entry entry = widget as Gtk.Entry; 179 string text = entry.get_text (); 180 181 bool parse_result = true; 182 Value parsed_value; 183 184 if (holder_g_type == Type.INT) { 185 // TODO: replace this with the following once we upgrade to a newer valac 186 // parse_result = int.try_parse (text, out parsed_value); 187 parsed_value = int.parse (text); 188 } else if (holder_g_type == Type.UINT) { 189 // TODO: replace this with the following once we upgrade to a newer valac 190 // parse_result = uint.try_parse (text, out parsed_value); 191 parsed_value = int.parse (text); 192 } else if (holder_g_type == Type.FLOAT) { 193 // TODO: replace this with the following once we upgrade to a newer valac 194 // parse_result = float.try_parse (text, out parsed_value); 195 parsed_value = double.parse (text); 196 } else if (holder_g_type == Type.DOUBLE) { 197 // TODO: replace this with the following once we upgrade to a newer valac 198 // parse_result = double.try_parse (text, out parsed_value); 199 parsed_value = double.parse (text); 200 } else { 201 parsed_value = text; 202 } 203 204 return store_parsed_value (parse_result, parsed_value, holder, entry); 205 } 206 } 207 208 private bool get_param_values () { 209 bool validation_result = true; 210 for (int i = 0; ; i++) { 211 Gda.Holder? holder = params.get_nth_holder (i); 212 if (holder == null) { 213 break; 214 } 215 validation_result &= set_value_for_holder (holder, entries[i]); 216 } 217 218 return validation_result; 219 } 220 221 private void on_response (Gtk.Dialog source, int response_id) { 222 switch (response_id) { 223 case Action.RUN_QUERY: 224 run_query (); 225 break; 226 case Action.CANCEL: 227 destroy (); 228 break; 229 } 230 } 231 232 public void run_query () { 233 if (!get_param_values ()) { 234 return; 235 } 236 237 parent_view.run_query_statement (query, statement, params); 238 destroy (); 239 } 240 241 private void write_response (string? response_text) { 242 response_msg.label = response_text; 243 } 244} 245