1 /* Copyright (C) 2003-2009 Jesper K. Pedersen <blackie@kde.org>
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public
5 License as published by the Free Software Foundation; either
6 version 2 of the License, or (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; see the file COPYING. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
20 #include "ThumbnailView/ThumbnailFacade.h"
21 #include <KActionCollection>
22 #include "BreadcrumbViewer.h"
25 #include "StatisticsDialog.h"
26 #include "Settings/SettingsDialog.h"
27 #include <qapplication.h>
29 #include <QResizeEvent>
30 #include <QContextMenuEvent>
33 #include <QCloseEvent>
34 #include <QVBoxLayout>
36 #include "ThumbnailView/ThumbnailBuilder.h"
37 #include "AnnotationDialog/Dialog.h"
39 #include <qmessagebox.h>
40 #include "Viewer/ViewerWidget.h"
41 #include "WelcomeDialog.h"
43 #include "Utilities/ShowBusyCursor.h"
46 #include <q3widgetstack.h>
47 #include "HTMLGenerator/HTMLDialog.h"
48 #include <kstatusbar.h>
49 #include "ImageCounter.h"
51 #include <kmessagebox.h>
52 #include "Settings/SettingsData.h"
53 #include "Browser/BrowserWidget.h"
54 #include "DB/ImageDB.h"
55 #include "Utilities/Util.h"
56 #include "Utilities/List.h"
57 #include <kapplication.h>
60 #include "DeleteDialog.h"
61 #include <ksimpleconfig.h>
62 #include <kcmdlineargs.h>
64 #include <kiconloader.h>
65 #include <kpassworddialog.h>
66 #include <KShortcutsDialog>
68 #include "ExternalPopup.h"
69 #include <kstandardaction.h>
70 #include <kedittoolbar.h>
71 #include "ImportExport/Export.h"
72 #include "ImportExport/Import.h"
73 #include <config-kpa-kipi.h>
75 # include "Plugins/Interface.h"
76 # include <libkipi/pluginloader.h>
77 # include <libkipi/plugin.h>
79 #include <config-kpa-exiv2.h>
81 # include "Exif/ReReadDialog.h"
83 #include "ImageManager/ImageLoader.h"
84 #include "SplashScreen.h"
86 #include "SearchBar.h"
87 #include "TokenEditor.h"
88 #include "DB/CategoryCollection.h"
90 #include "DateBar/DateBarWidget.h"
91 #include "DB/ImageDateCollection.h"
92 #include "InvalidDateFinder.h"
93 #include "DB/ImageInfo.h"
94 #include "DB/ResultId.h"
95 #include "DB/Result.h"
100 # include "Exif/Info.h"
101 # include "Exif/InfoDialog.h"
102 # include "Exif/Database.h"
105 #include "FeatureDialog.h"
106 #include "ImageManager/ImageRequest.h"
107 #include "ImageManager/Manager.h"
109 #include <config-kpa-sqldb.h>
111 # include "SQLDB/Database.h"
112 # include "SQLDB/ConfigFileHandler.h"
113 # include "SQLDB/QueryErrors.h"
114 # include <kprogressdialog.h>
119 #include "DirtyIndicator.h"
120 #include "Utilities/ShowBusyCursor.h"
121 #include <KToggleAction>
122 #include <KActionMenu>
125 #include <qclipboard.h>
127 #include <KInputDialog>
128 #include "DB/Result.h"
129 #include "ThumbnailView/enums.h"
131 MainWindow::Window
* MainWindow::Window::_instance
= 0;
133 MainWindow::Window::Window( QWidget
* parent
)
134 :KXmlGuiWindow( parent
),
135 _annotationDialog(0),
136 _deleteDialog( 0 ), _htmlDialog(0), _tokenEditor( 0 )
138 SplashScreen::instance()->message( i18n("Loading Database") );
141 bool gotConfigFile
= load();
142 if ( !gotConfigFile
)
144 SplashScreen::instance()->message( i18n("Loading Main Window") );
146 QWidget
* top
= new QWidget( this );
147 QVBoxLayout
* lay
= new QVBoxLayout( top
);
148 setCentralWidget( top
);
150 _stack
= new Q3WidgetStack( top
, "_stack" );
151 lay
->addWidget( _stack
, 1 );
153 _dateBar
= new DateBar::DateBarWidget( top
);
154 lay
->addWidget( _dateBar
);
156 Q3Frame
* line
= new Q3Frame( top
);
157 line
->setFrameStyle( Q3Frame::HLine
| Q3Frame::Plain
);
158 line
->setLineWidth(1);
159 lay
->addWidget( line
);
161 _browser
= new Browser::BrowserWidget( _stack
);
162 _thumbnailView
= new ThumbnailView::ThumbnailFacade();
164 _stack
->addWidget( _browser
);
165 _stack
->addWidget( _thumbnailView
->gui() );
166 _stack
->raiseWidget( _browser
);
175 _autoSaveTimer
= new QTimer( this );
176 connect( _autoSaveTimer
, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) );
177 startAutoSaveTimer();
179 connect( _browser
, SIGNAL( showingOverview() ), this, SLOT( showBrowser() ) );
180 connect( _browser
, SIGNAL( pathChanged( const Browser::BreadcrumbList
& ) ), _pathIndicator
, SLOT( setBreadcrumbs( const Browser::BreadcrumbList
& ) ) );
181 connect( _pathIndicator
, SIGNAL( widenToBreadcrumb( const Browser::Breadcrumb
& ) ), _browser
, SLOT( widenToBreadcrumb( const Browser::Breadcrumb
& ) ) );
182 connect( _browser
, SIGNAL( pathChanged( const Browser::BreadcrumbList
& ) ), this, SLOT( updateDateBar( const Browser::BreadcrumbList
& ) ) );
183 connect( _dateBar
, SIGNAL( dateSelected( const DB::ImageDate
&, bool ) ), _thumbnailView
, SLOT( gotoDate( const DB::ImageDate
&, bool ) ) );
184 connect( _dateBar
, SIGNAL( toolTipInfo( const QString
& ) ), this, SLOT( showDateBarTip( const QString
& ) ) );
185 connect( Settings::SettingsData::instance(), SIGNAL( histogramSizeChanged( const QSize
& ) ), _dateBar
, SLOT( setHistogramBarSize( const QSize
& ) ) );
188 connect( _dateBar
, SIGNAL( dateRangeChange( const DB::ImageDate
& ) ),
189 this, SLOT( setDateRange( const DB::ImageDate
& ) ) );
190 connect( _dateBar
, SIGNAL( dateRangeCleared() ), this, SLOT( clearDateRange() ) );
192 connect( _thumbnailView
, SIGNAL( showImage( const DB::ResultId
& ) ), this, SLOT( showImage( const DB::ResultId
& ) ) );
193 connect( _thumbnailView
, SIGNAL( showSelection() ), this, SLOT( slotView() ) );
194 connect( _thumbnailView
, SIGNAL( currentDateChanged( const QDateTime
& ) ), _dateBar
, SLOT( setDate( const QDateTime
& ) ) );
196 connect( _thumbnailView
, SIGNAL( fileIdUnderCursorChanged( const DB::ResultId
& ) ), this, SLOT( slotSetFileName( const DB::ResultId
& ) ) );
197 connect( DB::ImageDB::instance(), SIGNAL( totalChanged( uint
) ), this, SLOT( updateDateBar() ) );
198 connect( DB::ImageDB::instance(), SIGNAL( dirty() ), _dirtyIndicator
, SLOT( markDirtySlot() ) );
199 connect( DB::ImageDB::instance()->categoryCollection(), SIGNAL( categoryCollectionChanged() ), this, SLOT( slotOptionGroupChanged() ) );
200 connect( _browser
, SIGNAL( imageCount(uint
)), _partial
, SLOT( showBrowserMatches(uint
) ) );
201 connect( _thumbnailView
, SIGNAL( selectionChanged(int) ), this, SLOT( slotThumbNailSelectionChanged(int) ) );
203 connect( _dirtyIndicator
, SIGNAL( dirty() ), _thumbnailView
, SLOT(repaintScreen() ) );
205 QTimer::singleShot( 0, this, SLOT( delayedInit() ) );
206 slotThumbNailSelectionChanged(0);
208 // Automatically save toolbar settings
209 setAutoSaveSettings();
212 MainWindow::Window::~Window()
214 DB::ImageDB::deleteInstance();
216 Exif::Database::deleteInstance();
220 void MainWindow::Window::delayedInit()
222 SplashScreen
* splash
= SplashScreen::instance();
225 if ( Settings::SettingsData::instance()->searchForImagesOnStart() ) {
226 splash
->message( i18n("Searching for New Files") );
227 qApp
->processEvents();
228 QTimer
* timer
= new QTimer( this );
229 connect( timer
, SIGNAL( timeout() ), DB::ImageDB::instance(), SLOT( slotRescan() ) );
230 timer
->setSingleShot( true );
234 if ( !Settings::SettingsData::instance()->delayLoadingPlugins() ) {
235 splash
->message( i18n( "Loading Plug-ins" ) );
242 KCmdLineArgs
*args
= KCmdLineArgs::parsedArgs();
243 if ( args
->isSet( "import" ) ) {
244 // I need to do this in delayed init to get the import window on top of the normal window
245 ImportExport::Import::imageImport( KCmdLineArgs::makeURL( args
->getOption("import").toLocal8Bit() ) );
248 // I need to postpone this otherwise the tip dialog will not get focus on start up
249 KTipDialog::showTip( this );
254 Exif::Database
* exifDB
= Exif::Database::instance(); // Load the database
255 if ( exifDB
->isAvailable() && !exifDB
->isOpen() ) {
256 KMessageBox::sorry( this, i18n("EXIF database cannot be opened. Check that the image root directory is writable.") );
262 bool MainWindow::Window::slotExit()
264 if ( Utilities::runningDemo() ) {
265 QString txt
= i18n("<p><b>Delete Your Temporary Demo Database</b></p>"
266 "<p>I hope you enjoyed the KPhotoAlbum demo. The demo database was copied to "
267 "/tmp, should it be deleted now? If you do not delete it, it will waste disk space; "
268 "on the other hand, if you want to come back and try the demo again, you "
269 "might want to keep it around with the changes you made through this session.</p>" );
270 int ret
= KMessageBox::questionYesNoCancel( this, txt
, i18n("Delete Demo Database"),
271 KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel(),
272 QString::fromLatin1("deleteDemoDatabase") );
273 if ( ret
== KMessageBox::Cancel
)
275 else if ( ret
== KMessageBox::Yes
) {
276 Utilities::deleteDemo();
280 // pass through to the check for dirtyness.
284 if ( _dirtyIndicator
->isSaveDirty() ) {
285 int ret
= KMessageBox::warningYesNoCancel( this, i18n("Do you want to save the changes?"),
286 i18n("Save Changes?") );
287 if ( ret
== KMessageBox::Cancel
)
289 if ( ret
== KMessageBox::Yes
) {
292 if ( ret
== KMessageBox::No
) {
293 QDir().remove( Settings::SettingsData::instance()->imageDirectory() + QString::fromLatin1(".#index.xml") );
302 void MainWindow::Window::slotOptions()
304 if ( ! _optionsDialog
) {
305 _optionsDialog
= new Settings::SettingsDialog( this );
306 connect( _optionsDialog
, SIGNAL( changed() ), this, SLOT( reloadThumbnailsAndFlushCache() ) );
307 connect( _optionsDialog
, SIGNAL( changed() ), this, SLOT( startAutoSaveTimer() ) );
309 _optionsDialog
->show();
312 void MainWindow::Window::slotCreateImageStack()
314 const DB::Result
& list
= selected();
315 if (list
.size() < 2) {
316 // it doesn't make sense to make a stack from one image, does it?
320 bool ok
= DB::ImageDB::instance()->stack( list
);
322 if ( KMessageBox::questionYesNo( this,
323 i18n("Some of the selected images already belong to a stack. "
324 "Do you want to remove them from their stacks and create a "
325 "completely new one?"), i18n("Stacking Error")) == KMessageBox::Yes
) {
326 DB::ImageDB::instance()->unstack( list
);
327 if ( ! DB::ImageDB::instance()->stack( list
) ) {
328 KMessageBox::sorry( this,
329 i18n("Unknown error, stack creation failed."),
330 i18n("Stacking Error"));
338 // FIXME: here we should invoke a fancy dialog for user's pleasure
340 DirtyIndicator::markDirty();
341 // The current item might have just became invisible
342 _thumbnailView
->setCurrentItem( list
.at(0) );
343 _thumbnailView
->updateDisplayModel();
346 /** @short Make the selected image the head of a stack
348 * The whole point of image stacking is to group images together and then select
349 * one of them as the "most important". This function is (maybe just a
350 * temporary) way of promoting a selected image to the "head" of a stack it
351 * belongs to. In future, it might get replaced by a Ligtroom-like interface.
353 void MainWindow::Window::slotSetStackHead()
355 const DB::Result
& list
= selected();
356 if ( list
.size() != 1 ) {
357 // this should be checked by enabling/disabling of QActions
361 setStackHead( *list
.begin() );
364 void MainWindow::Window::setStackHead( const DB::ResultId image
)
366 if ( ! image
.fetchInfo()->isStacked() )
369 unsigned int oldOrder
= image
.fetchInfo()->stackOrder();
371 DB::Result others
= DB::ImageDB::instance()->getStackFor( image
);
373 for ( DB::Result::const_iterator it
= others
.begin(); it
!= others
.end(); ++it
) {
374 DB::ResultId current
= *it
;
375 if ( current
== image
) {
376 current
.fetchInfo()->setStackOrder( 1 );
377 } else if ( current
.fetchInfo()->stackOrder() < oldOrder
) {
378 current
.fetchInfo()->setStackOrder( current
.fetchInfo()->stackOrder() + 1 );
382 DirtyIndicator::markDirty();
383 _thumbnailView
->updateDisplayModel();
386 void MainWindow::Window::slotUnStackImages()
388 const DB::Result
& list
= selected();
392 DB::ImageDB::instance()->unstack( list
);
393 DirtyIndicator::markDirty();
394 _thumbnailView
->updateDisplayModel();
397 void MainWindow::Window::slotConfigureAllImages()
399 configureImages( false );
402 void MainWindow::Window::slotConfigureImagesOneAtATime()
404 configureImages( true );
407 void MainWindow::Window::configureImages( bool oneAtATime
)
409 const DB::Result
& list
= selected();
410 if (list
.isEmpty()) {
411 KMessageBox::sorry( this, i18n("No item is selected."), i18n("No Selection") );
414 DB::ImageInfoList images
;
415 Q_FOREACH(DB::ImageInfoPtr info
, list
.fetchInfos()) {
418 configureImages( images
, oneAtATime
);
422 void MainWindow::Window::configureImages( const DB::ImageInfoList
& list
, bool oneAtATime
)
424 _instance
->configImages( list
, oneAtATime
);
428 void MainWindow::Window::configImages( const DB::ImageInfoList
& list
, bool oneAtATime
)
430 createAnnotationDialog();
431 _annotationDialog
->configure( list
, oneAtATime
);
432 if ( _annotationDialog
->thumbnailShouldReload() )
433 reloadThumbnails(true);
434 else if ( _annotationDialog
->thumbnailTextShouldReload() )
435 _thumbnailView
->reload(false, false);
439 void MainWindow::Window::slotSearch()
441 createAnnotationDialog();
442 DB::ImageSearchInfo searchInfo
= _annotationDialog
->search();
443 if ( !searchInfo
.isNull() )
444 _browser
->addSearch( searchInfo
);
447 void MainWindow::Window::createAnnotationDialog()
449 Utilities::ShowBusyCursor dummy
;
450 if ( !_annotationDialog
.isNull() )
453 _annotationDialog
= new AnnotationDialog::Dialog( 0 );
456 void MainWindow::Window::slotSave()
458 Utilities::ShowBusyCursor dummy
;
459 statusBar()->showMessage(i18n("Saving..."), 5000 );
460 DB::ImageDB::instance()->save( Settings::SettingsData::instance()->imageDirectory() + QString::fromLatin1("index.xml"), false );
461 _dirtyIndicator
->saved();
462 QDir().remove( Settings::SettingsData::instance()->imageDirectory() + QString::fromLatin1(".#index.xml") );
463 statusBar()->showMessage(i18n("Saving... Done"), 5000 );
466 void MainWindow::Window::slotDeleteSelected()
468 if ( ! _deleteDialog
)
469 _deleteDialog
= new DeleteDialog( this );
470 if ( _deleteDialog
->exec( selected() ) != QDialog::Accepted
)
473 DirtyIndicator::markDirty();
476 void MainWindow::Window::slotCopySelectedURLs()
479 Q_FOREACH(const DB::ImageInfoPtr info
, selected().fetchInfos()) {
480 const QString fileName
= info
->fileName(DB::AbsolutePath
);
481 urls
.append( fileName
);
483 QMimeData
* mimeData
= new QMimeData
;
484 urls
.populateMimeData(mimeData
);
486 QApplication::clipboard()->setMimeData( mimeData
);
489 void MainWindow::Window::slotReReadExifInfo()
492 QStringList files
= DB::ImageDB::instance()->CONVERT(selectedOnDisk());
493 static Exif::ReReadDialog
* dialog
= 0;
495 dialog
= new Exif::ReReadDialog( this );
496 if ( dialog
->exec( files
) == QDialog::Accepted
)
497 DirtyIndicator::markDirty();
502 DB::Result
MainWindow::Window::selected(bool keepSortOrderOfDatabase
)
504 if ( _thumbnailView
->gui() == _stack
->visibleWidget() )
505 return _thumbnailView
->selection( keepSortOrderOfDatabase
);
510 void MainWindow::Window::slotViewNewWindow()
512 slotView( false, false );
516 * Returns a list of files that are both selected and on disk. If there are no
517 * selected files, returns all files form current context that are on disk.
519 DB::Result
MainWindow::Window::selectedOnDisk()
521 const DB::Result
& list
= selected();
523 return DB::ImageDB::instance()->currentScope( true );
525 DB::Result listOnDisk
;
526 Q_FOREACH(DB::ResultId id
, list
) {
527 const QString fileName
= id
.fetchInfo()->fileName(DB::AbsolutePath
);
528 if ( DB::ImageInfo::imageOnDisk( fileName
) )
529 listOnDisk
.append(id
);
535 void MainWindow::Window::slotView( bool reuse
, bool slideShow
, bool random
)
537 launchViewer( selected(), reuse
, slideShow
, random
);
540 void MainWindow::Window::launchViewer(const DB::Result
& inputMediaList
, bool reuse
, bool slideShow
, bool random
)
542 DB::Result mediaList
= inputMediaList
;
544 if (mediaList
.isEmpty()) {
545 mediaList
= _thumbnailView
->imageList( ThumbnailView::ViewOrder
);
546 } else if (mediaList
.size() == 1) {
547 // we fake it so it appears the user has selected all images
548 // and magically scrolls to the originally selected one
549 DB::ResultId first
= mediaList
.at(0);
550 mediaList
= _thumbnailView
->imageList( ThumbnailView::ViewOrder
);
551 seek
= mediaList
.indexOf(first
);
554 if (mediaList
.isEmpty())
555 mediaList
= DB::ImageDB::instance()->currentScope( false );
557 if (mediaList
.isEmpty()) {
558 KMessageBox::sorry( this, i18n("There are no images to be shown.") );
563 mediaList
= DB::Result(Utilities::shuffleList(mediaList
.rawIdList()));
566 // Here, we need to switch back to the StringList until the Viewer is
568 QStringList fileNameList
= DB::ImageDB::instance()->CONVERT(mediaList
);
570 Viewer::ViewerWidget
* viewer
;
571 if ( reuse
&& Viewer::ViewerWidget::latest() ) {
572 viewer
= Viewer::ViewerWidget::latest();
574 viewer
->activateWindow();
577 viewer
= new Viewer::ViewerWidget(Viewer::ViewerWidget::ViewerWindow
,
578 &_viewerInputMacros
);
579 connect( viewer
, SIGNAL( rotated() ), this, SLOT( reloadThumbnailsAndFlushCache() ) );
580 connect( viewer
, SIGNAL( soughtTo(const DB::ResultId
&) ), _thumbnailView
, SLOT( changeSingleSelection(const DB::ResultId
&) ) );
582 viewer
->show( slideShow
);
583 viewer
->load( fileNameList
, seek
< 0 ? 0 : seek
);
587 void MainWindow::Window::slotSortByDateAndTime()
589 DB::ImageDB::instance()->sortAndMergeBackIn( selected( true /* sort with oldest first */ ));
590 showThumbNails( DB::ImageDB::instance()->search( Browser::BrowserWidget::instance()->currentContext() ) );
591 DirtyIndicator::markDirty();
595 QString
MainWindow::Window::welcome()
597 WelcomeDialog
dialog( this );
599 return dialog
.configFileName();
602 void MainWindow::Window::closeEvent( QCloseEvent
* e
)
606 // If I made it here, then the user canceled
610 e
->setAccepted(true);
614 void MainWindow::Window::slotLimitToSelected()
616 Utilities::ShowBusyCursor dummy
;
617 showThumbNails( selected() );
620 void MainWindow::Window::setupMenuBar()
623 KStandardAction::save( this, SLOT( slotSave() ), actionCollection() );
624 KStandardAction::quit( this, SLOT( slotExit() ), actionCollection() );
625 _generateHtml
= actionCollection()->addAction( QString::fromLatin1("exportHTML") );
626 _generateHtml
->setText( i18n("Generate HTML...") );
627 connect( _generateHtml
, SIGNAL(triggered()), this, SLOT( slotExportToHTML() ) );
629 KAction
* a
= actionCollection()->addAction( QString::fromLatin1("import"), this, SLOT( slotImport() ) );
630 a
->setText( i18n( "Import...") );
632 a
= actionCollection()->addAction( QString::fromLatin1("export"), this, SLOT( slotExport() ) );
633 a
->setText( i18n( "Export/Copy Images...") );
637 a
= KStandardAction::back( _browser
, SLOT( back() ), actionCollection() );
638 connect( _browser
, SIGNAL( canGoBack( bool ) ), a
, SLOT( setEnabled( bool ) ) );
639 a
->setEnabled( false );
641 a
= KStandardAction::forward( _browser
, SLOT( forward() ), actionCollection() );
642 connect( _browser
, SIGNAL( canGoForward( bool ) ), a
, SLOT( setEnabled( bool ) ) );
643 a
->setEnabled( false );
645 a
= KStandardAction::home( _browser
, SLOT( home() ), actionCollection() );
646 connect( a
, SIGNAL( activated() ), _dateBar
, SLOT( clearSelection() ) );
648 a
= KStandardAction::redisplay( _browser
, SLOT( go() ), actionCollection() );
651 KStandardAction::copy( this, SLOT( slotCopySelectedURLs() ), actionCollection() );
652 _selectAll
= KStandardAction::selectAll( _thumbnailView
, SLOT( selectAll() ), actionCollection() );
653 KStandardAction::find( this, SLOT( slotSearch() ), actionCollection() );
655 _deleteSelected
= actionCollection()->addAction(QString::fromLatin1("deleteSelected"));
656 _deleteSelected
->setText( i18n( "Delete Selected" ) );
657 _deleteSelected
->setIcon( KIcon( QString::fromLatin1("edit-delete") ) );
658 _deleteSelected
->setShortcut( Qt::Key_Delete
);
659 connect( _deleteSelected
, SIGNAL( triggered() ), this, SLOT( slotDeleteSelected() ) );
661 a
= actionCollection()->addAction(QString::fromLatin1("removeTokens"), this, SLOT( slotRemoveTokens() ));
662 a
->setText( i18n("Remove Tokens") );
664 a
= actionCollection()->addAction(QString::fromLatin1("showListOfFiles"), this, SLOT( slotShowListOfFiles() ));
665 a
->setText( i18n("Open List of Files...")) ;
668 _configOneAtATime
= actionCollection()->addAction( QString::fromLatin1("oneProp"), this, SLOT( slotConfigureImagesOneAtATime() ) );
669 _configOneAtATime
->setText( i18n( "Annotate Individual Items" ) );
670 _configOneAtATime
->setShortcut( Qt::CTRL
+Qt::Key_1
);
672 _configAllSimultaniously
= actionCollection()->addAction( QString::fromLatin1("allProp"), this, SLOT( slotConfigureAllImages() ) );
673 _configAllSimultaniously
->setText( i18n( "Annotate Multiple Items at a Time" ) );
674 _configAllSimultaniously
->setShortcut( Qt::CTRL
+Qt::Key_2
);
676 _createImageStack
= actionCollection()->addAction( QString::fromLatin1("createImageStack"), this, SLOT( slotCreateImageStack() ) );
677 _createImageStack
->setText( i18n("Merge Images into a Stack") );
678 _createImageStack
->setShortcut( Qt::CTRL
+ Qt::Key_3
);
680 _unStackImages
= actionCollection()->addAction( QString::fromLatin1("unStackImages"), this, SLOT( slotUnStackImages() ) );
681 _unStackImages
->setText( i18n("Remove Images from Stack") );
683 _setStackHead
= actionCollection()->addAction( QString::fromLatin1("setStackHead"), this, SLOT( slotSetStackHead() ) );
684 _setStackHead
->setText( i18n("Set as First Image in Stack") );
685 _setStackHead
->setShortcut( Qt::CTRL
+ Qt::Key_4
);
687 _rotLeft
= actionCollection()->addAction( QString::fromLatin1("rotateLeft"), this, SLOT( slotRotateSelectedLeft() ) );
688 _rotLeft
->setText( i18n( "Rotate counterclockwise" ) );
689 _rotLeft
->setShortcut( 0 );
692 _rotRight
= actionCollection()->addAction( QString::fromLatin1("rotateRight"), this, SLOT( slotRotateSelectedRight() ) );
693 _rotRight
->setText( i18n( "Rotate clockwise" ) );
697 _view
= actionCollection()->addAction( QString::fromLatin1("viewImages"), this, SLOT( slotView() ) );
698 _view
->setText( i18n("View") );
699 _view
->setShortcut( Qt::CTRL
+Qt::Key_I
);
701 _viewInNewWindow
= actionCollection()->addAction( QString::fromLatin1("viewImagesNewWindow"), this, SLOT( slotViewNewWindow() ) );
702 _viewInNewWindow
->setText( i18n("View (In New Window)") );
704 _runSlideShow
= actionCollection()->addAction( QString::fromLatin1("runSlideShow"), this, SLOT( slotRunSlideShow() ) );
705 _runSlideShow
->setText( i18n("Run Slide Show") );
706 _runSlideShow
->setIcon( KIcon( QString::fromLatin1("view-presentation") ) );
707 _runSlideShow
->setShortcut( Qt::CTRL
+Qt::Key_R
);
709 _runRandomSlideShow
= actionCollection()->addAction( QString::fromLatin1("runRandomizedSlideShow"), this, SLOT( slotRunRandomizedSlideShow() ) );
710 _runRandomSlideShow
->setText( i18n( "Run Randomized Slide Show" ) );
712 a
= actionCollection()->addAction( QString::fromLatin1("collapseAllStacks"),
713 _thumbnailView
, SLOT( collapseAllStacks() ) );
714 connect(_thumbnailView
, SIGNAL( collapseAllStacksEnabled(bool) ), a
, SLOT( setEnabled(bool) ));
715 a
->setEnabled(false);
716 a
->setText( i18n("Collapse all stacks" ));
718 a
= actionCollection()->addAction( QString::fromLatin1("expandAllStacks"),
719 _thumbnailView
, SLOT( expandAllStacks() ) );
720 connect(_thumbnailView
, SIGNAL( expandAllStacksEnabled(bool) ), a
, SLOT( setEnabled(bool) ));
721 a
->setEnabled(false);
722 a
->setText( i18n("Expand all stacks" ));
724 QActionGroup
* grp
= new QActionGroup( this );
726 a
= actionCollection()->add
<KToggleAction
>( QString::fromLatin1("orderIncr"), this, SLOT( slotOrderIncr() ) );
727 a
->setText( i18n("Show &Oldest First") ) ;
728 a
->setActionGroup(grp
);
729 a
->setChecked( !Settings::SettingsData::instance()->showNewestThumbnailFirst() );
731 a
= actionCollection()->add
<KToggleAction
>( QString::fromLatin1("orderDecr"), this, SLOT( slotOrderDecr() ) );
732 a
->setText( i18n("Show &Newest First") );
733 a
->setActionGroup(grp
);
734 a
->setChecked( Settings::SettingsData::instance()->showNewestThumbnailFirst() );
736 _sortByDateAndTime
= actionCollection()->addAction( QString::fromLatin1("sortImages"), this, SLOT( slotSortByDateAndTime() ) );
737 _sortByDateAndTime
->setText( i18n("Sort Selected by Date && Time") );
739 _limitToMarked
= actionCollection()->addAction( QString::fromLatin1("limitToMarked"), this, SLOT( slotLimitToSelected() ) );
740 _limitToMarked
->setText( i18n("Limit View to Marked") );
742 _jumpToContext
= actionCollection()->addAction( QString::fromLatin1("jumpToContext"), this, SLOT( slotJumpToContext() ) );
743 _jumpToContext
->setText( i18n("Jump to Context") );
744 _jumpToContext
->setShortcut( Qt::CTRL
+Qt::Key_J
);
745 _jumpToContext
->setIcon( KIcon( QString::fromLatin1( "kphotoalbum" ) ) ); // icon suggestion: go-jump (don't know the exact meaning though, so I didn't replace it right away
747 _lock
= actionCollection()->addAction( QString::fromLatin1("lockToDefaultScope"), this, SLOT( lockToDefaultScope() ) );
748 _lock
->setText( i18n("Lock Images") );
750 _unlock
= actionCollection()->addAction( QString::fromLatin1("unlockFromDefaultScope"), this, SLOT( unlockFromDefaultScope() ) );
751 _unlock
->setText( i18n("Unlock") );
753 a
= actionCollection()->addAction( QString::fromLatin1("changeScopePasswd"), this, SLOT( changePassword() ) );
754 a
->setText( i18n("Change Password...") );
757 _setDefaultPos
= actionCollection()->addAction( QString::fromLatin1("setDefaultScopePositive"), this, SLOT( setDefaultScopePositive() ) );
758 _setDefaultPos
->setText( i18n("Lock Away All Other Items") );
760 _setDefaultNeg
= actionCollection()->addAction( QString::fromLatin1("setDefaultScopeNegative"), this, SLOT( setDefaultScopeNegative() ) );
761 _setDefaultNeg
->setText( i18n("Lock Away Current Set of Items") );
764 a
= actionCollection()->addAction( QString::fromLatin1("findUnavailableImages"), this, SLOT( slotShowNotOnDisk() ) );
765 a
->setText( i18n("Display Images and Videos Not on Disk") );
767 a
= actionCollection()->addAction( QString::fromLatin1("findImagesWithInvalidDate"), this, SLOT( slotShowImagesWithInvalidDate() ) );
768 a
->setText( i18n("Display Images and Videos with Incomplete Dates...") );
770 #ifdef DOES_STILL_NOT_WORK_IN_KPA4
771 a
= actionCollection()->addAction( QString::fromLatin1("findImagesWithChangedMD5Sum"), this, SLOT( slotShowImagesWithChangedMD5Sum() ) );
772 a
->setText( i18n("Display Images and Videos with Changed MD5 Sum") );
773 #endif //DOES_STILL_NOT_WORK_IN_KPA4
775 a
= actionCollection()->addAction( QString::fromLatin1("rebuildMD5s"), this, SLOT( slotRecalcCheckSums() ) );
776 a
->setText( i18n("Recalculate Checksum") );
778 a
= actionCollection()->addAction( QString::fromLatin1("rescan"), DB::ImageDB::instance(), SLOT( slotRescan() ) );
779 a
->setText( i18n("Rescan for Images and Videos") );
782 a
= actionCollection()->addAction( QString::fromLatin1( "recreateExifDB" ), this, SLOT( slotRecreateExifDB() ) );
783 a
->setText( i18n("Recreate Exif Search Database") );
785 a
= actionCollection()->addAction( QString::fromLatin1("reReadExifInfo"), this, SLOT( slotReReadExifInfo() ) );
786 a
->setText( i18n("Read EXIF Info From Files...") );
790 a
= actionCollection()->addAction( QString::fromLatin1("convertBackend"), this, SLOT( convertBackend() ) );
791 a
->setText( i18n("Convert Backend...(Experimental!)" ) );
795 a
= actionCollection()->addAction( QString::fromLatin1("buildThumbs"), this, SLOT( slotBuildThumbnails() ) );
796 a
->setText( i18n("Build Thumbnails") );
798 a
= actionCollection()->addAction( QString::fromLatin1("statistics"), this, SLOT( slotStatistics() ) );
799 a
->setText( i18n("Statistics") );
803 KStandardAction::preferences( this, SLOT( slotOptions() ), actionCollection() );
804 KStandardAction::keyBindings( this, SLOT( slotConfigureKeyBindings() ), actionCollection() );
805 KStandardAction::configureToolbars( this, SLOT( slotConfigureToolbars() ), actionCollection() );
807 a
= actionCollection()->addAction( QString::fromLatin1("readdAllMessages"), this, SLOT( slotReenableMessages() ) );
808 a
->setText( i18n("Enable All Messages") );
810 _viewMenu
= actionCollection()->add
<KActionMenu
>( QString::fromLatin1("configureView") );
811 _viewMenu
->setText( i18n("Configure Current View") );
813 _viewMenu
->setIcon( KIcon( QString::fromLatin1( "view-list-details" ) ) );
814 _viewMenu
->setDelayed( false );
816 QActionGroup
* viewGrp
= new QActionGroup( this );
817 viewGrp
->setExclusive( true );
819 _smallListView
= actionCollection()->add
<KToggleAction
>( QString::fromLatin1("smallListView"), _browser
, SLOT( slotSmallListView() ) );
820 _smallListView
->setText( i18n("Tree") );
821 _viewMenu
->addAction( _smallListView
);
822 _smallListView
->setActionGroup( viewGrp
);
824 _largeListView
= actionCollection()->add
<KToggleAction
>( QString::fromLatin1("largelistview"), _browser
, SLOT( slotLargeListView() ) );
825 _largeListView
->setText( i18n("Tree with User Icons") );
826 _viewMenu
->addAction( _largeListView
);
827 _largeListView
->setActionGroup( viewGrp
);
829 #if 0 // I see no reason for this one.
830 _smallIconView
= actionCollection()->add
<KToggleAction
>( QString::fromLatin1("smalliconview"), _browser
, SLOT( slotSmallIconView() ) );
831 _smallIconView
->setText( i18n("Icons") );
832 _viewMenu
->addAction( _smallIconView
);
833 _smallIconView
->setActionGroup( viewGrp
);
836 _largeIconView
= actionCollection()->add
<KToggleAction
>( QString::fromLatin1("largeiconview"), _browser
, SLOT( slotLargeIconView() ) );
837 _largeIconView
->setText( i18n("Icons") );
838 _viewMenu
->addAction( _largeIconView
);
839 _largeIconView
->setActionGroup( viewGrp
);
841 connect( _browser
, SIGNAL( isViewChangeable( bool ) ), viewGrp
, SLOT( setEnabled( bool ) ) );
843 connect( _browser
, SIGNAL( currentViewTypeChanged( DB::Category::ViewType
) ),
844 this, SLOT( slotUpdateViewMenu( DB::Category::ViewType
) ) );
846 KStandardAction::tipOfDay( this, SLOT(showTipOfDay()), actionCollection() );
848 a
= actionCollection()->add
<KToggleAction
>( QString::fromLatin1("showToolTipOnImages") );
849 a
->setText( i18n("Show Tooltips in Thumbnails Window") );
850 a
->setShortcut( Qt::CTRL
+Qt::Key_T
);
851 connect( a
, SIGNAL(toggled(bool)), _thumbnailView
, SLOT( showToolTipsOnImages( bool ) ) );
854 a
= actionCollection()->addAction( QString::fromLatin1("runDemo"), this, SLOT( runDemo() ) );
855 a
->setText( i18n("Run KPhotoAlbum Demo") );
857 a
= actionCollection()->addAction( QString::fromLatin1("features"), this, SLOT( showFeatures() ) );
858 a
->setText( i18n("KPhotoAlbum Feature Status") );
860 a
= actionCollection()->addAction( QString::fromLatin1("showVideo"), this, SLOT(showVideos()) );
861 a
->setText( i18n( "Show Demo Videos") );
863 // Context menu actions
865 _showExifDialog
= actionCollection()->addAction( QString::fromLatin1("showExifInfo"), this, SLOT( slotShowExifInfo() ) );
866 _showExifDialog
->setText( i18n("Show Exif Info") );
868 _recreateThumbnails
= actionCollection()->addAction( QString::fromLatin1("recreateThumbnails"), _thumbnailView
, SLOT( slotRecreateThumbnail() ) );
869 _recreateThumbnails
->setText( i18n("Recreate Selected Thumbnails") );
871 createGUI( QString::fromLatin1( "kphotoalbumui.rc" ) );
874 void MainWindow::Window::slotExportToHTML()
877 _htmlDialog
= new HTMLGenerator::HTMLDialog( this );
878 _htmlDialog
->exec(selectedOnDisk());
881 void MainWindow::Window::startAutoSaveTimer()
883 int i
= Settings::SettingsData::instance()->autoSave();
884 _autoSaveTimer
->stop();
886 _autoSaveTimer
->start( i
* 1000 * 60 );
890 void MainWindow::Window::slotAutoSave()
892 if ( _dirtyIndicator
->isAutoSaveDirty() ) {
893 Utilities::ShowBusyCursor dummy
;
894 statusBar()->showMessage(i18n("Auto saving...."));
895 DB::ImageDB::instance()->save( Settings::SettingsData::instance()->imageDirectory() + QString::fromLatin1(".#index.xml"), true );
896 statusBar()->showMessage(i18n("Auto saving.... Done"), 5000);
897 _dirtyIndicator
->autoSaved();
902 void MainWindow::Window::showThumbNails()
904 reloadThumbnails(false);
905 _stack
->raiseWidget( _thumbnailView
->gui() );
906 _thumbnailView
->gui()->setFocus();
907 updateStates( true );
910 void MainWindow::Window::showBrowser()
912 statusBar()->clearMessage();
913 _stack
->raiseWidget( _browser
);
914 _browser
->setFocus();
915 updateStates( false );
919 void MainWindow::Window::slotOptionGroupChanged()
921 // FIXME: What if annotation dialog is open? (if that's possible)
922 delete _annotationDialog
;
923 _annotationDialog
= 0;
924 DirtyIndicator::markDirty();
927 void MainWindow::Window::showTipOfDay()
929 KTipDialog::showTip( this, QString(), true );
933 void MainWindow::Window::runDemo()
935 KProcess
* process
= new KProcess
;
936 *process
<< QLatin1String("kphotoalbum") << QLatin1String("-demo");
937 process
->startDetached();
940 bool MainWindow::Window::load()
942 // Let first try to find a config file.
943 KCmdLineArgs
*args
= KCmdLineArgs::parsedArgs();
946 if ( args
->isSet( "c" ) ) {
947 configFile
= args
->getOption( "c" );
949 else if ( args
->isSet( "demo" ) )
950 configFile
= Utilities::setupDemo();
952 bool showWelcome
= false;
953 KConfigGroup config
= KGlobal::config()->group(QString());
954 if ( config
.hasKey( QString::fromLatin1("configfile") ) ) {
955 configFile
= config
.readEntry
<QString
>( QString::fromLatin1("configfile"), QString() );
956 if ( !QFileInfo( configFile
).exists() )
963 SplashScreen::instance()->hide();
964 configFile
= welcome();
967 if ( configFile
.isNull() )
970 if (configFile
.startsWith( QString::fromLatin1( "~" ) ) )
971 configFile
= QDir::home().path() + QString::fromLatin1( "/" ) + configFile
.mid(1);
973 // To avoid a race conditions where both the image loader thread creates an instance of
974 // Options, and where the main thread crates an instance, we better get it created now.
975 Settings::SettingsData::setup( QFileInfo( configFile
).absolutePath() );
977 if ( Settings::SettingsData::instance()->showSplashScreen() ) {
978 SplashScreen::instance()->show();
979 qApp
->processEvents();
983 QString backEnd
= Settings::SettingsData::instance()->backend();
984 // Command line override for backend
985 if ( args
->isSet( "e" ) )
986 backEnd
= args
->getOption( "e" );
988 // Initialize correct back-end
989 if ( backEnd
== QString::fromLatin1("sql") ) {
991 // SQL back-end needs some extra configuration first
992 KConfigGroup config
= KGlobal::config()->group(QString::fromLatin1("SQLDB"));
994 SQLDB::DatabaseAddress address
= SQLDB::readConnectionParameters(config
);
996 // Initialize SQLDB with the paramaters
997 DB::ImageDB::setupSQLDB(address
);
1000 catch (SQLDB::Error
& e
){
1001 KMessageBox::error(this, i18n("SQL backend initialization failed, "
1002 "because following error occurred:\n%1",e
.whatAsQString()));
1005 KMessageBox::error(this, i18n("SQL database support is not compiled in."));
1008 else if ( backEnd
== QString::fromLatin1("xml") );
1010 KMessageBox::error(this, i18n("Invalid database backend: %1",backEnd
));
1013 if (backEnd
!= QString::fromLatin1("xml")) {
1015 KMessageBox::questionYesNo(this, i18n("Do you want to use XML backend instead?"));
1016 if (answer
!= KMessageBox::Yes
)
1020 DB::ImageDB::setupXMLDB( configFile
);
1025 void MainWindow::Window::contextMenuEvent( QContextMenuEvent
* e
)
1027 if ( _stack
->visibleWidget() == _thumbnailView
->gui() ) {
1029 menu
.addAction( _configOneAtATime
);
1030 menu
.addAction( _configAllSimultaniously
);
1031 menu
.addAction( _createImageStack
);
1032 menu
.addAction( _unStackImages
);
1033 menu
.addAction( _setStackHead
);
1034 menu
.addSeparator();
1035 menu
.addAction( _runSlideShow
);
1036 menu
.addAction(_runRandomSlideShow
);
1038 menu
.addAction( _showExifDialog
);
1041 menu
.addSeparator();
1042 menu
.addAction(_rotLeft
);
1043 menu
.addAction(_rotRight
);
1044 menu
.addAction(_recreateThumbnails
);
1045 menu
.addSeparator();
1047 menu
.addAction(_view
);
1048 menu
.addAction(_viewInNewWindow
);
1050 ExternalPopup
* externalCommands
= new ExternalPopup( &menu
);
1051 DB::ImageInfoPtr info
= _thumbnailView
->mediaIdUnderCursor().fetchInfo();
1053 externalCommands
->populate( info
, DB::ImageDB::instance()->CONVERT(selected() ));
1054 QAction
* action
= menu
.addMenu( externalCommands
);
1055 if (info
.isNull() && selected().isEmpty())
1056 action
->setEnabled( false );
1058 menu
.exec( QCursor::pos() );
1060 delete externalCommands
;
1062 e
->setAccepted(true);
1065 void MainWindow::Window::setDefaultScopePositive()
1067 Settings::SettingsData::instance()->setCurrentLock( _browser
->currentContext(), false );
1070 void MainWindow::Window::setDefaultScopeNegative()
1072 Settings::SettingsData::instance()->setCurrentLock( _browser
->currentContext(), true );
1075 void MainWindow::Window::lockToDefaultScope()
1077 int i
= KMessageBox::warningContinueCancel( this,
1078 i18n( "<p>The password protection is only a means of allowing your little sister "
1079 "to look in your images, without getting to those embarrassing images from "
1080 "your last party.</p>"
1081 "<p>In other words, anyone with access to the index.xml file can easily "
1082 "circumvent this password.</b></p>"),
1083 i18n("Password Protection"),
1084 KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
1085 QString::fromLatin1( "lockPassWordIsNotEncruption" ) );
1086 if ( i
== KMessageBox::Cancel
)
1089 setLocked( true, false );
1092 void MainWindow::Window::unlockFromDefaultScope()
1094 bool OK
= ( Settings::SettingsData::instance()->password().isEmpty() );
1096 KPasswordDialog
dialog( this );
1097 dialog
.setPrompt( i18n("Type in Password to Unlock") );
1098 const int code
= dialog
.exec();
1099 if ( code
== QDialog::Rejected
)
1101 const QString passwd
= dialog
.password();
1103 OK
= (Settings::SettingsData::instance()->password() == passwd
);
1106 KMessageBox::sorry( this, i18n("Invalid password.") );
1108 setLocked( false, false );
1111 void MainWindow::Window::setLocked( bool locked
, bool force
)
1113 static QPixmap
* lockedPix
= new QPixmap( SmallIcon( QString::fromLatin1( "object-locked" ) ) );
1114 _lockedIndicator
->setFixedWidth( lockedPix
->width() );
1117 _lockedIndicator
->setPixmap( *lockedPix
);
1119 _lockedIndicator
->setPixmap( QPixmap() );
1121 Settings::SettingsData::instance()->setLocked( locked
, force
);
1123 _lock
->setEnabled( !locked
);
1124 _unlock
->setEnabled( locked
);
1125 _setDefaultPos
->setEnabled( !locked
);
1126 _setDefaultNeg
->setEnabled( !locked
);
1130 void MainWindow::Window::changePassword()
1132 bool OK
= ( Settings::SettingsData::instance()->password().isEmpty() );
1134 KPasswordDialog dialog
;
1137 dialog
.setPrompt( i18n("Type in Old Password") );
1138 const int code
= dialog
.exec();
1139 if ( code
== QDialog::Rejected
)
1141 const QString passwd
= dialog
.password();
1143 OK
= (Settings::SettingsData::instance()->password() == QString(passwd
));
1146 KMessageBox::sorry( this, i18n("Invalid password.") );
1149 dialog
.setPrompt( i18n("Type in New Password") );
1150 const int code
= dialog
.exec();
1151 if ( code
== QDialog::Accepted
)
1152 Settings::SettingsData::instance()->setPassword( dialog
.password() );
1155 void MainWindow::Window::slotConfigureKeyBindings()
1157 Viewer::ViewerWidget
* viewer
= new Viewer::ViewerWidget
; // Do not show, this is only used to get a key configuration
1158 KShortcutsDialog
* dialog
= new KShortcutsDialog();
1159 dialog
->addCollection( actionCollection(), i18n( "General" ) );
1160 dialog
->addCollection( viewer
->actions(), i18n("Viewer") );
1164 KIPI::PluginLoader::PluginList list
= _pluginLoader
->pluginList();
1165 for( KIPI::PluginLoader::PluginList::Iterator it
= list
.begin(); it
!= list
.end(); ++it
) {
1166 KIPI::Plugin
* plugin
= (*it
)->plugin();
1168 dialog
->addCollection( plugin
->actionCollection(), (*it
)->comment() );
1172 createAnnotationDialog();
1173 dialog
->addCollection( _annotationDialog
->actions(), i18n("Annotation Dialog" ) );
1175 dialog
->configure();
1181 void MainWindow::Window::slotSetFileName( const DB::ResultId
& id
)
1184 statusBar()->clearMessage();
1186 statusBar()->showMessage( id
.fetchInfo()->fileName(DB::AbsolutePath
), 4000 );
1189 void MainWindow::Window::slotThumbNailSelectionChanged(int selectionSize
)
1191 _configAllSimultaniously
->setEnabled(selectionSize
> 1);
1192 _configOneAtATime
->setEnabled(selectionSize
>= 1);
1193 _createImageStack
->setEnabled(selectionSize
> 1);
1194 _unStackImages
->setEnabled(selectionSize
>= 1);
1195 _setStackHead
->setEnabled(selectionSize
== 1); // FIXME: do we want to check if it's stacked here?
1196 _sortByDateAndTime
->setEnabled(selectionSize
> 1);
1197 _recreateThumbnails
->setEnabled(selectionSize
>= 1);
1198 _rotLeft
->setEnabled(selectionSize
>= 1);
1199 _rotRight
->setEnabled(selectionSize
>= 1);
1202 void MainWindow::Window::rotateSelected( int angle
)
1204 const DB::Result
& list
= selected();
1205 if (list
.isEmpty()) {
1206 KMessageBox::sorry( this, i18n("No item is selected."),
1207 i18n("No Selection") );
1209 Q_FOREACH(DB::ImageInfoPtr info
, list
.fetchInfos()) {
1210 info
->rotate(angle
);
1212 _dirtyIndicator
->markDirty();
1213 reloadThumbnailsAfterRotation();
1217 void MainWindow::Window::slotRotateSelectedLeft()
1219 rotateSelected( -90 );
1222 void MainWindow::Window::slotRotateSelectedRight()
1224 rotateSelected( 90 );
1227 void MainWindow::Window::reloadThumbnails(bool flushCache
)
1229 _thumbnailView
->reload( flushCache
);
1230 slotThumbNailSelectionChanged( _thumbnailView
->selection().size() );
1233 void MainWindow::Window::reloadThumbnailsAndFlushCache()
1235 reloadThumbnails(true);
1238 void MainWindow::Window::reloadThumbnailsAfterRotation()
1240 _thumbnailView
->reload( true, false );
1243 void MainWindow::Window::slotUpdateViewMenu( DB::Category::ViewType type
)
1245 if ( type
== DB::Category::TreeView
)
1246 _smallListView
->setChecked( true );
1247 else if ( type
== DB::Category::ThumbedTreeView
)
1248 _largeListView
->setChecked( true );
1250 else if ( type
== DB::Category::IconView
)
1251 _smallIconView
->setChecked( true );
1253 else if ( type
== DB::Category::ThumbedIconView
)
1254 _largeIconView
->setChecked( true );
1257 void MainWindow::Window::slotShowNotOnDisk()
1259 DB::Result notOnDisk
;
1260 Q_FOREACH(DB::ResultId id
, DB::ImageDB::instance()->images()) {
1261 const DB::ImageInfoPtr info
= id
.fetchInfo();
1262 QFileInfo
fi( info
->fileName(DB::AbsolutePath
) );
1264 notOnDisk
.append(id
);
1267 showThumbNails(notOnDisk
);
1271 void MainWindow::Window::slotShowImagesWithChangedMD5Sum()
1273 #ifdef DOES_STILL_NOT_WORK_IN_KPA4
1274 Utilities::ShowBusyCursor dummy
;
1275 StringSet changed
= DB::ImageDB::instance()->imagesWithMD5Changed();
1276 showThumbNails( changed
.toList() );
1277 #else // DOES_STILL_NOT_WORK_IN_KPA4
1278 qFatal("Code commented out in MainWindow::Window::slotShowImagesWithChangedMD5Sum");
1279 #endif // DOES_STILL_NOT_WORK_IN_KPA4
1283 void MainWindow::Window::updateStates( bool thumbNailView
)
1285 _selectAll
->setEnabled( thumbNailView
);
1286 _deleteSelected
->setEnabled( thumbNailView
);
1287 _limitToMarked
->setEnabled( thumbNailView
);
1290 void MainWindow::Window::slotRunSlideShow()
1292 slotView( true, true );
1295 void MainWindow::Window::slotRunRandomizedSlideShow()
1297 slotView( true, true, true );
1300 MainWindow::Window
* MainWindow::Window::theMainWindow()
1302 Q_ASSERT( _instance
);
1306 void MainWindow::Window::slotConfigureToolbars()
1308 KEditToolBar
dlg(guiFactory());
1309 connect(&dlg
, SIGNAL( newToolbarConfig() ),
1310 SLOT( slotNewToolbarConfig() ));
1315 void MainWindow::Window::slotNewToolbarConfig()
1321 void MainWindow::Window::slotImport()
1323 ImportExport::Import::imageImport();
1326 void MainWindow::Window::slotExport()
1328 ImportExport::Export::imageExport(selectedOnDisk());
1331 void MainWindow::Window::slotReenableMessages()
1333 int ret
= KMessageBox::questionYesNo( this, i18n("<p>Really enable all message boxes where you previously "
1334 "checked the do-not-show-again check box?</p>" ) );
1335 if ( ret
== KMessageBox::Yes
)
1336 KMessageBox::enableAllMessages();
1340 void MainWindow::Window::setupPluginMenu()
1342 QMenu
* menu
= findChild
<QMenu
*>( QString::fromLatin1("plugins") );
1344 KMessageBox::error( this, i18n("<p>KPhotoAlbum hit an internal error (missing plug-in menu in MainWindow::Window::setupPluginMenu). This indicate that you forgot to do a make install. If you did compile KPhotoAlbum yourself, then please run make install. If not, please report this as a bug.</p><p>KPhotoAlbum will continue execution, but it is not entirely unlikely that it will crash later on due to the missing make install.</p>" ), i18n("Internal Error") );
1345 _hasLoadedPlugins
= true;
1346 return; // This is no good, but lets try and continue.
1351 connect( menu
, SIGNAL( aboutToShow() ), this, SLOT( loadPlugins() ) );
1352 _hasLoadedPlugins
= false;
1354 menu
->setEnabled(false);
1355 _hasLoadedPlugins
= true;
1359 void MainWindow::Window::loadPlugins()
1362 Utilities::ShowBusyCursor dummy
;
1363 if ( _hasLoadedPlugins
)
1366 _pluginInterface
= new Plugins::Interface( this, "demo interface" );
1367 connect( _pluginInterface
, SIGNAL( imagesChanged( const KUrl::List
& ) ), this, SLOT( slotImagesChanged( const KUrl::List
& ) ) );
1369 QStringList ignores
;
1370 ignores
<< QString::fromLatin1( "CommentsEditor" )
1371 << QString::fromLatin1( "HelloWorld" );
1373 _pluginLoader
= new KIPI::PluginLoader( ignores
, _pluginInterface
);
1374 connect( _pluginLoader
, SIGNAL( replug() ), this, SLOT( plug() ) );
1375 _pluginLoader
->loadPlugins();
1378 connect( _thumbnailView
, SIGNAL( selectionChanged(int) ), this, SLOT( slotSelectionChanged(int) ) );
1379 _hasLoadedPlugins
= true;
1381 // Make sure selection is updated also when plugin loading is
1382 // delayed. This is needed, because selection might already be
1383 // non-empty when loading the plugins.
1384 slotSelectionChanged(selected().size());
1389 void MainWindow::Window::plug()
1392 unplugActionList( QString::fromLatin1("import_actions") );
1393 unplugActionList( QString::fromLatin1("export_actions") );
1394 unplugActionList( QString::fromLatin1("image_actions") );
1395 unplugActionList( QString::fromLatin1("tool_actions") );
1396 unplugActionList( QString::fromLatin1("batch_actions") );
1398 QList
<QAction
*> importActions
;
1399 QList
<QAction
*> exportActions
;
1400 QList
<QAction
*> imageActions
;
1401 QList
<QAction
*> toolsActions
;
1402 QList
<QAction
*> batchActions
;
1404 KIPI::PluginLoader::PluginList list
= _pluginLoader
->pluginList();
1405 for( KIPI::PluginLoader::PluginList::Iterator it
= list
.begin(); it
!= list
.end(); ++it
) {
1406 KIPI::Plugin
* plugin
= (*it
)->plugin();
1407 if ( !plugin
|| !(*it
)->shouldLoad() )
1410 plugin
->setup( this );
1412 QList
<KAction
*> actions
= plugin
->actions();
1413 for( QList
<KAction
*>::Iterator it
= actions
.begin(); it
!= actions
.end(); ++it
) {
1414 KIPI::Category category
= plugin
->category( *it
);
1415 if ( category
== KIPI::ImagesPlugin
|| category
== KIPI::CollectionsPlugin
)
1416 imageActions
.append( *it
);
1418 else if ( category
== KIPI::ImportPlugin
)
1419 importActions
.append( *it
);
1421 else if ( category
== KIPI::ExportPlugin
)
1422 exportActions
.append( *it
);
1424 else if ( category
== KIPI::ToolsPlugin
)
1425 toolsActions
.append( *it
);
1427 else if ( category
== KIPI::BatchPlugin
)
1428 batchActions
.append( *it
);
1431 kDebug() << "Unknow category\n";
1434 KConfigGroup group
= KGlobal::config()->group( QString::fromLatin1("Shortcuts") );
1435 plugin
->actionCollection()->importGlobalShortcuts( &group
);
1439 setPluginMenuState( "importplugin", importActions
);
1440 setPluginMenuState( "exportplugin", exportActions
);
1441 setPluginMenuState( "imagesplugins", imageActions
);
1442 setPluginMenuState( "batch_plugins", batchActions
);
1443 setPluginMenuState( "tool_plugins", toolsActions
);
1445 // For this to work I need to pass false as second arg for createGUI
1446 plugActionList( QString::fromLatin1("import_actions"), importActions
);
1447 plugActionList( QString::fromLatin1("export_actions"), exportActions
);
1448 plugActionList( QString::fromLatin1("image_actions"), imageActions
);
1449 plugActionList( QString::fromLatin1("tool_actions"), toolsActions
);
1450 plugActionList( QString::fromLatin1("batch_actions"), batchActions
);
1454 void MainWindow::Window::setPluginMenuState( const char* name
, const QList
<QAction
*>& actions
)
1456 QMenu
* menu
= findChild
<QMenu
*>( QString::fromLatin1(name
) );
1458 menu
->setEnabled(actions
.count() != 0);
1463 void MainWindow::Window::slotImagesChanged( const KUrl::List
& urls
)
1465 for( KUrl::List::ConstIterator it
= urls
.begin(); it
!= urls
.end(); ++it
) {
1466 ImageManager::Manager::instance()->removeThumbnail( (*it
).path() );
1468 _dirtyIndicator
->markDirty();
1469 reloadThumbnails(true);
1472 DB::ImageSearchInfo
MainWindow::Window::currentContext()
1474 return _browser
->currentContext();
1477 QString
MainWindow::Window::currentBrowseCategory() const
1479 return _browser
->currentCategory();
1482 void MainWindow::Window::slotSelectionChanged( int count
)
1485 _pluginInterface
->slotSelectionChanged( count
!= 0 );
1491 void MainWindow::Window::resizeEvent( QResizeEvent
* )
1493 if ( Settings::SettingsData::ready() && isVisible() )
1494 Settings::SettingsData::instance()->setWindowGeometry( Settings::MainWindow
, geometry() );
1497 void MainWindow::Window::moveEvent( QMoveEvent
* )
1499 if ( Settings::SettingsData::ready() && isVisible() )
1500 Settings::SettingsData::instance()->setWindowGeometry( Settings::MainWindow
, geometry() );
1504 void MainWindow::Window::slotRemoveTokens()
1506 if ( !_tokenEditor
)
1507 _tokenEditor
= new TokenEditor( this );
1508 _tokenEditor
->show();
1509 connect( _tokenEditor
, SIGNAL( finished() ), _browser
, SLOT( go() ) );
1512 void MainWindow::Window::slotShowListOfFiles()
1514 QStringList list
= KInputDialog::getMultiLineText( i18n("Open List of Files"), i18n("Enter file names") )
1515 .split( QChar::fromLatin1('\n'), QString::SkipEmptyParts
);
1516 if ( list
.isEmpty() )
1520 for ( QStringList::const_iterator it
= list
.constBegin(); it
!= list
.constEnd(); ++it
) {
1521 QString fileName
= Utilities::imageFileNameToAbsolute( *it
);
1522 DB::ResultId id
= DB::ImageDB::instance()->ID_FOR_FILE(fileName
);
1528 KMessageBox::sorry( this, i18n("No images matching your input were found."), i18n("No Matches") );
1530 showThumbNails( out
);
1533 void MainWindow::Window::updateDateBar( const Browser::BreadcrumbList
& path
)
1535 static QString lastPath
= QString::fromLatin1("ThisStringShouldNeverBeSeenSoWeUseItAsInitialContent");
1536 if ( path
.toString() != lastPath
)
1538 lastPath
= path
.toString();
1541 void MainWindow::Window::updateDateBar()
1543 _dateBar
->setImageDateCollection( DB::ImageDB::instance()->rangeCollection() );
1547 void MainWindow::Window::slotShowImagesWithInvalidDate()
1549 InvalidDateFinder
finder( this );
1550 if ( finder
.exec() == QDialog::Accepted
)
1554 void MainWindow::Window::showDateBarTip( const QString
& msg
)
1556 statusBar()->showMessage( msg
, 3000 );
1559 void MainWindow::Window::slotJumpToContext()
1561 DB::ResultId id
=_thumbnailView
->currentItem();
1562 if ( !id
.isNull() ) {
1563 // QWERTY: addImageView should take id as well.
1564 QString fileName
= id
.fetchInfo()->fileName(DB::AbsolutePath
);
1565 _browser
->addImageView( fileName
);
1569 void MainWindow::Window::setDateRange( const DB::ImageDate
& range
)
1571 DB::ImageDB::instance()->setDateRange( range
, _dateBar
->includeFuzzyCounts() );
1573 reloadThumbnails(false);
1576 void MainWindow::Window::clearDateRange()
1578 DB::ImageDB::instance()->clearDateRange();
1580 reloadThumbnails(false);
1583 void MainWindow::Window::showThumbNails(const DB::Result
& items
)
1585 _thumbnailView
->setImageList( items
);
1586 _partial
->setMatchCount(items
.size());
1590 void MainWindow::Window::convertBackend()
1592 #ifdef SQLDB_SUPPORT
1593 // Converting from SQLDB to the same SQLDB will not work and there
1594 // is currently no way to check if two SQL back-ends use the same
1595 // database. So this is my current workaround for it.
1596 if (dynamic_cast<SQLDB::Database
*>(DB::ImageDB::instance())) {
1597 KMessageBox::sorry(this, i18n("Database conversion from SQL database is not yet supported."));
1601 KConfigGroup config
= KGlobal::config()->group( QString::fromLatin1("SQLDB") );
1602 if (!config
.exists()) {
1604 KMessageBox::questionYesNo(this, i18n("You should set SQL database settings before the conversion. "
1605 "Do you want to do this now?"));
1606 if (ret
!= KMessageBox::Yes
)
1608 if (!_optionsDialog
)
1609 _optionsDialog
= new Settings::SettingsDialog(this);
1610 _optionsDialog
->showBackendPage();
1611 ret
= _optionsDialog
->exec();
1612 if (ret
!= Settings::SettingsDialog::Accepted
)
1616 SQLDB::DatabaseAddress address
= SQLDB::readConnectionParameters(config
);
1618 SQLDB::Database
sqlBackend(address
);
1620 // TODO: ask if old database should be flushed first
1622 KProgressDialog
dialog(this);
1623 dialog
.setModal(true);
1624 dialog
.setCaption(i18n("Converting database"));
1626 (QString::fromLatin1("<p><b><nobr>%1</nobr></b></p><p>%2</p>")
1627 .arg(i18n("Converting database to SQL."))
1628 .arg(i18n("Please wait.")));
1629 dialog
.setAllowCancel(false);
1630 dialog
.setAutoClose(true);
1631 dialog
.setFixedSize(dialog
.sizeHint());
1632 dialog
.setMinimumDuration(0);
1633 qApp
->processEvents();
1635 DB::ImageDB::instance()->convertBackend(&sqlBackend
, dialog
.progressBar());
1637 KMessageBox::information(this, i18n("Database conversion is ready."));
1639 catch (SQLDB::Error
& e
) {
1640 KMessageBox::error(this, i18n("Database conversion failed, because following error occurred:\n%1",e
.whatAsQString()));
1645 void MainWindow::Window::slotRecalcCheckSums()
1647 DB::ImageDB::instance()->slotRecalcCheckSums( selected() );
1650 void MainWindow::Window::slotShowExifInfo()
1653 DB::Result items
= selectedOnDisk();
1654 if (!items
.isEmpty()) {
1655 Exif::InfoDialog
* exifDialog
= new Exif::InfoDialog(items
.at(0), this);
1661 void MainWindow::Window::showFeatures()
1663 FeatureDialog
dialog(this);
1667 void MainWindow::Window::showImage( const DB::ResultId
& id
)
1669 launchViewer(DB::Result(id
), true, false, false);
1672 void MainWindow::Window::slotBuildThumbnails()
1674 new ThumbnailView::ThumbnailBuilder( this );
1677 void MainWindow::Window::slotOrderIncr()
1679 _thumbnailView
->setSortDirection( ThumbnailView::OldestFirst
);
1682 void MainWindow::Window::slotOrderDecr()
1684 _thumbnailView
->setSortDirection( ThumbnailView::NewestFirst
);
1687 void MainWindow::Window::showVideos()
1689 KRun::runUrl(KUrl(QString::fromLatin1("http://www.kphotoalbum.org/videos.html")), QString::fromLatin1( "text/html" ), this );
1692 void MainWindow::Window::slotStatistics()
1694 static StatisticsDialog
* dialog
= new StatisticsDialog(this);
1698 void MainWindow::Window::setupStatusBar()
1700 // Avoid flicker in the statusbar when moving over dates from the datebar
1701 QFont
f( statusBar()->font() );
1702 f
.setStyleHint( QFont::TypeWriter
);
1703 f
.setFamily( QString::fromLatin1( "courier" ) );
1705 statusBar()->setFont( f
);
1707 KHBox
* indicators
= new KHBox( statusBar());
1708 _dirtyIndicator
= new DirtyIndicator( indicators
);
1710 _lockedIndicator
= new QLabel( indicators
);
1711 setLocked( Settings::SettingsData::instance()->locked(), true );
1713 statusBar()->addPermanentWidget( indicators
, 0 );
1715 _partial
= new ImageCounter( statusBar() );
1716 statusBar()->addPermanentWidget( _partial
, 0 );
1718 ImageCounter
* total
= new ImageCounter( statusBar() );
1719 statusBar()->addPermanentWidget( total
, 0 );
1720 total
->setTotal( DB::ImageDB::instance()->totalCount() );
1721 connect( DB::ImageDB::instance(), SIGNAL( totalChanged( uint
) ), total
, SLOT( setTotal( uint
) ) );
1723 _pathIndicator
= new BreadcrumbViewer
;
1724 statusBar()->addWidget( _pathIndicator
, 1 );
1727 void MainWindow::Window::slotRecreateExifDB()
1730 Exif::Database::instance()->recreate();
1734 void MainWindow::Window::createSarchBar()
1736 // Set up the search tool bar
1737 SearchBar
* bar
= new SearchBar( this );
1738 bar
->setLineEditEnabled(false);
1739 bar
->setObjectName( QString::fromAscii("searchBar" ) );
1741 connect( bar
, SIGNAL( textChanged( const QString
& ) ), _browser
, SLOT( slotLimitToMatch( const QString
& ) ) );
1742 connect( bar
, SIGNAL( returnPressed() ), _browser
, SLOT( slotInvokeSeleted() ) );
1743 connect( bar
, SIGNAL( keyPressed( QKeyEvent
* ) ), _browser
, SLOT( scrollKeyPressed( QKeyEvent
* ) ) );
1744 connect( _browser
, SIGNAL( viewChanged() ), bar
, SLOT( reset() ) );
1745 connect( _browser
, SIGNAL( isSearchable( bool ) ), bar
, SLOT( setLineEditEnabled( bool ) ) );
1748 #include "Window.moc"