1 | /*************************************************************************** |
2 | * Copyright (C) 1999-2006 by Éric Bischoff <ebischoff@nerim.net> * |
3 | * Copyright (C) 2007 by Albert Astals Cid <aacid@kde.org> * |
4 | * * |
5 | * This program is free software; you can redistribute it and/or modify * |
6 | * it under the terms of the GNU General Public License as published by * |
7 | * the Free Software Foundation; either version 2 of the License, or * |
8 | * (at your option) any later version. * |
9 | ***************************************************************************/ |
10 | |
11 | /* Top level window */ |
12 | |
13 | #include "toplevel.h" |
14 | |
15 | #include <kapplication.h> |
16 | #include <kmessagebox.h> |
17 | #include <kfiledialog.h> |
18 | #include <klocale.h> |
19 | #include <kstandarddirs.h> |
20 | #include <kio/netaccess.h> |
21 | #include <kaction.h> |
22 | #include <kstandardaction.h> |
23 | #include <kstandardshortcut.h> |
24 | #include <kstandardgameaction.h> |
25 | #include <kactioncollection.h> |
26 | #include <ktoggleaction.h> |
27 | #include <ktogglefullscreenaction.h> |
28 | #include <kimageio.h> |
29 | #include <kmimetype.h> |
30 | #include <kconfiggroup.h> |
31 | #include <ktemporaryfile.h> |
32 | #include <kdeprintdialog.h> |
33 | #include <kcombobox.h> |
34 | |
35 | #include <QClipboard> |
36 | #include <QPrintDialog> |
37 | #include <QPrinter> |
38 | |
39 | #include "playground.h" |
40 | #include "soundfactory.h" |
41 | #include "playgrounddelegate.h" |
42 | |
43 | static const char *DEFAULT_THEME = "default_theme.theme" ; |
44 | |
45 | // Constructor |
46 | TopLevel::TopLevel() |
47 | : KXmlGuiWindow(0) |
48 | { |
49 | QString board, language; |
50 | |
51 | playGround = new PlayGround(this); |
52 | playGround->setObjectName( QLatin1String( "playGround" ) ); |
53 | |
54 | soundFactory = new SoundFactory(this); |
55 | |
56 | setCentralWidget(playGround); |
57 | |
58 | playgroundsGroup = new QActionGroup(this); |
59 | playgroundsGroup->setExclusive(true); |
60 | |
61 | languagesGroup = new QActionGroup(this); |
62 | languagesGroup->setExclusive(true); |
63 | |
64 | setupKAction(); |
65 | |
66 | playGround->registerPlayGrounds(); |
67 | soundFactory->registerLanguages(); |
68 | |
69 | readOptions(board, language); |
70 | changeGameboard(board); |
71 | changeLanguage(language); |
72 | } |
73 | |
74 | // Destructor |
75 | TopLevel::~TopLevel() |
76 | { |
77 | delete soundFactory; |
78 | } |
79 | |
80 | static bool actionSorterByName(QAction *a, QAction *b) |
81 | { |
82 | return a->text().localeAwareCompare(b->text()) < 0; |
83 | } |
84 | |
85 | // Register an available gameboard |
86 | void TopLevel::registerGameboard(const QString &, const QString &board, const QPixmap &pixmap) |
87 | { |
88 | KToggleAction *t = new KToggleAction(menuText, this); |
89 | actionCollection()->addAction(board, t); |
90 | t->setData(board); |
91 | connect(t, SIGNAL(toggled(bool)), SLOT(changeGameboard())); |
92 | playgroundsGroup->addAction(t); |
93 | QList<QAction*> actionList = playgroundsGroup->actions(); |
94 | qSort(actionList.begin(), actionList.end(), actionSorterByName); |
95 | unplugActionList( QLatin1String( "playgroundList" ) ); |
96 | plugActionList( QLatin1String( "playgroundList" ), actionList ); |
97 | |
98 | playgroundCombo->addItem(menuText,QVariant(pixmap)); |
99 | playgroundCombo->setItemData(playgroundCombo->count()-1,QVariant(board),BOARD_THEME); |
100 | } |
101 | |
102 | // Register an available language |
103 | void TopLevel::registerLanguage(const QString &code, const QString &soundFile, bool enabled) |
104 | { |
105 | KToggleAction *t = new KToggleAction(KGlobal::locale()->languageCodeToName(code), this); |
106 | t->setEnabled(enabled); |
107 | actionCollection()->addAction(soundFile, t); |
108 | t->setData(soundFile); |
109 | sounds.insert(code, soundFile); |
110 | connect(t, SIGNAL(toggled(bool)), SLOT(changeLanguage())); |
111 | languagesGroup->addAction(t); |
112 | QList<QAction*> actionList = languagesGroup->actions(); |
113 | actionList.removeAll(actionCollection()->action(QLatin1String( "speech_no_sound" ))); |
114 | qSort(actionList.begin(), actionList.end(), actionSorterByName); |
115 | unplugActionList( QLatin1String( "languagesList" ) ); |
116 | plugActionList( QLatin1String( "languagesList" ), actionList ); |
117 | } |
118 | |
119 | // Switch to another gameboard |
120 | void TopLevel::changeGameboardFromCombo(int index) |
121 | { |
122 | QString newBoard = playgroundCombo->itemData(index,BOARD_THEME).toString(); |
123 | changeGameboard(newBoard); |
124 | } |
125 | |
126 | void TopLevel::changeGameboard() |
127 | { |
128 | QAction *action = qobject_cast<QAction*>(sender()); |
129 | // ignore toggling of "nonchecked" actions |
130 | if (action->isChecked()) |
131 | { |
132 | QString newGameBoard = action->data().toString(); |
133 | changeGameboard(newGameBoard); |
134 | } |
135 | } |
136 | |
137 | void TopLevel::changeGameboard(const QString &newGameBoard) |
138 | { |
139 | if (newGameBoard == playGround->currentGameboard()) return; |
140 | |
141 | QString fileToLoad; |
142 | QFileInfo fi(newGameBoard); |
143 | if (fi.isRelative()) |
144 | { |
145 | QStringList list = KGlobal::dirs()->findAllResources("appdata" , QLatin1String( "pics/" ) + newGameBoard); |
146 | if (!list.isEmpty()) fileToLoad = list.first(); |
147 | } |
148 | else |
149 | { |
150 | fileToLoad = newGameBoard; |
151 | } |
152 | |
153 | int index = playgroundCombo->findData(fileToLoad, BOARD_THEME); |
154 | playgroundCombo->setCurrentIndex(index); |
155 | QAction *action = actionCollection()->action(fileToLoad); |
156 | if (action && playGround->loadPlayGround(fileToLoad)) |
157 | { |
158 | action->setChecked(true); |
159 | |
160 | // Change gameboard in the remembered options |
161 | writeOptions(); |
162 | } |
163 | else |
164 | { |
165 | // Something bad just happened, try the default playground |
166 | if (newGameBoard != QLatin1String(DEFAULT_THEME)) |
167 | { |
168 | changeGameboard(QLatin1String(DEFAULT_THEME)); |
169 | } |
170 | else |
171 | { |
172 | KMessageBox::error(this, i18n("Error while loading the playground." )); |
173 | } |
174 | } |
175 | } |
176 | |
177 | void TopLevel::changeLanguage() |
178 | { |
179 | QAction *action = qobject_cast<QAction*>(sender()); |
180 | QString soundFile = action->data().toString(); |
181 | changeLanguage(soundFile); |
182 | } |
183 | |
184 | // Switch to another language |
185 | void TopLevel::changeLanguage(const QString &soundFile) |
186 | { |
187 | if (soundFile == soundFactory->currentSoundFile()) return; |
188 | |
189 | QString fileToLoad; |
190 | QFileInfo fi(soundFile); |
191 | if (fi.isRelative()) |
192 | { |
193 | const QStringList list = KGlobal::dirs()->findAllResources("appdata" , QLatin1String( "sounds/" ) + soundFile); |
194 | if (!list.isEmpty()) fileToLoad = list.first(); |
195 | } |
196 | else |
197 | { |
198 | fileToLoad = soundFile; |
199 | } |
200 | |
201 | // Change language effectively |
202 | QAction *action = actionCollection()->action(fileToLoad); |
203 | if (action && soundFactory->loadLanguage(fileToLoad)) |
204 | { |
205 | action->setChecked(true); |
206 | |
207 | // Change language in the remembered options |
208 | writeOptions(); |
209 | } |
210 | else |
211 | { |
212 | KMessageBox::error(this, i18n("Error while loading the sound file." )); |
213 | soundOff(); |
214 | } |
215 | } |
216 | |
217 | // Play a sound |
218 | void TopLevel::playSound(const QString &ref) const |
219 | { |
220 | soundFactory->playSound(ref); |
221 | } |
222 | |
223 | // Read options from preferences file |
224 | void TopLevel::readOptions(QString &board, QString &language) |
225 | { |
226 | KConfigGroup config(KGlobal::config(), "General" ); |
227 | |
228 | QString option = config.readEntry("Sound" , "on" ); |
229 | bool soundEnabled = option.indexOf(QLatin1String( "on" )) == 0; |
230 | board = config.readEntry("Gameboard" , DEFAULT_THEME); |
231 | language = config.readEntry("Language" , "" ); |
232 | bool keepAspectRatio = config.readEntry("KeepAspectRatio" , false); |
233 | |
234 | if (soundEnabled) |
235 | { |
236 | if (language.isEmpty()) |
237 | { |
238 | language = sounds.value(KGlobal::locale()->language(), QLatin1String( "en.soundtheme" )); |
239 | } |
240 | } |
241 | else |
242 | { |
243 | soundOff(); |
244 | language = QString(); |
245 | } |
246 | |
247 | lockAspectRatio(keepAspectRatio); |
248 | } |
249 | |
250 | // Write options to preferences file |
251 | void TopLevel::writeOptions() |
252 | { |
253 | KConfigGroup config(KGlobal::config(), "General" ); |
254 | config.writeEntry("Sound" , actionCollection()->action(QLatin1String( "speech_no_sound" ))->isChecked() ? "off" : "on" ); |
255 | |
256 | config.writeEntry("Gameboard" , playGround->currentGameboard()); |
257 | |
258 | config.writeEntry("Language" , soundFactory->currentSoundFile()); |
259 | |
260 | config.writeEntry("KeepAspectRatio" , playGround->isAspectRatioLocked()); |
261 | } |
262 | |
263 | // KAction initialization (aka menubar + toolbar init) |
264 | void TopLevel::setupKAction() |
265 | { |
266 | QAction *action; |
267 | |
268 | //Game |
269 | KStandardGameAction::gameNew(this, SLOT(fileNew()), actionCollection()); |
270 | KStandardGameAction::load(this, SLOT(fileOpen()), actionCollection()); |
271 | KStandardGameAction::save(this, SLOT(fileSave()), actionCollection()); |
272 | KStandardGameAction::print(this, SLOT(filePrint()), actionCollection()); |
273 | KStandardGameAction::quit(kapp, SLOT(quit()), actionCollection()); |
274 | |
275 | action = actionCollection()->addAction( QLatin1String( "game_save_picture" )); |
276 | action->setText(i18n("Save &as Picture..." )); |
277 | connect(action, SIGNAL(triggered(bool)), SLOT(filePicture())); |
278 | |
279 | //Edit |
280 | action = KStandardAction::copy(this, SLOT(editCopy()), actionCollection()); |
281 | actionCollection()->addAction(action->objectName(), action); |
282 | |
283 | action = KStandardAction::undo(0, 0, actionCollection()); |
284 | playGround->connectUndoAction(action); |
285 | action = KStandardAction::redo(0, 0, actionCollection()); |
286 | playGround->connectRedoAction(action); |
287 | |
288 | //Speech |
289 | KToggleAction *t = new KToggleAction(i18n("&No Sound" ), this); |
290 | actionCollection()->addAction( QLatin1String( "speech_no_sound" ), t); |
291 | connect(t, SIGNAL(triggered(bool)), SLOT(soundOff())); |
292 | languagesGroup->addAction(t); |
293 | |
294 | KStandardAction::fullScreen(this, SLOT(toggleFullScreen()), this, actionCollection()); |
295 | |
296 | t = new KToggleAction(i18n("&Lock Aspect Ratio" ), this); |
297 | actionCollection()->addAction( QLatin1String( "lock_aspect_ratio" ), t); |
298 | connect(t, SIGNAL(triggered(bool)), this, SLOT(lockAspectRatio(bool))); |
299 | |
300 | playgroundCombo = new KComboBox(this); |
301 | playgroundCombo->setMinimumWidth(200); |
302 | playgroundCombo->view()->setMinimumHeight(100); |
303 | playgroundCombo->view()->setMinimumWidth(200); |
304 | playgroundCombo->view()->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); |
305 | |
306 | PlaygroundDelegate *playgroundDelegate = new PlaygroundDelegate(playgroundCombo->view()); |
307 | playgroundCombo->setItemDelegate(playgroundDelegate); |
308 | |
309 | connect(playgroundCombo, SIGNAL(currentIndexChanged(int)),this,SLOT(changeGameboardFromCombo(int))); |
310 | |
311 | KAction *widgetAction = new KAction(this); |
312 | widgetAction->setDefaultWidget(playgroundCombo); |
313 | actionCollection()->addAction( QLatin1String( "playgroundSelection" ),widgetAction); |
314 | |
315 | setupGUI(ToolBar | Keys | Save | Create); |
316 | } |
317 | |
318 | void TopLevel::saveNewToolbarConfig() |
319 | { |
320 | // this destroys our actions lists ... |
321 | KXmlGuiWindow::saveNewToolbarConfig(); |
322 | // ... so plug them again |
323 | plugActionList( QLatin1String( "playgroundList" ), playgroundsGroup->actions() ); |
324 | plugActionList( QLatin1String( "languagesList" ), languagesGroup->actions() ); |
325 | } |
326 | |
327 | // Reset gameboard |
328 | void TopLevel::fileNew() |
329 | { |
330 | playGround->reset(); |
331 | } |
332 | |
333 | // Load gameboard |
334 | void TopLevel::fileOpen() |
335 | { |
336 | KUrl url = KFileDialog::getOpenUrl(KUrl("kfiledialog:///<ktuberling>" ), |
337 | QString::fromLatin1("*.tuberling|%1\n*|%2" ).arg(i18n("KTuberling files" ), i18n("All files" ))); |
338 | |
339 | open(url); |
340 | } |
341 | |
342 | void TopLevel::open(const KUrl &url) |
343 | { |
344 | if (url.isEmpty()) |
345 | return; |
346 | |
347 | QString name; |
348 | |
349 | KIO::NetAccess::download(url, name, this); |
350 | |
351 | switch(playGround->loadFrom(name)) |
352 | { |
353 | case PlayGround::NoError: |
354 | // good |
355 | break; |
356 | |
357 | case PlayGround::OldFileVersionError: |
358 | KMessageBox::error(this, i18n("The saved file is from an old version of KTuberling and unfortunately cannot be opened with this version." )); |
359 | break; |
360 | |
361 | case PlayGround::OtherError: |
362 | KMessageBox::error(this, i18n("Could not load file." )); |
363 | break; |
364 | } |
365 | |
366 | |
367 | KIO::NetAccess::removeTempFile( name ); |
368 | } |
369 | |
370 | // Save gameboard |
371 | void TopLevel::fileSave() |
372 | { |
373 | KUrl url = KFileDialog::getSaveUrl |
374 | ( KUrl("kfiledialog:///<ktuberling>" ), |
375 | QString::fromLatin1("*.tuberling|%1" ).arg(i18n("KTuberling files" )), this, QString(), KFileDialog::ConfirmOverwrite); |
376 | |
377 | if (url.isEmpty()) |
378 | return; |
379 | |
380 | KTemporaryFile tempFile; // for network saving |
381 | QString name; |
382 | if( !url.isLocalFile() ) |
383 | { |
384 | if (tempFile.open()) name = tempFile.fileName(); |
385 | else |
386 | { |
387 | KMessageBox::error(this, i18n("Could not save file." )); |
388 | return; |
389 | } |
390 | } |
391 | else |
392 | { |
393 | name = url.path(); |
394 | } |
395 | |
396 | if( !playGround->saveAs( name ) ) |
397 | { |
398 | KMessageBox::error(this, i18n("Could not save file." )); |
399 | return; |
400 | } |
401 | |
402 | if( !url.isLocalFile() ) |
403 | { |
404 | if (!KIO::NetAccess::upload(name, url, this)) |
405 | KMessageBox::error(this, i18n("Could not save file." )); |
406 | } |
407 | } |
408 | |
409 | // Save gameboard as picture |
410 | void TopLevel::filePicture() |
411 | { |
412 | const QString patternsString = KImageIO::pattern(KImageIO::Writing); |
413 | QStringList patterns = patternsString.split("\n" ); |
414 | // Favor png |
415 | if (!patterns.isEmpty()) { |
416 | QString firstLine = patterns[0]; |
417 | patterns.removeAt(0); |
418 | if (firstLine.contains(" *.png" )) { |
419 | firstLine.remove(" *.png" ); |
420 | firstLine.prepend("*.png " ); |
421 | } |
422 | patterns.prepend(firstLine); |
423 | } |
424 | const KUrl url = KFileDialog::getSaveUrl(KUrl(QString("kfiledialog:///<ktuberling>" )), patterns.join("\n" ), this, QString(), KFileDialog::ConfirmOverwrite); |
425 | |
426 | if( url.isEmpty() ) |
427 | return; |
428 | |
429 | KTemporaryFile tempFile; // for network saving |
430 | QString name; |
431 | if( !url.isLocalFile() ) |
432 | { |
433 | tempFile.open(); |
434 | name = tempFile.fileName(); |
435 | } |
436 | else |
437 | { |
438 | name = url.path(); |
439 | } |
440 | |
441 | KMimeType::Ptr mime = KMimeType::findByUrl(url, 0, true, true); |
442 | if (!KImageIO::isSupported(mime->name(), KImageIO::Writing)) |
443 | { |
444 | KMessageBox::error(this, i18n("Unknown picture format." )); |
445 | return; |
446 | }; |
447 | |
448 | QStringList types = KImageIO::typeForMime(mime->name()); |
449 | if (types.isEmpty()) return; // TODO error dialog? |
450 | |
451 | QPixmap picture(playGround->getPicture()); |
452 | |
453 | if (!picture.save(name, types.at(0).toLatin1())) |
454 | { |
455 | KMessageBox::error |
456 | (this, i18n("Could not save file." )); |
457 | return; |
458 | } |
459 | |
460 | if( !url.isLocalFile() ) |
461 | { |
462 | if (! KIO::NetAccess::upload(name, url, this)) |
463 | KMessageBox::error(this, i18n("Could not save file." )); |
464 | } |
465 | |
466 | } |
467 | |
468 | // Save gameboard as picture |
469 | void TopLevel::filePrint() |
470 | { |
471 | QPrinter printer; |
472 | bool ok; |
473 | |
474 | QPrintDialog *printDialog = KdePrint::createPrintDialog(&printer, this); |
475 | printDialog->setWindowTitle(i18n("Print %1" , actionCollection()->action(playGround->currentGameboard())->iconText())); |
476 | ok = printDialog->exec(); |
477 | delete printDialog; |
478 | if (!ok) return; |
479 | playGround->repaint(); |
480 | if (!playGround->printPicture(printer)) |
481 | KMessageBox::error(this, |
482 | i18n("Could not print picture." )); |
483 | else |
484 | KMessageBox::information(this, |
485 | i18n("Picture successfully printed." )); |
486 | } |
487 | |
488 | // Copy modified area to clipboard |
489 | void TopLevel::editCopy() |
490 | { |
491 | QClipboard *clipboard = QApplication::clipboard(); |
492 | QPixmap picture(playGround->getPicture()); |
493 | |
494 | clipboard->setPixmap(picture); |
495 | } |
496 | |
497 | // Toggle sound off |
498 | void TopLevel::soundOff() |
499 | { |
500 | actionCollection()->action(QLatin1String( "speech_no_sound" ))->setChecked(true); |
501 | writeOptions(); |
502 | } |
503 | |
504 | bool TopLevel::isSoundEnabled() const |
505 | { |
506 | return !actionCollection()->action(QLatin1String( "speech_no_sound" ))->isChecked(); |
507 | } |
508 | |
509 | void TopLevel::toggleFullScreen() |
510 | { |
511 | KToggleFullScreenAction::setFullScreen( this, actionCollection()->action(QLatin1String( "fullscreen" ))->isChecked()); |
512 | } |
513 | |
514 | void TopLevel::lockAspectRatio(bool lock) |
515 | { |
516 | actionCollection()->action(QLatin1String( "lock_aspect_ratio" ))->setChecked(lock); |
517 | playGround->lockAspectRatio(lock); |
518 | writeOptions(); |
519 | } |
520 | |