1 /*
2 # PostgreSQL Database Modeler (pgModeler)
3 #
4 # Copyright 2006-2020 - Raphael Araújo e Silva <raphael@pgmodeler.io>
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation version 3.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # The complete text of GPLv3 is at LICENSE file on source code root directory.
16 # Also, you can get the complete GNU General Public License at <http://www.gnu.org/licenses/>
17 */
18 
19 #include "modelexportform.h"
20 #include "taskprogresswidget.h"
21 #include "configurationform.h"
22 #include "pgmodeleruins.h"
23 
24 bool ModelExportForm::low_verbosity = false;
25 
ModelExportForm(QWidget * parent,Qt::WindowFlags f)26 ModelExportForm::ModelExportForm(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f)
27 {
28 	model=nullptr;
29 	viewp=nullptr;
30 	setupUi(this);
31 
32 	sql_file_sel = new FileSelectorWidget(this);
33 	sql_file_sel->setFileDialogTitle(tr("Export model to SQL file"));
34 	sql_file_sel->setMimeTypeFilters({"application/sql", "application/octet-stream"});
35 	sql_file_sel->setDefaultSuffix("sql");
36 	sql_file_sel->setAcceptMode(QFileDialog::AcceptSave);
37 	export_to_file_grid->addWidget(sql_file_sel, 0, 2);
38 
39 	img_file_sel = new FileSelectorWidget(this);
40 	img_file_sel->setFileDialogTitle(tr("Export model to graphics file"));
41 	img_file_sel->setAcceptMode(QFileDialog::AcceptSave);
42 	export_to_img_grid->addWidget(img_file_sel, 1, 2, 1, 2);
43 
44 	dict_file_sel = new FileSelectorWidget(this);
45 	dict_file_sel->setFileDialogTitle(tr("Export model to data dictionary"));
46 	dict_file_sel->setMimeTypeFilters({"text/html", "application/octet-stream"});
47 	dict_file_sel->setDefaultSuffix("html");
48 	dict_file_sel->setAcceptMode(QFileDialog::AcceptSave);
49 	export_to_dict_grid->addWidget(dict_file_sel, 1, 2, 1, 5);
50 
51 	htmlitem_del=new HtmlItemDelegate(this);
52 	output_trw->setItemDelegateForColumn(0, htmlitem_del);
53 
54 	export_thread=new QThread(this);
55 	export_hlp.moveToThread(export_thread);
56 
57 	connect(sql_file_sel, SIGNAL(s_fileSelected(QString)), this, SLOT(enableExport()));
58 	connect(sql_file_sel, SIGNAL(s_selectorCleared()), this, SLOT(enableExport()));
59 	connect(img_file_sel, SIGNAL(s_fileSelected(QString)), this, SLOT(enableExport()));
60 	connect(img_file_sel, SIGNAL(s_selectorCleared()), this, SLOT(enableExport()));
61 	connect(dict_file_sel, SIGNAL(s_fileSelected(QString)), this, SLOT(enableExport()));
62 	connect(dict_file_sel, SIGNAL(s_selectorCleared()), this, SLOT(enableExport()));
63 	connect(export_to_file_rb, SIGNAL(clicked()), this, SLOT(selectExportMode()));
64 	connect(export_to_dbms_rb, SIGNAL(clicked()), this, SLOT(selectExportMode()));
65 	connect(export_to_img_rb, SIGNAL(clicked()), this, SLOT(selectExportMode()));
66 	connect(export_to_dict_rb, SIGNAL(clicked()), this, SLOT(selectExportMode()));
67 	connect(pgsqlvers_chk, SIGNAL(toggled(bool)), pgsqlvers1_cmb, SLOT(setEnabled(bool)));
68 	connect(close_btn, SIGNAL(clicked(bool)), this, SLOT(close()));
69 	connect(export_btn, SIGNAL(clicked()), this, SLOT(exportModel()));
70 	connect(drop_chk, SIGNAL(toggled(bool)), drop_db_rb, SLOT(setEnabled(bool)));
71 	connect(drop_chk, SIGNAL(toggled(bool)), drop_objs_rb, SLOT(setEnabled(bool)));
72 
73 	connect(export_thread, &QThread::started,
74 	[&](){
75 
76 		output_trw->setUniformRowHeights(true);
77 
78 		if(export_to_dbms_rb->isChecked())
79 			export_hlp.exportToDBMS();
80 		else if(export_to_img_rb->isChecked())
81 		{
82 			if(png_rb->isChecked())
83 				export_hlp.exportToPNG();
84 			else
85 				export_hlp.exportToSVG();
86 		}
87 		else if(export_to_dict_rb->isChecked())
88 			export_hlp.exportToDataDict();
89 		else
90 			export_hlp.exportToSQL();
91 	});
92 
93 	connect(export_thread, &QThread::finished, [&](){
94 		output_trw->setUniformRowHeights(false);
95 	});
96 
97 	connect(&export_hlp, SIGNAL(s_progressUpdated(int,QString,ObjectType,QString,bool)), this, SLOT(updateProgress(int,QString,ObjectType,QString,bool)), Qt::BlockingQueuedConnection);
98 	connect(&export_hlp, SIGNAL(s_exportFinished()), this, SLOT(handleExportFinished()));
99 	connect(&export_hlp, SIGNAL(s_exportCanceled()), this, SLOT(handleExportCanceled()));
100 	connect(&export_hlp, SIGNAL(s_errorIgnored(QString,QString,QString)), this, SLOT(handleErrorIgnored(QString,QString,QString)));
101 	connect(&export_hlp, SIGNAL(s_exportAborted(Exception)), this, SLOT(captureThreadError(Exception)));
102 	connect(cancel_btn, SIGNAL(clicked(bool)), this, SLOT(cancelExport()));
103 	connect(connections_cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(editConnections()));
104 	connect(svg_rb, SIGNAL(toggled(bool)), zoom_cmb, SLOT(setDisabled(bool)));
105 	connect(svg_rb, SIGNAL(toggled(bool)), zoom_lbl, SLOT(setDisabled(bool)));
106 	connect(svg_rb, SIGNAL(toggled(bool)), page_by_page_chk, SLOT(setDisabled(bool)));
107 	connect(svg_rb, SIGNAL(toggled(bool)), this, SLOT(selectImageFormat()));
108 	connect(png_rb, SIGNAL(toggled(bool)), this, SLOT(selectImageFormat()));
109 	connect(ignore_error_codes_chk, SIGNAL(toggled(bool)), error_codes_edt, SLOT(setEnabled(bool)));
110 	connect(standalone_rb, SIGNAL(toggled(bool)), this, SLOT(selectDataDictType()));
111 	connect(splitted_rb, SIGNAL(toggled(bool)), this, SLOT(selectDataDictType()));
112 
113 	pgsqlvers_cmb->addItems(PgSqlVersions::AllVersions);
114 	pgsqlvers1_cmb->addItems(PgSqlVersions::AllVersions);
115 
116 	double values[]={ ModelWidget::MinimumZoom, 0.10, 0.25, 0.5, 0.75, 1, 1.25, 1.50, 1.75, 2,
117 										2.25, 2.50, 2.75, 3, 3.25, 3.50, 3.75, ModelWidget::MaximumZoom };
118 	unsigned cnt=sizeof(values)/sizeof(double);
119 
120 	for(unsigned i=0; i < cnt; i++)
121 		zoom_cmb->addItem(QString("%1%").arg(values[i] * 100), QVariant(values[i]));
122 
123 	zoom_cmb->setCurrentText(QString("100%"));
124 	settings_tbw->setTabEnabled(1, false);
125 
126 	selectImageFormat();
127 	selectDataDictType();
128 }
129 
setLowVerbosity(bool value)130 void ModelExportForm::setLowVerbosity(bool value)
131 {
132 	low_verbosity = value;
133 }
134 
exec(ModelWidget * model)135 void ModelExportForm::exec(ModelWidget *model)
136 {
137 	if(model)
138 	{
139 		this->model=model;
140 		ConnectionsConfigWidget::fillConnectionsComboBox(connections_cmb, true, Connection::OpExport);
141 		selectExportMode();
142 		QDialog::exec();
143 	}
144 }
145 
handleErrorIgnored(QString err_code,QString err_msg,QString cmd)146 void ModelExportForm::handleErrorIgnored(QString err_code, QString err_msg, QString cmd)
147 {
148 	QTreeWidgetItem *item=nullptr;
149 
150 	item=PgModelerUiNs::createOutputTreeItem(output_trw, tr("Error code <strong>%1</strong> found and ignored. Proceeding with export.").arg(err_code),
151 																					 QPixmap(PgModelerUiNs::getIconPath("msgbox_alerta")), nullptr, false);
152 
153 	PgModelerUiNs::createOutputTreeItem(output_trw, PgModelerUiNs::formatMessage(err_msg),
154 																			QPixmap(PgModelerUiNs::getIconPath("msgbox_alerta")),	item, false);
155 
156 	PgModelerUiNs::createOutputTreeItem(output_trw, cmd, QPixmap(), item, false);
157 }
158 
updateProgress(int progress,QString msg,ObjectType obj_type,QString cmd,bool is_code_gen)159 void ModelExportForm::updateProgress(int progress, QString msg, ObjectType obj_type, QString cmd, bool is_code_gen)
160 {
161 	QTreeWidgetItem *item=nullptr;
162 	QString text=PgModelerUiNs::formatMessage(msg);
163 	QPixmap ico;
164 
165 	progress_lbl->setText(text);
166 	progress_pb->setValue(progress);
167 
168 	if(obj_type!=ObjectType::BaseObject)
169 		ico=QPixmap(PgModelerUiNs::getIconPath(obj_type));
170 	else if(!cmd.isEmpty())
171 		ico=QPixmap(PgModelerUiNs::getIconPath("codigosql"));
172 	else
173 		ico=QPixmap(PgModelerUiNs::getIconPath("msgbox_info"));
174 
175 	ico_lbl->setPixmap(ico);
176 
177 	// If low_verbosity is set only messages hinted by obj_type == BaseObject are show because they hold key info messages
178 	if(!is_code_gen && (!low_verbosity || (low_verbosity && obj_type == ObjectType::BaseObject && cmd.isEmpty())))
179 	{
180 		item=PgModelerUiNs::createOutputTreeItem(output_trw, text, ico, nullptr, false);
181 
182 		if(!cmd.isEmpty())
183 			PgModelerUiNs::createOutputTreeItem(output_trw, cmd, QPixmap(), item, false);
184 	}
185 }
186 
exportModel()187 void ModelExportForm::exportModel()
188 {
189 	try
190 	{
191 		output_trw->clear();
192 		settings_tbw->setTabEnabled(1, true);
193 		settings_tbw->setCurrentIndex(1);
194 		enableExportModes(false);
195 		cancel_btn->setEnabled(true);
196 
197 		//Export to png
198 		if(export_to_img_rb->isChecked())
199 		{
200 			viewp=new QGraphicsView(model->scene);
201 
202 			if(png_rb->isChecked())
203 				export_hlp.setExportToPNGParams(model->scene, viewp, img_file_sel->getSelectedFile(),
204 																				zoom_cmb->itemData(zoom_cmb->currentIndex()).toDouble(),
205 																				show_grid_chk->isChecked(), show_delim_chk->isChecked(),
206 																				page_by_page_chk->isChecked());
207 			else
208 				export_hlp.setExportToSVGParams(model->scene, img_file_sel->getSelectedFile(),
209 																				show_grid_chk->isChecked(),
210 																				show_delim_chk->isChecked());
211 
212 			export_thread->start();
213 		}
214 		else
215 		{
216 			progress_lbl->setText(tr("Initializing model export..."));
217 
218 			if(low_verbosity)
219 				PgModelerUiNs::createOutputTreeItem(output_trw, tr("<strong>Low verbosity is set:</strong> only key informations and errors will be displayed."),
220 																						QPixmap(PgModelerUiNs::getIconPath("msgbox_alerta")), nullptr, false);
221 
222 			//Exporting to sql file
223 			if(export_to_file_rb->isChecked())
224 			{
225 				progress_lbl->setText(tr("Saving file '%1'").arg(sql_file_sel->getSelectedFile()));
226 				export_hlp.setExportToSQLParams(model->db_model, sql_file_sel->getSelectedFile(), pgsqlvers_cmb->currentText());
227 				export_thread->start();
228 			}
229 			else if(export_to_dict_rb->isChecked())
230 			{
231 				export_hlp.setExportToDataDictParams(model->db_model, dict_file_sel->getSelectedFile(), incl_index_chk->isChecked(), splitted_rb->isChecked());
232 				export_thread->start();
233 			}
234 			//Exporting directly to DBMS
235 			else
236 			{
237 				QString version;
238 				Connection *conn=reinterpret_cast<Connection *>(connections_cmb->itemData(connections_cmb->currentIndex()).value<void *>());
239 
240 				//If the user chose a specific version
241 				if(pgsqlvers1_cmb->isEnabled())
242 					version=pgsqlvers1_cmb->currentText();
243 
244 				export_hlp.setExportToDBMSParams(model->db_model, conn, version, ignore_dup_chk->isChecked(),
245 												 drop_chk->isChecked() && drop_db_rb->isChecked(),
246 												 drop_chk->isChecked() && drop_objs_rb->isChecked());
247 
248 				if(ignore_error_codes_chk->isChecked())
249 					export_hlp.setIgnoredErrors(error_codes_edt->text().simplified().split(' '));
250 
251 				export_thread->start();
252 			}
253 		}
254 	}
255 	catch(Exception &e)
256 	{
257 		Messagebox msg_box;
258 
259 		finishExport(tr("Exporting process aborted!"));
260 		msg_box.show(e);
261 	}
262 }
263 
selectExportMode()264 void ModelExportForm::selectExportMode()
265 {
266 	QList<QRadioButton *> radios={ export_to_dbms_rb, export_to_img_rb, export_to_file_rb, export_to_dict_rb};
267 	QWidgetList wgts={ export_to_dbms_wgt, export_to_img_wgt, export_to_file_wgt, export_to_dict_wgt };
268 	int i=0;
269 
270 	for(QRadioButton *rb : radios)
271 	{
272 		rb->blockSignals(true);
273 		rb->setChecked((!sender() && rb==export_to_dbms_rb) || (sender()==rb));
274 		wgts[i++]->setEnabled(rb->isChecked());
275 		rb->blockSignals(false);
276 	}
277 
278 	pgsqlvers1_cmb->setEnabled(export_to_dbms_rb->isChecked() && pgsqlvers_chk->isChecked());
279 	enableExport();
280 }
281 
captureThreadError(Exception e)282 void ModelExportForm::captureThreadError(Exception e)
283 {
284 	QTreeWidgetItem *item=PgModelerUiNs::createOutputTreeItem(output_trw, PgModelerUiNs::formatMessage(e.getErrorMessage()),
285 																														QPixmap(PgModelerUiNs::getIconPath("msgbox_erro")), nullptr, false, true);
286 
287 	PgModelerUiNs::createExceptionsTree(output_trw, e, item);
288 
289 	ico_lbl->setPixmap(QPixmap(PgModelerUiNs::getIconPath("msgbox_erro")));
290 	finishExport(tr("Exporting process aborted!"));
291 
292 	throw Exception(e.getErrorMessage(), e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
293 }
294 
cancelExport()295 void ModelExportForm::cancelExport()
296 {
297 	export_hlp.cancelExport();
298 	cancel_btn->setEnabled(false);
299 }
300 
handleExportCanceled()301 void ModelExportForm::handleExportCanceled()
302 {
303 	QPixmap ico=QPixmap(PgModelerUiNs::getIconPath("msgbox_alerta"));
304 	QString msg=tr("Exporting process canceled by user!");
305 
306 	finishExport(msg);
307 	ico_lbl->setPixmap(ico);
308 	PgModelerUiNs::createOutputTreeItem(output_trw, msg, ico);
309 }
310 
handleExportFinished()311 void ModelExportForm::handleExportFinished()
312 {
313 	QPixmap ico=QPixmap(PgModelerUiNs::getIconPath("msgbox_info"));
314 	QString msg=tr("Exporting process sucessfully ended!");
315 
316 	finishExport(msg);
317 	ico_lbl->setPixmap(ico);
318 	PgModelerUiNs::createOutputTreeItem(output_trw, msg, ico);
319 }
320 
finishExport(const QString & msg)321 void ModelExportForm::finishExport(const QString &msg)
322 {
323 	if(export_thread->isRunning())
324 		export_thread->quit();
325 
326 	enableExportModes(true);
327 
328 	cancel_btn->setEnabled(false);
329 	progress_pb->setValue(100);
330 	progress_lbl->setText(msg);
331 	progress_lbl->repaint();
332 
333 	if(viewp)
334 	{
335 		export_thread->wait();
336 		delete viewp;
337 		viewp=nullptr;
338 	}
339 }
340 
enableExportModes(bool value)341 void ModelExportForm::enableExportModes(bool value)
342 {
343 	export_to_dbms_rb->setEnabled(value);
344 	export_to_file_rb->setEnabled(value);
345 	export_to_img_rb->setEnabled(value);
346 	export_to_dict_rb->setEnabled(value);
347 	export_btn->setEnabled(value);
348 	close_btn->setEnabled(value);
349 }
350 
closeEvent(QCloseEvent * event)351 void ModelExportForm::closeEvent(QCloseEvent *event)
352 {
353 	/* Ignore the close event when the thread is running this avoid
354   close the form and make thread execute in background */
355 	if(export_thread->isRunning())
356 		event->ignore();
357 }
358 
editConnections()359 void ModelExportForm::editConnections()
360 {
361 	try
362 	{
363 		if(connections_cmb->currentIndex()==connections_cmb->count()-1)
364 		{
365 			ConnectionsConfigWidget::openConnectionsConfiguration(connections_cmb, true);
366 			emit s_connectionsUpdateRequest();
367 		}
368 	}
369 	catch(Exception &e)
370 	{
371 		Messagebox msg_box;
372 		msg_box.show(e);
373 	}
374 
375 	enableExport();
376 }
377 
enableExport()378 void ModelExportForm::enableExport()
379 {
380 	export_btn->setEnabled((export_to_dbms_rb->isChecked() && connections_cmb->currentIndex() > 0 && connections_cmb->currentIndex() != connections_cmb->count()-1) ||
381 												 (export_to_file_rb->isChecked() && !sql_file_sel->getSelectedFile().isEmpty()) ||
382 												 (export_to_img_rb->isChecked() && !img_file_sel->getSelectedFile().isEmpty()) ||
383 												 (export_to_dict_rb->isChecked() && !dict_file_sel->getSelectedFile().isEmpty()));
384 }
385 
selectImageFormat()386 void ModelExportForm::selectImageFormat()
387 {
388 	if(png_rb->isChecked())
389 	{
390 		img_file_sel->setMimeTypeFilters({"image/png", "application/octet-stream"});
391 		img_file_sel->setDefaultSuffix("png");
392 	}
393 	else
394 	{
395 		img_file_sel->setMimeTypeFilters({"image/svg+xml", "application/octet-stream"});
396 		img_file_sel->setDefaultSuffix("svg");
397 	}
398 }
399 
selectDataDictType()400 void ModelExportForm::selectDataDictType()
401 {
402 	if(standalone_rb->isChecked())
403 		dict_file_sel->setFileMode(QFileDialog::AnyFile);
404 	else
405 		dict_file_sel->setFileMode(QFileDialog::Directory);
406 }
407