1 /*
2    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
3    All rights reserved.
4 
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8 
9    1. Redistributions of source code must retain the above copyright
10       notice, this list of conditions and the following disclaimer.
11    2. Redistributions in binary form must reproduce the above copyright
12       notice, this list of conditions and the following disclaimer in the
13       documentation and/or other materials provided with the distribution.
14 
15    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26 
27 
28 #include "kpMainWindow.h"
29 #include "kpMainWindowPrivate.h"
30 
31 #include "widgets/kpColorCells.h"
32 #include "lgpl/generic/kpColorCollection.h"
33 #include "lgpl/generic/kpUrlFormatter.h"
34 #include "widgets/toolbars/kpColorToolBar.h"
35 
36 #include <KActionCollection>
37 #include <KMessageBox>
38 #include <KSelectAction>
39 #include <KStandardGuiItem>
40 #include "kpLogCategories.h"
41 
42 #include <QFileDialog>
43 #include <QAction>
44 
45 //---------------------------------------------------------------------
46 
KDEColorCollectionNames()47 static QStringList KDEColorCollectionNames ()
48 {
49     return kpColorCollection::installedCollections ();
50 }
51 
52 //---------------------------------------------------------------------
53 
54 // private
setupColorsMenuActions()55 void kpMainWindow::setupColorsMenuActions ()
56 {
57     KActionCollection *ac = actionCollection ();
58 
59 
60     d->actionColorsDefault = ac->addAction (QStringLiteral("colors_default"));
61     d->actionColorsDefault->setText (i18n ("Use KolourPaint Defaults"));
62     connect (d->actionColorsDefault, &QAction::triggered,
63              this, &kpMainWindow::slotColorsDefault);
64 
65     d->actionColorsKDE = ac->add <KSelectAction> (QStringLiteral("colors_kde"));
66     d->actionColorsKDE->setText (i18nc ("@item:inmenu colors", "Use KDE's"));
67     // TODO: Will this slot be called spuriously if there are no colors
68     //       installed?
69     connect (d->actionColorsKDE,
70              static_cast<void (KSelectAction::*)(QAction*)>(&KSelectAction::triggered),
71              this, &kpMainWindow::slotColorsKDE);
72 
73     for (const auto &colName : ::KDEColorCollectionNames ()) {
74         d->actionColorsKDE->addAction (colName);
75     }
76 
77     d->actionColorsOpen = ac->addAction (QStringLiteral("colors_open"));
78     d->actionColorsOpen->setText (i18nc ("@item:inmenu colors", "&Open..."));
79     connect (d->actionColorsOpen, &QAction::triggered, this, &kpMainWindow::slotColorsOpen);
80 
81     d->actionColorsReload = ac->addAction (QStringLiteral("colors_reload"));
82     d->actionColorsReload->setText (i18nc ("@item:inmenu colors", "Reloa&d"));
83     connect (d->actionColorsReload, &QAction::triggered,
84              this, &kpMainWindow::slotColorsReload);
85 
86     d->actionColorsSave = ac->addAction (QStringLiteral("colors_save"));
87     d->actionColorsSave->setText (i18nc ("@item:inmenu colors", "&Save"));
88     connect (d->actionColorsSave, &QAction::triggered,
89              this, &kpMainWindow::slotColorsSave);
90 
91     d->actionColorsSaveAs = ac->addAction (QStringLiteral("colors_save_as"));
92     d->actionColorsSaveAs->setText (i18nc ("@item:inmenu colors", "Save &As..."));
93     connect (d->actionColorsSaveAs, &QAction::triggered,
94              this, &kpMainWindow::slotColorsSaveAs);
95 
96     d->actionColorsAppendRow = ac->addAction (QStringLiteral("colors_append_row"));
97     d->actionColorsAppendRow->setText (i18nc ("@item:inmenu colors", "Add Row"));
98     connect (d->actionColorsAppendRow, &QAction::triggered,
99              this, &kpMainWindow::slotColorsAppendRow);
100 
101     d->actionColorsDeleteRow = ac->addAction (QStringLiteral("colors_delete_row"));
102     d->actionColorsDeleteRow->setText (i18nc ("@item:inmenu colors", "Delete Last Row"));
103     connect (d->actionColorsDeleteRow, &QAction::triggered,
104              this, &kpMainWindow::slotColorsDeleteRow);
105 
106 
107     enableColorsMenuDocumentActions (false);
108 }
109 
110 //---------------------------------------------------------------------
111 
112 // private
createColorBox()113 void kpMainWindow::createColorBox ()
114 {
115     d->colorToolBar = new kpColorToolBar (i18n ("Color Box"), this);
116 
117     // (needed for QMainWindow::saveState())
118     d->colorToolBar->setObjectName ( QStringLiteral("Color Box" ));
119 
120     connect (colorCells (), &kpColorCells::rowCountChanged,
121              this, &kpMainWindow::slotUpdateColorsDeleteRowActionEnabled);
122 }
123 
124 //---------------------------------------------------------------------
125 
126 // private
enableColorsMenuDocumentActions(bool enable)127 void kpMainWindow::enableColorsMenuDocumentActions (bool enable)
128 {
129     d->actionColorsDefault->setEnabled (enable);
130     d->actionColorsKDE->setEnabled (enable);
131     d->actionColorsOpen->setEnabled (enable);
132     d->actionColorsReload->setEnabled (enable);
133 
134     d->actionColorsSave->setEnabled (enable);
135     d->actionColorsSaveAs->setEnabled (enable);
136 
137     d->actionColorsAppendRow->setEnabled (enable);
138 
139     d->colorMenuDocumentActionsEnabled = enable;
140 
141     slotUpdateColorsDeleteRowActionEnabled ();
142 }
143 
144 //---------------------------------------------------------------------
145 
146 // private slot
slotUpdateColorsDeleteRowActionEnabled()147 void kpMainWindow::slotUpdateColorsDeleteRowActionEnabled ()
148 {
149     // Currently, this is always enabled since kpColorCells guarantees that
150     // there will be at least one row of cells (which might all be of the
151     // invalid color).
152     //
153     // But this method is left here for future extensibility.
154     d->actionColorsDeleteRow->setEnabled (
155         d->colorMenuDocumentActionsEnabled && (colorCells ()->rowCount () > 0));
156 }
157 
158 //---------------------------------------------------------------------
159 
160 
161 // Used in 2 situations:
162 //
163 // 1. User opens a color without using the "Use KDE's" submenu.
164 // 2. User attempts to open a color using the "Use KDE's" submenu but the
165 //    opening fails.
166 //
167 // TODO: Maybe we could put the 3 actions (for different ways of opening
168 //       colors) in an exclusive group -- this might eliminate the need for
169 //       this hack.
170 //
171 // private
deselectActionColorsKDE()172 void kpMainWindow::deselectActionColorsKDE ()
173 {
174     d->actionColorsKDE->setCurrentItem (-1);
175 }
176 
177 //---------------------------------------------------------------------
178 
179 
180 // private
queryCloseColors()181 bool kpMainWindow::queryCloseColors ()
182 {
183 #if DEBUG_KP_MAIN_WINDOW
184     qCDebug(kpLogMainWindow) << "kpMainWindow::queryCloseColors() colorCells.modified="
185               << colorCells ()->isModified ();
186 #endif
187 
188     toolEndShape ();
189 
190     if (!colorCells ()->isModified ()) {
191         return true;  // ok to close
192     }
193 
194     int result = KMessageBox::Cancel;
195 
196 
197     if (!colorCells ()->url ().isEmpty ())
198     {
199         result = KMessageBox::warningYesNoCancel (this,
200             i18n ("The color palette \"%1\" has been modified.\n"
201                   "Do you want to save it?",
202                   kpUrlFormatter::PrettyFilename (colorCells ()->url ())),
203             QString ()/*caption*/,
204             KStandardGuiItem::save (), KStandardGuiItem::discard ());
205     }
206     else
207     {
208         const QString name = colorCells ()->colorCollection ()->name ();
209         if (!name.isEmpty ())
210         {
211             result = KMessageBox::warningYesNoCancel (this,
212                 i18n ("The KDE color palette \"%1\" has been modified.\n"
213                       "Do you want to save it to a file?",
214                       name),
215                 QString ()/*caption*/,
216                 KStandardGuiItem::save (), KStandardGuiItem::discard ());
217         }
218         else
219         {
220             result = KMessageBox::warningYesNoCancel (this,
221                 i18n ("The default color palette has been modified.\n"
222                       "Do you want to save it to a file?"),
223                 QString ()/*caption*/,
224                 KStandardGuiItem::save (), KStandardGuiItem::discard ());
225         }
226     }
227 
228     switch (result)
229     {
230     case KMessageBox::Yes:
231         return slotColorsSave ();  // close only if save succeeds
232     case KMessageBox::No:
233         return true;  // close without saving
234     default:
235         return false;  // don't close current doc
236     }
237 }
238 
239 //---------------------------------------------------------------------
240 
241 
242 // private
openDefaultColors()243 void kpMainWindow::openDefaultColors ()
244 {
245     colorCells ()->setColorCollection (
246         kpColorCells::DefaultColorCollection ());
247 }
248 
249 //---------------------------------------------------------------------
250 
251 // private slot
slotColorsDefault()252 void kpMainWindow::slotColorsDefault ()
253 {
254     // Call just in case.
255     toolEndShape ();
256 
257     if (!queryCloseColors ()) {
258         return;
259     }
260 
261     openDefaultColors ();
262 
263     deselectActionColorsKDE ();
264 }
265 
266 //---------------------------------------------------------------------
267 
268 // private
openKDEColors(const QString & name)269 bool kpMainWindow::openKDEColors (const QString &name)
270 {
271 #if DEBUG_KP_MAIN_WINDOW
272     qCDebug(kpLogMainWindow) << "kpMainWindow::openKDEColors(" << name << ")";
273 #endif
274 
275     kpColorCollection colorCol;
276     if (colorCol.openKDE (name, this))
277     {
278     #if DEBUG_KP_MAIN_WINDOW
279         qCDebug(kpLogMainWindow) << "opened";
280     #endif
281         colorCells ()->setColorCollection (colorCol);
282         return true;
283     }
284     else
285     {
286     #if DEBUG_KP_MAIN_WINDOW
287         qCDebug(kpLogMainWindow) << "failed to open";
288     #endif
289         return false;
290     }
291 }
292 
293 //---------------------------------------------------------------------
294 
295 // private slot
slotColorsKDE()296 void kpMainWindow::slotColorsKDE ()
297 {
298     // Call in case an error dialog appears.
299     toolEndShape ();
300 
301     const int curItem = d->actionColorsKDE->currentItem ();
302 
303     if (!queryCloseColors ())
304     {
305         deselectActionColorsKDE ();
306         return;
307     }
308 
309     // queryCloseColors() calls slotColorSave(), which can call
310     // slotColorSaveAs(), which can call deselectActionColorsKDE().
311     d->actionColorsKDE->setCurrentItem (curItem);
312 
313     const QStringList colNames = ::KDEColorCollectionNames ();
314     const int selected = d->actionColorsKDE->currentItem ();
315     Q_ASSERT (selected >= 0 && selected < colNames.size ());
316 
317     if (!openKDEColors (colNames [selected])) {
318         deselectActionColorsKDE ();
319     }
320 }
321 
322 //---------------------------------------------------------------------
323 
324 // private
openColors(const QUrl & url)325 bool kpMainWindow::openColors (const QUrl &url)
326 {
327     return colorCells ()->openColorCollection (url);
328 }
329 
330 //---------------------------------------------------------------------
331 
332 // private slot
slotColorsOpen()333 void kpMainWindow::slotColorsOpen ()
334 {
335     // Call due to dialog.
336     toolEndShape ();
337 
338     QFileDialog fd(this);
339     fd.setDirectoryUrl(colorCells ()->url());
340     fd.setWindowTitle(i18nc ("@title:window", "Open Color Palette"));
341 
342     if (fd.exec ())
343     {
344         if (!queryCloseColors ()) {
345             return;
346         }
347 
348         QList<QUrl> selected = fd.selectedUrls();
349         if ( selected.count() && openColors(selected[0]) ) {
350           deselectActionColorsKDE();
351         }
352     }
353 }
354 
355 //---------------------------------------------------------------------
356 
357 // private slot
slotColorsReload()358 void kpMainWindow::slotColorsReload ()
359 {
360     toolEndShape ();
361 
362     if (colorCells ()->isModified ())
363     {
364         int result = KMessageBox::Cancel;
365 
366         if (!colorCells ()->url ().isEmpty ())
367         {
368             result = KMessageBox::warningContinueCancel (this,
369                 i18n ("The color palette \"%1\" has been modified.\n"
370                       "Reloading will lose all changes since you last saved it.\n"
371                       "Are you sure?",
372                       kpUrlFormatter::PrettyFilename (colorCells ()->url ())),
373                 QString ()/*caption*/,
374                 KGuiItem(i18n ("&Reload")));
375         }
376         else
377         {
378             const QString name = colorCells ()->colorCollection ()->name ();
379             if (!name.isEmpty ())
380             {
381                 result = KMessageBox::warningContinueCancel (this,
382                     i18n ("The KDE color palette \"%1\" has been modified.\n"
383                           "Reloading will lose all changes.\n"
384                           "Are you sure?",
385                           colorCells ()->colorCollection ()->name ()),
386                     QString ()/*caption*/,
387                     KGuiItem (i18n ("&Reload")));
388             }
389             else
390             {
391                 result = KMessageBox::warningContinueCancel (this,
392                     i18n ("The default color palette has been modified.\n"
393                           "Reloading will lose all changes.\n"
394                           "Are you sure?"),
395                     QString ()/*caption*/,
396                     KGuiItem (i18n ("&Reload")));
397             }
398         }
399 
400     #if DEBUG_KP_MAIN_WINDOW
401         qCDebug(kpLogMainWindow) << "result=" << result
402                   << "vs KMessageBox::Continue" << KMessageBox::Continue;
403     #endif
404         if (result != KMessageBox::Continue) {
405             return;
406         }
407     }
408 
409 
410     if (!colorCells ()->url ().isEmpty ())
411     {
412         openColors (colorCells ()->url ());
413     }
414     else
415     {
416         const QString name = colorCells ()->colorCollection ()->name ();
417         if (!name.isEmpty ()) {
418             openKDEColors (name);
419         }
420         else {
421             openDefaultColors ();
422         }
423     }
424 }
425 
426 //---------------------------------------------------------------------
427 
428 
429 // private slot
slotColorsSave()430 bool kpMainWindow::slotColorsSave ()
431 {
432     // Call due to dialog.
433     toolEndShape ();
434 
435     if (colorCells ()->url ().isEmpty ())
436     {
437         return slotColorsSaveAs ();
438     }
439 
440     return colorCells ()->saveColorCollection ();
441 }
442 
443 //---------------------------------------------------------------------
444 
445 // private slot
slotColorsSaveAs()446 bool kpMainWindow::slotColorsSaveAs ()
447 {
448     // Call due to dialog.
449     toolEndShape ();
450 
451     QFileDialog fd(this);
452     fd.setDirectoryUrl(colorCells ()->url());
453     fd.setWindowTitle(i18n("Save Color Palette As"));
454     fd.setAcceptMode(QFileDialog::AcceptSave);
455     // Note that QFileDialog takes care of asking the user to confirm overwriting.
456 
457     if (fd.exec ())
458     {
459         QList<QUrl> selected = fd.selectedUrls();
460         if ( !selected.count() || !colorCells ()->saveColorCollectionAs(selected[0]) ) {
461           return false;
462         }
463 
464         // We're definitely using our own color collection now.
465         deselectActionColorsKDE ();
466 
467         return true;
468     }
469 
470     return false;
471 }
472 
473 //---------------------------------------------------------------------
474 
475 // private slot
slotColorsAppendRow()476 void kpMainWindow::slotColorsAppendRow ()
477 {
478     // Call just in case.
479     toolEndShape ();
480 
481     kpColorCells *colorCells = d->colorToolBar->colorCells ();
482     colorCells->appendRow ();
483 }
484 
485 //---------------------------------------------------------------------
486 
487 // private slot
slotColorsDeleteRow()488 void kpMainWindow::slotColorsDeleteRow ()
489 {
490     // Call just in case.
491     toolEndShape ();
492 
493     kpColorCells *colorCells = d->colorToolBar->colorCells ();
494     colorCells->deleteLastRow ();
495 }
496