1/*
2 Copyright (c) 2012 Montel Laurent <montel@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18
19*/
20
21#include "tableactionmenu.h"
22#include "textedit.h"
23#include "inserttabledialog.h"
24#include "tableformatdialog.h"
25#include "tablecellformatdialog.h"
26
27#include <KActionCollection>
28#include <KAction>
29#include <KIcon>
30#include <KLocalizedString>
31
32#include <QTextTable>
33#include <QPointer>
34
35namespace KPIMTextEdit {
36
37class TableActionMenuPrivate
38{
39 public:
40 TableActionMenuPrivate( KActionCollection *ac, TextEdit *edit, TableActionMenu *qq )
41 : actionCollection( ac ), textEdit( edit ), q( qq )
42 {
43 }
44
45 void _k_slotInsertRowBelow();
46 void _k_slotInsertRowAbove();
47 void _k_slotInsertColumnBefore();
48 void _k_slotInsertColumnAfter();
49
50 void _k_slotInsertTable();
51
52 void _k_slotRemoveRowBelow();
53 void _k_slotRemoveRowAbove();
54 void _k_slotRemoveColumnBefore();
55 void _k_slotRemoveColumnAfter();
56 void _k_slotRemoveCellContents();
57
58 void _k_slotMergeCell();
59 void _k_slotMergeSelectedCells();
60 void _k_slotTableFormat();
61 void _k_slotTableCellFormat();
62 void _k_slotSplitCell();
63 void _k_updateActions( bool forceUpdate = false );
64
65 KAction *actionInsertTable;
66
67 KAction *actionInsertRowBelow;
68 KAction *actionInsertRowAbove;
69
70 KAction *actionInsertColumnBefore;
71 KAction *actionInsertColumnAfter;
72
73 KAction *actionRemoveRowBelow;
74 KAction *actionRemoveRowAbove;
75
76 KAction *actionRemoveColumnBefore;
77 KAction *actionRemoveColumnAfter;
78
79 KAction *actionMergeCell;
80 KAction *actionMergeSelectedCells;
81 KAction *actionSplitCell;
82
83 KAction *actionTableFormat;
84 KAction *actionTableCellFormat;
85
86 KAction *actionRemoveCellContents;
87
88 KActionCollection *actionCollection;
89 TextEdit *textEdit;
90 TableActionMenu *q;
91};
92
93void TableActionMenuPrivate::_k_slotRemoveCellContents()
94{
95 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
96 QTextTable *table = textEdit->textCursor().currentTable();
97 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
98 if ( cell.isValid() ) {
99 const QTextCursor firstCursor = cell.firstCursorPosition();
100 const QTextCursor endCursor = cell.lastCursorPosition();
101 QTextCursor cursor = textEdit->textCursor();
102 cursor.beginEditBlock();
103 cursor.setPosition( firstCursor.position() );
104 cursor.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
105 endCursor.position() - firstCursor.position() );
106 cursor.removeSelectedText();
107 cursor.endEditBlock();
108 }
109 }
110}
111
112void TableActionMenuPrivate::_k_slotRemoveRowBelow()
113{
114 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
115 QTextTable *table = textEdit->textCursor().currentTable();
116 if ( table ) {
117 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
118 if ( cell.row()<table->rows() - 1 ) {
119 table->removeRows( cell.row(), 1 );
120 }
121 }
122 }
123}
124
125void TableActionMenuPrivate::_k_slotRemoveRowAbove()
126{
127 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
128 QTextTable *table = textEdit->textCursor().currentTable();
129 if ( table ) {
130 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
131 if ( cell.row() >= 1 ) {
132 table->removeRows( cell.row() - 1, 1 );
133 }
134 }
135 }
136}
137
138void TableActionMenuPrivate::_k_slotRemoveColumnBefore()
139{
140 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
141 QTextTable *table = textEdit->textCursor().currentTable();
142 if ( table ) {
143 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
144 if ( cell.column() > 0 ) {
145 table->removeColumns( cell.column() - 1, 1 );
146 }
147 }
148 }
149}
150
151void TableActionMenuPrivate::_k_slotRemoveColumnAfter()
152{
153 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
154 QTextTable *table = textEdit->textCursor().currentTable();
155 if ( table ) {
156 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
157 if ( cell.column()<table->columns() - 1 ) {
158 table->removeColumns( cell.column(), 1 );
159 }
160 }
161 }
162}
163
164void TableActionMenuPrivate::_k_slotInsertRowBelow()
165{
166 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
167 QTextTable *table = textEdit->textCursor().currentTable();
168 if ( table ) {
169 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
170 if ( cell.row()<table->rows() ) {
171 table->insertRows( cell.row() + 1, 1 );
172 } else {
173 table->appendRows( 1 );
174 }
175 }
176 }
177}
178
179void TableActionMenuPrivate::_k_slotInsertRowAbove()
180{
181 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
182 QTextTable *table = textEdit->textCursor().currentTable();
183 if ( table ) {
184 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
185 table->insertRows( cell.row(), 1 );
186 }
187 }
188}
189
190void TableActionMenuPrivate::_k_slotInsertColumnBefore()
191{
192 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
193 QTextTable *table = textEdit->textCursor().currentTable();
194 if ( table ) {
195 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
196 table->insertColumns( cell.column(), 1 );
197 }
198 }
199}
200
201void TableActionMenuPrivate::_k_slotInsertColumnAfter()
202{
203 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
204 QTextTable *table = textEdit->textCursor().currentTable();
205 if ( table ) {
206 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
207 if ( cell.column()<table->columns() ) {
208 table->insertColumns( cell.column() + 1, 1 );
209 } else {
210 table->appendColumns( 1 );
211 }
212 }
213 }
214}
215
216void TableActionMenuPrivate::_k_slotInsertTable()
217{
218 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
219 QPointer<InsertTableDialog> dialog = new InsertTableDialog( textEdit );
220 if ( dialog->exec() ) {
221 QTextCursor cursor = textEdit->textCursor();
222 QTextTableFormat tableFormat;
223 tableFormat.setBorder( dialog->border() );
224 const int numberOfColumns( dialog->columns() );
225 QVector<QTextLength> contrains;
226 const QTextLength::Type type = dialog->typeOfLength();
227 const int length = dialog->length();
228
229 const QTextLength textlength( type, length / numberOfColumns );
230 for ( int i = 0; i < numberOfColumns; ++i ) {
231 contrains.append( textlength );
232 }
233 tableFormat.setColumnWidthConstraints( contrains );
234 tableFormat.setAlignment(Qt::AlignLeft);
235 QTextTable *table = cursor.insertTable( dialog->rows(), numberOfColumns );
236 table->setFormat( tableFormat );
237 }
238 delete dialog;
239 }
240}
241
242void TableActionMenuPrivate::_k_slotMergeCell()
243{
244 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
245 QTextTable *table = textEdit->textCursor().currentTable();
246 if ( table ) {
247 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
248 table->mergeCells( cell.row(), cell.column(), 1, cell.columnSpan() + 1 );
249 }
250 }
251}
252
253void TableActionMenuPrivate::_k_slotMergeSelectedCells()
254{
255 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
256 QTextTable *table = textEdit->textCursor().currentTable();
257 if ( table ) {
258 table->mergeCells( textEdit->textCursor() );
259 }
260 }
261}
262
263void TableActionMenuPrivate::_k_slotTableFormat()
264{
265 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
266 QTextTable *table = textEdit->textCursor().currentTable();
267 if ( table ) {
268 QPointer<TableFormatDialog> dialog = new TableFormatDialog( textEdit );
269 const int numberOfColumn( table->columns() );
270 const int numberOfRow( table->rows() );
271 dialog->setColumns( numberOfColumn );
272 dialog->setRows( numberOfRow );
273 QTextTableFormat tableFormat = table->format();
274 dialog->setBorder( tableFormat.border() );
275 dialog->setSpacing( tableFormat.cellSpacing() );
276 dialog->setPadding( tableFormat.cellPadding() );
277 dialog->setAlignment( tableFormat.alignment() );
278 if ( tableFormat.hasProperty( QTextFormat::BackgroundBrush ) ) {
279 dialog->setTableBackgroundColor( tableFormat.background().color() );
280 }
281 QVector<QTextLength> contrains = tableFormat.columnWidthConstraints();
282 if ( !contrains.isEmpty() ) {
283 dialog->setTypeOfLength( contrains.at( 0 ).type() );
284 dialog->setLength( contrains.at( 0 ).rawValue() * numberOfColumn );
285 }
286
287 if ( dialog->exec() ) {
288 const int newNumberOfColumns( dialog->columns() );
289 if ( ( newNumberOfColumns != numberOfColumn ) ||
290 ( dialog->rows() != numberOfRow ) ) {
291 table->resize( dialog->rows(), newNumberOfColumns );
292 }
293 tableFormat.setBorder( dialog->border() );
294 tableFormat.setCellPadding( dialog->padding() );
295 tableFormat.setCellSpacing( dialog->spacing() );
296 tableFormat.setAlignment( dialog->alignment() );
297
298 QVector<QTextLength> contrains;
299 const QTextLength::Type type = dialog->typeOfLength();
300 const int length = dialog->length();
301
302 const QTextLength textlength( type, length / newNumberOfColumns );
303 for ( int i = 0; i < newNumberOfColumns; ++i ) {
304 contrains.append( textlength );
305 }
306 tableFormat.setColumnWidthConstraints( contrains );
307 const QColor tableBackgroundColor = dialog->tableBackgroundColor();
308 if ( dialog->useBackgroundColor() ) {
309 if ( tableBackgroundColor.isValid() ) {
310 tableFormat.setBackground( tableBackgroundColor );
311 }
312 } else {
313 tableFormat.clearBackground();
314 }
315 table->setFormat( tableFormat );
316 }
317 delete dialog;
318 }
319 }
320}
321
322void TableActionMenuPrivate::_k_slotTableCellFormat()
323{
324 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
325 QTextTable *table = textEdit->textCursor().currentTable();
326 if ( table ) {
327 QTextTableCell cell = table->cellAt( textEdit->textCursor() );
328 QPointer<TableCellFormatDialog> dialog = new TableCellFormatDialog( textEdit );
329 QTextTableCellFormat format = cell.format().toTableCellFormat();
330 if ( format.hasProperty( QTextFormat::BackgroundBrush ) ) {
331 dialog->setTableCellBackgroundColor( format.background().color() );
332 }
333 dialog->setVerticalAlignment( format.verticalAlignment() );
334 if ( dialog->exec() ) {
335 if ( dialog->useBackgroundColor() ) {
336 const QColor tableCellColor = dialog->tableCellBackgroundColor();
337 if ( tableCellColor.isValid() ) {
338 format.setBackground( tableCellColor );
339 }
340 } else {
341 format.clearBackground();
342 }
343 format.setVerticalAlignment( dialog->verticalAlignment() );
344 cell.setFormat( format );
345 }
346 delete dialog;
347 }
348 }
349}
350
351void TableActionMenuPrivate::_k_slotSplitCell()
352{
353 if ( textEdit->textMode() == KRichTextEdit::Rich ) {
354 QTextTable *table = textEdit->textCursor().currentTable();
355 if ( table ) {
356 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
357 if ( cell.columnSpan() > 1 || cell.rowSpan() > 1 ) {
358 table->splitCell( cell.row(), cell.column(),
359 qMax( 1, cell.rowSpan() - 1 ),
360 qMax( 1, cell.columnSpan() - 1 ) );
361 _k_updateActions();
362 }
363 }
364 }
365}
366
367void TableActionMenuPrivate::_k_updateActions( bool forceUpdate )
368{
369 if ( ( textEdit->textMode() == KRichTextEdit::Rich ) || forceUpdate ) {
370 QTextTable *table = textEdit->textCursor().currentTable();
371 const bool isTable = ( table != 0 );
372 actionInsertRowBelow->setEnabled( isTable );
373 actionInsertRowAbove->setEnabled( isTable );
374
375 actionInsertColumnBefore->setEnabled( isTable );
376 actionInsertColumnAfter->setEnabled( isTable );
377
378 actionRemoveRowBelow->setEnabled( isTable );
379 actionRemoveRowAbove->setEnabled( isTable );
380
381 actionRemoveColumnBefore->setEnabled( isTable );
382 actionRemoveColumnAfter->setEnabled( isTable );
383
384 if ( table ) {
385 const QTextTableCell cell = table->cellAt( textEdit->textCursor() );
386
387 int firstRow = -1;
388 int numRows = -1;
389 int firstColumn = -1;
390 int numColumns = -1;
391 textEdit->textCursor().selectedTableCells ( &firstRow, &numRows, &firstColumn, &numColumns );
392 const bool hasSelectedTableCell =
393 ( firstRow != -1 ) && ( numRows != -1 ) &&
394 ( firstColumn != -1 ) && ( numColumns != -1 );
395 if ( cell.column()>table->columns() - 2 ) {
396 actionMergeCell->setEnabled( false );
397 } else {
398 actionMergeCell->setEnabled( true );
399 }
400 if ( cell.columnSpan() > 1 || cell.rowSpan() > 1 ) {
401 actionSplitCell->setEnabled( true );
402 } else {
403 actionSplitCell->setEnabled( false );
404 }
405 actionTableCellFormat->setEnabled( true );
406 actionMergeSelectedCells->setEnabled( hasSelectedTableCell );
407 } else {
408 actionSplitCell->setEnabled( false );
409 actionMergeCell->setEnabled( false );
410 actionMergeSelectedCells->setEnabled( false );
411 }
412 actionTableFormat->setEnabled( isTable );
413 actionTableCellFormat->setEnabled( isTable );
414 actionRemoveCellContents->setEnabled( isTable );
415 }
416}
417
418TableActionMenu::TableActionMenu( KActionCollection *ac, TextEdit *textEdit )
419 : KActionMenu( textEdit ), d( new TableActionMenuPrivate( ac, textEdit, this ) )
420{
421 KActionMenu *insertMenu = new KActionMenu( i18n( "Insert" ), this );
422 addAction( insertMenu );
423
424 d->actionInsertTable = new KAction( KIcon( QLatin1String( "insert-table" ) ), i18n( "Table..." ), this );
425 insertMenu->addAction( d->actionInsertTable );
426 ac->addAction( QLatin1String( "insert_new_table" ), d->actionInsertTable );
427 connect( d->actionInsertTable, SIGNAL(triggered(bool)),
428 SLOT(_k_slotInsertTable()) );
429
430 insertMenu->addSeparator();
431 d->actionInsertRowBelow =
432 new KAction( KIcon( QLatin1String( "edit-table-insert-row-below" ) ),
433 i18n( "Row Below" ), this );
434 insertMenu->addAction( d->actionInsertRowBelow );
435 ac->addAction( QLatin1String( "insert_row_below" ), d->actionInsertRowBelow );
436 connect( d->actionInsertRowBelow, SIGNAL(triggered(bool)),
437 SLOT(_k_slotInsertRowBelow()) );
438
439 d->actionInsertRowAbove =
440 new KAction( KIcon( QLatin1String( "edit-table-insert-row-above" ) ),
441 i18n( "Row Above" ), this );
442 insertMenu->addAction( d->actionInsertRowAbove );
443 ac->addAction( QLatin1String( "insert_row_above" ), d->actionInsertRowAbove );
444 connect( d->actionInsertRowAbove, SIGNAL(triggered(bool)),
445 SLOT(_k_slotInsertRowAbove()) );
446
447 insertMenu->addSeparator();
448 d->actionInsertColumnBefore =
449 new KAction( KIcon( QLatin1String( "edit-table-insert-column-left" ) ),
450 i18n( "Column Before" ), this );
451 insertMenu->addAction( d->actionInsertColumnBefore );
452 ac->addAction( QLatin1String( "insert_column_before" ), d->actionInsertColumnBefore );
453
454 connect( d->actionInsertColumnBefore, SIGNAL(triggered(bool)),
455 SLOT(_k_slotInsertColumnBefore()) );
456
457 d->actionInsertColumnAfter =
458 new KAction( KIcon( QLatin1String( "edit-table-insert-column-right" ) ),
459 i18n( "Column After" ), this );
460 insertMenu->addAction( d->actionInsertColumnAfter );
461 ac->addAction( QLatin1String( "insert_column_after" ), d->actionInsertColumnAfter );
462 connect( d->actionInsertColumnAfter, SIGNAL(triggered(bool)),
463 SLOT(_k_slotInsertColumnAfter()) );
464
465 KActionMenu *removeMenu = new KActionMenu( i18n( "Delete" ), this );
466 addAction( removeMenu );
467
468 d->actionRemoveRowBelow = new KAction( i18n( "Row Below" ), this );
469 removeMenu->addAction( d->actionRemoveRowBelow );
470 ac->addAction( QLatin1String( "remove_row_below" ), d->actionRemoveRowBelow );
471 connect( d->actionRemoveRowBelow, SIGNAL(triggered(bool)),
472 SLOT(_k_slotRemoveRowBelow()) );
473
474 d->actionRemoveRowAbove = new KAction( i18n( "Row Above" ), this );
475 removeMenu->addAction( d->actionRemoveRowAbove );
476 ac->addAction( QLatin1String( "remove_row_above" ), d->actionRemoveRowAbove );
477 connect( d->actionRemoveRowAbove, SIGNAL(triggered(bool)),
478 SLOT(_k_slotRemoveRowAbove()) );
479
480 removeMenu->addSeparator();
481 d->actionRemoveColumnBefore = new KAction( i18n( "Column Before" ), this );
482 removeMenu->addAction( d->actionRemoveColumnBefore );
483 ac->addAction( QLatin1String( "remove_column_before" ), d->actionRemoveColumnBefore );
484
485 connect( d->actionRemoveColumnBefore, SIGNAL(triggered(bool)),
486 SLOT(_k_slotRemoveColumnBefore()) );
487
488 d->actionRemoveColumnAfter = new KAction( i18n( "Column After" ), this );
489 removeMenu->addAction( d->actionRemoveColumnAfter );
490 ac->addAction( QLatin1String( "remove_column_after" ), d->actionRemoveColumnAfter );
491 connect( d->actionRemoveColumnAfter, SIGNAL(triggered(bool)),
492 SLOT(_k_slotRemoveColumnAfter()) );
493
494 removeMenu->addSeparator();
495 d->actionRemoveCellContents = new KAction( i18n( "Cell Contents" ), this );
496 removeMenu->addAction( d->actionRemoveCellContents );
497 ac->addAction( QLatin1String( "remove_cell_contents" ), d->actionRemoveCellContents );
498 connect( d->actionRemoveCellContents, SIGNAL(triggered(bool)),
499 SLOT(_k_slotRemoveCellContents()) );
500
501 addSeparator();
502
503 d->actionMergeCell =
504 new KAction( KIcon( QLatin1String( "edit-table-cell-merge" ) ),
505 i18n( "Join With Cell to the Right" ), this );
506 ac->addAction( QLatin1String( "join_cell_to_the_right" ), d->actionMergeCell );
507 connect( d->actionMergeCell, SIGNAL(triggered(bool)),
508 SLOT(_k_slotMergeCell()) );
509 addAction( d->actionMergeCell );
510
511 d->actionMergeSelectedCells = new KAction( i18n( "Join Selected Cells" ), this );
512 ac->addAction( QLatin1String( "join_cell_selected_cells" ), d->actionMergeSelectedCells );
513 connect( d->actionMergeSelectedCells, SIGNAL(triggered(bool)),
514 SLOT(_k_slotMergeSelectedCells()) );
515 addAction( d->actionMergeSelectedCells );
516
517 d->actionSplitCell =
518 new KAction( KIcon( QLatin1String( "edit-table-cell-split" ) ),
519 i18n( "Split cells" ), this );
520 ac->addAction( QLatin1String( "split_cells" ), d->actionSplitCell );
521 connect( d->actionSplitCell, SIGNAL(triggered(bool)),
522 SLOT(_k_slotSplitCell()) );
523 addAction( d->actionSplitCell );
524
525 addSeparator();
526
527 d->actionTableFormat = new KAction( i18n( "Table Format..." ), this );
528 ac->addAction( QLatin1String( "table_format" ), d->actionTableFormat );
529 connect( d->actionTableFormat, SIGNAL(triggered(bool)),
530 SLOT(_k_slotTableFormat()) );
531 addAction( d->actionTableFormat );
532
533 d->actionTableCellFormat = new KAction( i18n( "Table Cell Format..." ), this );
534 ac->addAction( QLatin1String( "table_cell_format" ), d->actionTableCellFormat );
535 connect( d->actionTableCellFormat, SIGNAL(triggered(bool)),
536 SLOT(_k_slotTableCellFormat()) );
537 addAction( d->actionTableCellFormat );
538
539 connect( textEdit, SIGNAL(cursorPositionChanged()),
540 this, SLOT(_k_updateActions()) );
541 d->_k_updateActions( true );
542}
543
544TableActionMenu::~TableActionMenu()
545{
546 delete d;
547}
548
549}
550
551#include "moc_tableactionmenu.cpp"
552