1 /***************************************************************************
2 qgstableeditordialog.cpp
3 ------------------------
4 begin : January 2020
5 copyright : (C) 2020 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgstableeditordialog.h"
17 #include "qgstableeditorwidget.h"
18 #include "qgsmessagebar.h"
19 #include "qgsgui.h"
20 #include "qgsdockwidget.h"
21 #include "qgspanelwidgetstack.h"
22 #include "qgstableeditorformattingwidget.h"
23 #include "qgssettings.h"
24
25 #include <QClipboard>
26 #include <QMessageBox>
27
QgsTableEditorDialog(QWidget * parent)28 QgsTableEditorDialog::QgsTableEditorDialog( QWidget *parent )
29 : QMainWindow( parent )
30 {
31 setupUi( this );
32 setWindowTitle( tr( "Table Designer" ) );
33
34 setAttribute( Qt::WA_DeleteOnClose );
35 setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
36
37 QgsGui::enableAutoGeometryRestore( this );
38
39 QGridLayout *viewLayout = new QGridLayout();
40 viewLayout->setSpacing( 0 );
41 viewLayout->setContentsMargins( 0, 0, 0, 0 );
42 centralWidget()->layout()->setSpacing( 0 );
43 centralWidget()->layout()->setContentsMargins( 0, 0, 0, 0 );
44
45 mMessageBar = new QgsMessageBar( centralWidget() );
46 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
47 static_cast< QGridLayout * >( centralWidget()->layout() )->addWidget( mMessageBar, 0, 0, 1, 1, Qt::AlignTop );
48
49 mTableWidget = new QgsTableEditorWidget();
50 mTableWidget->setContentsMargins( 0, 0, 0, 0 );
51 viewLayout->addWidget( mTableWidget, 0, 0 );
52 mViewFrame->setLayout( viewLayout );
53 mViewFrame->setContentsMargins( 0, 0, 0, 0 );
54
55 mTableWidget->setFocus();
56 mTableWidget->setTableContents( QgsTableContents() << ( QgsTableRow() << QgsTableCell() ) );
57
58 connect( mTableWidget, &QgsTableEditorWidget::tableChanged, this, [ = ]
59 {
60 if ( !mBlockSignals )
61 emit tableChanged();
62 } );
63
64 const int minDockWidth( fontMetrics().boundingRect( QStringLiteral( "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) ).width() );
65
66 mPropertiesDock = new QgsDockWidget( tr( "Cell Contents" ), this );
67 mPropertiesDock->setObjectName( QStringLiteral( "FormattingDock" ) );
68 mPropertiesStack = new QgsPanelWidgetStack();
69 mPropertiesDock->setWidget( mPropertiesStack );
70 mPropertiesDock->setMinimumWidth( minDockWidth );
71
72 mFormattingWidget = new QgsTableEditorFormattingWidget();
73 mFormattingWidget->setDockMode( true );
74 mPropertiesStack->setMainPanel( mFormattingWidget );
75
76 mPropertiesDock->setFeatures( QDockWidget::NoDockWidgetFeatures );
77
78 connect( mFormattingWidget, &QgsTableEditorFormattingWidget::backgroundColorChanged, mTableWidget, &QgsTableEditorWidget::setSelectionBackgroundColor );
79
80 connect( mFormattingWidget, &QgsTableEditorFormattingWidget::horizontalAlignmentChanged, mTableWidget, &QgsTableEditorWidget::setSelectionHorizontalAlignment );
81 connect( mFormattingWidget, &QgsTableEditorFormattingWidget::verticalAlignmentChanged, mTableWidget, &QgsTableEditorWidget::setSelectionVerticalAlignment );
82 connect( mFormattingWidget, &QgsTableEditorFormattingWidget::cellPropertyChanged, mTableWidget, &QgsTableEditorWidget::setSelectionCellProperty );
83
84 connect( mFormattingWidget, &QgsTableEditorFormattingWidget::textFormatChanged, this, [ = ]
85 {
86 mTableWidget->setSelectionTextFormat( mFormattingWidget->textFormat() );
87 } );
88
89 connect( mFormattingWidget, &QgsTableEditorFormattingWidget::numberFormatChanged, this, [ = ]
90 {
91 mTableWidget->setSelectionNumericFormat( mFormattingWidget->numericFormat() );
92 } );
93 connect( mFormattingWidget, &QgsTableEditorFormattingWidget::rowHeightChanged, mTableWidget, &QgsTableEditorWidget::setSelectionRowHeight );
94 connect( mFormattingWidget, &QgsTableEditorFormattingWidget::columnWidthChanged, mTableWidget, &QgsTableEditorWidget::setSelectionColumnWidth );
95
96 connect( mTableWidget, &QgsTableEditorWidget::activeCellChanged, this, [ = ]
97 {
98 mFormattingWidget->setBackgroundColor( mTableWidget->selectionBackgroundColor() );
99 mFormattingWidget->setNumericFormat( mTableWidget->selectionNumericFormat(), mTableWidget->hasMixedSelectionNumericFormat() );
100 mFormattingWidget->setRowHeight( mTableWidget->selectionRowHeight() );
101 mFormattingWidget->setColumnWidth( mTableWidget->selectionColumnWidth() );
102 mFormattingWidget->setTextFormat( mTableWidget->selectionTextFormat() );
103 mFormattingWidget->setHorizontalAlignment( mTableWidget->selectionHorizontalAlignment() );
104 mFormattingWidget->setVerticalAlignment( mTableWidget->selectionVerticalAlignment() );
105 mFormattingWidget->setCellProperty( mTableWidget->selectionCellProperty() );
106
107 updateActionNamesFromSelection();
108
109 mFormattingWidget->setEnabled( !mTableWidget->isHeaderCellSelected() );
110 } );
111 updateActionNamesFromSelection();
112
113 addDockWidget( Qt::RightDockWidgetArea, mPropertiesDock );
114
115 mActionImportFromClipboard->setEnabled( !QApplication::clipboard()->text().isEmpty() );
116 connect( QApplication::clipboard(), &QClipboard::dataChanged, this, [ = ]() { mActionImportFromClipboard->setEnabled( !QApplication::clipboard()->text().isEmpty() ); } );
117
118 connect( mActionImportFromClipboard, &QAction::triggered, this, &QgsTableEditorDialog::setTableContentsFromClipboard );
119 connect( mActionClose, &QAction::triggered, this, &QMainWindow::close );
120 connect( mActionInsertRowsAbove, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::insertRowsAbove );
121 connect( mActionInsertRowsBelow, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::insertRowsBelow );
122 connect( mActionInsertColumnsBefore, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::insertColumnsBefore );
123 connect( mActionInsertColumnsAfter, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::insertColumnsAfter );
124 connect( mActionDeleteRows, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::deleteRows );
125 connect( mActionDeleteColumns, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::deleteColumns );
126 connect( mActionSelectRow, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::expandRowSelection );
127 connect( mActionSelectColumn, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::expandColumnSelection );
128 connect( mActionSelectAll, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::selectAll );
129 connect( mActionClear, &QAction::triggered, mTableWidget, &QgsTableEditorWidget::clearSelectedCells );
130 connect( mActionIncludeHeader, &QAction::toggled, this, [ = ]( bool checked )
131 {
132 mTableWidget->setIncludeTableHeader( checked );
133 emit includeHeaderChanged( checked );
134 } );
135
136 // restore the toolbar and dock widgets positions using Qt settings API
137 const QgsSettings settings;
138
139 const QByteArray state = settings.value( QStringLiteral( "LayoutDesigner/tableEditorState" ), QByteArray(), QgsSettings::App ).toByteArray();
140 if ( !state.isEmpty() && !restoreState( state ) )
141 {
142 QgsDebugMsg( QStringLiteral( "restore of table editor dialog UI state failed" ) );
143 }
144 }
145
closeEvent(QCloseEvent *)146 void QgsTableEditorDialog::closeEvent( QCloseEvent * )
147 {
148 QgsSettings settings;
149 // store the toolbar/dock widget settings using Qt settings API
150 settings.setValue( QStringLiteral( "LayoutDesigner/tableEditorState" ), saveState(), QgsSettings::App );
151 }
152
setTableContentsFromClipboard()153 bool QgsTableEditorDialog::setTableContentsFromClipboard()
154 {
155 if ( QApplication::clipboard()->text().isEmpty() )
156 return false;
157
158 if ( QMessageBox::question( this, tr( "Import Content From Clipboard" ),
159 tr( "Importing content from clipboard will overwrite current table content. Are you sure?" ) ) != QMessageBox::Yes )
160 return false;
161
162 QgsTableContents contents;
163 const QStringList lines = QApplication::clipboard()->text().split( '\n' );
164 for ( const QString &line : lines )
165 {
166 if ( !line.isEmpty() )
167 {
168 QgsTableRow row;
169 const QStringList cells = line.split( '\t' );
170 for ( const QString &text : cells )
171 {
172 const QgsTableCell cell( text );
173 row << cell;
174 }
175 contents << row;
176 }
177 }
178
179 if ( !contents.isEmpty() )
180 {
181 setTableContents( contents );
182 emit tableChanged();
183 return true;
184 }
185
186 return false;
187 }
188
setTableContents(const QgsTableContents & contents)189 void QgsTableEditorDialog::setTableContents( const QgsTableContents &contents )
190 {
191 mBlockSignals = true;
192 mTableWidget->setTableContents( contents );
193 mTableWidget->resizeRowsToContents();
194 mTableWidget->resizeColumnsToContents();
195 mBlockSignals = false;
196 }
197
tableContents() const198 QgsTableContents QgsTableEditorDialog::tableContents() const
199 {
200 return mTableWidget->tableContents();
201 }
202
tableRowHeight(int row)203 double QgsTableEditorDialog::tableRowHeight( int row )
204 {
205 return mTableWidget->tableRowHeight( row );
206 }
207
tableColumnWidth(int column)208 double QgsTableEditorDialog::tableColumnWidth( int column )
209 {
210 return mTableWidget->tableColumnWidth( column );
211 }
212
setTableRowHeight(int row,double height)213 void QgsTableEditorDialog::setTableRowHeight( int row, double height )
214 {
215 mTableWidget->setTableRowHeight( row, height );
216 }
217
setTableColumnWidth(int column,double width)218 void QgsTableEditorDialog::setTableColumnWidth( int column, double width )
219 {
220 mTableWidget->setTableColumnWidth( column, width );
221 }
222
includeTableHeader() const223 bool QgsTableEditorDialog::includeTableHeader() const
224 {
225 return mActionIncludeHeader->isChecked();
226 }
227
setIncludeTableHeader(bool included)228 void QgsTableEditorDialog::setIncludeTableHeader( bool included )
229 {
230 mActionIncludeHeader->setChecked( included );
231 }
232
tableHeaders() const233 QVariantList QgsTableEditorDialog::tableHeaders() const
234 {
235 return mTableWidget->tableHeaders();
236 }
237
setTableHeaders(const QVariantList & headers)238 void QgsTableEditorDialog::setTableHeaders( const QVariantList &headers )
239 {
240 mTableWidget->setTableHeaders( headers );
241 }
242
registerExpressionContextGenerator(QgsExpressionContextGenerator * generator)243 void QgsTableEditorDialog::registerExpressionContextGenerator( QgsExpressionContextGenerator *generator )
244 {
245 mFormattingWidget->registerExpressionContextGenerator( generator );
246 }
247
updateActionNamesFromSelection()248 void QgsTableEditorDialog::updateActionNamesFromSelection()
249 {
250 const int rowCount = mTableWidget->rowsAssociatedWithSelection().size();
251 const int columnCount = mTableWidget->columnsAssociatedWithSelection().size();
252
253 mActionInsertRowsAbove->setEnabled( rowCount > 0 );
254 mActionInsertRowsBelow->setEnabled( rowCount > 0 );
255 mActionDeleteRows->setEnabled( rowCount > 0 );
256 mActionSelectRow->setEnabled( rowCount > 0 );
257 if ( rowCount == 0 )
258 {
259 mActionInsertRowsAbove->setText( tr( "Rows Above" ) );
260 mActionInsertRowsBelow->setText( tr( "Rows Below" ) );
261 mActionDeleteRows->setText( tr( "Delete Rows" ) );
262 mActionSelectRow->setText( tr( "Select Rows" ) );
263 }
264 else if ( rowCount == 1 )
265 {
266 mActionInsertRowsAbove->setText( tr( "Row Above" ) );
267 mActionInsertRowsBelow->setText( tr( "Row Below" ) );
268 mActionDeleteRows->setText( tr( "Delete Row" ) );
269 mActionSelectRow->setText( tr( "Select Row" ) );
270 }
271 else
272 {
273 mActionInsertRowsAbove->setText( tr( "%1 Rows Above" ).arg( rowCount ) );
274 mActionInsertRowsBelow->setText( tr( "%1 Rows Below" ).arg( rowCount ) );
275 mActionDeleteRows->setText( tr( "Delete %1 Rows" ).arg( rowCount ) );
276 mActionSelectRow->setText( tr( "Select %1 Rows" ).arg( rowCount ) );
277 }
278
279 mActionInsertColumnsBefore->setEnabled( columnCount > 0 );
280 mActionInsertColumnsAfter->setEnabled( columnCount > 0 );
281 mActionDeleteColumns->setEnabled( columnCount > 0 );
282 mActionSelectColumn->setEnabled( columnCount > 0 );
283 if ( columnCount == 0 )
284 {
285 mActionInsertColumnsBefore->setText( tr( "Columns Before" ) );
286 mActionInsertColumnsAfter->setText( tr( "Columns After" ) );
287 mActionDeleteColumns->setText( tr( "Delete Columns" ) );
288 mActionSelectColumn->setText( tr( "Select Columns" ) );
289 }
290 else if ( columnCount == 1 )
291 {
292 mActionInsertColumnsBefore->setText( tr( "Column Before" ) );
293 mActionInsertColumnsAfter->setText( tr( "Column After" ) );
294 mActionDeleteColumns->setText( tr( "Delete Column" ) );
295 mActionSelectColumn->setText( tr( "Select Column" ) );
296 }
297 else
298 {
299 mActionInsertColumnsBefore->setText( tr( "%1 Columns Before" ).arg( columnCount ) );
300 mActionInsertColumnsAfter->setText( tr( "%1 Columns After" ).arg( columnCount ) );
301 mActionDeleteColumns->setText( tr( "Delete %1 Columns" ).arg( columnCount ) );
302 mActionSelectColumn->setText( tr( "Select %1 Columns" ).arg( columnCount ) );
303 }
304 }
305
306 #include "qgstableeditordialog.h"
307