diff --git a/ChangeLog b/ChangeLog index 9311cb53..1fc251a4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3727 +1,3739 @@ +2019-11-09 Robby Stephenson + + * Released Tellico 3.2.2. + +2019-10-20 Robby Stephenson + + * Fixed bug to revert to behavior of selecting newly added entries (Bug 413217). + +2019-10-06 Robby Stephenson + + * Added importer for LibraryThing.com JSON collection files (Bug 411095). + 2019-08-04 Robby Stephenson * Fixed bug with calculating relative file links (Bug 410551). 2019-07-09 Robby Stephenson * Released Tellico 3.2.1. 2019-07-06 Robby Stephenson * Fixed selection bug when modifying an entry after selection changed (Bug 391614). 2019-07-01 Robby Stephenson * Fixed bug with not updating status bar when cancelling an entry update (Bug 325591). 2019-06-16 Robby Stephenson * Fixed file preview generation. * Added data source for Kino-Teatr.ua. 2019-05-25 Robby Stephenson * Released Tellico 3.2. 2019-05-24 Robby Stephenson * Updated data source for TheGamesDB.net (Bug 407811). 2019-04-18 Robby Stephenson * Added data source for ComicVine.com. 2019-03-10 Robby Stephenson * Updated VNDB.org data source. 2019-02-24 Robby Stephenson * Corrected server for National Library of Lithuania (Bug 404743). 2019-02-20 Robby Stephenson * Added data source for MobyGames.com. 2019-02-14 Robby Stephenson * Updated IGDB data source to API v3. 2019-02-10 Robby Stephenson * Improved collection merging efficiency (Bug 349410). 2019-01-19 Robby Stephenson * Fixed bug reading title from KinoPoisk (Bug 403184). 2019-01-04 Robby Stephenson * Fixed bug with setting IMDB rating in various locales (Bug 401894). 2019-01-02 Robby Stephenson * Added Amazon sites for BR, AU, IN, MX, and TR. 2018-12-18 Robby Stephenson * Released Tellico 3.1.4. 2018-12-17 Robby Stephenson * Updated Kino.de data source. 2018-12-09 Robby Stephenson * Updated KinoPoisk.ru data source. 2018-10-30 Robby Stephenson * Updated bedetheque.com data source to use https. 2018-10-24 Robby Stephenson * Fixed bug to escape the group name when filtering in the Group View (Bug 399928). 2018-10-02 Robby Stephenson * Fixed bug when using a regular expression for filtering in a number field (Bug 399323). 2018-09-02 Robby Stephenson * Released Tellico 3.1.3. * Updated TheGamesDB data source. 2018-08-24 Robby Stephenson * Updated Amazon source to include multiple content listings in movie plots (Bug 396211). 2018-08-22 Robby Stephenson * Fixed LTR direction for year within Video template (Debian Bug #904259) 2018-07-08 Robby Stephenson * Fixed bug that cleared selection on modification when a filter was applied (Bug 394343). 2018-07-02 Robby Stephenson * Updated SRU fetcher to allow multiple marc records within a SRW result (DNB). 2018-04-18 Robby Stephenson * Updated IMDb data source (Bug 393239). 2018-04-02 Robby Stephenson * Updated BibTeXML importer. 2018-03-28 Robby Stephenson * Fixed compilation for Qt 5.6 (Bug 392457). 2018-03-28 Robby Stephenson * Released Tellico 3.1.2. * Updated Kino.de data source. 2018-03-18 Robby Stephenson * Fixed bug with inconsistent selection (Bug 391614). * Added a workaround for a crash when linking to both Exempi and KFileMetadata. 2018-02-24 Robby Stephenson * Updated IMDb data source. 2018-02-07 Robby Stephenson * Updated ISBNdb.com data source to v3. 2018-02-05 Robby Stephenson * Fixed "Filter by Group" (Bug 389931). 2018-01-30 Robby Stephenson * Updated MusicBrainz data source to Web Service v2. 2018-01-15 Robby Stephenson * Released Tellico 3.1.1. * Removed deprecated Wine.com data source. 2018-01-14 Robby Stephenson * Updated Kino.de data source. * Updated IGDB data source. 2018-01-09 Robby Stephenson * Fixed bug with OMDB settings not being saved (Bug 388703). * Updated IMDb data source. 2017-12-15 Robby Stephenson * Revert change to clear search dialog on new search (Bug 357799). 2017-11-19 Robby Stephenson * Fixed bug with filter selection (Bug 387130). 2017-11-17 Robby Stephenson * Fixed bug with multi-selection and entry editing (Bug 387053). 2017-11-12 Robby Stephenson * Fixed bug with incorrect entry titles in icon view and multiple entry icons (Bug 386548). * Fixed a few ISO-6937 character encodings. 2017-11-06 Robby Stephenson * Fixed bug for "losing" icons after modifying a collection (Bug 386549). 2017-11-02 Robby Stephenson * Fixed bug with duplicated colons in CSV importer (Bug 386483). 2017-11-01 Robby Stephenson * Improved "Filter by Group" to use a regular expression (Bug 386011). 2017-10-14 Robby Stephenson * Updated Goodreads importer to use https endpoint. 2017-10-13 Robby Stephenson * Released Tellico 3.1. 2017-09-20 Robby Stephenson * Fixed crashing bugs with remote image loading (Bug 382572, Bug 379607, Bug 384104). 2017-09-10 Robby Stephenson * Fixed bug with showing stars in column view for float values (Bug 384547). * Fixed bug with comparing float values (Bug 384547). 2017-08-26 Robby Stephenson * Fixed building with CMake 3.9+ (Bug 382680). 2017-07-16 Robby Stephenson * Added data source for kino.de. 2017-07-07 Robby Stephenson * Added message dialog for Amazon Associate warning (Bug 364784). 2017-07-06 Robby Stephenson * Added DBUS option for filtering exported entries (Bug 382035). 2017-07-02 Robby Stephenson * Fixed bug of running out of memory when writing very large XML files (Bug 380832). * Improved performance for filtering large collections by avoiding needless field value formatting. 2017-06-22 Robby Stephenson * Fixed bug with some icons not being shown (Bug 378477). 2017-05-29 Robby Stephenson * Switched from using libdiscid to libcdio, which includes cdtext. 2017-05-06 Robby Stephenson * Removed Freebase data source. 2017-05-01 Robby Stephenson * Fixed track length for CD audio (Bug 379426). 2017-04-30 Robby Stephenson * Added data source for IGDB.com. 2017-04-16 Robby Stephenson * Fixed bug showing icons for custom collection (Bug 378852). 2017-04-09 Robby Stephenson * Added data source for https://opensource.dbc.dk. * Added PEGI rating to GiantBomb results (Bug 375996). 2017-03-27 Robby Stephenson * Released Tellico 3.0.2. 2017-03-26 Robby Stephenson * Added data source for VideoGameGeek.com 2017-03-25 Robby Stephenson * Updated Douban data source to API v2 (from XML to JSON). 2017-03-24 Robby Stephenson * Fixed bug with image location for reports, introduced in v3.0.1 (Bug 377790). 2017-03-16 Robby Stephenson * Updated the IBS.it data source (Bug 373774). 2017-03-11 Robby Stephenson * Added data source for KinoPoisk.ru. 2017-03-04 Robby Stephenson * Added data source for OMDBAPI.com. 2017-02-20 Robby Stephenson * Released Tellico 3.0.1. 2017-02-12 Robby Stephenson * Removed Microsoft Academic Search data source (defunct). * Updated DBLP data source. 2017-02-08 Robby Stephenson * Fixed relative file locations in HTML export (Bug 376134). 2017-02-05 Robby Stephenson * Fixed image open dialog to remember last location (Bug 376002). * Added ESRB rating for "Everyone 10+" (Bug 375995). 2017-01-30 Robby Stephenson * Fixed Bibsys z39.50 settings (Bug 375758). * Fixed "Report Bug" menu item to link to bugs.kde.org (Bug 375760). * Update MARCXML2MODS stylesheet to version 3.6. 2017-01-29 Robby Stephenson * Fixed Google Scholar data source. 2017-01-15 Robby Stephenson * Fixed bug with Rating drawing size in list view (Bug 372560). 2016-12-19 Robby Stephenson * Fixed bug with truncated first two characters of root folder when importing a file listing (Bug 373918). 2016-11-08 Robby Stephenson * Released Tellico 3.0. 2016-06-15 Robby Stephenson * Added native data source for Bedetheque.com 2016-05-25 Robby Stephenson * Improved performance when deleting many entries. 2016-05-03 Robby Stephenson * Use elided text in Choice combobox to avoid wide windows (Bug 362028). 2016-04-26 Robby Stephenson * Added menu items for each Url field in icon view (Bug 250913). 2016-04-25 Robby Stephenson * Fixed crashing bug with empty table rows (Bug 361622). 2016-04-16 Robby Stephenson * Fixed filter comparison with custom dates (Bug 361625). 2016-01-15 Sven Werlen * Fixed "Defaults" button in Collection Fields dialog (Bug 357637). 2015-12-06 Robby Stephenson * Released Tellico 2.3.11. 2015-10-25 Robby Stephenson * Removed the Yahoo fetcher. 2015-10-22 Robby Stephenson * Added user-defined query pairs for SRUFetcher url. 2015-08-22 Robby Stephenson * Improved PDF importer for Science Direct files. 2015-08-16 Robby Stephenson * Fixed key accelerator conflict for 'D' in Collection Fields dialog (Bug 351226). 2015-08-05 Robby Stephenson * Fixed bug with using external fetcher source with MODS output. 2015-07-19 Robby Stephenson * Fixed a crashing bug with Nepomuk and certain file types (Bug 345458). 2015-06-02 Robby Stephenson * Fixed a bug with HTML exporting titles with single-quotes (Bug 348381). 2015-05-27 Robby Stephenson * Removed the Citebase fetcher. 2015-05-24 Robby Stephenson * Fixed a bug showing horizontal scroll bar with many table columns (Bug 348189). 2015-03-15 Robby Stephenson * Removed the PilotDB exporter. 2015-02-15 Robby Stephenson * Released Tellico 2.3.10. 2015-02-14 Robby Stephenson * Updated the Moviemeter data source to use the new JSON API. * Removed the local KXmlRpc copy since it is no longer needed. * Updated cover path in DarkHorse fetcher. 2015-01-23 Robby Stephenson * Updated the Discogs data source to use the new JSON API. 2014-12-22 Robby Stephenson * Added filter rules for numbers less than and greater. 2014-12-12 Robby Stephenson * Converted the BoardGameGeek fetcher from a script to the XML API. 2014-12-06 Robby Stephenson * Added importer for BoardGameGeek collections. 2014-12-05 Robby Stephenson * Fixed bug with importing Goodreads collection. 2014-11-09 Robby Stephenson * Added workaround for incorrect cover thumbnail in BoardGameGeek fetcher. 2014-10-20 Robby Stephenson * Updated CSV importer to recognize LibraryThing files. 2014-10-19 Robby Stephenson * Fixed bug with Allocine API search using punctuation (Bug 337432). 2014-10-11 Robby Stephenson * Added data source for Mathematical Review. * Fixed crashing bug with some ISBNdb results (Bug 339063). * Updated Producer results for IMDb and TheMovieDB fetchers (Bug 336765). 2014-10-11 Pino Toscano * Update internal libcsv copy to 3.0.3. 2014-10-09 Pino Toscano * Switch to using libdiscid for reading audio CD info. 2014-06-22 Robby Stephenson * Released Tellico 2.3.9. 2014-05-26 Robby Stephenson * Fixed bug with writing out link-only images in HTML exporter (Bug 330649). 2014-05-25 Robby Stephenson * Fixed character encoding in Allocine fetcher (Bug 334527). * Removed IMDb country choice since it's now unavailable (Bug 330641). 2014-04-27 Robby Stephenson * Disabled Discogs image fetching since OAuth is now required. 2014-04-17 Robby Stephenson * Fixed CSV importer bug, causing a hang (Bug 329677, Debian Bug 729503). 2014-01-31 Robby Stephenson * Updated TheMovieDB fetcher to API version 3. 2014-01-30 Robby Stephenson * Fixed crashing bug in IMDB fetcher (Bug 330591). 2013-11-14 Robby Stephenson * Fixed crashing bug in command-line importing (Debian bug 729499). 2013-11-08 Robby Stephenson * Fixed type error in Microsoft Academic Search fetcher. 2013-11-07 Robby Stephenson * Fixed error in setting modified date for entry in certain cases (Bug 326911). 2013-11-06 Robby Stephenson * Improved GCstar import and export to handle custom GCstar fields. 2013-10-13 Robby Stephenson * Updated IMDb fetcher (Bug 325673). 2013-07-07 Robby Stephenson * Released Tellico 2.3.8. 2013-07-04 Robby Stephenson * Disabled fetchers for Spanish Ministry of Culture, Beyazperde, Filmstarts, ScreenRush, and Sensacine. 2013-07-01 Robby Stephenson * Updated ISBNdb.com fetcher to use v2 of API. 2013-06-09 Robby Stephenson * Added Bibtex importing for drag/drop text (Bug 319182). 2013-06-08 Robby Stephenson * Fixed Entrex/Pubmed fetcher for summary requests (Bug 319501). 2013-04-05 Robby Stephenson * Fixed bug with retaining allowed values when adding entries from data sources (Bug 317905). 2013-03-27 Robby Stephenson * Fixed bug with not properly escaping text in CSV exporter (Bug 317473). 2013-03-24 Robby Stephenson * Updated GiantBombFetcher for XML changes in responses. 2013-03-11 Robby Stephenson * Fixed regexp in Google Scholar fetcher to set cookie (Bug 316550). 2013-03-10 Robby Stephenson * Update Allocine API to use JSON instead of XML. 2013-03-03 Robby Stephenson * Add coverartarchive.org to MusicBrainz data source. 2013-02-09 Robby Stephenson * Fixed character encoding in IMDB results list (Bug 314113). 2013-02-02 Robby Stephenson * Added fetcher for VNDB.org. 2013-01-27 Robby Stephenson * Added Dewey Decimal and LCC to ISBNdb.org results. 2013-01-26 Robby Stephenson * Fixed crashing bug with OpenLibrary fetcher. 2013-01-23 Robby Stephenson * Released Tellico 2.3.7. 2013-01-18 Robby Stephenson * Fixed a bug with the number field not catching a modified value (Bug 313304). 2013-01-08 Robby Stephenson * Fixed a bug with editing the toolbar config in KDE 4.9.2+. 2012-12-07 Robby Stephenson * Added a config setting for using a Google API key for book searches. * Updated fetcher for IMDb. 2012-11-21 Robby Stephenson * Added VinoXML importer. 2012-11-03 Robby Stephenson * Added fetcher for DBLP.org. 2012-10-14 Robby Stephenson * Fixed a bug that caused loans to fail to get checked-in (Bug 307958). 2012-09-18 Robby Stephenson * Added fetcher for thegamesdb.net. 2012-09-09 Robby Stephenson * Changed Griffith importer to use XML files instead of python script and assuming a sqlite3 database. 2012-09-03 Robby Stephenson * Updated Delicious Library importer. 2012-08-26 Robby Stephenson * Removed API key configuration for Discogs since it's no longer needed. 2012-08-22 Robby Stephenson * Corrected character encoding for DVDfr search. * Corrected title vs. original title in DVDfr results. * Corrected actor and role order in Allocine fetcher. 2012-08-07 Robby Stephenson * Fixed a bug with adding new fields when importing bibtex (Bug 304767). 2012-07-13 Robby Stephenson * Released Tellico 2.3.6. 2012-06-10 Robby Stephenson * Added simple importer for CIW files from ISI. 2012-05-30 Robby Stephenson * Added data sources for IMDB in French, Spanish, German, Italian, and Portuguese. 2012-05-12 Robby Stephenson * Fixed a bug with loading images from Google Book search (Bug 299789). 2012-05-06 Robby Stephenson * Added API data source for HathiTrust. 2012-05-04 Robby Stephenson * Fixed a bug when using a local image directory for a file that has not yet been saved (Bug 299130). 2012-04-20 Robby Stephenson * Added API data sources for Allocine, ScreenRush, FilmStarts, SensaCine, and Beyazperde. 2012-03-23 Robby Stephenson * Fixed bugs in IBS.it data source. 2012-03-14 Robby Stephenson * Added data source for Springer Link. 2012-03-09 Robby Stephenson * Added data source for the Microsoft Academic Search. 2012-02-26 Robby Stephenson * Fixed a crashing bug when entering multiple ISBN values in search. * Improved Amazon search for US UPC values from non-UPC sites. 2012-01-15 Robby Stephenson * Fixed a crashing bug when using the scanner dialog. * Released Tellico 2.3.5. 2011-12-26 Robby Stephenson * Fixed bug with merging entries. Entry IDs will never be different so don't warn the user (Bug 289346). * Fixed bug with Cancel not working in the Entry Merge dialog. 2011-12-17 Robby Stephenson * Refactored fetcher tests to check for network access before running * Updated Discogs fetcher to use API v2 2011-12-14 Robby Stephenson * Fixed importing of multi-line notes from Alexandria (Bug 289022). 2011-11-30 Robby Stephenson * Updated fetcher for Allocine.fr. 2011-10-28 Robby Stephenson * Added filter rules for dates before and after. 2011-10-13 Robby Stephenson * Fixed broken script for searching Dark Horse Comics. 2011-10-05 Robby Stephenson * Fixed bug that always showed checkmark for boolean field values (Bug 283444). 2011-10-04 Robby Stephenson * Updated GCstar exporter to include images. 2011-09-27 Robby Stephenson * Added Amazon data sources for China, Spain, and Italy. 2011-09-24 Robby Stephenson * Released Tellico 2.3.4. 2011-09-18 Robby Stephenson * Fixed bug when editing month in the date widget in KDE 4.7 (Bug 281365). 2011-08-30 Robby Stephenson * Fixed showing a doubled filter count (Bug 281082). 2011-08-21 Robby Stephenson * Added data source for Google Book search. 2011-07-23 Robby Stephenson * Updated filter rules to match against values without diacritics for "Contains" criters (Bug 222400). 2011-07-20 Robby Stephenson * Fixed bug in year from Douban.com data source. * Fixed TMDB search for multiple person results. 2011-07-01 Robby Stephenson * Added importer for MovieMeter.nl. 2011-06-24 Robby Stephenson * Fixed crashing bug with CSV importer and changing delimiters. 2011-05-29 Robby Stephenson * Made Entry View always visible. * Moved icon view to share space with the list view (Bug 250912). 2011-05-20 Robby Stephenson * Added importer for Goodreads collections. 2011-05-13 Pedro Miguel Carvalho * Added shortcut key to switch main window to/from full screen state. * Added shortcut key to show/hide menubar (Bug 251157). 2011-05-07 Pedro Miguel Carvalho * Added UI controls for changing icon size (Bug 250907). 2011-05-07 Robby Stephenson * Fixed bug with image link in Tri-Column report template (Bug 272744). 2011-05-06 Robby Stephenson * Added French, Spanish, and German codes for TheMovieDB. * Added icon cache to improve performance with large images (Patch from Pedro Miguel Carvalho, Bug 272583). 2011-05-01 Robby Stephenson * Changed image loading to use known id instead of recalculating, improves performance. 2011-04-10 Robby Stephenson * Improved performance for loading and sorting large collections. * Released Tellico 2.3.3. 2011-04-04 Robby Stephenson * Fixed bug with loans not being updated for removed entries (Bug 270129). 2011-03-21 Robby Stephenson * Fixed crashing bug for editing some values (Bug 269044). 2011-03-19 Robby Stephenson * Fixed editing existing filters (Bug 268829). 2011-03-07 Robby Stephenson * Added Manga search for AnimeNfo. 2011-02-27 Robby Stephenson * Fixed parsing bug with AnimeNfo results. 2011-02-23 Regis Boudin * Updated videodev include for compatibility with Linux kernel 2.6.38+. 2011-02-21 Robby Stephenson * Added data source for douban.com. 2011-02-19 Robby Stephenson * Added data source fetcher for filmaster.com. 2011-02-18 Regis Boudin * Fixed build with GCC 4.6. 2011-02-13 Robby Stephenson * Added check for duplicate bibtex keys (Bug 245225). 2011-02-02 Robby Stephenson * Added capability to import ADS format for z39.50 sources. 2011-01-11 Robby Stephenson * Fixed bug with parsing empty table values (Bug 261108). 2011-01-03 Robby Stephenson * Fixed parsing bug with IMDb results (Bug 262036). 2010-12-12 Robby Stephenson * Released Tellico 2.3.2. 2010-12-11 Robby Stephenson * Updated Bibtex handler to translate ~ to non-breaking space. 2010-12-08 Robby Stephenson * Updated Allocine script to version 0.7.3 (Bug 258281). 2010-12-03 Robby Stephenson * Fixed sorting multiple numeric values in column view. * Fixed sorting for numeric values in group view. 2010-12-01 Robby Stephenson * Fixed bug with FreeDB results not using track artists (Bug 258541). * Fixed bug with importing Bibtex file with keyword and keywords fields (Bug 258269); 2010-11-23 Robby Stephenson * Fixed Google Scholar fetcher to properly set bibtex cookie. 2010-11-21 Robby Stephenson * Added cover art to MusicBrainz fetcher. 2010-11-20 Robby Stephenson * Switched Nepomuk requirement to optional for file metadata. 2010-11-14 Robby Stephenson * Fixed bug with updating groups for derived values (Bug 256374). 2010-11-10 Robby Stephenson * Fixed bug with list view settings not being saved between sessions (Bug 256373). 2010-11-06 Robby Stephenson * Released Tellico 2.3.1. 2010-11-02 Robby Stephenson * Fixed edit dialog behavior when closing to be consistent with discarding data (Bug: 255938). 2010-10-21 Robby Stephenson * Fixed error in Italian translation that caused HTML error and add workaround for future problems (Bug: 254863). 2010-10-17 Robby Stephenson * Updated Freebase source for adapted /music/release schema. 2010-10-14 Robby Stephenson * Updated IMDb data source for new layout (Bug: 253549). 2010-10-13 Robby Stephenson * Added options in CSV importer and exporter for table delimiters. 2010-09-20 Robby Stephenson * Changed URL field output to truncate link text to 30 letters (Bug: 250880). 2010-09-12 Robby Stephenson * Increased max icon size to 256. 2010-09-11 Robby Stephenson * Changed updating match algorithm to try to improve results when having multiple good matches (Bug: 250886). 2010-09-10 Robby Stephenson * Updated GCstar plugin fetcher to use a separate thread. * Fixed crash with z39.50 fetcher (Bug: 250795). 2010-09-07 Robby Stephenson * Updated GCstar plugin fetcher to work with comic books. 2010-09-03 Robby Stephenson * Fixed parsing for director and writer in IMDB fetcher (Bug: 249096). 2010-09-02 Robby Stephenson * Updated DTD and added unit test for validation. 2010-08-29 Robby Stephenson * Added GCstar import/export for comic book collections. 2010-08-28 Robby Stephenson * Fixed bug with adding new fields during CSV import. 2010-08-25 Robby Stephenson * Added GCstar export for wine collections. * Improved GCstar import for wine collections. 2010-08-21 Robby Stephenson * Fixed filter view to apply filter when item is selected (Bug: 248657). 2010-08-16 Robby Stephenson * Improved exporter to add option for limiting exported fields (Bug 246390). 2010-08-13 Robby Stephenson * Added data source fetcher for dvdfr.com. 2010-08-07 Robby Stephenson * Released Tellico 2.3. 2010-07-29 Robby Stephenson * Fixed sorting by rating when rating has double digits (Bug: 246202). 2010-07-25 Robby Stephenson * Fixed IMDb matching on partial titles (Bug: 245665). * Fixed IMDb fetching of alternative titles. 2010-07-11 Robby Stephenson * Fixed IMDb fetching of audio track and color (Bug: 244159). 2010-07-10 Robby Stephenson * Fixed metadata import for file listing. 2010-07-09 Robby Stephenson * Added Dewey Decimal and LoC Classification to z3950 MODS import. * Fix subtitle value in MODS import. 2010-06-19 Robby Stephenson * Added filter for data source list. 2010-05-04 Robby Stephenson * Updated BoardGameGeek fetcher for new API. 2010-05-02 Robby Stephenson * Added data source for Freebase. 2010-04-29 Robby Stephenson * Fixed crashing bug for loading images whose size exceeded the cache size. 2010-04-28 Robby Stephenson * Changed Amazon Japan video search to include DVDs. * Added new fetcher for combining results from multiple sources. 2010-04-27 Robby Stephenson * Updated allocine.fr script to version 0.7.1, patch from Romain Henriet. 2010-03-29 Robby Stephenson * Fixed bug with cursor position in date field entry. 2010-03-27 Robby Stephenson * Added fetcher for openlibrary.org (requires QJSON lib). 2010-03-24 Robby Stephenson * Added clear button for rating widget (BUG: 227982). * Control characters are now stripped from all text files on import (BUG: 231302). * Removed local FindKcddb cmake module in favor of system FindKdeMultimedia. 2010-03-23 Robby Stephenson * Fixed selection bug when duplicating an entry (BUG: 231125). 2010-03-22 Robby Stephenson * Fixed crash in exporting HTML for collections with no grouping field (BUG: 231302). * Fixed bug that showed info dialog multiple times when changing image location. 2010-02-24 Robby Stephenson * Fixed crash in CSV importer. 2010-02-21 Robby Stephenson * Tweaked entry ID values to start at 1 instead of 0. 2010-02-13 Robby Stephenson * Released Tellico 2.2. 2010-02-06 Robby Stephenson * Enabled Korganizer integration for tracking loans. 2010-01-27 Robby Stephenson * Fixed some hyphenation problems when typing 978 ISBN values. * Enabled KAddressBook integration for borrowers. 2010-01-15 Robby Stephenson * Fixed bug in en_GB translation that affected file selection. 2010-01-13 Robby Stephenson * Enabled filter dialog to apply empty filter. 2010-01-03 Robby Stephenson * Improved performance for modifying many entries at once. 2010-01-01 Robby Stephenson * Added Giant Bomb data source. 2009-12-31 Robby Stephenson * Fixed adding "link-only" files (BUG #220645). * Fixed Discogs track download and title search. 2009-12-22 Robby Stephenson * Updated CrossRef data source to allow authentication via email address. * Updated Crossref data source for new schema. 2009-12-19 Robby Stephenson * Added MusicBrainz data source. 2009-12-18 Robby Stephenson * Fixed setting correct permissions of backup file (BUG #219259). * Fixed formatting of multiple people with auto-formatting (BUG #219268). 2009-12-16 Robby Stephenson * Added themoviedb.org data source. 2009-12-12 Robby Stephenson * Improve updating from Amazon to include book title or album title in search. 2009-12-04 Robby Stephenson * Added option for turning webcam off. 2009-11-30 Robby Stephenson * Enabled JavaScript for report dialog, incase a template wants to use it. * Fixed crashing bug for sorting during HTML export. * Fixed multiple selection in entry view (KDE #216122). 2009-11-20 Robby Stephenson * Fixed IMDB search to find director and writers. * Fixed Polish ISBN formatting. * Released Tellico 2.1.1. 2009-11-17 Robby Stephenson * Fixed sorting in icon view. 2009-11-16 Robby Stephenson * Fixed bug that kept filters from being saved. BUG#214672. 2009-11-12 Robby Stephenson * Updated script for searching allocine.fr. 2009-11-11 Robby Stephenson * Changed webcam image preview to only appear when searching by ISBN or UPC/EAN. 2009-11-10 Robby Stephenson * Fixed sorting when auto-formatting is on. 2009-11-06 Robby Stephenson * Fixed crashing bug related to some table values. * FIxed crashing bug related to column sorting. BUG#214661. 2009-11-01 Robby Stephenson * Released Tellico 2.1. 2009-10-31 Robby Stephenson * Fixed crashing bug for loading ISBN list from file. 2009-10-22 Robby Stephenson * Fixed bug with the sort order not being saved. 2009-10-21 Robby Stephenson * Fixed bug that auto-formatting was not always applied. 2009-10-11 Robby Stephenson * Fixed grouping to always show group for empty value. * Fixed crashing bug when filtering entry with empty title. * Fixed compilation for KDE < 4.2. * Fixed filtering bug when modifying entry value. 2009-10-10 Robby Stephenson * Added PAM/PRISM translator to SRU fetcher. 2009-10-07 Robby Stephenson * Updated Amazon and Crossref search to no longer store key in the KWallet. 2009-10-03 Robby Stephenson * Fixed bug to allow multiple values in table columns. 2009-09-30 Robby Stephenson * Added Wine.com data source. 2009-09-29 Robby Stephenson * Updated GCstar import support for video games and board games. 2009-09-24 Robby Stephenson * Updated ISBNdb.com fetcher and Discogs fetcher to allow user access keys. 2009-09-23 Robby Stephenson * Updated the SRU fetcher to leave the response format empty. 2009-09-22 Robby Stephenson * Fixed crash when exporting to HTML under certain conditions. * Removed the GCcfilms exporter and added the GCstar exporter. 2009-09-21 Robby Stephenson * Fixed typo in DTD. 2009-09-20 Robby Stephenson * Released Tellico 2.0. 2009-09-07 Robby Stephenson * Enabled multiple ISBN and LCCN search for SRU fetcher. 2009-09-06 Robby Stephenson * Updated the "Multiple ISBN" dialog box to validate ISBN and CueCat input. 2009-09-04 Robby Stephenson * Updated webcam support to use libv4l library. 2009-09-01 Robby Stephenson * Released Tellico 2.0pre2. 2009-08-23 Robby Stephenson * Added default fields for entry creation date and last-modified date. * Removed dependency on QCA2 library. 2009-08-09 Robby Stephenson * Updated Tellico user mailing list address. * Released Tellico 2.0pre1. 2009-08-01 Robby Stephenson * Updated HTML export to use country and language code both for sorting. * Updated Tellico home page URL. * Moved to document syntax 11 for removing Dependent and ReadOnly fields. * Added ID field to each new collection by default. 2009-03-13 Petri Damstén * Added scanning support to image widget. * Added 'Open With...' -button to image widget 2009-03-10 Robby Stephenson * Added reading album artist from TPE2 tags in mp3 files. 2009-02-14 Robby Stephenson * Released Tellico 1.3.5. 2009-02-04 Robby Stephenson * Reverted change from 2007 that merged entries by combining all values in multiple-value fields. 2009-02-02 Robby Stephenson * Fixed the CueCat decoder to work for ISBN searches, as well as UPC. 2009-01-30 Robby Stephenson * Updated Deliciour Library 1 importer to handle movies and games. * Fixed Ubuntu bug#317822, don't mark collection modified when image is found in local data directory. * Fixed query bug with z39.50 ISBN search. 2008-12-07 Robby Stephenson * Updated it.po, thanks to Valerio Ricci. 2008-10-23 Robby Stephenson * Added date, time, and username as available params for the XSLT export. 2008-09-13 Robby Stephenson * Released Tellico 1.3.4. 2008-09-04 Robby Stephenson * Fixed error with consecutive tabs in CSV import. 2008-08-26 Robby Stephenson * Fixed bug with image links in reports not being linked correctly. 2008-08-20 Robby Stephenson * Added minimal searching for board games from Amazon. 2008-08-07 Robby Stephenson * Changed Choice fields to sort by position in list. 2008-07-25 Robby Stephenson * Added ESC key for clearing quick filter. * Improved drag/drop to match on file extension after checking mimetype. 2008-07-22 Robby Stephenson * Updated IMDb plot regexp. 2008-07-09 Robby Stephenson * Fixed EntryView to honor copy() command properly, for clipboard. * Released Tellico 1.3.3. 2008-06-24 Robby Stephenson * Updated Porbase URL in z39.50 server list. 2008-06-13 Robby Stephenson * Updated drag-and-drop to allow HTTP urls. 2008-06-12 Robby Stephenson * Changed Arxiv fetcher to remove ID version number for results. 2008-06-07 Robby Stephenson * Fixed bug with merging file catalogs, to properly match on URL. 2008-05-24 Robby Stephenson * Released Tellico 1.3.2.1. * Fixed en_GB translation to remove context strings. 2008-05-23 Robby Stephenson * Released Tellico 1.3.2. * Include Canadian LCC values, patch from Peter Stevenson. 2008-05-19 Robby Stephenson * Updated entry creation to always add default values. Fixes Debian bug 481639. 2008-04-29 Robby Stephenson * Improved IMDb search results. 2008-04-15 Robby Stephenson * When adding a book to a bibliography, set the type to 'book'. 2008-04-11 Robby Stephenson * Updated LCCN search to validate format. * Updated entry match to compare validated LCCN. * Updated z39.50 search to allow multiple LCCN values. 2008-04-09 Robby Stephenson * Changed loader to eliminate possible duplicates for fields allowing multiple values (like keywords). * Updated PubMed search to add all keywords. * Fixed PubMed search to use utf8 encoding. 2008-04-08 Robby Stephenson * Changed XML loader to strip whitespace from non-paragraph values. 2008-04-06 Robby Stephenson * Added languages and editors to Amazon book import. 2008-03-15 Robby Stephenson * Updated mods2tellico.xsl to try to capture more of mods info converted from refbase. 2008-03-13 Robby Stephenson * Updated mods2tellico.xsl to try to capture more of bibtex info converted from wikindx. 2008-03-10 Robby Stephenson * Released Tellico 1.3.1. * Added Discogs.com fetcher. 2008-03-06 Robby Stephenson * Added Google Scholar search. 2008-03-05 Robby Stephenson * Fixed bug that prevented using bibtex format for external scripts. 2008-03-03 Robby Stephenson * Changed "ISBN not found" dialog to only appear if multiple ISBN values are being searched. 2008-03-01 Robby Stephenson * Fixed bug with SRU format not getting remembered in config dialog. * Fixed bug with entries with multiple titles not getting linked in the HTML export. 2008-02-29 Robby Stephenson * Added LCCN lookup to z39.50 source. * Added LCCN lookup to SRU source. * Updated CrossRef fetcher to use new unixref format with more data. 2008-02-26 Robby Stephenson * Added DOI search to Entrez/Pubmed interface. * Fixed bug with some free-form date fields not getting formatted correctly, resulting in a blank string in column view and reports. 2008-02-12 Robby Stephenson * Improved loader to delay loading linked images as long as possible. 2008-02-11 Robby Stephenson * Fixed Bibtex import for keywords field. 2008-02-07 Robby Stephenson * Updated Delicious Library importer to look for cover images. * Updated BoardGameGeek script to grab cover image, patch from Sven Werlen. 2008-01-29 Robby Stephenson * Released Tellico 1.3. * Fixed bug with stepping through selected entries after editing one. 2008-01-20 Robby Stephenson * Fixed bug with matching ISBN values for updating entries. 2008-01-11 Robby Stephenson * Released Tellico 1.3pre1. 2008-01-09 Robby Stephenson * Updated Spanish Ministry of Culture script. 2008-01-05 Robby Stephenson * Updated IMDB search to fix director and TV cast. 2007-12-11 Robby Stephenson * Added DCOP calls to set the view filter and to show entries. * Improved filter to allow specifying field name with '='. 2007-12-10 Robby Stephenson * Added importer for Delicious Library files. 2007-12-06 Robby Stephenson * Updated Dependant field parsing to allow %{id} to grab entry id. * Added context menu option in icon view to change sorting. * Extended the DCOP interface to allow adding and removing entries, reading field values, and setting field values. 2007-11-30 Robby Stephenson * Added capability to save a link to an image only, instead of saving it to the users image directory. Setting an image field property link=true will make that the default. 2007-11-26 Robby Stephenson * Updated to allow a "required" property to be included to indicate that a on-empty value should always be present when saving. * Updated to use libcsv from Robert Gamble, allowing newlines 2007-11-25 Robby Stephenson * Fixed bug with creating filter from group item. * Added Ukrainian translation from Serhij Dubyk Сергій Дубик. 2007-11-15 Robby Stephenson * Added a sharedmimeinfo file. 2007-11-13 Robby Stephenson * Added Bibsonomy fetcher. * Added citebase.org fetcher. 2007-11-06 Robby Stephenson * Added option for importing bibtex in Unicode (UTF-8). * Added importing from Referencer. 2007-11-05 Robby Stephenson * Added arxiv.org API fetcher. 2007-11-04 Robby Stephenson * Fixed bugs related to compilation with gcc 4.3. 2007-10-27 Robby Stephenson * Added option for saving images in local relative directory to data file. 2007-10-26 Robby Stephenson * Added menu command for merging entries, based on patch from Cyril Dangerville. 2007-10-24 Robby Stephenson * Added CrossRef search for DOI. 2007-10-21 Robby Stephenson * Fixed list view sorting to take into account automatic formatting. * Fixed date sorting to put empty dates first. 2007-10-19 Robby Stephenson * Fixed sorting bug for images. * Fixed sorting bug for empty dates in HTML export, patch from Jake Maciejewski. 2007-10-15 Robby Stephenson * Fixed crashing bug in bibtex importer. 2007-10-14 Robby Stephenson * Added PDF importer, using exempi XMP library. * Added drag & drop support for PDF importer. 2007-10-13 Robby Stephenson * Added DOI and URL to default bibtex collection fields. 2007-09-23 Robby Stephenson * Added data source for using GCstar plugins. * Fixed bug with escaping the image id for file names. 2007-09-22 Robby Stephenson * Released Tellico 1.2.14. 2007-09-20 Robby Stephenson * Added importer for Griffith database. 2007-09-10 Robby Stephenson * Updated Spanish Ministry of Culture script to search out of prints book and add notes field. * Updated tellico2html.js file for searching in HTML export. * Updated GCstar import to parse new XML format in GCstar version 1.2. 2007-08-27 Robby Stephenson * Updated allocine.fr source script to version 0.4, thanks to Mathias Monnerville. * Updated the Amazon.com search to allow searching for comic books. * Updated the isbndb.com search to allow searching for comic books. * Fixed ISBNdb search. 2007-08-25 Robby Stephenson * Changed default SRU port from 7090 to 80. 2007-08-23 Robby Stephenson * Update CSS in HTML export. 2007-08-10 Robby Stephenson * Fixed bug with yaz_iconv not flushing complete string for z39.50 search. 2007-08-09 Robby Stephenson * Added MARCXML to possible SRU format. * Fixed bug with MARC stylesheets to work with better with embedded records. 2007-08-03 Robby Stephenson * Updated entry updating to work with all collection types. 2007-07-28 Robby Stephenson * Released Tellico 1.2.13. 2007-07-24 Robby Stephenson * Added spell-check for text and paragraph fields. 2007-07-22 Robby Stephenson * Added compatibility with yaz3. 2007-07-19 Robby Stephenson * Added importer for GCstar files. * Fixed bug with namespace handling in Tellico XML loading. 2007-07-14 Robby Stephenson * Added Copac and National Library of Lithuania to z39.50 list. 2007-07-08 Robby Stephenson * Fixed crashing bug when adding items to a collection with loans. 2007-07-04 Robby Stephenson * Released Tellico 1.2.12. * Changed z39.60 search event loop in an attemp to fix some intermittent freezes. 2007-06-26 Robby Stephenson * Changed Quick Filter to split text on white space and do an AND search. * Bumped automake requirement to version 1.8 or later for macro to work. * Fixed HTML export to not rewrite file location for files referenced in the XSL file which don't exist. * Fixed Column View report to sort numerically when needed. * Fixed Date fields to suppress empty date values. * Fixed Date comparisons to work for single digits, patch from Jake Maciejewski. * Fixed Fields Dialog to show warnings when clicking Ok. 2007-06-22 Robby Stephenson * Added '\%' to bibtex translation table for comment escaping. 2007-05-30 Robby Stephenson * Fixed potential recursion bug for dependent fields. 2007-05-26 Robby Stephenson * Fixed bug that didn't write image size options when printing. 2007-05-09 Robby Stephenson * Added Turkish translation, thanks to Ali Isingor. 2007-05-08 Robby Stephenson * Released Tellico 1.2.11. 2007-05-06 Robby Stephenson * Improved file saving performance by caching image info on load. 2007-05-04 Robby Stephenson * Fixed bug that lost some images when loading directly from XML. 2007-04-13 Robby Stephenson * Fixed sorting for Dependent fields to match on subfields. 2007-04-12 Robby Stephenson * Fixed BibteXML export to include author. * Fixed book collection conversion to bibliography to set entry type. 2007-04-10 Robby Stephenson * Updated IMDb search for director and writer. * Fixed CDDB lookup on OpenBSD, patch from Marc Espie. 2007-04-07 Robby Stephenson * Released Tellico 1.2.10. 2007-04-03 Robby Stephenson * Improved accuracy and comprehensiveness of merging collections. * Changed collection merge to concatenate paragraph fields. * Fixed bug that hid some field groups after undoing append collection. 2007-04-01 Robby Stephenson * Added Blu-ray and HD DVD to video formats. 2007-03-31 Robby Stephenson * Updated ibs.it searching. * Fixed searching to replace all HTML entities. * Fixed character encoding for Alexandria import. 2007-03-29 Robby Stephenson * Updated Amazon API to 2007-02-22. * Fixed error handling for ISBN-13 searches. * Fixed bug that showed "ISBN Not Found" dialog in error. 2007-03-27 Robby Stephenson * Cleaned-up up ONIX export a bit. * Fixed Alexandria export. * Fixed importing covers from Alexandria. 2007-03-24 Robby Stephenson * Fixed bug that caused some amazon results to be hidden when repeating a search. 2007-03-20 Robby Stephenson * Fixed hanging bug when stopping a z39.50 search. 2007-03-14 Robby Stephenson * Fixed crashing bug when undoing a collection import or replacement. 2007-03-01 Robby Stephenson * Released Tellico 1.2.9. 2007-02-25 Robby Stephenson * Relaxed MODS processing to allow any result with typeOfResource="text" * Fixed bug with adding space after commas in edit widget. * Fixed bug with formatting of dependent fields always being capitalized. 2007-02-20 Robby Stephenson * Updated IMDb parsing for alternative titles and certifications. 2007-02-06 Robby Stephenson * Fixed sorting for rating groups with rating = 10. 2007-02-03 Robby Stephenson * Released Tellico 1.2.8. 2007-01-22 Robby Stephenson * Updated CDDB import to grab extd data as comments, and category as keyword. 2007-01-21 Robby Stephenson * Updated audio file importer to take disc number into account for mp3, ogg, and flac files. 2007-01-13 Robby Stephenson * Changed IMDB rating to allow float values. * Fixed compile error for z3950 connection, patch from Markus Brueffer. 2007-01-04 Robby Stephenson * Fixed cleanup for deleting scripts installed from newstuff. 2007-01-03 Robby Stephenson * Fixed busy cursor hanging in newstuff download dialog. 2006-12-21 Robby Stephenson * Fixed "ISBN Not Found" dialog to have selectable text. 2006-12-19 Robby Stephenson * Fixed bug with comparing relative URLs for merging file catalogs. 2006-12-17 Robby Stephenson * Fixed bug with some results in z39.50 search not showing up, due to search events getting processed out of order. 2006-12-07 Robby Stephenson * Fixed bug with secondary and tertiary sorting. 2006-12-06 Robby Stephenson * Bumped the Amazon ECS version to 2006-11-30. * Updated Amazon search to allow ISBN-13 values. * Fixed bug with saving URLs without the protocol. * Changed entry comparison for files to match only on URL value. * Released Tellico 1.2.7. 2006-12-04 Robby Stephenson * Improved performance for copying and deleting multiple entries. 2006-11-30 Robby Stephenson * Updated sorting to take title articles into account. 2006-11-29 Robby Stephenson * Added ISBN hyphenation rules for German, Czech, and Dutch. * Fixed bug that caused image loss when loading from data file. 2006-11-10 Robby Stephenson * Released Tellico 1.2.6. 2006-11-09 Robby Stephenson * Improved TV show matching for IMDB. 2006-11-05 Robby Stephenson * Fixed crashing bug in ProgressItem, reported by Izaak Branderhorst. 2006-11-03 Robby Stephenson * Released Tellico 1.2.5. 2006-11-01 Robby Stephenson * Fixed another bug with over-writing images on entry update. 2006-10-24 Robby Stephenson * Updated yahoo audio search webservice url. 2006-10-18 Robby Stephenson * Added warning when importing CSV without assigning any fields. * Updated allocine script (v 0.3), from Mathias Monnerville. 2006-10-16 Robby Stephenson * Added board game collection, patch from Steve Beattie. * Added script for searching boardgamegeek.com, patch from Steve Beattie. 2006-10-15 Robby Stephenson * Branched 1.2.x series. * Fixed encoding bug with allocine.fr script. 2006-10-14 Robby Stephenson * Released Tellico 1.2.4. 2006-10-12 Robby Stephenson * Fixed bug with overwriting images when updating entries. 2006-10-11 Robby Stephenson * Improved performance on loading and unloading. * Changed Nintendo 'Revolution' to 'Wii'. 2006-10-05 Robby Stephenson * Fixed off-by-one error in file listing volume name reader. * Update allocine fetcher. 2006-09-25 Robby Stephenson * Fixed bug with fetch dialog showing all sources, instead of just relevant ones. 2006-09-24 Robby Stephenson * Released Tellico 1.2.3. 2006-09-20 Robby Stephenson * Added button to search dialog to get additional results. 2006-09-14 Robby Stephenson * Updated to work with KDE 3.3.1 or later. 2006-09-09 Robby Stephenson * Fixed bug with HTML export not including tellico2html.js. 2006-09-07 Robby Stephenson * Fixed bug that could cause loss of images. * Fixed URL output in Image List report. * Released Tellico 1.2.2. 2006-09-06 Robby Stephenson * Released Tellico 1.2.1. 2006-09-05 Robby Stephenson * Fixed CDDB cache importer to always assume utf-8, as in libkcddb. * Changed bibtex exporter to add braces or quotes around url fields. 2006-09-04 Robby Stephenson * Fixed infinite loop in file listing importer. * Increased default image cache size to 10 meg. * Reworked image loading to avoid reloading large images as much as possible. 2006-09-02 Robby Stephenson * Fixed bug with CDDB not working (always using debug for Pink Floyd!) 2006-09-01 Robby Stephenson * Fixed bug with completion not working in filter dialog. * Fixed bug with template image gradients not getting updated properly. 2006-08-31 Robby Stephenson * Released Tellico 1.2. 2006-08-30 Robby Stephenson * Fixed crashing bug when using right-click in list view. 2006-08-16 Robby Stephenson * Added Abstract and Keywords to default fields for bibliographies. 2006-08-15 Robby Stephenson * Released Tellico 1.2-pre3. 2006-08-09 Robby Stephenson * Released Tellico 1.2-pre2. 2006-08-07 Robby Stephenson * Fixed bug with merging fields in CSV importer. 2006-08-06 Robby Stephenson * Released Tellico 1.2-pre1. 2006-08-06 Robby Stephenson * Added "next" and "prev" buttons to entry editor. 2006-08-04 Robby Stephenson * Changed HTML export to use libxml2 instead of KHTML for parsing. That should fix the "all text on one line" problem. * Bumped minimum libxml2 verstion to 2.6.0 or greater. 2006-07-30 Robby Stephenson * Added bundled scripts for allocine.fr, Spanish Minstry of Culture, and Dark Horse Comics, all from Mathias Monnerville. 2005-11-13 Robby Stephenson * Bumped minimum KDE version to 3.3. 2006-07-18 Robby Stephenson * Added illustrator field to MODS translator. 2006-07-13 Robby Stephenson * Changed document saving to remember what format (Zip or XML) was opened, and save the file in the same format. 2006-07-04 Robby Stephenson * Updated sorting in HTML export to be locale-aware. * Added auto-completion to filter dialog. 2006-06-30 Robby Stephenson * Improved ISBN-13 compliance. 2006-06-24 Robby Stephenson * Fixed bug with relative url links in template. 2006-06-22 Robby Stephenson * Fixed memory leak in image handling. 2006-06-16 Robby Stephenson * Changed IMDb and Amazon to always use iso-8859-1 encoding for URL. 2006-06-12 Robby Stephenson * Fixed bug with HTML export and MSIE. 2006-06-11 Robby Stephenson * Added date stamp to all report templates. 2006-06-07 Robby Stephenson * Fixed IMDb bug that hid partial name matches for duplicate names. * Implemented workaround for NetAccess::download() that doesn't show download progress info. 2006-06-03 Robby Stephenson * Added validator for unmodified CueCat bar code reader. 2006-05-07 Robby Stephenson * Released Tellico 1.1.6. 2006-05-04 Regis Boudin * Added French documentation. 2006-05-04 Robby Stephenson * Fixed bug that prevented some images from being written to data directory. 2006-04-22 Robby Stephenson * Add font and color config to templates. 2006-04-19 Robby Stephenson * Fixed bug with multiple ISBN search for z29.50. * Fixed "disappearing paragraph" bug in Fancy template. * Fixed name order for IBS searches. * Released Tellico 1.1.5. 2006-04-12 Robby Stephenson * Added Xbox 360, Revolution and Playstation3 to game collection. 2006-04-11 Robby Stephenson * Added view config saving for custom collections based on URL. * Fixed crashing bug when deleting and re-adding fields by same name. * Fixed bug with HTML decoding in IMDb plot summary. 2006-04-10 Robby Stephenson * Fixed bug that re-added default fields when adding search results. 2006-04-07 Robby Stephenson * Added OS check (linux only) for enabling cd-text support. 2006-04-06 Robby Stephenson * Updated Spanish, thanks to Alejandro Hamann and Quique. * Fixed bug with writing temporary files, even when using app data dir. 2006-04-03 Robby Stephenson * Improved PubMed fetcher to grab URL links. 2006-04-02 Robby Stephenson * Changed grouping list to be sorted alphabetically. * Fixed OpenOffice.org plugin build for OOo SDK 2.0.2. * Released Tellico 1.1.4. 2006-03-29 Robby Stephenson * Added referrer to IBS fetcher so images get downloaded. 2006-03-27 Robby Stephenson * Changed XSL templates to output valid HTML 4.01 Strict, patch from Karl Ove Hufthammer. 2006-03-25 Robby Stephenson * Added button for installing new templates. 2006-03-24 Robby Stephenson * Added context menu item in table widget for clearing table. 2006-03-23 Robby Stephenson * Added template preview button in configuration dialog. * Added field for relative folder path to file catalog listing. * Fixed bug with incorrectly setting document modified when loading images. * Fixed bug with loading loans for entries with non-consecutive ids. * Fixed bug that prevented XSLT files with spaces or other non-encoded characters from being opened. * Fixed bug that caused crashes when changing grouping options for some fields. 2006-03-15 Robby Stephenson * Fixed bug that prevented files saved with versions < 0.8 from being opened. 2006-03-12 Robby Stephenson * Released Tellico 1.1.3. 2006-03-10 Robby Stephenson * Fixed bug with comic book template not showing up in a couple of places in the GUI. * Updated MARC2MODS3 stylesheet to MODS 3.1 from LoC. * Added preset z39.50 server list. 2006-03-09 Robby Stephenson * Added isbndb.com fetcher. * Fixed z39.50 search to use proper encoding for search terms. 2006-03-07 Robby Stephenson * Changed ISO 5426 converter to grab diaeresis instead of umlaut. 2006-03-05 Robby Stephenson * Added patch from Karl Ove Hufthammer, for lang-specific sorting. * Released Tellico 1.1.2. 2006-03-04 Robby Stephenson * Fixed file save permissions to remember group. * Fixed backup file permissions. 2006-03-02 Robby Stephenson * Fixed mess with shared pointers holding shared pointers holding shared pointers... * Fixed button size in HTML export. 2006-02-28 Robby Stephenson * Updated fr.po, thanks to Mathias Monnerville. * Fixed GCfilms importer to read running time correctly. * Use IMDb url for updating if available. 2006-02-27 Robby Stephenson * Added pt_BR, thanks to Claudio Felix. 2006-02-23 Robby Stephenson * Added dialog for importing new data sources. 2006-02-22 Robby Stephenson * Fixed HTML export to always use relative links. * Fixed Fancy template column widths, patch from Karl Ove Hufthammer. 2006-02-20 Robby Stephenson * Add Ant Movie Catalog file importer. 2006-02-19 Robby Stephenson * Allow data sources to overwrite current data when updating. 2006-02-18 Robby Stephenson * Added dialog to choose from multiple CDDB lookup responses. * Fields can have default values. * Changed default IMDB server to akas.imdb.com. 2006-02-18 Robby Stephenson * Fixed cleanup when oowriter is closed. * Fixed character encoding for IMDB searches. * Released Tellico 1.1.1. * Branched Tellico 1.1.x. 2006-02-17 Robby Stephenson * Fixed image linking in HTML export. * Updated admin/ and Makefile.am for recent KDE updates. * Changed installation directory of .desktop file to use xdg_appsdir. 2006-02-14 Robby Stephenson * Fixed templates to scale images down to max of 150x200. 2006-02-08 Robby Stephenson * Fixed bug with Video template not showing nationality. * Added ISO 5426 and ISO 6937 character set converters, adapted from MARC4J; 2006-02-07 Robby Stephenson * Dual-licensed the documentation under the GFDL and the FreeBSD Documentation License. * Released Tellico 1.1. 2006-02-04 Robby Stephenson * Added track length to CDDB lookup result. 2006-02-01 Robby Stephenson * Fixed bug with not deleting some temporary files. 2006-01-29 Robby Stephenson * Updated bibtex-translation.xml, thanks to Norbert Nemec. * Updated es.po, thanks to Alejandro Hamann. 2006-01-25 Robby Stephenson * Relaxed MODS importer to look for publisher, isbn, or lccn to determine the entry is a book. Previously, a typeOfResource="text" was required. * Allowed z39.50 source to ask for MODS directly. * Added message box for stderr output from scripts. * Added simple GRS-1 importer for z39.50 fetcher. 2006-01-24 Robby Stephenson * Fixed bug with potential data los for saving image files after changing config option. * Released Tellico 1.1pre3. 2006-01-22 Robby Stephenson * Released Tellico 1.1pre2. * Improved entry views with some graphics, a la amaroK. 2006-01-21 Robby Stephenson * Added drag-n-drop from Mozilla Firefox to image widget. * Changed default behavior to include images in data file, and added a config option. * Added a message box warning about performance when the user first opens a file with more than 100 images. 2006-01-20 Robby Stephenson * Fixed bug with parsing arguments to external data source scripts. 2006-01-19 Robby Stephenson * AnimneNfo.com source added by default, and IBS.it added if Italian is in the user's language list. * Alexandria exporter includes publishing year now. 2006-01-17 Robby Stephenson * Alexandria importer includes publishing year now. 2006-01-15 Robby Stephenson * Changed file saving to be smart about which images need written to cache. Should improve save-time. * Fixed bug with undo/redo not correctly reverting to old values. 2006-01-14 Robby Stephenson * Added Russian translation, from Artur Kalimullin. 2006-01-12 Robby Stephenson * Released Tellico 1.1pre1. * Added/updated SRU fetcher. 2006-01-11 Robby Stephenson * Added Internet Bookshop (ibs.it) book source. 2006-01-10 Robby Stephenson * Added AnimeNfo.com search source. 2006-01-09 Robby Stephenson * Fixed Debian bug #346414 in hu.po. 2006-01-08 Robby Stephenson * Changed image loader to read and write images inside the Tellico shared directory to speed up loading, etc. 2005-12-06 Robby Stephenson * Merged OpenOffice.org interop. Requires SDK for compilation. 2006-01-05 Robby Stephenson * Added Yahoo! Album Search source. * Added Tri-Column video report template. 2005-12-15 Robby Stephenson * Expand external app fetcher to include any of the fetch keys. 2005-12-08 Robby Stephenson * Increased max table columns to 10. 2005-12-07 Robby Stephenson * Updated Amazon fetcher to allow selecting keywords or not. 2005-12-05 Robby Stephenson * Changed bibtex behavior to not add braces around capital letters. 2005-11-29 Robby Stephenson * Moved no-capitalization list to config dialog. 2005-11-28 Robby Stephenson * Updated Hungarian, from Csaba Zakarias. 2005-11-17 Robby Stephenson * Added separate UPC searching for Amazon. 2005-11-16 Robby Stephenson * Added CD-Text reading to FreeDBImporter, plus option to disable compilation in case of platform issues. * Move to Amazon ECS version 4. 2005-11-15 Robby Stephenson * Added IMDB rating field to fetcher. 2005-11-13 Robby Stephenson * Bumped minimum KDE version to 3.2 and minimum QT version to 3.2. 2005-11-10 Robby Stephenson * Custom data source fields are now configurable. 2005-11-07 Robby Stephenson * Made size of IMDb cast list configurable. * Broke out cite actions into submenu, and added copying bibtex to clipboard. 2005-11-03 Robby Stephenson * Bumped XML syntax version to 9 to include file catalog and changing default music collection track field to 3 columns. 2005-11-01 Robby Stephenson * Fixed compilation on GNU/kFreeBSD, Debian bug #336949, patch from Aurelien Jarno. 2005-10-29 Robby Stephenson * Improved table fields to show column titles. * Changed buttons to a right-click popup. 2005-10-27 Robby Stephenson * Improved audio file metadata importer to include track length as an additional column in the track field. 2005-10-26 Robby Stephenson * The audio file importer now checks for track numbers in the file name, patch from Andras Mantia. 2005-10-19 Robby Stephenson * Added a string store to reduce memory consumption. 2005-10-17 Robby Stephenson * Released Tellico 1.0.3. 2005-10-15 Robby Stephenson * Added buttons for inserting and removing rows from a table. 2005-10-13 Robby Stephenson * Enabled auto-updating for certain entries and data sources. 2005-10-12 Robby Stephenson * Updated it.po, from Lorenzo Novaro. 2005-10-11 Robby Stephenson * New and modified entries now show labels indicating so. 2005-10-08 Robby Stephenson * Improved status bar. 2005-10-07 Robby Stephenson * Updated CalendarHandler to compile with KDEPIM 3.5. 2005-10-06 Robby Stephenson * Fixed bug with bibtex macro names always being imported in lowercase. 2005-10-03 Robby Stephenson * Fixed bug with initialization, patch from Marco Clemencic. 2005-10-02 Robby Stephenson * Released Tellico 1.0.2. * Updated IMDB fetcher. 2005-09-25 Robby Stephenson * Fixed bug with auto-detecting MARC encoding for z39.50 import. 2005-09-22 Robby Stephenson * Fixed bug with CSV importer not properly parsing some non-CSV files. * Fixed bug with entry selection in icon view. * Added GCfilms exporter; 2005-09-21 Robby Stephenson * Fixed bug with showing invalid loan due date. * Added GCfilms importer. * Added IMDB link when fetching info from IMDB. * limited number of cast results form IMDB to 20; 2005-09-20 Robby Stephenson * Released Tellico 1.0.1. 2005-09-19 Robby Stephenson * Fixed bug with loading utf-8 encoded bibtex files. 2005-09-18 Robby Stephenson * Fixed bug with variable icon sizes in config dialog. 2005-09-16 Robby Stephenson * Fixed bug for merging field properties, which mostly showed up as double-colons in music collection tracks. * Fixed crashing bug with modifying filters and cancelling. 2005-09-11 Robby Stephenson * Added Norwegian Nynorsk translation from Karl Ove Hufthammer. 2005-09-10 Robby Stephenson * Fixed bug in audio file metadata import that was overwriting track listings incorrectly. * Fixed crashing bug for selecting groups and entries in group view. 2005-09-09 Robby Stephenson * Released Tellico 1.0. 2005-09-05 Robby Stephenson * Added Polish translaion from Marek Janukowicz. 2005-08-22 Robby Stephenson * Fixed bug with external application fetcher not reading path entries correctly. * Updated Spanish translation, thanks to Brian Hughes. 2005-08-21 Robby Stephenson * Updated Swedish, thanks to Peter Landgren. * Added patch from Nix to dynamically resize table columns. 2005-08-20 Robby Stephenson * Released Tellico 1.0pre2. 2005-08-14 Robby Stephenson * Changed audio file metadata to ignore case when doing album comparisons. * Changed collection loader to better compensate for old rating fields. * Changed AddEntry command to act on multiple entries. 2005-08-11 Robby Stephenson * Updated Dutch translation, thanks to Fred Marchee. * Updated Fininsh translation, thanks to Teuvo Eloranta. 2005-08-06 Robby Stephenson * Released Tellico 1.0pre1. 2005-08-04 Robby Stephenson * When exporting HTML, copy any relative images, etc. 2005-08-01 Robby Stephenson * Strip HTML tags from paragraph export to PilotDB. 2005-07-30 Robby Stephenson * Make the amazon importer default to medium images instead of none. 2005-07-28 Robby Stephenson * Make the 'Fancy' template default. 2005-07-01 Robby Stephenson * Released Tellico 0.13.8. 2005-06-30 Robby Stephenson * Fixed bug with CSV import parsing. 2005-06-28 Robby Stephenson * Added license exception for linking to OpenSSL. 2005-06-25 Robby Stephenson * Added a status message timeout for main window and fetch dialog. 2005-06-23 Robby Stephenson * Fixed crashing bug for fields with empty categories. 2005-06-22 Robby Stephenson * Added initial Catalan translation from David Majà Martínez. 2005-06-20 Robby Stephenson * Added fetcher for reading output from an external application. * Fixed bug with wrong icon size for entry action. 2005-06-17 Robby Stephenson * Added video game collection type. 2005-06-12 Robby Stephenson * Added capability for recursively reading all CDDB cache files, based on patch from Marco Hofmann. 2005-06-08 Robby Stephenson * Fixed bug with file extension defaulting to .bc. 2005-06-02 Robby Stephenson * Changed Table field to allow up to 5 columns. Deprecated Table2 type. 2005-06-01 Robby Stephenson * Added Rating field type. 2005-05-31 Robby Stephenson * Fixed bug with grouping by checkbox fields. 2005-05-30 Robby Stephenson * Updated Norwegian translation. * Added Unimarc support to z39.50 importer. 2005-05-25 Robby Stephenson * Released Tellico 0.13.7. 2005-05-25 Robby Stephenson * Changed bibtex exporter to put crossref'd entries at the end. Only one level of crossref is allowed. 2005-05-19 Robby Stephenson * Fixed bug with changing between rating and normal choice field. 2005-05-18 Robby Stephenson * Updated Dutch translation, thanks to Fred Marchee. 2005-05-13 Robby Stephenson * Fixed bug with clearing table fields. * Added ONIX export, borrowing format from Alexandria. 2005-05-10 Robby Stephenson * Changed contact email to tellico-users mailing list. 2005-04-27 Robby Stephenson * Fixed corner case xml element naming for new fields. * Fixed typo on allowed and description in fields editor. 2005-04-10 Robby Stephenson * Fixed bug with having fields named after xml elements, like entry or field. 2005-04-09 Robby Stephenson * Added a PubMed search source, thanks to an XSL stylesheet from Michaël Zugaro. 2005-04-08 Robby Stephenson * Added some accelerators, thanks to patch from Felix Berger. 2005-04-07 Robby Stephenson * Added Undo/Redo framework. 2005-04-03 Robby Stephenson * Released Tellico 0.13.6. 2005-03-29 Robby Stephenson * Fixed bug with adding multiple entries from fetch dialog not added additional images. * Added Portuguese translation, thanks to Ligia Moreira. 2005-03-23 Robby Stephenson * Changed internet search dialog to clear results when running a new search. * Changed selection so that hidden items are not selected. 2005-03-16 Robby Stephenson * Added shortcuts for entry grouping and filter. 2005-03-08 Robby Stephenson * Released Tellico 0.13.5. 2005-03-02 Robby Stephenson * Updated German translation, thanks to Gerrit Albrecht. 2005-03-01 Robby Stephenson * Fixed crash for some amazon searches. * Fixed character encoding for Japanese amazon searches. 2005-02-27 Robby Stephenson * Released Tellico 0.13.4 2005-02-25 Robby Stephenson * Added a configure param to disable cddb even if kcddb headers found. 2005-02-24 Robby Stephenson * Updated bibtex translation map, fixing some bugs. * Fixed bug with Amazon search character encoding. * Changed Bibtex handler to convert {MIT} to MIT, importing and exporting. 2005-02-23 Robby Stephenson * Fixed bug with grouping being enabled for paragraphs. 2005-02-17 Robby Stephenson * Fixed bug with exporting empty groups in HTML. * Added capability for adding loan due notice to KOrganizer calendar. 2005-02-14 Robby Stephenson * Fixed bug with RIS importer messing up entry types. 2005-02-10 Robby Stephenson * Released Tellico 0.13.3. 2005-02-08 Robby Stephenson * Fixed bug in configure script for disabling amazon, thanks to Markus Brueffer. * Fixed bug with spin boxes not enabling Apply button in Config Dialog. * Fixed bug with audio file metadata import for compilations. 2005-02-05 Robby Stephenson * Update some bibtex character translations. * Released Tellico 0.13.2. 2005-02-03 Robby Stephenson * Updated IMDb fetcher. * Updated bibtex import and export to recognize {X} constructions. * Fixed bug with locale encoding for HTML export. * Changed statusbar to show tooltips for menu items. 2005-02-02 Robby Stephenson * Added France and Canada locales to Amazon.com sources. 2005-02-01 Robby Stephenson * Fixed bug with importing RIS with no final space. * Fixed Debian bug #290467 for gcc 4.0 compilation, thanks to Regis Boudin. 2005-01-28 Robby Stephenson * Removed find dialog since the filter works so much better. 2005-01-27 Robby Stephenson * Removed option for toggling entry count show in group view. * Added check-out for lending items with view. 2005-01-25 Robby Stephenson * Fixed Debian bug #292165 with bibtex export of umlauts. 2005-01-21 Robby Stephenson * Updated Swedish translation, thanks to Peter Landgren. 2005-01-20 Robby Stephenson * Added saved filters with view. * Changed IMDb cast page to decode HTML entities, thanks to Rafa Kortes. 2005-01-12 Robby Stephenson * Changed double click in the Internet Search dialog to add the entry. 2005-01-11 Robby Stephenson * Added collection report dialog. 2005-01-06 Robby Stephenson * Improved file loading efficiency somewhat. 2005-01-05 Robby Stephenson * Changed image loading to be on-demand for local zip files. 2004-12-19 Robby Stephenson * Added DCOP interface for exporting files. * Added vertical line between columns in list view. * Translated xsl entry template names. 2004-12-18 Robby Stephenson * Added DCOP interface for importing files. 2004-12-17 Robby Stephenson * Branched to 0.13.x. * Updated audio file importer to use "(Various)" for multi-artist albums. 2004-12-11 Robby Stephenson * Fixed a bug in PilotDB exporting, I think. * Added PilotDB export support for Date fields. * Released Tellico 0.13.1. 2004-12-10 Robby Stephenson * Fixed some date formatting. 2004-12-09 Robby Stephenson * Fixed auto bibtex key generation for empty keys. 2004-12-08 Robby Stephenson * Fixed Entry View to react to changes in the color palette. 2004-12-06 Robby Stephenson * Increased maz z39.50 port to 999999. * Fixed some KMAX, KMIN template issues with casts. * Added column shading for detailed view, per KDE bug 59791. 2004-12-03 Robby Stephenson * Updated Finnish translation, thanks to Tuevo Eloranta. * Updated Norwegian translation, thanks to Leif Mathis Gaup. * Fixed *BSD compile, with patch from Markus Brüffer. 2004-12-01 Robby Stephenson * Enabled relative URLs in URL fields. * Disabled running executables in URL fields. * Released Tellico 0.13. 2004-11-30 Robby Stephenson * Removed all the KStaticDeleter references. Don't get fancy. * Updated title sorts to work for articles ending with an apostrophe. * Updated audio file metadata importer to recognize albums with multiple artists and change track list accordingly. * Fixed CSV importer to properly find pre-existing, non-default fields. 2004-11-25 Robby Stephenson * Added stars for up to a 10 rating. * Added checkmark in entry templates for bool fields. 2004-11-20 Robby Stephenson * Added Spanish tips translation from ventolera. 2004-11-19 Robby Stephenson * Released Tellico 0.13pre3. 2004-11-18 Robby Stephenson * Added command-line options for loading bibtex, MODS, and RIS files. 2004-11-17 Robby Stephenson * Updated Amazon fetcher to work with more than 10 ISBN. * Updated z39.50 fetcher to work with more than 10 ISBN. * Added language lookup table to mods2tellico.xsl. * Updated lyxpipe cite to auto-generate bibtex key if necessary. * Added "load ISBN list from file" to search dialog. 2004-11-14 Robby Stephenson * Released Tellico 0.13pre2. 2004-11-12 Robby Stephenson * Improved printing speed. * Added user and password config for z39.50. * Added address field to default bibliography. 2004-11-10 Robby Stephenson * Updated French translation, thanks to Regis Boudin. * Fixed layout bug in config dialog. 2004-11-09 Robby Stephenson * Added fix similar to kde bug 86188 for including linux/cdrom.h. * Changed latex cite to separate multiple cites with ", " instead of "," to better match pybliographer. 2004-11-08 Robby Stephenson * Added host config to IMDB. * Added image download option to Amazon and IMDB. * Updated kde-common/admin/cvs.sh to allow other automake versions. * Released Tellico 0.13pre1. 2004-11-07 Robby Stephenson * Added character set config to z39.50. * Changed main document to tellico.xml. 2004-10-31 Robby Stephenson * Updated initial minimum width to 600 pixels. * Added group selection entry to collection menu. * Updated Find Dialog to remove FromBeginning (didn't make sense) 2004-10-23 Robby Stephenson * Updated HTML export and printing stylesheets a bit. 2004-10-18 Robby Stephenson * Added patch to compile on g++ 2.95 from Markus Brueffer. 2004-10-11 Robby Stephenson * Added patch from wwp to fix saving file on quit. 2004-10-02 Robby Stephenson * Added confirm message box when deleting an entry. 2004-09-23 Robby Stephenson * Changed default file textension to .tc. 2004-09-15 Robby Stephenson * Added Unrated certification to default video collection. 2004-09-14 Robby Stephenson * Renamed Bookcase to Tellico * Added IMDB fetcher. * Incremented DTD version to 7, changed root element to tellico. * Changed zip file to use maindoc.xml as do the KOffice files. 2004-09-10 Robby Stephenson * Added new icons from Virginie Quesnay. 2004-09-09 Robby Stephenson * Updated French translation, thanks to Regis Boudin. * Removed libcdda (cdparanoia) dependency, using cd-discid code instead. * Added rating widget in entry editor, using stars. 2004-09-07 Robby Stephenson * Really fixed gcc 2.95 compilation. * Added RIS importer. 2004-09-02 Robby Stephenson * Released Bookcase 0.11. 2004-09-01 Robby Stephenson * Added FreeDB importer. 2004-08-27 Robby Stephenson * Fixed compile on gcc 2.95. 2004-08-26 Robby Stephenson * Added Alexandria importer. * Added Alexandria exporter. 2004-08-25 Robby Stephenson * Updated Norwegian translation, thanks to Leif Mathis Gaup. * Read album covers from .directory files in audio file importer. 2004-08-24 Robby Stephenson * Added support for reading audio file meta-data. 2004-08-22 Robby Stephenson * Updated paragraph fields to remember line-breaks. * Released Bookcase 0.10. 2004-08-20 Robby Stephenson * Added HTML formatting to paragraphs in entry templates. * Updated German translation, thanks to Gerrit M. Albrecht. 2004-08-18 Robby Stephenson * Added Latin1Literal class to speed up string comparisons. * Added MODS import menu item. 2004-08-10 Robby Stephenson * Added "export selected entries only" option. 2004-08-09 Robby Stephenson * Fixed UI file location. * Released Bookcase 0.10pre2. 2004-08-08 Robby Stephenson * Released Bookcase 0.10pre1. * Added entry icon to fetch dialog button. 2004-08-03 Robby Stephenson * Fixed bug for exporting HTML for entries with multiple titles. * Changed HTML export to put entry files in separate directory. * Added link to parent collection file for entry templates. 2004-08-01 Robby Stephenson * Updated French translation, thanks to Rui Nibau. * Changed to show a spinbox for single number values; 2004-07-30 Robby Stephenson * Fixed bug with capitalization of words split by dash or comma, and ending with apostrophes. 2004-07-13 Robby Stephenson * Fixed bugs with enabling apply/ok buttons in fields dialog for extended properties, and disappearing groups when renaming fields. 2004-07-02 Robby Stephenson * Added collection and group view in stack. 2004-06-26 Robby Stephenson * Fixed bug with copying selected entries. 2004-06-21 Robby Stephenson * Fixed gcc 3.4 compilation bug in src/translators/btparse/err.h 2004-06-07 Robby Stephenson * Update Norwegian translation, thanks to Leif Mathis Gaup. 2004-05-27 Robby Stephenson * Released Bookcase 0.9.3. * Updated italian translation, thanks to FaUsT. 2004-05-25 Robby Stephenson * Fixed crashing bug for exporting when no fields can be grouped. 2004-05-24 Robby Stephenson * Restructured ISBN code to get rid of some old cruft. * Added patch from Regis Boudin to add French ISBN formatting and remove old copyright restricted ISBN code. 2004-05-22 Robby Stephenson * Added entry group templates. 2004-05-19 Robby Stephenson * Changed focus to move to next entry when one is deleted. * Changed document loader to allow i18n attribute indicate translation. * Fixed bug with empty group names. 2004-05-17 Robby Stephenson * Released Bookcase 0.9.2. 2004-05-15 Robby Stephenson * Fixed bug with xslt import with libxml < 2.6. * Fixed bug with losing extended properties when cloning fields/ 2004-05-10 Robby Stephenson * Added Amazon fetcher. * Added SRU Fetcher. 2004-05-07 Robby Stephenson * Fixed bug with XSLT importer. * Added MODS XSLT importer file. 2004-05-06 Robby Stephenson * Branched 0.9.x 2004-05-02 Robby Stephenson * Changed Fields Dialog to close when creating a new collection or opening one. * Removed GCJ/java checks from configure. * Released Bookcase 0.9.1 2004-04-30 Robby Stephenson * Fixed i18n extraction for UI and gettext extensions. * Removed Japanese translation because of encoding errors. * Updated CSV import to know about current fields when appending or merging 2004-04-29 Robby Stephenson * Updated German translation from Gerrit Albrecht. 2004-04-27 Robby Stephenson * Fixed bug with lyxpipe using LYXSRV insted of LYXCMD. * Changed quick filter to wait 200 ms before updating the filter. 2004-04-21 Robby Stephenson * Added wait cursor for file saving. * Fixed bug with created a double collection when failing to load the initial file. * Make sure column view updates when an image is cleared. 2004-04-18 Robby Stephenson * Released Bookcase 0.9. 2004-04-16 Robby Stephenson * Improved visibilty of image size in Fancy.xsl. * Updated Italian translation, thanks to FaUsT. * Updated Finnish translation, thanks to Teuvo. * Added hidden pref for pixmap size in detailed view. * Changed HTML exporter to use titles as filenames. 2004-04-13 Robby Stephenson * Added hidden preference for pilotdb export charset encoding. 2004-04-09 Robby Stephenson * Improved bibtex export to detect duplicates keys and add 'a'b, 'b', etc. * Released Bookcase 0.9pre1 2004-03-26 Robby Stephenson * Updated printing and HTML export to include images. * Updated Detailed View to show images, too. 2004-03-25 Robby Stephenson * Updated Italian translation, thanks to FaUsT. * Changed toolbar icon for New Entry to collection icon on top of empty mime type. 2004-03-24 Robby Stephenson * Added menu item in group view for filtering on groups. * Added config options for image sizes in printout. 2004-03-22 Robby Stephenson * Fixed another bug with HTML export character encoding. * Added config panel for bibtex quotation style and lyxpipe location 2004-03-20 Robby Stephenson * Fixed Paragraph fields to properly signal modified in the Entry Editor. * Fixed crashing bug for fields with nothing but whitespace in the datafile. * Updated printing stylesheet so that images can be included. 2004-03-19 Robby Stephenson * Changed document opening so that if the first field has the name of "_default", all the default fields will be added. 2004-03-17 Robby Stephenson * Changed Dependent fields to allow grouping. * Changed Fields Dialog to allow changing the field types, in some limited cases. 2004-03-15 Robby Stephenson * Fixed crashing bug in PilotDB export with images. * Fixed bug with "other" delimiter freezing the app. 2004-03-13 Robby Stephenson * Fixed bug with trying to reopen Untitled after converting to a bibliography. * Added support for lyxpipe and citing entries in bibliography. 2004-03-12 Robby Stephenson * Changed to be more aggressive in creating URL fields on Bibtex import. 2004-03-10 Robby Stephenson * Added Compact and Fancy entry templates. * Released Bookcase 0.8.5. 2004-03-07 Robby Stephenson * Fixed bug in Video template for when there's no Cast template. * Fixed bug with detecting old versions of gcc and vector::at. 2004-03-05 Robby Stephenson * Fixed bug with filter dialog layout and KDE 3.2. 2004-03-04 Robby Stephenson * Fixed bug with corrupt zip files when more than one entry references the same image. 2004-02-28 Robby Stephenson * Fixed the gcc 2.95 compile bug for sure. 2004-02-25 Robby Stephenson * Fixed crashing bug in Data::Entry::field(). * Fixed XSLTHandler to work for URLs, so XSLT files can reference other files. 2004-02-22 Robby Stephenson * Fixed bug with HTML entities in XSLT stylesheet params. 2004-02-20 Robby Stephenson * Fixed bug with duplicate menu items in KDE 3.2.x. * Fixed bug to properly reorder categories when reordering fields. * Released Bookcase 0.8.4. 2004-02-19 Robby Stephenson * Fixed auto-capitalize and auto-format to take affect immediately. 2004-02-18 Robby Stephenson * Added Czech translation, thanks to Robert Kratky. 2004-02-17 Robby Stephenson * Fixed compilation issues for gcc 2.95 and FreeBSD. * Changed the Config Dialog to use a semi-colon to separated the articles, prefixes, and suffixes instead of a comma, for consistency. They're still saved in the config file with a comma, though, so old options should be fine. 2004-02-14 Robby Stephenson * Fixed problem when trying to reopen last file and getting error. * Added drag & drop support for the image widget. 2004-02-13 Robby Stephenson * Updated Estonian translation, from Toomas Nigola. * Changed to remember sorting config in group view. 2004-02-12 Robby Stephenson * Fixed crashing bug when renaming a single-category field. 2004-02-11 Robby Stephenson * Changed Entry Editor to redo page layout when deleting fields. * Added merge functionality when importing collections. * Fixed bug with exporting HTML in non-utf8. 2004-02-09 Robby Stephenson * Updated Spanish translation, thanks to Quique. * Added sorting by group count. 2004-02-07 Robby Stephenson * Added workaround for layout bug in Keramik style. 2004-02-04 Robby Stephenson * Fixed saving visible columns yet again. * Changed XSLT parser to prevent all network calls. * Fixed crashing bug when deleting a field. * Fixed compilation bug when using libxml > 2.6.0 but libxslt < 1.1. * Released Bookcase 0.8.3. 2004-02-04 Robby Stephenson * Fixed file truncation for file with some Unicode characters. * Released Bookcase 0.8.2. 2004-02-03 Robby Stephenson * Released Bookcase 0.8.1. 2004-02-02 Robby Stephenson * Added Album entry template. * Added Finnish translation, thanks to Teuvo Eloranta. * Added --with-xml-catalog configure option. 2004-01-30 Robby Stephenson * Fixed compile bug with src/Makefile.am and libxslt. * Fixed bug with $datadir param in Default entry template. * Added Video entry template. * Added EAN to ISBN conversion for 978 and 979 codes. Patch from Martijn Pieters. 2004-01-27 Robby Stephenson * Fixed bug with Select All not working. * Fixed some compilation issues. * Updated French translation from Rui Nibau. 2004-01-25 Robby Stephenson * Fixed DTD for new collections. * Updated some accel keys. * Changed Requires to KDE 3.1 or higher since KZip is used. * Released Bookcase 0.8. 2004-01-23 Robby Stephenson * Released Bookcase 0.8pre1 2004-01-22 Robby Stephenson * Added Finnish translation from Teuvo Eloranta. * Added "Export to XML" option since file format is Zip now. * Switched to new XML parser in libxml2 > 2.6.0 2004-01-13 Robby Stephenson * Added new Collection menu. 2004-01-09 Robby Stephenson * Changed entry method for bibtex string macros. Triple-clicking on list view items is stupid. 2004-01-08 Robby Stephenson * Added zip file format for collections with images. 2004-01-01 Robby Stephenson * Added cut, copy, and paste functions. * Added new entry and copy entry functions. * Added find previous, select all, and deselection functions. 2003-12-31 Robby Stephenson * Changed double clicking an entry to show the editor. * Moved entry edit widget to dialog box. 2003-12-13 Robby Stephenson * Updated French translation, thanks to RNB. 2003-12-11 Robby Stephenson * Added image fields. * Changed auto-completion to account for fields with multiple values. 2003-12-07 Robby Stephenson * Updated Norwegian translation, thanks to Leif Mathis Gaup. 2003-11-25 Robby Stephenson * Fixed bug in remembering visible columns for custom fields. * Added some speed-ups in loading time. * Released Bookcase 0.7.2 2003-11-24 Robby Stephenson * Fixed another ISBN format bug for non-english publishers. 2003-11-23 Robby Stephenson * Fixed bug with auto-capitalization when global setting was turned off. * Fixed bug with not saving options for empty collections. * Fixed bug with reordering visible fields. 2003-11-21 Robby Stephenson * Added Pilot-DB export option. * Fixed bug to update status line after deleting entries. 2003-11-12 Robby Stephenson * Added Derived field type for concatenating values from other fields. * Added default collections for trading cards, coins, stamps, comic books, and wines. 2003-11-11 Robby Stephenson * Updated German tranlation, thanks to Gerrit Albrecht. * Added Swedish translation, thanks to Karolina Lindqvist. * Fixed bug with adding 2-column table. * Cut the 0.7.x branch. * Added Derived field type. 2003-11-09 Robby Stephenson * Fixed charset encoding bug. * Fixed compile for GCC 2.96. * Changed bibtex and CSV export to default to locale encoding. * Fixed bug with field editor not updating title. * Released Bookcase 0.7.1. 2003-11-08 Robby Stephenson * Released Bookcase 0.7. 2003-11-05 Robby Stephenson * Fixed the tips file translation issue. * Released Bookcase 0.7pre2. 2003-11-03 Robby Stephenson * Fixed ISBN formatting bug for non-english language publishers. * Released Bookcase 0.7pre1. 2003-10-25 Robby Stephenson * Fixed documentation compile error for KDE 3.2. * Added CSV importer. * Changed Bibtexml importer and exporter to use internal classes rather than XSL stylesheets. 2003-10-24 Robby Stephenson * Added string macro editor for Bibtex collections. * Printing now only prints visible entries by default. 2003-10-21 Robby Stephenson * Added new group for all attributes formatted as names, so Editors and Authors can be grouped together. 2003-10-10 Robby Stephenson * Changed printing to print columns as shown in the view, and group by the current grouping. * Removed old options for printing fields selection. 2003-10-04 Robby Stephenson * Added capability to reorder fields in field editor. 2003-10-03 Robby Stephenson * Imported btparse library code to use for importing Bibtex files. It has a lot of warnings, but none should be fatal. 2003-09-23 Robby Stephenson * Added "Bibtex Field" to default Bibtex collections fields to aid in exporting to Bibtex and Bibtexml. * Bumped document syntax to 4. * Changed "attribute" elements in document file to "field". * Changed all unit elements to "entry" rather than the unit name. * Changed Boolean values to save as "true" instead of an implicit value. * Added CSV exporter. 2003-09-12 Robby Stephenson * Added 2-column Table field type. 2003-09-09 Robby Stephenson * Changed boolean values to show title in group view. 2003-09-03 Robby Stephenson * Fixed bug when deleting multiple books from detailed listview. 2003-08-31 Robby Stephenson * Changed field completion to be case-insensitive. * Fixed bug with saving listview columns. 2003-08-16 Robby Stephenson * Fixed bug with reopening last saved file. 2003-08-12 Robby Stephenson * Added French translation from RNB. 2003-08-08 Robby Stephenson * Added menu item for configuring toolbars. * Added menu item for configuring shortcut keys. 2003-07-31 Robby Stephenson * Added Table field type. 2003-07-30 Robby Stephenson * Fixed bug in saving visible columns. * Improved loading time. 2003-07-29 Robby Stephenson * Implemented adding file extension (.bc) when using File->Save As and an extension filter is shown. * Added status bar text showing number of selected books when more than 1 is selected. 2003-07-24 Robby Stephenson * Added menu comands for showing/hiding the group view and editor. 2003-07-23 Robby Stephenson * Added "--nofile" command line option for bypassing the "Open last file" config setting. * Added "Tip of the Day" dialog. * Changed default for showing unit count to true. * Changed Year field type to generic Number. * Improved sorting to sort numerically for Number fields. * Added Default button to Field Editor dialog. * Added additional format option to never capitalize or format. * Fixed last open file option to no longer be over-written when Bookcase is exited with an empty document. * Cut the 0.6.x branch. 2003-07-22 Robby Stephenson * Fixed crashing bug in "Edit Fields" dialog. 2003-07-21 Robby Stephenson * Released Bookcase 0.6.5 2003-07-09 Robby Stephenson * Added Estonian translation from Toomas Nigola. 2003-07-07 Robby Stephenson * Updated German translation from Ulf-Diether Ehlert. 2003-07-01 Robby Stephenson * Fixed translation bug with category names. * Added Bulgarian translation from Boyan Ivanov. 2003-06-25 Robby Stephenson * Fixed sorting bug for empty fields. * Released Bookcase 0.6.4 2003-06-19 Robby Stephenson * Changed focus to first tab when adding new book. * Disabled multiple value option for title field in field editor. * Fixed bug with completion object not being updated when a field is changed to all auto-completion. * Fixed drawing bug with toolbar label background. 2003-06-17 Robby Stephenson * Fixed bug with surname matching too much with periods in regexp. * Added Japanese translation from Linux Magazine. * Added Dutch translation from Liese De Vos. 2003-05-30 Robby Stephenson * Fix crashing bug when opening the filter dialog on KDE 3.0. 2003-05-27 Robby Stephenson * Released Bookcase 0.6.3 2003-05-26 Robby Stephenson * Added feature where the column view remembers the previous sorted column, so that items with the same sort key are subsequently sorted by previous column. 2003-05-25 Robby Stephenson * Add Spanish translation, thanks to Quique. * Updated Norwegian translation, thanks to Leif Mathis. * Fix bug with collapse/expand all not working in group view. * Fix bug with "Find" not properly traversing the collection. * Fix bug with sorted column not being saved between document loads. * Fix bug with changed category not being reflected in edit widget. * Fix bug with name prefixes not being limited to word boundaries. 2003-05-11 Robby Stephenson * Released Bookcase 0.6.2 2003-05-10 Robby Stephenson * Fixed bug with custom fields not showing up in column popup menu. * Fixed crashing when selecting a different book after modifying the current one and not saving it. * Fixed icon bug for column view. * Prettied-up some of layout code. 2003-05-09 Robby Stephenson * Added Norwegian translation from Leif Mathis Gaup. * Fixed printing bug for books with multiple groups. 2003-05-06 Robby Stephenson * Changed initial file opening so that if a new empty file was the last viewed, then no recent file is opened at next startup. * Fixed bug with initial collection not updating edit widgets. 2003-05-05 Robby Stephenson * Released Bookcase version 0.6.1 * Fixed automake 1.7 error with configure.in.in * Fixed UIC variable usage, primarily for *BSD platforms * Fixed double encoding bug on bibtexml inport * Fixed wrong namespace on bibtexml export * Changed to use KAcceleratorManager for keyboard accels on tabs * Fixed bug with the group view icon not getting changed back to the folder 2003-05-03 Robby Stephenson * Fixed bug with editing multiple books and getting asked every time if I'm sure. * Changed modified field names in the "Edit Fields" dialog to be colored, an din italic font instead of having an asterisk in front. 2003-05-02 Robby Stephenson * Released Bookcase version 0.6 * Changed Bibtex export to use the Locale character encoding instead of UTF-8 2003-04-22 Robby Stephenson * Added a URL attribute type * Changed articles, surname prefixes, and suffixes to be case-insensitive so the config value no longer has to include both capitalized and lower-case values * Broke out the attribute format flag for cleaner code 2003-04-13 Robby Stephenson * Added multiple selection, batch editing 2003-04-12 Robby Stephenson * Added RegExp Editor button to find dialog * Added Quick and Advanced filtering in the detailed list view 2003-04-10 Robby Stephenson * Added new "person" icon for grouping when the group is a name 2003-04-09 Robby Stephenson * Convert from KTabCtrl to QTabWidget, which has more documentation and is actively developed 2003-04-08 Robby Stephenson * Fixed bug with column order not retained on save 2003-04-05 Robby Stephenson * Fixed bug with validating multiple copyright years 2003-03-25 Robby Stephenson * Added surname prefixes like 'de' and 'von' 2003-03-23 Robby Stephenson * Fixed debug mistake. * Released Bookcase version 0.5.2a 2003-03-22 Robby Stephenson * Released Bookcase version 0.5.2 2003-03-21 Robby Stephenson * Added German translation from Gerrit M. Albrecht * Fixed bugs with empty suffix and article list * Fixed some i18n issues 2003-03-18 Robby Stephenson * Added Hungarian translation from Marcel Hilzinger 2003-03-15 Robby Stephenson * Fixed types and compile problems. Thanks, Dre! * Released Bookcase version 0.5.1 2003-03-14 Robby Stephenson * Put the book count in color. * Took out general export function for now. * Released Bookcase version 0.5 2003-03-07 Robby Stephenson * Added a new printing page in the configuration dialog * Added a checkmark pixmap for the boolean fields * Added a header menu to be able to select which fields to show in the detailed list view 2003-03-02 Robby Stephenson * Fixed character encoding bug for latin2 2003-01-25 Robby Stephenson * Fixed focus bug 2003-01-19 Robby Stephenson * Changed FORMAT_VERSION to 2 * Changed document format so that attributes which allow multiple entries are saved in multiple XML elements * Changed "keywords" attribute to "keyword" * Added check for document format version so that incompatible versions are not loaded. Older versions will be properly loaded, but once saved, previous versions of Bookcase will not load the new files properly. * Changed "Language" and "Copyright Year" to allow multiple entries. 2002-12-12 Robby Stephenson * Added namespaces to document file 2002-12-11 Robby Stephenson * Added import from Bibtexml using XSLT 2002-12-08 Robby Stephenson * Added export to Bibtex using XSLT * Added export to Bibtexml using XSLT 2002-12-03 Robby Stephenson * Released Bookcase version 0.4.1 * Added a Romanian translation from Iulian Ursache 2002-11-29 Robby Stephenson * Fixed compile problems with gcc 2.95 2002-11-24 Robby Stephenson * Released Bookcase version 0.4 * Printing improvements in XSLT layout 2002-11-10 Robby Stephenson * Added an Italian translation from FaUsT 2002-11-05 Robby Stephenson * Added a new toolbar for changing the grouping of the units * Changed the bookcase.spec file to add proper files 2002-10-20 Robby Stephenson * Released Bookcase version 0.3 2002-08-27 Robby Stephenson * Added two new XSLT files * Added printing support using KHTML * Added RPM spec file to automake 2002-02-21 Robby Stephenson * Released Bookcase version 0.2 2002-02-20 Robby Stephenson * Added BCColumnView::slotShowUnit() so to ensure that the proper listview in the stack is shown. * Added BCAttribute::capitalize for auto capitalization of names and titles. 2002-02-08 Robby Stephenson * Changed selection behavior so the two listviews do not sync now. Clicking in one clears the other. 2002-02-03 Robby Stephenson * Added article and suffix list to options in config dialog. 2002-01-11 Robby Stephenson * BCListView is now BCColumnView and can flip between different collections. Woo-hoo! Now multiple collections behave properly. Also started adding configuration options. 2002-01-10 Robby Stephenson * bctabcontrol.{h,cpp} Need KTabCtl::showTab to be public, so subclassed it. 2002-01-10 Robby Stephenson * configdialog.{h,cpp} Added beginnings of configuration dialog. 2001-12-06 Robby Stephenson * bcgroupview.cpp, bcattribute.cpp Fixed the bug for formatting names with suffixes like Jr. 2001-11-05 Robby Stephenson * bookcase.h Removed the File->Close() action item. 2001-11-05 Robby Stephenson * bcgroupview.cpp Fixed the bug where clicking the collapse or expand menu item on a BCUnitItem caused the pixmap to change to a folder. 2001-11-04 Robby Stephenson * Released Bookcase version 0.1 diff --git a/doc/importing-exporting.docbook b/doc/importing-exporting.docbook index a7ca2b68..9137d64e 100644 --- a/doc/importing-exporting.docbook +++ b/doc/importing-exporting.docbook @@ -1,356 +1,364 @@ Importing and Exporting Data &appname; is able to import and export a wide variety of data files, as well as search various Internet sites for information. Importing from the Internet &appname; is able to search various Internet sites using the Internet Search Dialog. Entries may be directly imported and added to your current collection. The various providers are configured via the Data Sources Options. Searches may use different criteria: Title, Person, ISBN, UPC/EAN, LCCN, or Keyword. Not all criteria are available for certain data sources. ISBN and LCCN values are for books only, while UPC or EAN values can apply to any type of item. Once a search is initiated, the Search button becomes Stop which will end a search. As results are found, they are added to the list directly under the search box, where selecting an item will show the result without adding it to the collection. Clicking the Add Entry button will add all the selected items to your collection. If the data source has more results than were initially requested, the Get More Results button becomes active. Clicking Clear will remove all the current results and reset the search. The &appname; Internet Search Dialog The &appname; Internet Search Dialog Only entries that match the current collection type will be found. The Description column provides additional information about the entry, in order to differentiate between videos in different formats, or books in different bindings, for example. Once an entry is successfully added to the collection, a checkmark is added to the first column in the list. Multiple entries can be added at once by using the standard &kde; method for multiple selection, which usually involves holding the &Shift; or &Ctrl; key when clicking on an item. To facilitate the use of barcode scanners, searches can include multiple ISBN/UPC values. Selecting the Multiple ISBN/UPC search check box will disable the search box and enable the Edit ISBN/UPC values... button, which will open a multi-line text entry box. Each ISBN should be entered on a line by itself. After closing the box, each ISBN will be validated for correct formatting. The ISBN validation is able to convert 13-digit EAN values, as well as full UPC codes, to the proper formatting. The ISBN list may also be read from a text file. Importing Data &appname; offers three different actions when importing data. Replace current collection will close the current collection, and create a new one with the data from the imported file. Append to current collection tells &appname; to add all the entries in the imported collection to the current one, and to add any fields which don't currently exist. The Merge collection action is the same as appending, except that each imported entry is compared to the current ones, and any identical entries are skipped. &appname; attempts to identify matching entries which are not completely identical by comparing significant fields and will then merge the entries. For example, music collections compare the artist and album, and the tracks would be merged for matching entries. The audio file importer is able to correctly build track lists by merging entries. Importing Data From Other Software &appname; can import data directly from a variety of other collection management programs, including GCstar, Alexandria, Delicious Library, Ant Movie Catalog, Referencer, and Griffith. Importing Other Data Formats &appname; can import data from a variety of other file formats, including CSV, bibtex, audio discs and files, MODS, &PDF;, and RIS. Importing &appname; Data Other &appname; data files may be imported directly. Replacing the current collection by importing a &appname; file is the same thing as just opening the file itself. The value of importing &appname; data is primarily for appending or merging two collections together. Importing CSV Data Comma-separated values (CSV) are a common way of importing and exporting tabular data. Each field value is separated by a comma, with one entry per line. The field titles may be included in the first line. The CSV importer is not limited to using a comma as the separator. Any character or string may be used. The CSV Import Dialog The CSV Import Dialog First, select the type of collection that you are importing. If you are appending or merging to your open collection, the type is limited to your current collection type. If the first line of the CSV file contains the field titles, click the check box and the importer will automatically compare the titles against the fields in the current collection. If a field title matches, the header for that column changes to show that the column has been assigned to that field. If the file uses a delimiter other than a comma, be sure to change that option accordingly. In order for &appname; to properly import the file, it must know which field corresponds to each column. If the column only has a number in the header, the data in that column will not be imported. You should assign fields to each column by selecting a column, either by clicking in it or changing the column number itself, then selecting the field to assign from the drop down box and clicking the Assign Field button. If you need to add a new field to the collection, the last item in the drop down box opens the Collection Fields Dialog. For compactness, only the first five lines of the imported CSV file are shown in the dialog. However, all the lines in the file will be imported. Importing Audio &CD; Data &appname; is able to use the freedb.org service to lookup information about a &CD;, including the track list. Depending on your distribution, settings for access to the service may be set in the &kde; &systemsettings;. The &CD; artist, title, genre, year, and track listing are all added. In addition, if the disc contains &CD;-Text, that information is read and added to the imported entry. Importing Audio File Metadata &appname; is able to scan a folder and read the tags for common audio file formats, such as mp3 and ogg. The songs are entered in a music collection, where each entry is an album. If the song files contain the track number, the song name is inserted in the correct spot in the track list. The artist and genre information is also added to the entry. If the song tags contain comments, they are appended to the comments field in the entry, preceded by the file name. In addition, if a folder contains a .directory file and the folder name matches an album title, the Icon entry in the desktop file is used as the cover image for the album. The audio file metadata importer can recursively scan a folder to find all audio files in any subfolder, though symbolic links are not followed. &appname; uses the TagLib library for reading the audio file metadata, and so can import data from any file type that TagLib understands. Importing Bibtex Data Bibtex is a bibliography format used with the LaTeX document preparation system. Various type of bibliographic references may be included in the file. &appname; imports bibtex files as a Bibliographic collection. If the bibtex importer encounters fields in the file which are not in the default bibliography collection, they are added as Simple Text fields, with two exceptions. If the field value contains more than 100 characters, it becomes a Paragraph field. If the field value appears to contain a &URL; or a file reference, then a &URL; field is created. &appname; uses an internal copy of the btparse library for parsing the bibtex files. Bibtexml is an &XML; representation of bibtex data, and the data from the imported bibtexml file is treated in the same way as bibtex data would be. Importing MODS Data MODS is a format for representing various types of media collections. Currently, only books are imported by &appname;, as a Bibliographic collection. Importing &PDF; Data If &appname; was compiled with exempi or poppler support, metadata from &PDF; files can be imported. Metadata may include title, author, and date information, as well as bibliographic identifiers which are then used to update other information. Importing RIS Data The RIS format is a bibliographic file format used by EndNote, Reference Manager, and others. &appname; imports RIS files as a Bibliographic collection. Importing Online Collections &appname; can connect to and import from websites that manage personal collections. Importing BoardGameGeek Collection BoardGameGeek is an online board gaming resource and community. &appname; can import the board games in a user's collection, as long as the collection is set to be publicly accessible. The imported collection may be limited to those items marked as being owned. Importing Goodreads Collection Goodreads is an online social network for readers to track book collections. &appname; can import the list of books in a user's collection, given either the user name or user ID, as long as the collection is set to be publicly accessible. + + +Importing LibraryThing Collection + +LibraryThing is an online service to help people catalog their books easily. &appname; can import the list of books in a user's collection, exported in JSON format. + + + Importing File Listings The best way to create a File Catalog is to import the contents of a folder. The folder may be searched recursively, to add all files found within. This importer is most useful for backup listings and media cataloging, such as &CD; or DVD listings. In addition, image previews of the file contents may be generated, although it can take some time to read a large number of files. The file previews are same as those shown in the &kde; file manager. Importing &XML; Data via XSLT Any &XML; file may be imported into &appname; provided an &XSL; stylesheet is available to convert the file to &appname; format. &appname; automatically loads the stylesheet and performs the &xslt; processing needed to load the file. Drag and Drop Dragging data files to the main &appname; window and dropping them will import the files, just as if the import command was made from the menus. Drag and drop works for the following file formats: Tellico, Bibtex, RIS, and &PDF;. Importing multiple files at once is also supported. So, for example, if you want to catalog several &PDF; files, select them in the file manager and drag them to the &appname; window. &appname; will import as much metadata from the files as it can, and then fetch additional information from various configured Internet sources. Exporting Data When exporting the data in the collection, the entry values may be exported as entered, or with the automatic formatting provided by &appname;. Additionally, the export may be limited to the currently selected entries of the collection as well, where the statusbar shows the number of selected entries. Exported text files, such as Bibtex or CSV, may use the Unicode (UTF-8) character encoding, or the current locale of the operating system. General Export Options General Export Options Exporting &XML; The file format for &appname; data is a zipped &XML; file. Exporting to &XML; merely creates the &XML; file without zipping it. Images may be included in the &XML; file as base64-encoded data in an image element, but doing so can create very large text files. Exporting Zip The standard file format for &appname; is a zipped file, contained the &XML; collection file, and optionally, all the images referenced in the collection. If the images are being stored in the application folder instead, exporting to a Zip file will create a self-contained data file, which includes all the images in the collection. Exporting &HTML; The &HTML; export uses the tellico2html.xsl stylesheet. Images are exported to a folder with the same name as the exported &HTML; file with _files appended. The default format is similar to the printed output, and allows various options for modifying the &HTML;. Field headers may be printed at the top of each column, but unfortunately, &kde; does not yet allow the table headers to be repeated on each page. The entries may be grouped as in the Group View, as well. Additionally, individual files may be created for each entry in the collection, with links created in the top-level &HTML; file. The entry files will be created in the same folder as the images. The entry &HTML; files will use the current stylesheet template, as shown in the Entry View. &HTML; Export Options &HTML; Export Options Exporting CSV Comma-separated values (CSV) are a common way of importing and exporting tabular data. Each field value is separated by a comma, with one entry per line. The field titles may be included as headers in the first line. Any character or string other than a comma may also be used to delimit the fields. CSV Export Options CSV Export Options Exporting Alexandria Alexandria is a book collection manager for the GNOME desktop environment. &appname; is able to export a limited subset of book collection fields to the default Alexandria data location. Exporting ONIX ONIX is an &XML; format for representing and communicating book industry product information, primarily for book vendors. &appname; can export book collections using a small subset of ONIX. Exporting Bibtex When exporting to Bibtex, the field values may be escaped with braces or quotation marks. If any string macros are used in the collection, they may optionally be exported as macros or expanded. For &URL; fields, &appname; may enclose the field values with the \url{...} tag. Finally, entries with no citation key may be skipped rather than have &appname; auto-generate the key. Bibtex Export Options Bibtex Export Options Exporting GCstar GCstar is another movie collection manager. &appname; is able to export most collection types to a GCstar data file. Exporting &XML; via &xslt; Finally, &appname; is able to process its internal &XML; representation of the collection data through an external &XSL; stylesheet before exporting. This type of export may be useful for generating text reports or other file types. Working With Citations When working with a bibliography, citations for the currently selected entries may be generated and used in various other applications. A citation in bibtex format can be copied to the clipboard, and then pasted in a latex file. Bibtex citations can also be pushed to an external application such as LyX or Kile using the so-called lyxpipe. diff --git a/icons/CMakeLists.txt b/icons/CMakeLists.txt index 7016edab..b9d7f85a 100644 --- a/icons/CMakeLists.txt +++ b/icons/CMakeLists.txt @@ -1,82 +1,83 @@ include(ECMInstallIcons) set(PIC_FILES album.png alexandria.png amc.png bibtex.png boardgame.png boardgamegeek.png book.png card.png checkmark.png cite.png coin.png comic.png deliciouslibrary.png file.png game.png gcstar.png - griffith.png goodreads.png + griffith.png + librarything.png nocover_album.png nocover_bibtex.png nocover_boardgame.png nocover_book.png nocover_comic.png nocover_game.png nocover_video.png person-open.png person.png README.icons README.quesnay referencer.png script.png script.svg sru.png stamp.png star_off.png star_on.png stars10.png stars1.png stars2.png stars3.png stars4.png stars5.png stars6.png stars7.png stars8.png stars9.png tellico_mime.svg tellico.png tellico.svg tellico.svg video.png vinoxml.png wine.png ) INSTALL(FILES ${PIC_FILES} DESTINATION ${TELLICO_DATA_INSTALL_DIR}/pics ) set(ICON_FILES 16-apps-tellico.png 16-mimetypes-application-x-tellico.png 22-apps-tellico.png 22-mimetypes-application-x-tellico.png 32-apps-tellico.png 32-mimetypes-application-x-tellico.png 48-apps-tellico.png 48-mimetypes-application-x-tellico.png 64-apps-tellico.png 64-mimetypes-application-x-tellico.png 128-apps-tellico.png 128-mimetypes-application-x-tellico.png ) ecm_install_icons(ICONS ${ICON_FILES} DESTINATION ${KDE_INSTALL_ICONDIR} THEME hicolor ) diff --git a/icons/icons.qrc b/icons/icons.qrc index 6928a582..bcee7435 100644 --- a/icons/icons.qrc +++ b/icons/icons.qrc @@ -1,52 +1,53 @@ album.png alexandria.png amc.png bibtex.png boardgamegeek.png boardgame.png book.png card.png checkmark.png cite.png coin.png comic.png deliciouslibrary.png file.png game.png gcstar.png goodreads.png griffith.png + librarything.png nocover_album.png nocover_bibtex.png nocover_boardgame.png nocover_book.png nocover_comic.png nocover_game.png nocover_video.png person-open.png person.png referencer.png script.png sru.png stamp.png star_off.png star_on.png stars10.png stars1.png stars2.png stars3.png stars4.png stars5.png stars6.png stars7.png stars8.png stars9.png tellico.png video.png vinoxml.png wine.png diff --git a/icons/librarything.png b/icons/librarything.png new file mode 100644 index 00000000..babe0c95 Binary files /dev/null and b/icons/librarything.png differ diff --git a/org.kde.tellico.appdata.xml b/org.kde.tellico.appdata.xml index 529431d0..9324a2f6 100644 --- a/org.kde.tellico.appdata.xml +++ b/org.kde.tellico.appdata.xml @@ -1,558 +1,563 @@ org.kde.tellico.desktop CC0-1.0 GPL-2.0+ Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico Tellico xxTellicoxx Tellico Collection Manager Menadžer kolekcija Gestor de col·leccions Gestor de col·leccions Správce sbírek Samlingshåndtering Sammlungsverwaltung Collection Manager Gestor de colecciones Kogude haldur Kokoelmahallinta Gestionnaire de collections Xestor de coleccións Gyűjteménykezelő Gerente de collection Pengelola Koleksi Gestore della collezione 소장품 관리 도구 Collectiebeheer Samlingsdatabase Zarządzanie zbiorem Gestor de Colecções Gerenciador de Coleções Менеджер коллекций Správca zbierok Upravljalnik zbirk Samlingshanterare Koleksiyon Yöneticisi Програма для керування збірками xxCollection Managerxx 收藏管理器

Tellico is an application for organizing your collections. It provides default templates for books, bibliographies, videos, music, video games, coins, stamps, trading cards, comic books, and wines. It allows you to enter your collection in a catalogue database, saving many different properties like title, author, etc.

Tellico je aplikacija za organiziranje vaše kolekcije. Omogućava početne predloške za knjige, bibliografije, videa, muziku, video igrice, kovanice, markice, kartice za razmjenu, stripove i vina. Dopušta vam ulaz u vašu kolekiju u katalošku bazu podataka, snimajući mnogo različitih osobina kao što su naslov, autor, itd.

El Tellico és una aplicació per organitzar col·leccions. Proporciona plantilles per defecte per a llibres, bibliografies, vídeos, música, videojocs, monedes, segells, cromos, còmics i vins. Permet introduir les col·leccions en una base de dades de catàlegs, desant moltes propietats diferents com el títol, l'autor, etc.

El Tellico és una aplicació per organitzar col·leccions. Proporciona plantilles per defecte per a llibres, bibliografies, vídeos, música, videojocs, monedes, segells, cromos, còmics i vins. Permet introduir les col·leccions en una base de dades de catàlegs, desant moltes propietats diferents com el títol, l'autor, etc.

Tellico er et program til at organisere dine samlinger. Det giver standardskabeloner til bøger, bibliografier, video, musik, computerspil, mønter, frimærker, byttekort, tegneserier og vine. Det lader dig angive din samling i en katalogdatabase, som gemmer mange forskellige egenskaber, såsom titel, forfatter osv.

Tellico ist eine Anwendung zur Organisation Ihrer Daten. Es enthält Standardvorlagen für Bücher, Literaturverzeichnisse, Videos, Musik, Videospiele, Comicbücher, Münzen, Briefmarken, Karten, Wein, Brettspiele und Dateikataloge. Sie können Ihre Sammlung in einer Katalog-Datenbank eingeben und viele verschiedene Eigenschaften wie Titel, Autor usw. darin speichern.

Tellico is an application for organising your collections. It provides default templates for books, bibliographies, videos, music, video games, coins, stamps, trading cards, comic books, and wines. It allows you to enter your collection in a catalogue database, saving many different properties like title, author, etc.

Tellico es una aplicación para organizar colecciones. Proporciona plantillas predeterminadas para libros, bibliografías, vídeos, música, videojuegos, monedas, sellos, cromos, cómics, y vinos. También le permite almacenar sus colecciones en una base de datos de catálogo, guardando diversas propiedades como el título, el autor, etc.

Tellico on kõikvõimalike kogude haldamiseks mõeldud rakendus. See pakub vaikimisi malle raamatute, bibliograafiate, videode, muusika, videomängude, müntide, markide, mängukaartide, koomiksite ja veinide tarbeks. Rakenduses saab oma kogu sisestada kataloogi andmebaasi, kuhu salvestatakse hulk omadusi, näiteks nimi, autor jne.

Tellico on sovellus kokoelmiesi järjestämiseen. Se tarjoaa oletusmallit kirjoille, lähdeluetteloille, videoille, musiikille, videopeleille, kolikoille, postimerkeille, keräilykorteille, sarjakuville ja viineille. Sen avulla voit syöttää kokoelmasi luettelotietokantaan eri ominaisuudet kuten otsikon, tekijän jne. säilyttäen.

Tellico est une application pour organiser vos collections. Il fournit des modèles par défaut pour les livres, les bibliographies, les vidéos, la musique, les jeux vidéo, les monnaies, les timbres, les cartes à collectionner, les bandes dessinées et les vins. Il vous permet de saisir votre collection dans une base de données de catalogue, enregistrant différentes propriétés telles que le titre, l'auteur, etc.

-

Tellico é un aplicativo para organizar coleccións. Fornece modelos predeterminados para libros, bibliografías, vídeos, música, videoxogos, moedas, selos, cartas de colección, banda deseñada, e vinos. Permítelle inserir a súa colección a unha base de datos de catálogo na que pode gardar moitas propiedades distintas, como o título de cada elemento, o seu autor, etc.

+

Tellico é unha aplicación para organizar coleccións. Fornece modelos predeterminados para libros, bibliografías, vídeos, música, videoxogos, moedas, selos, cartas de colección, banda deseñada, e vinos. Permítelle inserir a súa colección a unha base de datos de catálogo na que pode gardar moitas propiedades distintas, como o título de cada elemento, o seu autor, etc.

Tellico es un application pro organisar tu collectiones. Il forni patronos predefinite pro libros, bibliographias, videos, musica, video jocos, numisma, timbros, cartas de commercio, libros in designos e vinos. Il permitte te insertar tu collection inn un base de datos de catalogo, salveguardante multe proprietates como titulo, autor, etc.

Tellico adalah aplikasi untuk mengatur koleksi Anda. Ini menyediakan template baku untuk buku, bibliografi, video, musik, video game, koin, perangko, kartu perdagangan, buku komik, dan wine. Ini memungkinkan Anda untuk memasukkan koleksi Anda di database katalog, menyimpan banyak properti berbeda seperti judul, penulis, dll.

Tellico è un'applicazione per organizzare le tue collezioni. Fornisce modelli predefiniti per libri, bibliografie, video, musica, videogiochi, monete, francobolli, carte da gioco, fumetti e vini. Ti permette di inserire la tua collezione in una base di dati, salvando molte proprietà differenti come titolo, autore, ecc.

Tellico는 내 소장품을 관리하는 응용 프로그램입니다. 책, 서지, 비디오, 음악, 비디오 게임, 동전, 우표, 트레이드 카드, 만화 및 와인에 대한 기본 템플릿을 제공합니다. 카탈로그 데이터베이스에 소장품을 입력하여 제목, 작성자 등과 같은 다양한 속성을 저장할 수 있습니다.

Tellico is een toepassing voor het organiseren van uw verzamelingen. Het biedt standaard sjablonen voor boeken, bibliografieën, video's, muziek, videogames, munten, postzegels, visitekaartjes, strips en wijnen. Het stelt u in staat om uw verzameling in een catalogusdatabase in te voeren, waarin veel verschillende eigenschappen, zoals titel, auteur, etc. opgeslagen kunnen worden.

Tellico er eit program for å halda oversikt over samlingane dine. Det kjem med malar for bøker, litteraturlister, filmar, musikk, spel, myntar, frimerke, samlekort, teikneseriar og vin. Du kan katalogisera samlinga di i ein database og lagra mykje ulik informasjon, for eksempel tittel og forfattar.

Tellico jest programem do porządkowania twoich zbiorów. Dostarcza domyślnych szablonów dla książek, bibliografii, filmów, muzyki, gier wideo, monet, znaczków, kart handlowych, komiksów i win. Umożliwia wprowadzenie całego twojego zbioru do bazy danych katalogu, zapisując przy tym wiele różnych właściwości takich jak tytuł, autor, itp.

O Tellico é uma aplicação para organizar as suas colecções. Oferece modelos predefinidos para livros, bibliografias, vídeos, músicas, jogos de vídeo, moedas, selos, cromos, livros de banda desenhada, vinhos, entre outras colecções. Permite-lhe introduzir a sua colecção numa base de dados de catálogos, gravando diversas propriedades, como o título, o autor, etc.

Tellico é um aplicativo para organizar suas coleções. Oferece modelos predefinidos para livros, bibliografias, vídeos, músicas, jogos de vídeo game, moedas, selos, figurinhas, histórias em quadrinhos, vinhos, entre outras coleções. Permite-lhe introduzir sua coleção em um banco de dados de catálogos, gravando diversas propriedades, como título, autor, etc.

Tellico je aplikácia na organizáciu vašich zbierok. Poskytuje predvolené šablóny pre knihy, bibliografie, videá, hudbu, videohry, mince, známky, zberateľské karty, komiksy a vína. Umožní vám zadať vašu kolekciu do databázy katalógu, uložiť mnoho rozličných vlastností ako titul, autor atď.

Tellico je program za upravljanje vaših zbirk. Ponuja privzete predloge za knjige, bibliografije, videe, glasbo, igre, kovance, znamke, karte, stripe in vino. Omogoča vam, da vašo zbirko katalogizirate in shranite z različnimi lastnostmi kot so naslov, avtor, itd.

Tellico är ett program för att organisera samlingar. Det tillhandahåller standardmallar för böcker, bibliografier, videor, musik, videospel, mynt, frimärken, samlarbilder, serier och viner. Det låter dig mata in samlingen i en katalogdatabas, och spara många olika egenskaper som titel, författare, etc.

Tellico koleksiyonlarınızı yönetmek için bir programdır. Kitaplar, kaynakçalar, videolar, müzikler, video oyunları, paralar, pullar, kartlar, çizgi romanlar ve şaraplar için öntanımlı şablonlar içerir. Koleksiyonunuzu bir katalog veritabanına girmenize ve başlık, yazar gibi pek çok özelliğe göre kaydetmenize imkan tanır.

Tellico — програма для упорядковування ваших збірок. У ній передбачено типові шаблони для збірок книг, бібліографій, відео, музичних творів, відеоігор, нумізматики, марок, карток, коміксів та вин. За її допомогою ви зможете вести записи у форматі бази даних, зберігаючи різноманітні дані щодо елементів збірки, зокрема дані щодо назви, автора тощо.

xxTellico is an application for organizing your collections. It provides default templates for books, bibliographies, videos, music, video games, coins, stamps, trading cards, comic books, and wines. It allows you to enter your collection in a catalogue database, saving many different properties like title, author, etc.xx

Features:

Svojstva:

Característiques:

Característiques:

Vlastnosti:

Funktioner:

Funktionen:

Features:

Características:

Omadused:

Ominaisuuksia:

Fonctionnalités !

Funcionalidades:

Szolgáltatások:

Characteristicas:

Fitur:

Funzionalità:

기능:

Mogelijkheden:

Funksjonar:

Cechy:

Funcionalidades:

Funcionalidades:

Возможности:

Funkcie:

Zmožnosti:

Funktioner:

Özellikleri:

Можливості:

xxFeatures:xx

功能:

  • Supports default collections of books, bibliographic entries, videos, music, video games, comic books, coins, stamps, trading cards, wines, board games, and file catalogs
  • Podržava početne kolekcije knjiga, bibliografskih ulaza, videa, muzike, video igrica, stripova, kovanica, markica, kartica za razmjenu, vina, društvenih igara i datotečnih kataloga.
  • Funciona amb col·leccions per defecte de catàlegs de llibres, entrades bibliografies, vídeos, música, videojocs, còmics, monedes, segells, cromos, vins, jocs de taula i fitxers
  • Funciona amb col·leccions per defecte de catàlegs de llibres, entrades bibliografies, vídeos, música, videojocs, còmics, monedes, segells, cromos, vins, jocs de taula i fitxers
  • Understøtter standardsamlinger til bøger, bibliografier, video, musik, computerspil, mønter, frimærker, byttekort, tegneserier, vine, brætspil og filkataloger
  • Unterstützt Standardsammlungen für Bücher, Literaturverzeichnisse, Videos, Musik, Videospiele, Comicbücher, Münzen, Briefmarken, Karten, Wein, Brettspiele und Dateikataloge
  • Supports default collections of books, bibliographic entries, videos, music, video games, comic books, coins, stamps, trading cards, wines, board games, and file catalogues
  • Soporte para colecciones predeterminadas de libros, entradas bibliográficas, vídeos, música, videojuegos, cómics, monedas, sellos, cromos, vinos, juegos de mesa y catálogos de archivos
  • Vaikimisi on toetatud, raamatute, bibliograafiliste kirjete, videode, muusika, videomängude, koomiksite, müntide, markide, mängukaartide, veinide, lauamängude ja failikataloogide kogud
  • Tukee kirjojen, lähdeluettelotietueiden, videoiden, musiikin, videopelien, sarjakuvien, kolikoiden, postimerkkien, keräilykuvien, viinien, lautapelien ja tiedostoluettelojen oletuskokoelmia
  • Prends en charge par défaut des collections de livres, d'entrées bibliographiques, de vidéos, de musique, de jeux vidéo, de bandes dessinées, de monnaies, de timbres,de cartes à collectionner, de vins, de jeux de plateau et de catalogues de fichiers.
  • Compatíbel con coleccións predeterminadas de libros, entradas bibliográficas, vídeos, música, videoxogos, banda deseñada, moedas, selos, cartas de colección, viños, xogos de taboleiro e catálogos de ficheiros.
  • Il supporta collectiones predefinite de libros, entratas bibliographic, videos, musica, video jocos, libros in designo, numisma, timbros, cartas de commerciar, vinos, jocos de tabula e catalogos de files
  • Mendukung koleksi buku, entri bibliografi, video, musik, video game, buku komik, koin, perangko, kartu perdagangan, wine, permainan papan, dan katalog file standar yang didukung
  • Supporta collezioni standard di libri, voci bibliografiche, video, musica, videogiochi, fumetti, monete, francobolli, carte da gioco, vini, giochi da tavolo e collezioni di file
  • 서적, 서지 정보 항목, 비디오, 음악, 비디오 게임, 만화책, 동전, 우표, 트레이딩 카드, 와인, 보드 게임 및 파일 카탈로그의 기본 소장품 지원
  • Ondersteunt standaard verzamelingen van boeken, bibliografie-items, video's, muziek, videogames, strips, munten, postzegels, visitekaartjes, wijnen, bordspellen en catalogs van bestanden
  • Støttar samlingar av bøker, litteraturlister, filmar, musikk, spel, teikneseriar, myntar, frimerke, samlekort, vin, brettspel og filkatalogar
  • Obsługa domyślnych zbiorów książek, bibliografii, filmów, muzyki, gier wideo, monet, znaczków, kart handlowych, komiksów i win, gier planszowych oraz katalogów plików
  • Suporta diversas colecções de livros, itens bibliográficos, vídeos, músicas, jogos de vídeo, livros de banda desenhada, moedas, selos, cromos, vinhos, jogos de tabuleiros e catálogos de ficheiros
  • Suporte a diversas coleções de livros, itens bibliográficos, vídeos, músicas, jogos de vídeo game, histórias em quadrinhos, moedas, selos, figurinhas, vinhos, jogos de tabuleiro e catálogos de arquivos
  • Podporuje predvolené zbierky kníh, bibliografických položiek, videí, hudby, videohier, komiksov, mincí, známok, zberateľských kariet, vín, stolných hier a zakladačov
  • Podpira privzete zbirke za knjige, bibliografije, videe, glasbo, igre, kovance, znamke, karte, stripe, namizne igre, vino in zbirke datotek
  • Stöder standardsamlingar för böcker, bibliografiposter, videor, musik, videospel, serier, mynt, frimärken, samlarbilder, viner, brädspel och filkataloger
  • Kitapların, kaynakça girdilerinin, videoların, müziklerin, video oyunlarının, çizgi romanların, paraların, pulların, kartların, şarapların, oyunların ve dosya katalogların öntanımlı koleksiyonlarını destekler
  • Підтримка типових збірок книг, бібліографічних записів, відео, музики, відеоігор, коміксів, нумізматики, марок, карток, вин, настільних ігор та каталогів файлів.
  • xxSupports default collections of books, bibliographic entries, videos, music, video games, comic books, coins, stamps, trading cards, wines, board games, and file catalogsxx
  • Supports user-defined custom collections
  • Podržava korisnički definisane prilagođene kolekcije
  • Funciona amb col·leccions personalitzades definides per l'usuari
  • Funciona amb col·leccions personalitzades definides per l'usuari
  • Understøtter brugerdefinerede tilpassede samlinger
  • Unterstützt benutzerdefinierte Sammlungen
  • Supports user-defined custom collections
  • Soporta colecciones personalizadas definidas por el usuario
  • Toetatud on ka kasutaja loodud kohandatud kogud
  • Tukee käyttäjän mukauttamia kokoelmia
  • Prends en charge les collections personnalisées par l'utilisateur
  • Permite aos usuarios definir coleccións personalizadas.
  • Il supporta collectiones personalisate definite per le usator
  • Dukungan koleksi kustom yang ditentukan pengguna
  • Supporta collezioni personalizzate definite dall'utente
  • 사용자 정의 맞춤 소장품 지원
  • Ondersteunt gebruikergedefinieerde eigen verzamelingen
  • Støttar eigendefinerte samlingstypar
  • Obsługa zbiorów określonych przez użytkownika
  • Suporta colecções personalizadas pelo utilizador
  • Suporte a coleções personalizadas pelo usuário
  • Podporuje používateľom definované vlastné zbierky
  • Podpira uporabniško določene zbirke
  • Stöder användardefinierade anpassade samlingar
  • Kullanıcı tanımlı özel koleksiyonları destekler
  • Підтримка визначених користувачем нетипових збірок.
  • xxSupports user-defined custom collectionsxx
  • Supports any number of user-defined fields, of several different types: text, paragraph, list, checkbox, number, URL, date, images, and combinations
  • Podržava bilo koji broj korisnički definisanih polja, nekoliko različitih tipova: tekst, paragraf, lista, kvadtratić za označavanje, broj, URL, datum, slike i kombinacije.
  • Funciona amb qualsevol nombre de camps definits per l'usuari, de molts tipus diferents: text, paràgraf, llista, casella de selecció, nombre, URL, data, imatges i combinacions
  • Funciona amb qualsevol nombre de camps definits per l'usuari, de molts tipus diferents: text, paràgraf, llista, casella de selecció, nombre, URL, data, imatges i combinacions
  • Understøtter brugerdefinerede felter af mange forskellige typer, tekst, afsnit, liste, afkrydsningsfelt, nummer, URL, dato, billeder og kombinationer
  • Unterstützt eine beliebige Anzahl von benutzerdefinierten Feldern mit verschiedenen Typen: Text, Absatz, Liste, Ankreuzfeld, Nummer, Adresse (URL), Datum, Bilder und Kombinationen
  • Supports any number of user-defined fields, of several different types: text, paragraph, list, tickbox, number, URL, date, images, and combinations
  • Soporta cualquier número de campos definidos por el usuario, de varios tipos diferentes: texto, párrafo, lista, checkbox, número, URL, fecha, imágenes y combinaciones.
  • Toetatud on mis tahes hulk kasutaja määratud välju, mis võivad olla väga eri tüüpi: tekst, lõik, loend, märkekast, arv, URL, kuupäev, pilt või nende kombinatsioon
  • Tukee rajattomasti käyttäjän määrittämiä useamman tyypin kenttiä (teksti, kappale, luettelo, valintaruutu, luku-, verkko-osoite, päiväys, kuvat ja niiden yhdistelmät)
  • Prend en charge n'importe quel nombre de champs définis par l'utilisateur, de plusieurs types différents : texte, paragraphe, liste, case à cocher, nombre, URL, date, images et combinaisons
  • Permite ao usuario definir campos de varios tipos: texto, parágrafo, lista, lista con caixas para marcar, número, URL, data, imaxes, e combinacións.
  • Il supporta qualcunque numero de campos definite per le usator, de plure typos differente: texto, paragrapho, lista, quadrato de selection, numero, URL, data, imagines e combinationes
  • Mendukung angka apa pun dari bidang yang ditentukan, beberapa tipe yang berbeda: teks, paragraf, daftar, kotak centang, nomor, URL, tanggal, gambar, dan kombinasi
  • Supporta un qualsiasi numero di campi definiti dall'utente, di molti tipi differenti: testo, paragrafi, liste, caselle, numeri, URL, dati, immagini e combinazioni
  • 텍스트, 단락, 목록, 체크 상자, 번호, URL, 날짜, 이미지 및 조합 등 다양한 유형의 사용자 정의 필드 지원
  • Ondersteunt elk aantal gebruikergedefinieerde velden met verscheidene verschillende types: tekst, paragraaf, lijst, keuzevakje, aantal, URL, datum, afbeelding en combinaties
  • Støttar vilkårleg mange eigendefinerte felt i ulike variantar: tekst, avsnitt, liste, avkryssingsboks, tal, nettadresse, dato, bilete og kombinasjonar av desse variantane
  • Obsługa dowolnej liczby pól określonych przez użytkownika, wielu różnych rodzajów: tekst, akapit, spis, pole zaznaczane, liczba, adres URL, data, obraz i ich kombinacje
  • Suporta qualquer número de campos definidos pelo utilizador, de diferentes tipos: texto, parágrafo, lista, opção de marcação, número, URL, data, imagens e combinações
  • Suporte a qualquer número de campos definidos pelo usuário, de diferentes tipos: texto, parágrafo, lista, opção de marcação, número, URL, data, imagens e combinações
  • Podporuje ľubovoľný počet používateľom definovaných polí rôznych typov: text, odstavec, zoznam, checkbox, číslo, URL, dátum, obrázky a kombinácie
  • Podpira poljubno število uporabniško določenih polj različnih vrst: besedilo, odstavek, seznam, označno polje, številka, naslov URL, datum, slike in njihove poljubne kombinacije
  • Stöder hur många användardefinierade fält som helst av flera olika typer: text, stycke, lista, kryssruta, tal, webbadress, datum, bilder och kombinationer
  • Çok farklı türlerde kullanıcı tanımlı alanları destekler: metin, paragraf, liste, işaret kutusu, sayı, URL, tarih, resim ve bunların bileşimleri
  • Підтримка довільної кількості визначених користувачем полів записів: текст, абзац, список, поле для позначки, номер, адреса, дата, зображення та будь-які інші комбінації.
  • xxSupports any number of user-defined fields, of several different types: text, paragraph, list, checkbox, number, URL, date, images, and combinationsxx
  • Handles entries with multiple authors, genres, keywords, etc
  • Upravlja ulazima sa više autora, žanrova, ključnih riječi, itd.
  • Gestiona entrades amb diversos autors, gèneres, paraules clau, etc.
  • Gestiona entrades amb diversos autors, gèneres, paraules clau, etc.
  • Kan håndtere indgange med flere forfattere, genrer, nøgleord osv.
  • Verarbeitet Einträge mit mehreren Autoren, Genres, Stichwörtern usw.
  • Handles entries with multiple authors, genres, keywords, etc
  • Maneja entradas con varios autores, géneros, palabras clave, etc
  • Raskusi ei valmista ka kirjed, mis sisaldavad mitut autorit, žanri, võtmesõna jne.
  • Käsittelee tietueet, joissa on useampia tekijöitä, tyylilajeja, avainsanoja jne.
  • Gestion des entrées avec plusieurs auteurs, genres, mots-clés, etc
  • Pode xestionar entradas con varios autores, xéneros, palabras clave, etc.
  • Il manea entratas con multiple autores, generes, parolas clave, etc.
  • Menangani entri-entri dengan beberapa penulis, genre, kata kunci, dll.
  • Gestisce voci con autori multipli, generi, parole chiave, ecc.
  • 여러 저자, 장르, 키워드 등으로 항목 처리
  • Behandelt items met meerdere auteurs, genres, zoektermen, etc.
  • Kan handtera oppføringar som inneheld fleire forfattarar, sjangrar, nøkkelord, …
  • Obsługa wpisów z wieloma autorami, gatunkami, słowami kluczowymi, itp.
  • Lida com itens de vários autores, géneros, palavras-chave, etc
  • Lida com itens de vários autores, gêneros, palavras-chave, etc
  • Spracúva položky s viacerými autormi, žánrami, kľúčovými slovami atď.
  • Zna ravnati z vnosi z večimi avtorji, zvrstmi, ključnimi besedami, itd.
  • Hanterar poster med flera författare, genrer, nyckelord, etc.
  • Birden çok yazarı, türü ve anahtar kelimeyi işleyebilir
  • Обробка записів з декількома авторами, жанрами, ключовими словами тощо.
  • xxHandles entries with multiple authors, genres, keywords, etcxx
  • Automatically formats titles and names
  • Automatski formatira naslove i imena
  • Formata automàticament els títols i els noms
  • Formata automàticament els títols i els noms
  • Formaterer titler og navne automatisk
  • Automatische Formatierung von Titeln und Namen
  • Automatically formats titles and names
  • Formatea automáticamente títulos y nombres
  • Pealkirjasid ja nimesid saab lasta automaatselt vormindada
  • Muotoilee otsikot ja nimet automaattisesti
  • Formate automatiquement les titres et les noms
  • Aplica automaticamente un formato aos títulos e nomes.
  • Automaticamente da forma a titulos e nomines
  • Format secara otomatis judul-judul dan nama-nama
  • Formatta automaticamente titoli e nomi
  • 제목과 이름의 자동 서식 지정
  • Deelt automatisch titels en namen in
  • Automatisk formatering av titlar og namn
  • Samoczynne formatowanie tytułów i nazw
  • Formata automaticamente os títulos e nomes
  • Formata automaticamente os títulos e nomes
  • Automaticky formátuje tituly a názvy
  • Samodejno oblikuje naslove in imena
  • Formaterar automatiskt titlar och namn
  • Başlık ve isimleri otomatik biçimlendirir
  • Автоматичне форматування заголовків та назв.
  • xxAutomatically formats titles and namesxx
  • Supports collection searching and view filtering
  • Podržava pretragu kolekcije i filter pregleda
  • Admet la cerca de col·leccions i filtratge de la vista
  • Admet la cerca de col·leccions i filtratge de la vista
  • Understøtter søgning i samlinger og filtrering af visning
  • Unterstützt das Durchsuchen von Sammlungen und Filterung der Ansichten
  • Supports collection searching and view filtering
  • Soporta búsqueda de colecciones y filtrado de la vista
  • Toetatud on kogudes otsimine ja vaate filtreerimine
  • Tukee kokoelmien hakua ja näkymien suodatusta
  • Prend en charge la recherche dans la collection et une vue filtrée
  • Permite buscar nas coleccións e filtrar nas vistas.
  • Il supporta cerca de collection e filtro de vista
  • Dukungan pencarian koleksi dan pemfilteran tampilan
  • Supporta ricerche nella collezione e filtraggio della vista
  • 소장품 검색 및 보기 필터링 지원
  • Ondersteunt zoeken in verzamelingen en filtering op weergave
  • Støttar søk og filtrering i samlingar
  • Obsługa przedukiwania zbiorów i filtrowanych widoków
  • Suporta a pesquisa na colecção e a filtragem da janela
  • Suporte a pesquisa na coleção e filtragem da janela
  • Podporuje hľadanie kolekcií a filtrovanie zobrazenia
  • Podpira iskanje po zbirkah in filtriranje prikaza
  • Stöder sökning i samlingen och filtrering av visningen
  • Koleksiyon araması ve filtreleyerek görüntülemeyi destekler
  • Підтримка пошуку у збірці та фільтрування даних для перегляду.
  • xxSupports collection searching and view filteringxx
  • Sorts and groups collection by various properties
  • Sortira i grupira kolekcije po različitim osobinama
  • Ordena i agrupa les col·leccions per diverses propietats
  • Ordena i agrupa les col·leccions per diverses propietats
  • Sorterer og grupperer samling efter diverse egenskaber
  • Sortiert und Gruppiert Sammlungen nach verschiedenen Eigenschaften
  • Sorts and groups collection by various properties
  • Ordena y agrupa las colecciones en función de diversas propiedades
  • Kogusid saab sortida ja rühmitada eri omaduste järgi
  • Lajittelee ja ryhmittelee kokoelmia eri ominaisuuksien mukaan
  • Trie et groupe les collections suivant diverses propriétés
  • Permite ordenar ou agrupar as coleccións en funcións de varias propiedades.
  • Il ordina e gruppa per varie proprietates
  • Mengurutkan dan mengelompokkan koleksi dengan berbagai properti
  • Ordina e raggruppa la collezione in base a varie proprietà
  • 다양한 속성별 소장품 정렬 및 그룹
  • Sorteert en groepeert verzameling door verschillende eigenschappen
  • Sorterer og grupperer automatisk samlingane
  • Szeregowanie i grupowanie zbiorów według różnych właściwości
  • Ordena e agrupa a colecção de acordo com diversas propriedades
  • Ordena e agrupa a coleção de acordo com diversas propriedades
  • Triedi a zoskupuje kolekciu podľa rôznych vlastností
  • Zna razvrstiti in združevati zbirko po različnih lastnostih
  • Sorterar och grupperar samlingen enligt olika egenskaper
  • Koleksiyonları çeşitli özelliklerine göre sıralar ve gruplar
  • Упорядковування та групування збірки за різноманітними властивостями.
  • xxSorts and groups collection by various propertiesxx
  • Allows customizable entry templates through XSLT
  • Dopušta prilagođene ulazne predloške preko XSLT
  • Admet plantilles d'entrades personalitzades mitjançant XSLT
  • Admet plantilles d'entrades personalitzades mitjançant XSLT
  • Muliggør indgangsskabeloner som kan tilpasses via XSLT
  • Erlaubt anpassbare Vorlagen für Einträge mittels XSLT
  • Allows customisable entry templates through XSLT
  • Permite plantillas personalizadas de entradas a través de XSLT
  • XSLT abil on võimalik luua kohandatud kirjemalle
  • Sallii mukautetut tietuemallit XSLT:n avulla
  • Permet des modèles d'entrées personnalisable via XSLT
  • Permite personalizar os modelos para entradas mediante XSLT.
  • Il permitte patronos de entratas personalisate per XSLT
  • Kemungkinan men-template-kan entri yang dapat dikustomisasi melalui XSLT
  • Permette modelli di immissione personalizzabili attraverso XSLT
  • XSLT를 통해 사용자 지정 가능한 항목 템플릿 허용
  • Staat aanpasbare sjablonen van items toe via XSLT
  • Kan tilpassa oppføringsmalar ved hjelp av XSLT-stilsett
  • Własne szablony wpisów dzięki XSLT
  • Permite modelos de registos personalizados, através de XSLT
  • Permite modelos de registros personalizados, através de XSLT
  • Umožňuje upraviteľné šablóny položiek cez XSLT
  • Dovoljuje uporabno prilagodljivih predlog za vnos preko XSLT
  • Tillåter anpassningsbara inmatningsmallar via XSLT
  • XSLT üzerinden girdi şablonlarının özelleştirilmesine izin verir
  • Придатні до налаштовування шаблони записів на основі XSLT.
  • xxAllows customizable entry templates through XSLTxx
  • Imports MODS, Bibtex, RIS, CSV, PDF metadata, and many other formats
  • Uvozi iz MODS, Bibtex, RIS, CSV, PDF metapodatke, i mnogih drugih formata
  • Importa MODS, Bibtex, RIS, CSV, metadades de PDF, i molts altres formats
  • Importa MODS, Bibtex, RIS, CSV, metadades de PDF, i molts altres formats
  • Importerer MODS, Bibtex, RIS, CSV, PDF-metadata og mange andre formater
  • Import von MODS-, Bibtex-, RIS-, CSV-, PDF-Metadaten und vielen anderen Formaten
  • Imports MODS, Bibtex, RIS, CSV, PDF metadata, and many other formats
  • Importa MODS, Bibtex, RIS, CSV, metadatos PDF y muchos otros formatos
  • MODS-i, Bibtexi, RIS-i, CSV, PDF-i metaandmete ja veel paljude vormingute import
  • Tuo MODS-, BibTeX-, RIS-, CSV-, PDF-metatieto- ja muista tiedostomuodoista
  • Importe MODS, Bibtex, RIS, CSV, les métadonnées PDF, et beaucoup d'autres formats
  • Pode importar ficheiros en formato MODS, Bibtex, RIS, CSV, metadatos de PDF, e moitos outros formatos.
  • Il importa metadatos MODS, Bibtex, RIS, CSV, PDF e plure altere formatos
  • Mengimporkan format MODS, Bibtex, RIS, CSV, PDF metadata, dan banyak format lain
  • Importa MODS, Bibtex, RIS, CSV, metadati dei PDF, e molti altri formati
  • MODS, Bibtex, RIS, CSV, PDF 메타데이터 및 기타 다양한 형식 가져오기
  • Importeert MODS, Bibtex, RIS, CSV, PDF metadata en vele andere formaten
  • Kan importera metadata frå blant anna MODS-, BibTeX-, RIS-, CSV- og PDF-filer
  • Importowanie MODS, Bibtex, RIS, CSV, metadanych PDF i wielu innych formatów
  • Importa dos formatos MODS, Bibtex, RIS, CSV, meta-dados de PDF, entre muitos outros
  • Importa dos formatos MODS, Bibtex, RIS, CSV, metadados de PDF, entre muitos outros
  • Importuje MODS, Bibtex, RIS, CSV, PDF metadáta a veľa iných formátov
  • Zna uvoziti MODS, Bibtex, RIS, CSV, metapodatke PDF in druge vrste datotek
  • Importerar MODS, Bibtexd, RIS, CSV, PDF-metadata och många andra format
  • MODS, Bibtex, RIS, CSV, PDF üst verisi ve pek çok diğer biçimi içe aktarır
  • Імпортування метаданих MODS, Bibtex, RIS, CSV, PDF та даних у багатьох інших форматах.
  • xxImports MODS, Bibtex, RIS, CSV, PDF metadata, and many other formatsxx
  • Exports to Bibtex, ONIX, CSV, HTML, and other formats
  • Exporta a Bibtex, ONIX, CSV, HTML i altres formats
  • Exporta a Bibtex, ONIX, CSV, HTML i altres formats
  • Export zu Bibtex, ONIX, CSV, HTML, und andere Formate
  • Exports to Bibtex, ONIX, CSV, HTML, and other formats
  • Exporta a Bibtex, ONIX, CSV, HTML y otros formatos
  • Vie BibTeX-, ONIX-, CSV-, HTML- ja muihin tiedostomuotoihin
  • Exporte vers Bibtex, ONIX, CSV, HTML et beaucoup d'autres formats
  • Pode exportar coleccións nos formatos Bibtex, CSV, e HTML, entre outros.
  • Pengeksporan ke format Bibtex, ONIX, CSV, HTML, dan format lain
  • Esporta in formato Bibtex, ONIX, CSV, HTML e altri
  • Bibtex, ONIX, CSV, HTML 및 기타 형식으로 내보내기
  • Exporteert naar Bibtex, ONIX, CSV, HTML en andere formaten
  • Kan eksportera til BibTeX, ONIX, CSV, HTML og andre format
  • Eksportowanie Bibtex, ONIX, CSV, HTML i innych formatów
  • Exporta para o Bibtex, ONIX, CSV, HTML, entre outros formatos
  • Exporta para o Bibtex, ONIX, CSV, HTML, entre outros formatos
  • Exportuje do Bibtex, ONIX, CSV, HTML a iných formátov
  • Zna izvoziti v Bibtex, ONIX, CSV, HTML in druge vrste datotek
  • Exporterar till Bibtex, ONIX, CSV, HTML och andra format
  • Bibtex, ONIX, CSV, HTML, PilotDB ve pek çok diğer biçimi dışa aktarır
  • Експортування даних у форматах Bibtex, ONIX, CSV, HTML тощо.
  • xxExports to Bibtex, ONIX, CSV, HTML, and other formatsxx
  • Imports information directly from Amazon.com, IMDb, z39.50 servers, PubMed, SRU servers, CrossRef.org, various other websites, and from external scripts
  • Uvozi informacije direktno sa Amazon.com, IMDb, z39.50 servera, PubMed, SRU servera, CrossRef.org, različitih drugih web stranica, i iz vanjskih skripti
  • Importa informació directament des d'Amazon.com, IMDb, servidors z39.50, PubMed, servidors SRU, CrossRef.org, altres llocs web, i des de scripts externs
  • Importa informació directament des d'Amazon.com, IMDb, servidors z39.50, PubMed, servidors SRU, CrossRef.org, altres llocs web, i des de scripts externs
  • Importerer information direkte fra Amazon.com, IMDb, z39.50 servers, PubMed, SRU-servere, CrossRef.org, diverse andre websider og fra eksterne scripts
  • Importiert Informationen direkt von Amazon.com, IMDb, z39.50-Servern, PubMed, SRU-Servern, CrossRef.org, verschiedenen anderen Webseiten und von externen Skripten
  • Imports information directly from Amazon.com, IMDb, z39.50 servers, PubMed, SRU servers, CrossRef.org, various other websites, and from external scripts
  • Importa información directamente de Amazon.com, IMDb, z39.50 servers, PubMed, SRU servers, CrossRef.org, y más sitios web, además de hacerlo desde scripts externos
  • Teabe importimine otse Amazon.com-ist, IMDb-st, z39.50 serveritest, PubMed-ist, SRU serveritest, CrossRef.org-ist, veel paljudelt veebilehekülgedelt ja väliste skriptide kaudu
  • Tuo tietoa suoraan Amazon.comista, IMDb:stä, z39.50-palvelimilta, PubMedistä, SRU-palvelimilta, CrossRef.orgista, muilta verkkosivuilta ja ulkoisista skripteistä
  • Importe les informations directement depuis Amazon.com, IMDb, z39.50, PubMed, SRU, CrossRef.org et de nombreux autres sites web ainsi que depuis des scripts externes
  • Importa información directamente de Amazon.com, IMDb, servidores z39.50, PubMed, servidores SRU, CrossRef.org, doutros sitios web, e de scripts externos.
  • Il importa information directemente ab Amazon.com, IMDb, servitores z39.50, PubMed, seritores SRU, CrossRef.org, varie altere sitios web e ab external scripts
  • Pengimporkan informasi secara langsung dari situs Amazon.com, IMDb, z39.50 servers, PubMed, SRU servers, CrossRef.org, berbagai situs lain, dan dari skrip eksternal
  • Importa informazioni direttamente da Amazon.com, IMDb, server z39.50, PubMed, server SRU, CrossRef.org, svariati altri siti web, e da script esterni
  • Amazon.com, IMDb, z39.50 서버, PubMed, SRU 서버, CrossRef.org, 기타 여러 웹 사이트 및 외부 스크립트에서 직접 정보 가져오기
  • Importeert informatie direct uit Amazon.com, IMDb, z39.50-servers, PubMed, SRU-servers, CrossRef.org, verschillende andere websites en uit externe scripts
  • Kan henta informasjon frå Amazon.com, IMDb, z39.50-tenarar, PubMed, SRU-tenarar, CrossRef.org, ymse andre nettstadar og eksterne skript
  • Importowanie szczegółów bezpośrednio z Amazon.com, IMDb, serwerów z39.50, PubMed, serwerów SRU, CrossRef.org i różnych innych stron sieciowych, a także z zewnętrznych skryptów
  • Importa a informação directamente a partir da Amazon.com, IMDb, servidores de z39.50, PubMed, servidores de SRU, CrossRef.org, entre outros serviços, bem como de programas externos
  • Importa a informação diretamente a partir da Amazon.com, IMDb, servidores z39.50, PubMed, servidores SRU, CrossRef.org, entre outros serviços, bem como de scripts externos
  • Importuje informácie priamo z Amazon.com, IMDb, z39.50 serverov, PubMed, SRU serverov, CrossRef.org, rôznych iných webstránok a z externých skriptov
  • Zna uvoziti podatke neposredno iz Amazon.com, IMDb, strežnikov z39.50, PubMed, strežnikov SRU, CrossRef.org, drugih spletnih strani in zunanjih skript
  • Importerar information direkt från Amazon.com, IMDb, z39.50-servrar, PubMed, naSRU-servrar, CrossRef.org, diverse andra webbplatser, och från externa skript
  • Bilgileri doğrudan Amazon.com, IMDb, z39.50 sunucuları, PubMed, SRU sunucuları, CrossRef.org ve çeşitli başka sitelerden ve harici betiklerden içe aktarır
  • Імпортування даних безпосередньо із серверів Amazon.com, IMDb, z39.50, PubMed, SRU, CrossRef.org, інших сайтів та на основі зовнішніх скриптів.
  • xxImports information directly from Amazon.com, IMDb, z39.50 servers, PubMed, SRU servers, CrossRef.org, various other websites, and from external scriptsxx
  • Imports CDDB data for cataloging audio CDs
  • Uvozi CDDB podatke za popisivanje muzičkih CD-ova
  • Importa dades CDDB per a la catalogació dels CD d'àudio
  • Importa dades CDDB per a la catalogació dels CD d'àudio
  • Importerer CDDB-data til katalogisering af lyd-cd'er
  • Importiert CDDB-Daten für die Katalogisierung von Audi-CDs
  • Imports CDDB data for cataloguing audio CDs
  • Importa datos CDDB para catalogar CD de audio
  • CDDB andmete import audio-CD-de kataloogimiseks
  • Tuo ääni-CD:iden luokitteluun tarvittavat CDDB-tiedot
  • Importe les données CDDB pour cataloguer les CDs audio
  • Importa datos de CDDB para catalogar CD de son.
  • Il importa datos de CDDB pro catalogar CDs audio
  • Pengimporan data CDDB untuk pengkatalogan CD-CD audio
  • Importa dati CDDB per catalogare cd audio
  • 오디오 CD 카탈로그 작성을 위한 CDDB 데이터 가져오기
  • Importeert CDDB-gegevens voor catalogiseren van audio-cd's
  • Kan importera CDDB-data for lyd-CD-ar
  • Importowanie danych CDDB do katalogowanie płyt muzycznych
  • Importa os dados do CDDB para catalogar os CD's de áudio
  • Importa os dados CDDB para catalogar CDs de áudio
  • Importuje údaje CDDB pre katalogizáciu CD
  • Zna uvoziti podatke CDDB za katalogiziranje glasbenih CD-jev
  • Importerar CDDB-data för katalogisering av ljud-cd
  • CD'leri kataloglamak için CDDB verisini içe aktarır
  • Імпортування даних CDDB для каталогізації звукових компакт-дисків.
  • xxImports CDDB data for cataloging audio CDsxx
  • Scans and imports audio file collections, such as mp3 or ogg
  • Pretražuje i uvozi kolekcije muzičkih datoteka kao što su mp3 ili ogg
  • Explora i importa col·leccions de fitxers d'àudio, com MP3 o OGG
  • Explora i importa col·leccions de fitxers d'àudio, com MP3 o OGG
  • Scanner og importerer lydfilsamlinger såsom mp3 eller ogg
  • Durchsucht und importiert Sammlungen von Audio-Dateien wie zum Beispiel mp3 oder ogg
  • Scans and imports audio file collections, such as mp3 or ogg
  • Escanea e importa colecciones de archivos de audio, como por ejemplo mp3 u ogg
  • Helifailide (näiteks mp3 või ogg) kogude läbiuurimine ja import
  • Etsii ja tuo äänitiedostojen kuten MP3:ten ja Oggien kokoelmia
  • Scanne et importe les collections de fichiers audio, tels que les fichiers mp3 ou ogg
  • Analiza e importa coleccións de ficheiros de son, como MP3 ou OGG.
  • Il scande e importa collectiones de file audio, tal como mp3 o ogg
  • Pemindaian dan pengimporan koleksi file audio, sepertihalnya mp3 atau ogg
  • Cerca e importa collezioni di file audio, come mp3 oppure ogg
  • MP3 또는 OGG 같은 오디오 파일 소장품 검색 및 가져오기
  • Scant en importeert verzamelingen van audio-bestanden, zoals mp3 of ogg
  • Søkjer etter og importerer lydfilsamlingar, som MP3 og OGG
  • Przeszukiwanie i importowanie zbiorów plików dźwiękowych, takich jak mp3 czy ogg
  • Sonda e importa as colecções dos ficheiros de áudio, como MP3's ou OGG's
  • Verifica e importa as coleções de arquivos de áudio, como MP3 ou OGG
  • Prehľadá a importuje zbierky audio súborov, ako mp3 alebo ogg
  • Zna preiskati in uvoziti zbirke zvočnih datotek kot so mp3 ali ogg
  • Söker igenom och importera samlingar av ljudfiler, såsom mp3 eller ogg
  • Mp3 ve ogg gibi ses dosyalarını tarar ve içe aktarır
  • Сканування та імпортування збірок звукових файлів, зокрема mp3 та ogg.
  • xxScans and imports audio file collections, such as mp3 or oggxx
https://tellico-project.org https://bugs.kde.org/enter_bug.cgi?format=guided&product=tellico https://tellico-project.org/faq https://docs.kde.org/development/en/extragear-office/tellico/index.html The main window showing a simple item in a book collection La finestra principal mostra un element senzill en una col·lecció de llibres La finestra principal mostra un element senzill en una col·lecció de llibres Das Hauptfenster mit einem einfachen Eintrag einer Bücher-Sammlung The main window showing a simple item in a book collection La ventana principal mostrando un elemento sencillo de una colección de libros La fenêtre principale, qui affiche un élément simple au sein d'une collection de livres A xanela principal, mostrando un elemento sinxelo nunha colección de libros Window utama menampilkan item sederhana dalam koleksi buku La finestra principale che mostra un semplice elemento in una collezione di libri 도서 소장품의 간단한 항목을 표시하는 주 창 Het hoofdvenster die een eenvoudig item in een boekenverzameling toont Hovudvindauget som viser ei enkel oppføring i ei boksamling Okno główne pokazujące prosty element w zbiorze książek A janela principal a mostrar um único item numa colecção de livros A janela principal a mostrar um único item em uma coleção de livros + Hlavné okno zobrazujúce jednoduchú položku v kolekcii kníh Huvudfönstret som visar ett enkelt objekt i en boksamling Головне вікно програми із простим записом у збірці книжок xxThe main window showing a simple item in a book collectionxx https://cdn.kde.org/screenshots/tellico/tellico.png The main window showing side-by-side views of a game collection La finestra principal mostra vistes un al costat de l'altra d'una col·lecció de jocs La finestra principal mostra vistes un al costat de l'altra d'una col·lecció de jocs Das Hauptfenster mit einer zweispaltigen Ansicht einer Spiele-Sammlung The main window showing side-by-side views of a game collection La ventana principal mostrando vistas en paralelo de una colección de juegos La fenêtre principale, qui affiche des vues côte-à-côte d'une collection de jeux A xanela principal mostrando as vistas lado a lado dunha colección de xogos Window utama menampilkan tampilan sisi ke sisi dari sebuah koleksi permainan La finestra principale che mostra le viste affiancate di una collezione di libri 게임 소장품을 나란히 표시하는 주 창 Het hoofdvenster die een spellenverzameling naast elkaar toont Hovudvindauget som viser side-ved-side-visingaar av ei spelsamling Okno główne pokazujące widoki zbioru gier obok siebie A janela principal a mostrar vistas lado-a-lado de uma colecção de jogos A janela principal mostrando visões lado a lado de uma coleção de jogos + Hlavné okno zobrazujúce pohľady vedľa seba kolekcie hier Huvudfönstret som visar en spelsamling med visning sida vid sida Головне вікно програми із панелями паралельного перегляду збірки ігор xxThe main window showing side-by-side views of a game collectionxx https://cdn.kde.org/screenshots/tellico/tellico-games.png KDE tellico https://tellico-project.org/tellico-31-released https://tellico-project.org/tellico-311-released https://tellico-project.org/tellico-312-released https://tellico-project.org/tellico-313-released https://tellico-project.org/tellico-314-released https://tellico-project.org/tellico-32-released https://tellico-project.org/tellico-321-released + + https://tellico-project.org/tellico-322-released +
diff --git a/src/controller.cpp b/src/controller.cpp index 777db518..e0b29c89 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -1,675 +1,676 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "controller.h" #include "mainwindow.h" #include "groupview.h" #include "detailedlistview.h" #include "entryeditdialog.h" #include "entryview.h" #include "entryiconview.h" #include "entry.h" #include "entrygroup.h" #include "field.h" #include "filter.h" #include "filterdialog.h" #include "tellico_kernel.h" #include "collection.h" #include "document.h" #include "borrower.h" #include "filterview.h" #include "loanview.h" #include "entryupdater.h" #include "entrymerger.h" #include "utils/cursorsaver.h" #include "gui/lineedit.h" #include "gui/tabwidget.h" #include "tellico_debug.h" #include #include #include #include #include using Tellico::Controller; Controller* Controller::s_self = nullptr; Controller::Controller(Tellico::MainWindow* parent_) : QObject(parent_), m_mainWindow(parent_), m_working(false) { } Controller::~Controller() { } void Controller::addObserver(Tellico::Observer* obs) { m_observers.append(obs); } void Controller::removeObserver(Tellico::Observer* obs) { m_observers.removeAll(obs); } QString Controller::groupBy() const { return m_mainWindow->m_groupView->groupBy(); } QStringList Controller::expandedGroupBy() const { QStringList g; g << groupBy(); // special case for pseudo-group if(g[0] == Data::Collection::s_peopleGroupName) { g.clear(); Data::FieldList fields = Data::Document::self()->collection()->peopleFields(); foreach(Data::FieldPtr field, fields) { g << field->name(); } } // special case for no groups if(g[0].isEmpty()) { g.clear(); } return g; } QStringList Controller::sortTitles() const { QStringList list; list << m_mainWindow->m_detailedView->sortColumnTitle1(); list << m_mainWindow->m_detailedView->sortColumnTitle2(); list << m_mainWindow->m_detailedView->sortColumnTitle3(); return list; } QStringList Controller::visibleColumns() const { return m_mainWindow->m_detailedView->visibleColumns(); } Tellico::Data::EntryList Controller::visibleEntries() { return m_mainWindow->m_detailedView->visibleEntries(); } void Controller::slotCollectionAdded(Tellico::Data::CollPtr coll_) { MARK; // at start-up, this might get called too early, so check and bail if(!coll_ || !m_mainWindow->m_groupView) { return; } // do this first because the group view will need it later m_mainWindow->readCollectionOptions(coll_); m_mainWindow->slotUpdateToolbarIcons(); m_mainWindow->updateEntrySources(); // has to be called before all the addCollection() // calls in the widgets since they may want menu updates // blockAllSignals(true); m_mainWindow->m_detailedView->addCollection(coll_); m_mainWindow->m_groupView->addCollection(coll_); m_mainWindow->m_editDialog->resetLayout(coll_); if(!coll_->filters().isEmpty()) { m_mainWindow->addFilterView(); m_mainWindow->m_filterView->addCollection(coll_); m_mainWindow->m_viewTabs->setTabBarHidden(false); } if(!coll_->borrowers().isEmpty()) { m_mainWindow->addLoanView(); m_mainWindow->m_loanView->addCollection(coll_); m_mainWindow->m_viewTabs->setTabBarHidden(false); } // blockAllSignals(false); m_mainWindow->slotStatusMsg(i18n("Ready.")); m_selectedEntries.clear(); m_mainWindow->slotEntryCount(); // there really should be a lot of signals to connect to, but right now, the only one // is used when a field is added on a merge connect(&*coll_, &Data::Collection::mergeAddedField, this, &Controller::slotFieldAdded); emit collectionAdded(coll_->type()); updateActions(); connect(&*coll_, &Data::Collection::signalGroupsModified, m_mainWindow->m_groupView, &GroupView::slotModifyGroups); connect(&*coll_, &Data::Collection::signalRefreshField, this, &Controller::slotRefreshField); } void Controller::slotCollectionModified(Tellico::Data::CollPtr coll_) { // easiest thing is to signal collection deleted, then added? // FIXME: Signals for delete collection and then added are yucky slotCollectionDeleted(coll_); slotCollectionAdded(coll_); // https://bugs.kde.org/show_bug.cgi?id=386549 // at some point, I need to revisit the ::setImagesAreAvailable() methodology // there are too many workarounds in the code for that m_mainWindow->m_detailedView->slotRefreshImages(); } void Controller::slotCollectionDeleted(Tellico::Data::CollPtr coll_) { blockAllSignals(true); m_mainWindow->saveCollectionOptions(coll_); m_mainWindow->m_groupView->removeCollection(coll_); if(m_mainWindow->m_filterView) { m_mainWindow->m_filterView->slotReset(); } if(m_mainWindow->m_loanView) { m_mainWindow->m_loanView->slotReset(); } m_mainWindow->m_detailedView->removeCollection(coll_); m_mainWindow->m_entryView->clear(); blockAllSignals(false); // disconnect all signals from the collection // this is needed because the Collection::appendCollection() and mergeCollection() // functions signal collection deleted then added for the same collection coll_->disconnect(); } void Controller::slotFieldAdded(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) { addedField(coll_, field_); } +// TODO: should be adding entries to models rather than to widget observers void Controller::addedEntries(Tellico::Data::EntryList entries_) { blockAllSignals(true); foreach(Observer* obs, m_observers) { obs->addEntries(entries_); } m_mainWindow->slotQueueFilter(); blockAllSignals(false); } void Controller::modifiedEntries(Tellico::Data::EntryList entries_) { // when a new document is being loaded, loans are added to borrowers, which // end up calling Entry::checkIn() which called Document::saveEntry() which calls here // ignore that if(!m_mainWindow->m_initialized) { return; } blockAllSignals(true); foreach(Observer* obs, m_observers) { obs->modifyEntries(entries_); } m_mainWindow->m_entryView->slotRefresh(); // special case blockAllSignals(false); } void Controller::removedEntries(Tellico::Data::EntryList entries_) { blockAllSignals(true); foreach(Observer* obs, m_observers) { obs->removeEntries(entries_); } foreach(Data::EntryPtr entry, entries_) { m_selectedEntries.removeAll(entry); } m_mainWindow->slotEntryCount(); m_mainWindow->slotQueueFilter(); blockAllSignals(false); } void Controller::addedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) { foreach(Observer* obs, m_observers) { obs->addField(coll_, field_); } m_mainWindow->m_entryView->slotRefresh(); m_mainWindow->slotUpdateCollectionToolBar(coll_); m_mainWindow->slotQueueFilter(); } void Controller::removedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) { foreach(Observer* obs, m_observers) { obs->removeField(coll_, field_); } m_mainWindow->m_entryView->slotRefresh(); m_mainWindow->slotUpdateCollectionToolBar(coll_); m_mainWindow->slotQueueFilter(); } void Controller::modifiedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr oldField_, Tellico::Data::FieldPtr newField_) { foreach(Observer* obs, m_observers) { obs->modifyField(coll_, oldField_, newField_); } m_mainWindow->m_entryView->slotRefresh(); m_mainWindow->slotUpdateCollectionToolBar(coll_); m_mainWindow->slotQueueFilter(); } void Controller::reorderedFields(Tellico::Data::CollPtr coll_) { m_mainWindow->m_editDialog->resetLayout(coll_); m_mainWindow->m_detailedView->reorderFields(coll_->fields()); m_mainWindow->slotUpdateCollectionToolBar(coll_); m_mainWindow->m_entryView->slotRefresh(); } void Controller::slotClearSelection() { if(m_working) { return; } m_working = true; blockAllSignals(true); m_mainWindow->m_detailedView->clearSelection(); m_mainWindow->m_iconView->clearSelection(); m_mainWindow->m_groupView->clearSelection(); if(m_mainWindow->m_filterView) { m_mainWindow->m_filterView->clearSelection(); } if(m_mainWindow->m_loanView) { m_mainWindow->m_loanView->clearSelection(); } blockAllSignals(false); m_selectedEntries.clear(); updateActions(); m_mainWindow->slotEntryCount(); m_working = false; } void Controller::slotUpdateSelection(const Tellico::Data::EntryList& entries_) { if(m_working) { return; } m_working = true; m_selectedEntries = entries_; updateActions(); m_mainWindow->slotEntryCount(); m_working = false; } void Controller::slotUpdateSelectedEntries(const QString& source_) { if(m_selectedEntries.isEmpty()) { return; } // it deletes itself when done // signal mapper strings can't be empty, "_all" is set in mainwindow if(source_.isEmpty() || source_ == QLatin1String("_all")) { new EntryUpdater(m_selectedEntries.front()->collection(), m_selectedEntries, this); } else { new EntryUpdater(source_, m_selectedEntries.front()->collection(), m_selectedEntries, this); } } void Controller::slotDeleteSelectedEntries() { if(m_selectedEntries.isEmpty()) { return; } m_working = true; // confirm delete if(m_selectedEntries.count() == 1) { QString str = i18n("Do you really want to delete this entry?"); QString dontAsk = QStringLiteral("DeleteEntry"); int ret = KMessageBox::warningContinueCancel(Kernel::self()->widget(), str, i18n("Delete Entry"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), dontAsk); if(ret != KMessageBox::Continue) { m_working = false; return; } } else { QStringList names; foreach(Data::EntryPtr entry, m_selectedEntries) { names += entry->title(); } QString str = i18n("Do you really want to delete these entries?"); // historically called DeleteMultipleBooks, don't change QString dontAsk = QStringLiteral("DeleteMultipleBooks"); int ret = KMessageBox::warningContinueCancelList(Kernel::self()->widget(), str, names, i18n("Delete Multiple Entries"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), dontAsk); if(ret != KMessageBox::Continue) { m_working = false; return; } } GUI::CursorSaver cs; Kernel::self()->removeEntries(m_selectedEntries); updateActions(); m_working = false; // special case, the detailed list view selects the next item, so handle that // Data::EntryList newList; // for(GUI::ListViewItemListIt it(m_mainWindow->m_detailedView->selectedItems()); it.current(); ++it) { // newList.append(static_cast(it.current())->entry()); // } // slotUpdateSelection(m_mainWindow->m_detailedView, newList); slotClearSelection(); } void Controller::slotMergeSelectedEntries() { // merge requires at least 2 entries if(m_selectedEntries.count() < 2) { return; } new EntryMerger(m_selectedEntries, this); } void Controller::slotRefreshField(Tellico::Data::FieldPtr field_) { // DEBUG_LINE; // group view only needs to refresh if it's the title if(field_->name() == QLatin1String("title")) { m_mainWindow->m_groupView->populateCollection(); } m_mainWindow->m_detailedView->slotRefresh(); m_mainWindow->m_entryView->slotRefresh(); } void Controller::slotCopySelectedEntries() { if(m_selectedEntries.isEmpty()) { return; } // keep copy of selected entries Data::EntryList old = m_selectedEntries; GUI::CursorSaver cs; // need to create copies Data::EntryList entries; foreach(Data::EntryPtr it, m_selectedEntries) { entries.append(Data::EntryPtr(new Data::Entry(*it))); } Kernel::self()->addEntries(entries, false); slotUpdateSelection(old); } void Controller::blockAllSignals(bool block_) const { // sanity check if(!m_mainWindow->m_initialized) { return; } m_mainWindow->m_detailedView->blockSignals(block_); m_mainWindow->m_groupView->blockSignals(block_); m_mainWindow->m_quickFilter->blockSignals(block_); if(m_mainWindow->m_loanView) { m_mainWindow->m_loanView->blockSignals(block_); } if(m_mainWindow->m_filterView) { m_mainWindow->m_filterView->blockSignals(block_); } m_mainWindow->m_editDialog->blockSignals(block_); m_mainWindow->m_iconView->blockSignals(block_); } void Controller::slotUpdateFilter(Tellico::FilterPtr filter_) { blockAllSignals(true); // the view takes over ownership of the filter if(filter_ && !filter_->isEmpty()) { // clear the icon view selection only // the detailed view takes care of itself m_mainWindow->m_iconView->clearSelection(); m_selectedEntries.clear(); } updateActions(); m_mainWindow->m_detailedView->setFilter(filter_); // takes ownership if(!filter_ && m_mainWindow->m_filterView && !m_mainWindow->m_dontQueueFilter) { // for example, when quick filter clears the selection // the check against m_dontQueueFilter is to prevent the situation when the FilterView has an Entry selected // which sends an empty filter selection, which would then clear the whole FilterView selection m_mainWindow->m_filterView->clearSelection(); } blockAllSignals(false); m_mainWindow->slotEntryCount(); } void Controller::clearFilter() { blockAllSignals(true); m_mainWindow->m_quickFilter->clear(); m_mainWindow->m_detailedView->setFilter(Tellico::FilterPtr()); blockAllSignals(false); } void Controller::editEntry(Tellico::Data::EntryPtr entry_) const { m_mainWindow->slotShowEntryEditor(); m_mainWindow->m_editDialog->setContents(Data::EntryList() << entry_); } void Controller::plugCollectionActions(QMenu* popup_) { if(!popup_) { return; } popup_->addAction(m_mainWindow->action("coll_rename_collection")); popup_->addAction(m_mainWindow->action("coll_fields")); popup_->addAction(m_mainWindow->action("change_entry_grouping")); } void Controller::plugEntryActions(QMenu* popup_) { if(!popup_) { return; } // m_mainWindow->m_newEntry->plug(popup_); popup_->addAction(m_mainWindow->m_editEntry); popup_->addAction(m_mainWindow->m_copyEntry); popup_->addAction(m_mainWindow->m_deleteEntry); popup_->addAction(m_mainWindow->m_mergeEntry); popup_->addMenu(m_mainWindow->m_updateEntryMenu->menu()); // there's a bug in KActionMenu with KXMLGUIFactory::plugActionList // pluging the menu isn't enough to have the popup get populated plugUpdateMenu(popup_); popup_->addSeparator(); popup_->addAction(m_mainWindow->m_checkOutEntry); } void Controller::plugUpdateMenu(QMenu* popup_) { QMenu* updatePopup = nullptr; foreach(QAction* action, popup_->actions()) { if(action && action->text() == m_mainWindow->m_updateEntryMenu->text()) { updatePopup = action->menu(); break; } } if(!updatePopup) { return; } // I can't figure out why the actions get duplicated, but they do // so clear them all updatePopup->removeAction(m_mainWindow->m_updateAll); foreach(QAction* action, m_mainWindow->m_fetchActions) { updatePopup->removeAction(action); } // clear separator, too updatePopup->clear(); updatePopup->addAction(m_mainWindow->m_updateAll); updatePopup->addSeparator(); foreach(QAction* action, m_mainWindow->m_fetchActions) { updatePopup->addAction(action); } } void Controller::updateActions() const { const bool emptySelection = m_selectedEntries.isEmpty(); m_mainWindow->stateChanged(QStringLiteral("empty_selection"), emptySelection ? KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse); foreach(QAction* action, m_mainWindow->m_fetchActions) { action->setEnabled(!emptySelection); } //only enable citation items when it's a bibliography const bool isBibtex = Kernel::self()->collectionType() == Data::Collection::Bibtex; if(isBibtex) { m_mainWindow->action("cite_clipboard")->setEnabled(!emptySelection); m_mainWindow->action("cite_lyxpipe")->setEnabled(!emptySelection); } m_mainWindow->m_checkInEntry->setEnabled(canCheckIn()); if(m_selectedEntries.count() < 2) { m_mainWindow->m_editEntry->setText(i18n("&Edit Entry...")); m_mainWindow->m_copyEntry->setText(i18n("D&uplicate Entry")); m_mainWindow->m_updateEntryMenu->setText(i18n("&Update Entry")); m_mainWindow->m_deleteEntry->setText(i18n("&Delete Entry")); m_mainWindow->m_mergeEntry->setEnabled(false); } else { m_mainWindow->m_editEntry->setText(i18n("&Edit Entries...")); m_mainWindow->m_copyEntry->setText(i18n("D&uplicate Entries")); m_mainWindow->m_updateEntryMenu->setText(i18n("&Update Entries")); m_mainWindow->m_deleteEntry->setText(i18n("&Delete Entries")); m_mainWindow->m_mergeEntry->setEnabled(true); } } void Controller::addedBorrower(Tellico::Data::BorrowerPtr borrower_) { m_mainWindow->addLoanView(); // just in case foreach(Observer* obs, m_observers) { obs->addBorrower(borrower_); } m_mainWindow->m_viewTabs->setTabBarHidden(false); } void Controller::modifiedBorrower(Tellico::Data::BorrowerPtr borrower_) { foreach(Observer* obs, m_observers) { if(borrower_->isEmpty()) { obs->removeBorrower(borrower_); } else { obs->modifyBorrower(borrower_); } } hideTabs(); } void Controller::addedFilter(Tellico::FilterPtr filter_) { m_mainWindow->addFilterView(); // just in case foreach(Observer* obs, m_observers) { obs->addFilter(filter_); } m_mainWindow->m_viewTabs->setTabBarHidden(false); } void Controller::removedFilter(Tellico::FilterPtr filter_) { foreach(Observer* obs, m_observers) { obs->removeFilter(filter_); } hideTabs(); } void Controller::slotCheckOut() { if(m_selectedEntries.isEmpty()) { return; } Data::EntryList loanedEntries = m_selectedEntries; // check to see if any of the entries are already on-loan, and warn user QMap alreadyLoaned; foreach(Data::BorrowerPtr borrower, Data::Document::self()->collection()->borrowers()) { foreach(Data::LoanPtr loan, borrower->loans()) { if(m_selectedEntries.contains(loan->entry())) { alreadyLoaned.insert(loan->entry()->title(), loan->entry()); } } } if(!alreadyLoaned.isEmpty()) { KMessageBox::informationList(Kernel::self()->widget(), i18n("The following items are already loaned, but Tellico " "does not currently support lending an item multiple " "times. They will be removed from the list of items " "to lend."), alreadyLoaned.keys()); QMap::const_iterator it = alreadyLoaned.constBegin(); QMap::const_iterator end = alreadyLoaned.constEnd(); for( ; it != end; ++it) { loanedEntries.removeAll(it.value()); } if(loanedEntries.isEmpty()) { return; } } if(Kernel::self()->addLoans(loanedEntries)) { m_mainWindow->m_checkInEntry->setEnabled(true); } } void Controller::slotCheckIn() { slotCheckIn(m_selectedEntries); } void Controller::slotCheckIn(const Tellico::Data::EntryList& entries_) { if(entries_.isEmpty()) { return; } Data::LoanList loans; foreach(Data::EntryPtr entry, entries_) { // these have to be in the loop since if a borrower gets empty // it will be deleted, so the vector could change, for every entry iterator Data::BorrowerList vec = Data::Document::self()->collection()->borrowers(); foreach(Data::BorrowerPtr borrower, vec) { Data::LoanPtr l = borrower->loan(entry); if(l) { loans.append(l); // assume it's only loaned once break; } } } if(Kernel::self()->removeLoans(loans)) { m_mainWindow->m_checkInEntry->setEnabled(false); } hideTabs(); } void Controller::hideTabs() const { if((!m_mainWindow->m_filterView || m_mainWindow->m_filterView->isEmpty()) && (!m_mainWindow->m_loanView || m_mainWindow->m_loanView->isEmpty())) { int idx = m_mainWindow->m_viewTabs->indexOf(m_mainWindow->m_groupView); m_mainWindow->m_viewTabs->setCurrentIndex(idx); m_mainWindow->m_viewTabs->setTabBarHidden(true); } } bool Controller::canCheckIn() const { foreach(Data::EntryPtr entry, m_selectedEntries) { if(entry->field(QStringLiteral("loaned")) == QLatin1String("true")) { return true; } } return false; } void Controller::updatedFetchers() { m_mainWindow->updateEntrySources(); } diff --git a/src/detailedlistview.cpp b/src/detailedlistview.cpp index 5f1bb106..ba64f7eb 100644 --- a/src/detailedlistview.cpp +++ b/src/detailedlistview.cpp @@ -1,639 +1,644 @@ /*************************************************************************** Copyright (C) 2001-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "detailedlistview.h" #include "collection.h" #include "collectionfactory.h" #include "controller.h" #include "field.h" #include "entry.h" #include "tellico_debug.h" #include "tellico_kernel.h" #include "config/tellico_config.h" #include "models/entrymodel.h" #include "models/entrysortmodel.h" #include "models/modelmanager.h" #include "gui/detailedentryitemdelegate.h" #include "gui/ratingdelegate.h" #include "utils/string_utils.h" #include #include #include #include #include #include using namespace Tellico; using Tellico::DetailedListView; DetailedListView::DetailedListView(QWidget* parent_) : GUI::TreeView(parent_) , m_loadingCollection(false), m_currentContextColumn(-1) { setHeaderHidden(false); setSelectionMode(QAbstractItemView::ExtendedSelection); setAlternatingRowColors(true); setRootIsDecorated(false); setUniformRowHeights(true); connect(this, &QAbstractItemView::doubleClicked, this, &DetailedListView::slotDoubleClicked); // header menu header()->installEventFilter(this); header()->setMinimumSectionSize(20); m_headerMenu = new QMenu(this); m_columnMenu = new QMenu(this); connect(m_columnMenu, &QMenu::triggered, this, &DetailedListView::slotColumnMenuActivated); EntryModel* entryModel = new EntryModel(this); EntrySortModel* sortModel = new EntrySortModel(this); sortModel->setSortRole(EntryPtrRole); sortModel->setSourceModel(entryModel); setModel(sortModel); setItemDelegate(new DetailedEntryItemDelegate(this)); ModelManager::self()->setEntryModel(sortModel); connect(model(), &QAbstractItemModel::headerDataChanged, this, &DetailedListView::updateHeaderMenu); connect(model(), &QAbstractItemModel::headerDataChanged, this, &DetailedListView::updateColumnDelegates); connect(model(), &QAbstractItemModel::columnsInserted, this, &DetailedListView::hideNewColumn); connect(header(), &QHeaderView::sectionCountChanged, this, &DetailedListView::updateHeaderMenu); } DetailedListView::~DetailedListView() { } Tellico::EntryModel* DetailedListView::sourceModel() const { return static_cast(sortModel()->sourceModel()); } void DetailedListView::addCollection(Tellico::Data::CollPtr coll_) { if(!coll_) { return; } const QString configGroup = QStringLiteral("Options - %1").arg(CollectionFactory::typeName(coll_)); KConfigGroup config(KSharedConfig::openConfig(), configGroup); QString configN; if(coll_->type() == Data::Collection::Base) { QUrl url = Kernel::self()->URL(); for(int i = 0; i < Config::maxCustomURLSettings(); ++i) { QUrl u = config.readEntry(QStringLiteral("URL_%1").arg(i), QUrl()); if(u == url) { configN = QStringLiteral("_%1").arg(i); break; } } } // we don't want to immediately hide all these columns when adding fields disconnect(model(), &QAbstractItemModel::columnsInserted, this, &DetailedListView::hideNewColumn); sourceModel()->setImagesAreAvailable(false); sourceModel()->setFields(coll_->fields()); connect(model(), &QAbstractItemModel::columnsInserted, this, &DetailedListView::hideNewColumn); // we're not using saveState() and restoreState() since our columns are variable QStringList columnNames = config.readEntry(QLatin1String("ColumnNames") + configN, QStringList()); QList columnWidths = config.readEntry(QLatin1String("ColumnWidths") + configN, QList()); QList columnOrder = config.readEntry(QLatin1String("ColumnOrder") + configN, QList()); // just a broken-world check while(columnWidths.size() < columnNames.size()) { columnWidths << 0; } while(columnOrder.size() < columnNames.size()) { columnOrder << columnOrder.size(); } QList currentColumnOrder; // now restore widths and order for(int ncol = 0; ncol < header()->count(); ++ncol) { int idx = columnNames.indexOf(columnFieldName(ncol)); // column width of 0 means hidden if(idx < 0 || columnWidths.at(idx) <= 0) { hideColumn(ncol); if(idx > -1) { currentColumnOrder << ncol; } } else { setColumnWidth(ncol, columnWidths.at(idx)); currentColumnOrder << ncol; } } const int maxCount = qMin(currentColumnOrder.size(), columnOrder.size()); for(int i = 0; i < maxCount; ++i) { header()->moveSection(header()->visualIndex(currentColumnOrder.at(i)), columnOrder.at(i)); } // always hide tables and paragraphs for(int ncol = 0; ncol < coll_->fields().count(); ++ncol) { Data::FieldPtr field = model()->headerData(ncol, Qt::Horizontal, FieldPtrRole).value(); if(field) { if(field->type() == Data::Field::Table || field->type() == Data::Field::Para) { hideColumn(ncol); } } else { myDebug() << "no field for col" << ncol; } } // because some of the fields got hidden... updateColumnDelegates(); updateHeaderMenu(); checkHeader(); sortModel()->setSortColumn(config.readEntry(QLatin1String("SortColumn") + configN, -1)); sortModel()->setSecondarySortColumn(config.readEntry(QLatin1String("PrevSortColumn") + configN, -1)); sortModel()->setTertiarySortColumn(config.readEntry(QLatin1String("Prev2SortColumn") + configN, -1)); const int order = config.readEntry(QLatin1String("SortOrder") + configN, static_cast(Qt::AscendingOrder)); sortModel()->setSortOrder(static_cast(order)); setUpdatesEnabled(false); m_loadingCollection = true; addEntries(coll_->entries()); m_loadingCollection = false; setUpdatesEnabled(true); header()->setSortIndicator(sortModel()->sortColumn(), sortModel()->sortOrder()); } void DetailedListView::slotReset() { //clear() does not remove columns sourceModel()->clear(); } void DetailedListView::addEntries(Tellico::Data::EntryList entries_) { if(entries_.isEmpty()) { return; } sourceModel()->addEntries(entries_); if(!m_loadingCollection) { setState(entries_, NewState); + // TODO: this only scrolls to new entries in the list view, not the other widgets + // who use this selection model + setEntriesSelected(entries_); } } void DetailedListView::modifyEntries(Tellico::Data::EntryList entries_) { if(entries_.isEmpty()) { return; } sourceModel()->modifyEntries(entries_); setState(entries_, ModifiedState); } void DetailedListView::removeEntries(Tellico::Data::EntryList entries_) { if(entries_.isEmpty()) { return; } sourceModel()->removeEntries(entries_); } void DetailedListView::setState(Tellico::Data::EntryList entries_, int state) { foreach(Data::EntryPtr entry, entries_) { QModelIndex index = sourceModel()->indexFromEntry(entry); if(index.isValid()) { sourceModel()->setData(index, state, SaveStateRole); } else { myWarning() << "no index found for" << entry->id() << entry->title(); } } } void DetailedListView::removeCollection(Tellico::Data::CollPtr coll_) { if(!coll_) { myWarning() << "null coll pointer!"; return; } sourceModel()->clear(); } void DetailedListView::contextMenuEvent(QContextMenuEvent* event_) { QModelIndex index = indexAt(event_->pos()); if(!index.isValid()) { return; } QMenu menu(this); Controller::self()->plugEntryActions(&menu); menu.exec(event_->globalPos()); } // don't shadow QListView::setSelected void DetailedListView::setEntriesSelected(Data::EntryList entries_) { if(entries_.isEmpty()) { // don't move this one outside the block since it calls setCurrentItem(0) clearSelection(); return; } clearSelection(); EntrySortModel* proxyModel = static_cast(model()); foreach(Data::EntryPtr entry, entries_) { QModelIndex index = sourceModel()->indexFromEntry(entry); if(!proxyModel->mapFromSource(index).isValid()) { // clear the filter if we're trying to select an entry that is currently filtered out Controller::self()->clearFilter(); break; } } blockSignals(true); + QItemSelection itemSel; foreach(Data::EntryPtr entry, entries_) { - QModelIndex index = sourceModel()->indexFromEntry(entry); - selectionModel()->select(proxyModel->mapFromSource(index), QItemSelectionModel::Select | QItemSelectionModel::Rows); + QModelIndex index = proxyModel->mapFromSource(sourceModel()->indexFromEntry(entry)); + itemSel.select(index, index); } - //setCurrentIndex(index); + selectionModel()->select(itemSel, QItemSelectionModel::Select | QItemSelectionModel::Rows); blockSignals(false); + QModelIndex index = sourceModel()->indexFromEntry(entries_.first()); scrollTo(proxyModel->mapFromSource(index)); } bool DetailedListView::eventFilter(QObject* obj_, QEvent* event_) { if(event_->type() == QEvent::ContextMenu && obj_ == header()) { m_currentContextColumn = header()->logicalIndexAt(static_cast(event_)->pos()); m_headerMenu->exec(static_cast(event_)->globalPos()); return true; } return GUI::TreeView::eventFilter(obj_, event_); } void DetailedListView::slotDoubleClicked(const QModelIndex& index_) { Data::EntryPtr entry = index_.data(EntryPtrRole).value(); if(entry) { Controller::self()->editEntry(entry); } } void DetailedListView::slotColumnMenuActivated(QAction* action_) { const int col = action_->data().toInt(); if(col > -1) { // only column actions have data const bool isChecked = action_->isChecked(); setColumnHidden(col, !isChecked); // if we're showing a column, resize all sections if(isChecked) { resizeColumnToContents(col); adjustColumnWidths(); } } checkHeader(); } void DetailedListView::showAllColumns() { foreach(QAction* action, m_columnMenu->actions()) { if(action->isCheckable() && !action->isChecked()) { action->trigger(); } } } void DetailedListView::hideAllColumns() { for(int ncol = 0; ncol < header()->count(); ++ncol) { hideColumn(ncol); } foreach(QAction* action, m_columnMenu->actions()) { if(action->isCheckable()) { action->setChecked(false); } } checkHeader(); } void DetailedListView::hideCurrentColumn() { setColumnHidden(m_currentContextColumn, true); checkHeader(); } void DetailedListView::slotRefresh() { sortModel()->invalidate(); } void DetailedListView::setFilter(Tellico::FilterPtr filter_) { static_cast(sortModel())->setFilter(filter_); } Tellico::FilterPtr DetailedListView::filter() const { return static_cast(sortModel())->filter(); } void DetailedListView::addField(Tellico::Data::CollPtr, Tellico::Data::FieldPtr field) { sourceModel()->addFields(Data::FieldList() << field); } void DetailedListView::modifyField(Tellico::Data::CollPtr, Tellico::Data::FieldPtr oldField_, Tellico::Data::FieldPtr newField_) { Q_UNUSED(oldField_) sourceModel()->modifyField(oldField_, newField_); } void DetailedListView::removeField(Tellico::Data::CollPtr, Tellico::Data::FieldPtr field_) { sourceModel()->removeFields(Data::FieldList() << field_); } void DetailedListView::reorderFields(const Tellico::Data::FieldList& fields_) { QStringList columnNames; QList columnWidths, columnOrder; for(int ncol = 0; ncol < header()->count(); ++ncol) { // ignore hidden columns if(!isColumnHidden(ncol)) { columnNames << columnFieldName(ncol); columnWidths << columnWidth(ncol); columnOrder << header()->visualIndex(ncol); } } sourceModel()->setFields(fields_); QList currentColumnOrder; // now restore widths and order for(int ncol = 0; ncol < header()->count(); ++ncol) { int idx = columnNames.indexOf(columnFieldName(ncol)); // column width of 0 means hidden if(idx < 0 || columnWidths.at(idx) <= 0) { hideColumn(ncol); if(idx > -1) { currentColumnOrder << ncol; } } else { setColumnWidth(ncol, columnWidths.at(idx)); currentColumnOrder << ncol; } } const int maxCount = qMin(currentColumnOrder.size(), columnOrder.size()); for(int i = 0; i < maxCount; ++i) { header()->moveSection(header()->visualIndex(currentColumnOrder.at(i)), columnOrder.at(i)); } updateHeaderMenu(); } void DetailedListView::saveConfig(Tellico::Data::CollPtr coll_, int configIndex_) { const QString configGroup = QStringLiteral("Options - %1").arg(CollectionFactory::typeName(coll_)); KConfigGroup config(KSharedConfig::openConfig(), configGroup); // all of this is to have custom settings on a per file basis QString configN; if(coll_->type() == Data::Collection::Base) { QList info; for(int i = 0; i < Config::maxCustomURLSettings(); ++i) { QUrl u(config.readEntry(QStringLiteral("URL_%1").arg(i))); if(!u.isEmpty() && i != configIndex_) { configN = QStringLiteral("_%1").arg(i); ConfigInfo ci; ci.cols = config.readEntry(QLatin1String("ColumnNames") + configN, QStringList()); ci.widths = config.readEntry(QLatin1String("ColumnWidths") + configN, QList()); ci.order = config.readEntry(QLatin1String("ColumnOrder") + configN, QList()); ci.prevSort = config.readEntry(QLatin1String("PrevSortColumn") + configN, 0); ci.prev2Sort = config.readEntry(QLatin1String("Prev2SortColumn") + configN, 0); ci.sortOrder = config.readEntry(QLatin1String("SortOrder") + configN, static_cast(Qt::AscendingOrder)); info.append(ci); } } // subtract one since we're writing the current settings, too int limit = qMin(info.count(), Config::maxCustomURLSettings()-1); for(int i = 0; i < limit; ++i) { // starts at one since the current config will be written below configN = QStringLiteral("_%1").arg(i+1); config.writeEntry(QLatin1String("ColumnNames") + configN, info[i].cols); config.writeEntry(QLatin1String("ColumnWidths") + configN, info[i].widths); config.writeEntry(QLatin1String("ColumnOrder") + configN, info[i].order); config.writeEntry(QLatin1String("PrevSortColumn") + configN, info[i].prevSort); config.writeEntry(QLatin1String("Prev2SortColumn") + configN, info[i].prev2Sort); config.writeEntry(QLatin1String("SortOrder") + configN, info[i].sortOrder); // legacy entry item config.deleteEntry(QLatin1String("ColumnState") + configN); } configN = QStringLiteral("_0"); } QStringList colNames; QList widths, order; for(int ncol = 0; ncol < header()->count(); ++ncol) { // ignore hidden columns if(!isColumnHidden(ncol)) { colNames << columnFieldName(ncol); widths << columnWidth(ncol); order << header()->visualIndex(ncol); } } config.writeEntry(QLatin1String("ColumnNames") + configN, colNames); config.writeEntry(QLatin1String("ColumnWidths") + configN, widths); config.writeEntry(QLatin1String("ColumnOrder") + configN, order); const int sortCol1 = sortModel()->sortColumn(); const int sortCol2 = sortModel()->secondarySortColumn(); const int sortCol3 = sortModel()->tertiarySortColumn(); const int sortOrder = static_cast(sortModel()->sortOrder()); config.writeEntry(QLatin1String("SortColumn") + configN, sortCol1); config.writeEntry(QLatin1String("PrevSortColumn") + configN, sortCol2); config.writeEntry(QLatin1String("Prev2SortColumn") + configN, sortCol3); config.writeEntry(QLatin1String("SortOrder") + configN, sortOrder); // remove old entry item config.deleteEntry(QLatin1String("ColumnState") + configN); } QString DetailedListView::sortColumnTitle1() const { return model()->headerData(header()->sortIndicatorSection(), Qt::Horizontal).toString(); } QString DetailedListView::sortColumnTitle2() const { return model()->headerData(sortModel()->secondarySortColumn(), Qt::Horizontal).toString(); } QString DetailedListView::sortColumnTitle3() const { return model()->headerData(sortModel()->tertiarySortColumn(), Qt::Horizontal).toString(); } QStringList DetailedListView::visibleColumns() const { // we want the visual order, so use a QMap and sort by visualIndex QMap titleMap; for(int i = 0; i < header()->count(); ++i) { if(!isColumnHidden(i)) { titleMap.insert(header()->visualIndex(i), model()->headerData(i, Qt::Horizontal).toString()); } } return titleMap.values(); } // can't be const Tellico::Data::EntryList DetailedListView::visibleEntries() { // We could just return the full collection entry list if the filter is 0 // but printing depends on the sorted order Data::EntryList entries; for(int i = 0; i < model()->rowCount(); ++i) { Data::EntryPtr tmp = model()->data(model()->index(i, 0), EntryPtrRole).value(); if(tmp) { entries += tmp; } } return entries; } void DetailedListView::selectAllVisible() { QModelIndex topLeft = model()->index(0, 0); QModelIndex bottomRight = model()->index(model()->rowCount()-1, model()->columnCount()-1); QItemSelection selection(topLeft, bottomRight); selectionModel()->select(selection, QItemSelectionModel::Select); } int DetailedListView::visibleItems() const { return model()->rowCount(); } void DetailedListView::resetEntryStatus() { sourceModel()->clearSaveState(); } void DetailedListView::updateHeaderMenu() { // we only want to update the menu when the header count and model count agree if(model()->columnCount() != header()->count()) { myDebug() << "column counts disagree"; return; } m_headerMenu->clear(); m_headerMenu->addSection(i18n("View Columns")); m_columnMenu->clear(); for(int ncol = 0; ncol < header()->count(); ++ncol) { Data::FieldPtr field = model()->headerData(ncol, Qt::Horizontal, FieldPtrRole).value(); if(field && (field->type() == Data::Field::Table || field->type() == Data::Field::Para)) { continue; } QAction* act = m_columnMenu->addAction(model()->headerData(ncol, Qt::Horizontal).toString()); act->setData(ncol); act->setCheckable(true); act->setChecked(!isColumnHidden(ncol)); } QAction* columnAction = m_headerMenu->addMenu(m_columnMenu); columnAction->setText(i18nc("Noun, Menu name", "Columns")); columnAction->setIcon(QIcon::fromTheme(QStringLiteral("view-file-columns"))); QAction* actHideThis = m_headerMenu->addAction(i18n("Hide This Column")); connect(actHideThis, &QAction::triggered, this, &DetailedListView::hideCurrentColumn); QAction* actResize = m_headerMenu->addAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), i18n("Resize to Content")); connect(actResize, &QAction::triggered, this, &DetailedListView::resizeColumnsToContents); m_headerMenu->addSeparator(); QAction* actShowAll = m_headerMenu->addAction(i18n("Show All Columns")); connect(actShowAll, &QAction::triggered, this, &DetailedListView::showAllColumns); QAction* actHideAll = m_headerMenu->addAction(i18n("Hide All Columns")); connect(actHideAll, &QAction::triggered, this, &DetailedListView::hideAllColumns); } void DetailedListView::updateColumnDelegates() { for(int ncol = 0; ncol < header()->count(); ++ncol) { Data::FieldPtr field = model()->headerData(ncol, Qt::Horizontal, FieldPtrRole).value(); if(field && field->type() == Data::Field::Rating) { /// if we're not using the overall delegate, delete the delegate since we're setting a new on if(itemDelegateForColumn(ncol) != itemDelegate()) { delete itemDelegateForColumn(ncol); } RatingDelegate* delegate = new RatingDelegate(this); bool ok; // not used delegate->setMaxRating(Tellico::toUInt(field->property(QStringLiteral("maximum")), &ok)); setItemDelegateForColumn(ncol, delegate); } else { // reset column delegate to overall delegate setItemDelegateForColumn(ncol, itemDelegate()); } } } void DetailedListView::slotRefreshImages() { sourceModel()->setImagesAreAvailable(true); } void DetailedListView::adjustColumnWidths() { // this function is called when a column is shown // reduce all visible columns to their size hint, if they are wider than that for(int ncol = 0; ncol < header()->count(); ++ncol) { if(!isColumnHidden(ncol)) { const int width = sizeHintForColumn(ncol); if(columnWidth(ncol) > width) { resizeColumnToContents(ncol); } } } } void DetailedListView::resizeColumnsToContents() { for(int ncol = 0; ncol < header()->count(); ++ncol) { if(!isColumnHidden(ncol)) { resizeColumnToContents(ncol); } } } void DetailedListView::hideNewColumn(const QModelIndex& index_, int start_, int end_) { Q_UNUSED(index_); for(int ncol = start_; ncol <= end_; ++ncol) { hideColumn(ncol); } updateHeaderMenu(); // make sure to update checkable actions } void DetailedListView::checkHeader() { // the header disappears if all columns are hidden, so if the user hides all // columns, we turn around and show the title // // normally, I would expect a check like header()->count() == header()->hiddenSectionCount() // to tell me if all sections are hidden, but it often doesn't work, with the hiddenSectionCount() // being greater than count()! From testing, if the sizeHint() width is 0, then the header is hidden if(!header()->sizeHint().isEmpty()) { return; } // find title action in menu and activate it QAction* action = nullptr; QAction* fallbackAction = nullptr; foreach(QAction* tryAction, m_columnMenu->actions()) { const int ncol = tryAction->data().toInt(); if(ncol > -1 && columnFieldName(ncol) == QLatin1String("title")) { action = tryAction; break; } else if(ncol > -1 && !fallbackAction) { fallbackAction = tryAction; } } if(!action) { action = fallbackAction; } if(action) { action->setChecked(true); const int col = action->data().toInt(); // calling slotColumnMenuActivated() would be infinite loop setColumnHidden(col, false); resizeColumnToContents(col); } else { myDebug() << "found no action to show, still empty header!"; } } QString DetailedListView::columnFieldName(int ncol_) const { Data::FieldPtr field = model()->headerData(ncol_, Qt::Horizontal, FieldPtrRole).value(); return field ? field->name() : QString(); } diff --git a/src/gui/datewidget.cpp b/src/gui/datewidget.cpp index 45d85e57..e6502bb0 100644 --- a/src/gui/datewidget.cpp +++ b/src/gui/datewidget.cpp @@ -1,330 +1,329 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ // this class borrows heavily from kdateedit.h in the kdepim module // which is Copyright (c) 2002 Cornelius Schumacher // and published under the LGPL #include "datewidget.h" #include "spinbox.h" #include #include -#include #include #include #include #include #include #include #include #include #include using Tellico::GUI::DateWidget; class DateWidget::DatePickerAction : public QWidgetAction { Q_OBJECT public: DatePickerAction( KDatePicker *widget, QObject *parent ) : QWidgetAction( parent ), mDatePicker( widget ), mOriginalParent( widget->parentWidget() ) { } protected: QWidget *createWidget( QWidget *parent ) Q_DECL_OVERRIDE { mDatePicker->setParent( parent ); return mDatePicker; } void deleteWidget( QWidget *widget ) Q_DECL_OVERRIDE { if ( widget != mDatePicker ) { return; } mDatePicker->setParent( mOriginalParent ); } private: KDatePicker *mDatePicker; QWidget *mOriginalParent; }; DateWidget::DateWidget(QWidget* parent_) : QWidget(parent_) { QBoxLayout* l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); // 0 allows empty value m_daySpin = new SpinBox(0, 31, this); l->addWidget(m_daySpin, 1); l->setStretchFactor(m_daySpin, 1); m_monthCombo = new KComboBox(false, this); l->addWidget(m_monthCombo, 1); l->setStretchFactor(m_monthCombo, 1); // allow empty item m_monthCombo->addItem(QString()); for(int i = 1; ; ++i) { QString str = QDate::longMonthName(i); if(str.isEmpty()) { break; } m_monthCombo->addItem(str); } m_yearSpin = new SpinBox(QDate::fromJulianDay(0).year(), 9999, this); l->addWidget(m_yearSpin, 1); l->setStretchFactor(m_yearSpin, 1); void (SpinBox::* valueChangedInt)(int) = &SpinBox::valueChanged; void (KComboBox::* activatedInt)(int) = &KComboBox::activated; connect(m_daySpin, valueChangedInt, this, &DateWidget::slotDateChanged); connect(m_monthCombo, activatedInt, this, &DateWidget::slotDateChanged); connect(m_yearSpin, valueChangedInt, this, &DateWidget::slotDateChanged); m_dateButton = new QPushButton(this); m_dateButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-calendar"))); connect(m_dateButton, &QAbstractButton::clicked, this, &DateWidget::slotShowPicker); l->addWidget(m_dateButton, 0); m_menu = new QMenu(this); m_menu->hide(); m_picker = new KDatePicker(m_menu); m_picker->setCloseButton(false); connect(m_picker, &KDatePicker::dateEntered, this, &DateWidget::slotDateEntered); connect(m_picker, &KDatePicker::dateSelected, this, &DateWidget::slotDateSelected); m_menu->addAction(new DatePickerAction(m_picker, m_menu)); } DateWidget::~DateWidget() { } void DateWidget::slotDateChanged() { int day = m_daySpin->value(); day = qMin(qMax(day, m_daySpin->minimum()), m_daySpin->maximum()); int m = m_monthCombo->currentIndex(); m = qMin(qMax(m, 0), m_monthCombo->count()-1); int y = m_yearSpin->value(); y = qMin(qMax(y, m_yearSpin->minimum()), m_yearSpin->maximum()); // if all are valid, set this date if(day > m_daySpin->minimum() && m > 0 && y > m_yearSpin->minimum()) { QDate d(y, m, day); setDate(d); } emit signalModified(); } QDate DateWidget::date() const { // possible for either day, month, or year to be empty // in which case a null date is returned int day = m_daySpin->value(); // min value is the empty one if(day == m_daySpin->minimum()) { return QDate(); } int month = m_monthCombo->currentIndex(); if(month == 0) { return QDate(); } int year = m_yearSpin->value(); if(year == m_yearSpin->minimum()) { return QDate(); } return QDate(year, month, day); } QString DateWidget::text() const { // possible for either day, month, or year to be empty // but not all three bool empty = true; // format is "year-month-day" QString s; if(m_yearSpin->value() > m_yearSpin->minimum()) { s += QString::number(m_yearSpin->value()); empty = false; } s += QLatin1Char('-'); // first item is empty if(m_monthCombo->currentIndex() > 0) { // zero-pad to two digits if(m_monthCombo->currentIndex() < 10) { s += QLatin1Char('0'); } s += QString::number(m_monthCombo->currentIndex()); empty = false; } s += QLatin1Char('-'); if(m_daySpin->value() > m_daySpin->minimum()) { // zero-pad to two digits if(m_daySpin->value() < 10) { s += QLatin1Char('0'); } s += QString::number(m_daySpin->value()); empty = false; } return empty ? QString() : s; } void DateWidget::setDate(QDate date_) { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); m_daySpin->setMaximum(date_.daysInMonth()); m_daySpin->setValue(date_.day()); m_monthCombo->setCurrentIndex(date_.month()); // don't subtract 1 since there's the blank first item m_yearSpin->setValue(date_.year()); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); } void DateWidget::setDate(const QString& date_) { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); QStringList s = date_.split(QLatin1Char('-')); bool ok = true; int y = s.count() > 0 ? s[0].toInt(&ok) : m_yearSpin->minimum(); if(!ok) { y = m_yearSpin->minimum(); ok = true; } y = qMin(qMax(y, m_yearSpin->minimum()), m_yearSpin->maximum()); m_yearSpin->setValue(y); int m = s.count() > 1 ? s[1].toInt(&ok) : 0; if(!ok) { m = 0; ok = true; } m = qMin(qMax(m, 0), m_monthCombo->count()-1); m_monthCombo->setCurrentIndex(m); // need to update number of days in month // for now set date to 1 QDate date(y, (m == 0 ? 1 : m), 1); m_daySpin->blockSignals(true); m_daySpin->setMaximum(date.daysInMonth()); m_daySpin->blockSignals(false); int day = s.count() > 2 ? s[2].toInt(&ok) : m_daySpin->minimum(); if(!ok) { day = m_daySpin->minimum(); } day = qMin(qMax(day, m_daySpin->minimum()), m_daySpin->maximum()); m_daySpin->setValue(day); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); // if all are valid, set this date if(day > m_daySpin->minimum() && m > 0 && y > m_yearSpin->minimum()) { QDate d(y, m, day); m_picker->blockSignals(true); m_picker->setDate(d); m_picker->blockSignals(false); } } void DateWidget::clear() { m_daySpin->blockSignals(true); m_monthCombo->blockSignals(true); m_yearSpin->blockSignals(true); m_picker->blockSignals(true); m_daySpin->setValue(m_daySpin->minimum()); m_monthCombo->setCurrentIndex(0); m_yearSpin->setValue(m_yearSpin->minimum()); m_picker->setDate(QDate::currentDate()); m_daySpin->blockSignals(false); m_monthCombo->blockSignals(false); m_yearSpin->blockSignals(false); m_picker->blockSignals(false); } void DateWidget::slotShowPicker() { QRect desk = QApplication::desktop()->screenGeometry(this); QPoint popupPoint = mapToGlobal(QPoint(0, 0)); int dateFrameHeight = m_menu->sizeHint().height(); if(popupPoint.y() + height() + dateFrameHeight > desk.bottom()) { popupPoint.setY(popupPoint.y() - dateFrameHeight); } else { popupPoint.setY(popupPoint.y() + height()); } int dateFrameWidth = m_menu->sizeHint().width(); if(popupPoint.x() + width() > desk.right()) { popupPoint.setX(desk.right() - dateFrameWidth); } else { popupPoint.setX(popupPoint.x() + width() - dateFrameWidth); } if(popupPoint.x() < desk.left()) { popupPoint.setX( desk.left()); } if(popupPoint.y() < desk.top()) { popupPoint.setY(desk.top()); } QDate d = date(); if(d.isValid()) { m_picker->setDate(d); } m_menu->popup(popupPoint); } void DateWidget::slotDateSelected(QDate date_) { if(date_.isValid()) { setDate(date_); emit signalModified(); m_menu->hide(); } } void DateWidget::slotDateEntered(QDate date_) { if(date_.isValid()) { setDate(date_); emit signalModified(); } } #include "datewidget.moc" diff --git a/src/importdialog.cpp b/src/importdialog.cpp index b989a35c..2c4d2798 100644 --- a/src/importdialog.cpp +++ b/src/importdialog.cpp @@ -1,482 +1,490 @@ /*************************************************************************** Copyright (C) 2003-2014 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "importdialog.h" #include "document.h" #include "tellico_debug.h" #include "collection.h" #include "progressmanager.h" #include "utils/guiproxy.h" #include "translators/importer.h" #include "translators/tellicoimporter.h" #include "translators/bibteximporter.h" #include "translators/bibtexmlimporter.h" #include "translators/csvimporter.h" #include "translators/xsltimporter.h" #include "translators/audiofileimporter.h" #include "translators/alexandriaimporter.h" #include "translators/freedbimporter.h" #include "translators/risimporter.h" #include "translators/gcstarimporter.h" #include "translators/filelistingimporter.h" #include "translators/amcimporter.h" #include "translators/griffithimporter.h" #include "translators/pdfimporter.h" #include "translators/referencerimporter.h" #include "translators/deliciousimporter.h" #include "translators/goodreadsimporter.h" #include "translators/ciwimporter.h" #include "translators/vinoxmlimporter.h" #include "translators/boardgamegeekimporter.h" +#include "translators/librarythingimporter.h" #include "utils/datafileregistry.h" #include #include #include #include #include #include #include #include #include #include using Tellico::ImportDialog; ImportDialog::ImportDialog(Tellico::Import::Format format_, const QList& urls_, QWidget* parent_) : QDialog(parent_), m_importer(importer(format_, urls_)) { setModal(true); setWindowTitle(i18n("Import Options")); QVBoxLayout* mainLayout = new QVBoxLayout(); setLayout(mainLayout); QWidget* widget = new QWidget(this); mainLayout->addWidget(widget); QVBoxLayout* topLayout = new QVBoxLayout(widget); QGroupBox* groupBox = new QGroupBox(i18n("Import Options"), widget); QVBoxLayout* vlay = new QVBoxLayout(groupBox); topLayout->addWidget(groupBox, 0); m_radioReplace = new QRadioButton(i18n("&Replace current collection"), groupBox); m_radioReplace->setWhatsThis(i18n("Replace the current collection with the contents " "of the imported file.")); m_radioAppend = new QRadioButton(i18n("A&ppend to current collection"), groupBox); m_radioAppend->setWhatsThis(i18n("Append the contents of the imported file to the " "current collection. This is only possible when the " "collection types match.")); m_radioMerge = new QRadioButton(i18n("&Merge with current collection"), groupBox); m_radioMerge->setWhatsThis(i18n("Merge the contents of the imported file to the " "current collection. This is only possible when the " "collection types match. Entries must match exactly " "in order to be merged.")); if(m_importer->canImport(Data::Document::self()->collection()->type())) { // append by default? m_radioAppend->setChecked(true); } else { m_radioReplace->setChecked(true); m_radioAppend->setEnabled(false); m_radioMerge->setEnabled(false); } vlay->addWidget(m_radioReplace); vlay->addWidget(m_radioAppend); vlay->addWidget(m_radioMerge); m_buttonGroup = new QButtonGroup(widget); m_buttonGroup->addButton(m_radioReplace, Import::Replace); m_buttonGroup->addButton(m_radioAppend, Import::Append); m_buttonGroup->addButton(m_radioMerge, Import::Merge); QWidget* w = m_importer->widget(widget); // m_importer->readOptions(KSharedConfig::openConfig()); if(w) { w->layout()->setMargin(0); topLayout->addWidget(w, 0); } connect(m_buttonGroup, static_cast(&QButtonGroup::buttonClicked), m_importer, &Tellico::Import::Importer::slotActionChanged); topLayout->addStretch(); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton* okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(okButton, &QPushButton::clicked, this, &ImportDialog::slotOk); connect(buttonBox, &QDialogButtonBox::accepted, this, &ImportDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &ImportDialog::reject); KGuiItem ok = KStandardGuiItem::ok(); ok.setText(i18n("&Import")); KGuiItem::assign(okButton, ok); // want to grab default button action, too // since the importer might do something with widgets, don't just call it, do it after layout is done QTimer::singleShot(0, this, &ImportDialog::slotUpdateAction); } ImportDialog::~ImportDialog() { delete m_importer; m_importer = nullptr; } Tellico::Data::CollPtr ImportDialog::collection() { if(m_importer && !m_coll) { ProgressItem& item = ProgressManager::self()->newProgressItem(m_importer, m_importer->progressLabel(), true); connect(m_importer, &Import::Importer::signalTotalSteps, ProgressManager::self(), &ProgressManager::setTotalSteps); connect(m_importer, &Import::Importer::signalProgress, ProgressManager::self(), &ProgressManager::setProgress); connect(&item, &ProgressItem::signalCancelled, m_importer, &Import::Importer::slotCancel); ProgressItem::Done done(m_importer); m_coll = m_importer->collection(); } return m_coll; } QString ImportDialog::statusMessage() const { return m_importer ? m_importer->statusMessage() : QString(); } Tellico::Import::Action ImportDialog::action() const { if(m_radioReplace->isChecked()) { return Import::Replace; } else if(m_radioAppend->isChecked()) { return Import::Append; } else { return Import::Merge; } } // static Tellico::Import::Importer* ImportDialog::importer(Tellico::Import::Format format_, const QList& urls_) { #define CHECK_SIZE if(urls_.size() > 1) myWarning() << "only importing first URL" QUrl firstURL = urls_.isEmpty() ? QUrl() : urls_[0]; Import::Importer* importer = nullptr; switch(format_) { case Import::TellicoXML: CHECK_SIZE; importer = new Import::TellicoImporter(firstURL); break; case Import::Bibtex: importer = new Import::BibtexImporter(urls_); break; case Import::Bibtexml: CHECK_SIZE; importer = new Import::BibtexmlImporter(firstURL); break; case Import::CSV: CHECK_SIZE; importer = new Import::CSVImporter(firstURL); break; case Import::XSLT: CHECK_SIZE; importer = new Import::XSLTImporter(firstURL); break; case Import::MODS: CHECK_SIZE; importer = new Import::XSLTImporter(firstURL); { QString xsltFile = DataFileRegistry::self()->locate(QStringLiteral("mods2tellico.xsl")); if(!xsltFile.isEmpty()) { QUrl u = QUrl::fromLocalFile(xsltFile); static_cast(importer)->setXSLTURL(u); } else { myWarning() << "unable to find mods2tellico.xml!"; } } break; case Import::AudioFile: CHECK_SIZE; importer = new Import::AudioFileImporter(firstURL); break; case Import::Alexandria: CHECK_SIZE; importer = new Import::AlexandriaImporter(); break; case Import::FreeDB: CHECK_SIZE; importer = new Import::FreeDBImporter(); break; case Import::RIS: importer = new Import::RISImporter(urls_); break; case Import::GCstar: CHECK_SIZE; importer = new Import::GCstarImporter(firstURL); break; case Import::FileListing: CHECK_SIZE; importer = new Import::FileListingImporter(firstURL); break; case Import::AMC: CHECK_SIZE; importer = new Import::AMCImporter(firstURL); break; case Import::Griffith: CHECK_SIZE; importer = new Import::GriffithImporter(firstURL); break; case Import::PDF: importer = new Import::PDFImporter(urls_); break; case Import::Referencer: CHECK_SIZE; importer = new Import::ReferencerImporter(firstURL); break; case Import::Delicious: CHECK_SIZE; importer = new Import::DeliciousImporter(firstURL); break; case Import::Goodreads: CHECK_SIZE; importer = new Import::GoodreadsImporter(); break; case Import::GRS1: myDebug() << "GRS1 not implemented"; break; case Import::CIW: importer = new Import::CIWImporter(urls_); break; case Import::VinoXML: CHECK_SIZE; importer = new Import::VinoXMLImporter(firstURL); break; case Import::BoardGameGeek: CHECK_SIZE; importer = new Import::BoardGameGeekImporter(); break; + + case Import::LibraryThing: + CHECK_SIZE; + importer = new Import::LibraryThingImporter(); + break; } if(!importer) { myWarning() << "importer not created!"; return nullptr; } importer->setCurrentCollection(Data::Document::self()->collection()); return importer; #undef CHECK_SIZE } //static Tellico::Import::Importer* ImportDialog::importerForText(Tellico::Import::Format format_, const QString& text_) { Import::Importer* importer = nullptr; switch(format_) { case Import::Bibtex: importer = new Import::BibtexImporter(text_); break; default: break; } if(!importer) { myWarning() << "importer not created!"; return nullptr; } importer->setCurrentCollection(Data::Document::self()->collection()); return importer; } // static QString ImportDialog::fileFilter(Tellico::Import::Format format_) { QString text; switch(format_) { case Import::TellicoXML: text = i18n("Tellico Files") + QLatin1String(" (*.tc *.bc)") + QLatin1String(";;"); text += i18n("XML Files") + QLatin1String(" (*.xml)") + QLatin1String(";;"); break; case Import::Bibtex: text = i18n("Bibtex Files") + QLatin1String(" (*.bib)") + QLatin1String(";;"); break; case Import::CSV: text = i18n("CSV Files") + QLatin1String(" (*.csv)") + QLatin1String(";;"); break; case Import::Bibtexml: case Import::XSLT: case Import::MODS: case Import::Delicious: case Import::Griffith: text = i18n("XML Files") + QLatin1String(" (*.xml)") + QLatin1String(";;"); break; case Import::RIS: text = i18n("RIS Files") + QLatin1String(" (*.ris)") + QLatin1String(";;"); break; case Import::GCstar: text = i18n("GCstar Data Files") + QLatin1String(" (*.gcs *.gcf)") + QLatin1String(";;"); break; case Import::AMC: text = i18n("AMC Data Files") + QLatin1String(" (*.amc)") + QLatin1String(";;"); break; case Import::PDF: text = i18n("PDF Files") + QLatin1String(" (*.pdf)") + QLatin1String(";;"); break; case Import::Referencer: text = i18n("Referencer Files") + QLatin1String(" (*.reflib)") + QLatin1String(";;"); break; case Import::CIW: text = i18n("CIW Files") + QLatin1String(" (*.ciw)") + QLatin1String(";;"); break; case Import::VinoXML: text = i18n("VinoXML Data Files") + QLatin1String(" (*.vinoxml)") + QLatin1String(";;"); text += i18n("XML Files") + QLatin1String(" (*.xml)") + QLatin1String(";;"); break; case Import::AudioFile: case Import::Alexandria: case Import::FreeDB: case Import::FileListing: case Import::GRS1: case Import::Goodreads: case Import::BoardGameGeek: + case Import::LibraryThing: break; } return text + i18n("All Files") + QLatin1String(" (*)"); } // audio files are imported by directory // alexandria is a defined location, as is freedb // all others are files Tellico::Import::Target ImportDialog::importTarget(Tellico::Import::Format format_) { switch(format_) { case Import::AudioFile: case Import::FileListing: return Import::Dir; case Import::Alexandria: case Import::FreeDB: case Import::Goodreads: case Import::BoardGameGeek: + case Import::LibraryThing: return Import::None; default: return Import::File; } } QString ImportDialog::startDir(Tellico::Import::Format format_) { if(format_ == Import::GCstar) { QDir dir = QDir::home(); // able to cd if exists and readable if(dir.cd(QStringLiteral(".local/share/gcstar/"))) { return dir.absolutePath(); } } return QString(); } void ImportDialog::slotOk() { // some importers, like the CSV importer, can validate their settings if(!m_importer || m_importer->validImport()) { accept(); } else { myLog() << "not a valid import"; } } void ImportDialog::slotUpdateAction() { m_importer->slotActionChanged(m_buttonGroup->checkedId()); } // static Tellico::Data::CollPtr ImportDialog::importURL(Tellico::Import::Format format_, const QUrl& url_) { Import::Importer* imp = importer(format_, QList() << url_); if(!imp) { return Data::CollPtr(); } ProgressItem& item = ProgressManager::self()->newProgressItem(imp, imp->progressLabel(), true); connect(imp, &Import::Importer::signalTotalSteps, ProgressManager::self(), &ProgressManager::setTotalSteps); connect(imp, &Import::Importer::signalProgress, ProgressManager::self(), &ProgressManager::setProgress); connect(&item, &ProgressItem::signalCancelled, imp, &Import::Importer::slotCancel); ProgressItem::Done done(imp); Data::CollPtr c = imp->collection(); if(!c && !imp->statusMessage().isEmpty()) { GUI::Proxy::sorry(imp->statusMessage()); } delete imp; return c; } Tellico::Data::CollPtr ImportDialog::importText(Tellico::Import::Format format_, const QString& text_) { Import::Importer* imp = importerForText(format_, text_); if(!imp) { return Data::CollPtr(); } // the Done() constructor crashes for some reason, so just don't use it // 5/18/19 -> uncomment the progress Done again ProgressItem& item = ProgressManager::self()->newProgressItem(imp, imp->progressLabel(), true); connect(imp, &Import::Importer::signalTotalSteps, ProgressManager::self(), &ProgressManager::setTotalSteps); connect(imp, &Import::Importer::signalProgress, ProgressManager::self(), &ProgressManager::setProgress); connect(&item, &ProgressItem::signalCancelled, imp, &Import::Importer::slotCancel); ProgressItem::Done done(imp); Data::CollPtr c = imp->collection(); if(!c && !imp->statusMessage().isEmpty()) { GUI::Proxy::sorry(imp->statusMessage()); } delete imp; return c; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8250c562..051afc9a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,2388 +1,2391 @@ /*************************************************************************** Copyright (C) 2001-2014 Robby Stephenson Copyright (C) 2011 Pedro Miguel Carvalho ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "mainwindow.h" #include "tellico_kernel.h" #include "document.h" #include "detailedlistview.h" #include "entryeditdialog.h" #include "groupview.h" #include "viewstack.h" #include "collection.h" #include "collectionfactory.h" #include "entry.h" #include "configdialog.h" #include "filter.h" #include "filterdialog.h" #include "collectionfieldsdialog.h" #include "controller.h" #include "importdialog.h" #include "exportdialog.h" #include "core/filehandler.h" // needed so static mainWindow variable can be set #include "translators/htmlexporter.h" // for printing #include "entryview.h" #include "entryiconview.h" #include "images/imagefactory.h" // needed so tmp files can get cleaned #include "collections/collectioninitializer.h" #include "collections/bibtexcollection.h" // needed for bibtex string macro dialog #include "utils/bibtexhandler.h" // needed for bibtex options #include "utils/datafileregistry.h" #include "fetchdialog.h" #include "reportdialog.h" #include "bibtexkeydialog.h" #include "core/tellico_strings.h" #include "filterview.h" #include "loanview.h" #include "fetch/fetchmanager.h" #include "fetch/fetcherinitializer.h" #include "cite/actionmanager.h" #include "config/tellico_config.h" #include "core/netaccess.h" #include "dbusinterface.h" #include "models/models.h" #include "models/entryiconmodel.h" #include "models/entryselectionmodel.h" #include "newstuff/manager.h" #include "gui/drophandler.h" #include "gui/stringmapdialog.h" #include "gui/lineedit.h" #include "gui/statusbar.h" #include "gui/tabwidget.h" #include "gui/dockwidget.h" #include "utils/cursorsaver.h" #include "utils/guiproxy.h" #include "tellico_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // needed for copy, cut, paste slots #include #include #include #include #include #include namespace { static const int MAIN_WINDOW_MIN_WIDTH = 600; static const int MAX_IMAGES_WARN_PERFORMANCE = 200; QIcon mimeIcon(const char* s) { QMimeDatabase db; QMimeType ptr = db.mimeTypeForName(QLatin1String(s)); if(!ptr.isValid()) { myDebug() << "*** no icon for" << s; } return ptr.isValid() ? QIcon::fromTheme(ptr.iconName()) : QIcon(); } QIcon mimeIcon(const char* s1, const char* s2) { QMimeDatabase db; QMimeType ptr = db.mimeTypeForName(QLatin1String(s1)); if(!ptr.isValid()) { ptr = db.mimeTypeForName(QLatin1String(s2)); if(!ptr.isValid()) { myDebug() << "*** no icon for" << s1 << "or" << s2; } } return ptr.isValid() ? QIcon::fromTheme(ptr.iconName()) : QIcon(); } } using namespace Tellico; using Tellico::MainWindow; MainWindow::MainWindow(QWidget* parent_/*=0*/) : KXmlGuiWindow(parent_), m_updateAll(nullptr), m_statusBar(nullptr), m_editDialog(nullptr), m_groupView(nullptr), m_filterView(nullptr), m_loanView(nullptr), m_configDlg(nullptr), m_filterDlg(nullptr), m_collFieldsDlg(nullptr), m_stringMacroDlg(nullptr), m_bibtexKeyDlg(nullptr), m_fetchDlg(nullptr), m_reportDlg(nullptr), m_queuedFilters(0), m_initialized(false), m_newDocument(true), m_dontQueueFilter(false), m_savingImageLocationChange(false) { Controller::init(this); // the only time this is ever called! // has to be after controller init Kernel::init(this); // the only time this is ever called! GUI::Proxy::setMainWidget(this); setWindowIcon(QIcon::fromTheme(QStringLiteral("tellico"), QIcon(QLatin1String(":/icons/tellico")))); // initialize the status bar and progress bar initStatusBar(); // initialize all the collection types // which must be done before the document is created CollectionInitializer initCollections; // register all the fetcher types Fetch::FetcherInitializer initFetchers; // create a document, which also creates an empty book collection // must be done before the different widgets are created initDocument(); // set up all the actions, some connect to the document, so this must be after initDocument() initActions(); // create the different widgets in the view, some widgets connect to actions, so must be after initActions() initView(); // The edit dialog is not created until after the main window is initialized, so it can be a child. // So don't make any connections, don't read options for it until initFileOpen readOptions(); setAcceptDrops(true); DropHandler* drophandler = new DropHandler(this); installEventFilter(drophandler); new ApplicationInterface(this); new CollectionInterface(this); MARK_LINE; QTimer::singleShot(0, this, &MainWindow::slotInit); } MainWindow::~MainWindow() { qDeleteAll(m_fetchActions); m_fetchActions.clear(); } void MainWindow::slotInit() { MARK; // if the edit dialog exists, we know we've already called this function if(m_editDialog) { return; } m_editDialog = new EntryEditDialog(this); Controller::self()->addObserver(m_editDialog); m_toggleEntryEditor->setChecked(Config::showEditWidget()); slotToggleEntryEditor(); m_lockLayout->setActive(Config::lockLayout()); initConnections(); connect(ImageFactory::self(), &ImageFactory::imageLocationMismatch, this, &MainWindow::slotImageLocationMismatch); // Init DBUS NewStuff::Manager::self(); } void MainWindow::initStatusBar() { MARK; m_statusBar = new Tellico::StatusBar(this); setStatusBar(m_statusBar); } void MainWindow::initActions() { MARK; /************************************************* * File->New menu *************************************************/ QSignalMapper* collectionMapper = new QSignalMapper(this); void (QSignalMapper::* mappedInt)(int) = &QSignalMapper::mapped; connect(collectionMapper, mappedInt, this, &MainWindow::slotFileNew); KActionMenu* fileNewMenu = new KActionMenu(i18n("New"), this); fileNewMenu->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); fileNewMenu->setToolTip(i18n("Create a new collection")); fileNewMenu->setDelayed(false); actionCollection()->addAction(QStringLiteral("file_new_collection"), fileNewMenu); QAction* action; void (QSignalMapper::* mapVoid)() = &QSignalMapper::map; #define COLL_ACTION(TYPE, NAME, TEXT, TIP, ICON) \ action = actionCollection()->addAction(QStringLiteral(NAME), collectionMapper, mapVoid); \ action->setText(TEXT); \ action->setToolTip(TIP); \ action->setIcon(QIcon(QStringLiteral(":/icons/" ICON))); \ fileNewMenu->addAction(action); \ collectionMapper->setMapping(action, Data::Collection::TYPE); COLL_ACTION(Book, "new_book_collection", i18n("New &Book Collection"), i18n("Create a new book collection"), "book"); COLL_ACTION(Bibtex, "new_bibtex_collection", i18n("New B&ibliography"), i18n("Create a new bibtex bibliography"), "bibtex"); COLL_ACTION(ComicBook, "new_comic_book_collection", i18n("New &Comic Book Collection"), i18n("Create a new comic book collection"), "comic"); COLL_ACTION(Video, "new_video_collection", i18n("New &Video Collection"), i18n("Create a new video collection"), "video"); COLL_ACTION(Album, "new_music_collection", i18n("New &Music Collection"), i18n("Create a new music collection"), "album"); COLL_ACTION(Coin, "new_coin_collection", i18n("New C&oin Collection"), i18n("Create a new coin collection"), "coin"); COLL_ACTION(Stamp, "new_stamp_collection", i18n("New &Stamp Collection"), i18n("Create a new stamp collection"), "stamp"); COLL_ACTION(Card, "new_card_collection", i18n("New C&ard Collection"), i18n("Create a new trading card collection"), "card"); COLL_ACTION(Wine, "new_wine_collection", i18n("New &Wine Collection"), i18n("Create a new wine collection"), "wine"); COLL_ACTION(Game, "new_game_collection", i18n("New &Game Collection"), i18n("Create a new game collection"), "game"); COLL_ACTION(BoardGame, "new_boardgame_collection", i18n("New Boa&rd Game Collection"), i18n("Create a new board game collection"), "boardgame"); COLL_ACTION(File, "new_file_catalog", i18n("New &File Catalog"), i18n("Create a new file catalog"), "file"); action = actionCollection()->addAction(QStringLiteral("new_custom_collection"), collectionMapper, mapVoid); action->setText(i18n("New C&ustom Collection")); action->setToolTip(i18n("Create a new custom collection")); action->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); fileNewMenu->addAction(action); collectionMapper->setMapping(action, Data::Collection::Base); #undef COLL_ACTION /************************************************* * File menu *************************************************/ action = KStandardAction::open(this, SLOT(slotFileOpen()), actionCollection()); action->setToolTip(i18n("Open an existing document")); m_fileOpenRecent = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(const QUrl&)), actionCollection()); m_fileOpenRecent->setToolTip(i18n("Open a recently used file")); m_fileSave = KStandardAction::save(this, SLOT(slotFileSave()), actionCollection()); m_fileSave->setToolTip(i18n("Save the document")); action = KStandardAction::saveAs(this, SLOT(slotFileSaveAs()), actionCollection()); action->setToolTip(i18n("Save the document as a different file...")); action = KStandardAction::print(this, SLOT(slotFilePrint()), actionCollection()); { KHTMLPart w; // KHTMLPart printing was broken in KDE until KHTML 5.16 const QString version = w.componentData().version(); const uint major = version.section(QLatin1Char('.'), 0, 0).toUInt(); const uint minor = version.section(QLatin1Char('.'), 1, 1).toUInt(); if(major == 5 && minor < 16) { myWarning() << "Printing is broken for KDE Frameworks < 5.16. Please upgrade"; action->setEnabled(false); } } action->setToolTip(i18n("Print the contents of the document...")); action = KStandardAction::quit(this, SLOT(slotFileQuit()), actionCollection()); action->setToolTip(i18n("Quit the application")); /**************** Import Menu ***************************/ QSignalMapper* importMapper = new QSignalMapper(this); connect(importMapper, mappedInt, this, &MainWindow::slotFileImport); KActionMenu* importMenu = new KActionMenu(i18n("&Import"), this); importMenu->setIcon(QIcon::fromTheme(QStringLiteral("document-import"))); importMenu->setToolTip(i18n("Import the collection data from other formats")); importMenu->setDelayed(false); actionCollection()->addAction(QStringLiteral("file_import"), importMenu); #define IMPORT_ACTION(TYPE, NAME, TEXT, TIP, ICON) \ action = actionCollection()->addAction(QStringLiteral(NAME), importMapper, mapVoid); \ action->setText(TEXT); \ action->setToolTip(TIP); \ action->setIcon(ICON); \ importMenu->addAction(action); \ importMapper->setMapping(action, TYPE); IMPORT_ACTION(Import::TellicoXML, "file_import_tellico", i18n("Import Tellico Data..."), i18n("Import another Tellico data file"), QIcon::fromTheme(QStringLiteral("tellico"), QIcon(QLatin1String(":/icons/tellico")))); IMPORT_ACTION(Import::CSV, "file_import_csv", i18n("Import CSV Data..."), i18n("Import a CSV file"), mimeIcon("text/csv", "text/x-csv")); IMPORT_ACTION(Import::MODS, "file_import_mods", i18n("Import MODS Data..."), i18n("Import a MODS data file"), mimeIcon("text/xml")); IMPORT_ACTION(Import::Alexandria, "file_import_alexandria", i18n("Import Alexandria Data..."), i18n("Import data from the Alexandria book collection manager"), QIcon::fromTheme(QStringLiteral("alexandria"), QIcon(QLatin1String(":/icons/alexandria")))); IMPORT_ACTION(Import::Delicious, "file_import_delicious", i18n("Import Delicious Library Data..."), i18n("Import data from Delicious Library"), QIcon::fromTheme(QStringLiteral("deliciouslibrary"), QIcon(QLatin1String(":/icons/deliciouslibrary")))); IMPORT_ACTION(Import::Referencer, "file_import_referencer", i18n("Import Referencer Data..."), i18n("Import data from Referencer"), QIcon::fromTheme(QStringLiteral("referencer"), QIcon(QLatin1String(":/icons/referencer")))); IMPORT_ACTION(Import::Bibtex, "file_import_bibtex", i18n("Import Bibtex Data..."), i18n("Import a bibtex bibliography file"), mimeIcon("text/x-bibtex")); IMPORT_ACTION(Import::Bibtexml, "file_import_bibtexml", i18n("Import Bibtexml Data..."), i18n("Import a Bibtexml bibliography file"), mimeIcon("text/xml")); IMPORT_ACTION(Import::RIS, "file_import_ris", i18n("Import RIS Data..."), i18n("Import an RIS reference file"), QIcon::fromTheme(QStringLiteral(":/icons/cite"))); IMPORT_ACTION(Import::Goodreads, "file_import_goodreads", i18n("Import Goodreads Collection..."), i18n("Import a collection from Goodreads.com"), QIcon::fromTheme(QStringLiteral(":/icons/goodreads"))); + IMPORT_ACTION(Import::LibraryThing, "file_import_librarything", i18n("Import LibraryThing Collection..."), + i18n("Import a collection from LibraryThing.com"), QIcon::fromTheme(QStringLiteral(":/icons/librarything"))); + IMPORT_ACTION(Import::PDF, "file_import_pdf", i18n("Import PDF File..."), i18n("Import a PDF file"), mimeIcon("application/pdf")); IMPORT_ACTION(Import::AudioFile, "file_import_audiofile", i18n("Import Audio File Metadata..."), i18n("Import meta-data from audio files"), mimeIcon("audio/mp3", "audio/x-mp3")); #ifndef HAVE_TAGLIB action->setEnabled(false); #endif IMPORT_ACTION(Import::FreeDB, "file_import_freedb", i18n("Import Audio CD Data..."), i18n("Import audio CD information"), mimeIcon("media/audiocd", "application/x-cda")); #if !defined (HAVE_KCDDB) && !defined (HAVE_KF5KCDDB) action->setEnabled(false); #endif IMPORT_ACTION(Import::GCstar, "file_import_gcstar", i18n("Import GCstar Data..."), i18n("Import a GCstar data file"), QIcon::fromTheme(QStringLiteral("gcstar"), QIcon(QLatin1String(":/icons/gcstar")))); IMPORT_ACTION(Import::Griffith, "file_import_griffith", i18n("Import Griffith Data..."), i18n("Import a Griffith database"), QIcon::fromTheme(QStringLiteral("griffith"), QIcon(QLatin1String(":/icons/griffith")))); IMPORT_ACTION(Import::AMC, "file_import_amc", i18n("Import Ant Movie Catalog Data..."), i18n("Import an Ant Movie Catalog data file"), QIcon::fromTheme(QStringLiteral("amc"), QIcon(QLatin1String(":/icons/amc")))); IMPORT_ACTION(Import::BoardGameGeek, "file_import_boardgamegeek", i18n("Import BoardGameGeek Collection..."), i18n("Import a collection from BoardGameGeek.com"), QIcon(QLatin1String(":/icons/boardgamegeek"))); IMPORT_ACTION(Import::VinoXML, "file_import_vinoxml", i18n("Import VinoXML..."), i18n("Import VinoXML data"), QIcon(QLatin1String(":/icons/vinoxml"))); IMPORT_ACTION(Import::FileListing, "file_import_filelisting", i18n("Import File Listing..."), i18n("Import information about files in a folder"), mimeIcon("inode/directory")); IMPORT_ACTION(Import::XSLT, "file_import_xslt", i18n("Import XSL Transform..."), i18n("Import using an XSL Transform"), mimeIcon("application/xslt+xml", "text/x-xslt")); #undef IMPORT_ACTION /**************** Export Menu ***************************/ QSignalMapper* exportMapper = new QSignalMapper(this); connect(exportMapper, mappedInt, this, &MainWindow::slotFileExport); KActionMenu* exportMenu = new KActionMenu(i18n("&Export"), this); exportMenu->setIcon(QIcon::fromTheme(QStringLiteral("document-export"))); exportMenu->setToolTip(i18n("Export the collection data to other formats")); exportMenu->setDelayed(false); actionCollection()->addAction(QStringLiteral("file_export"), exportMenu); #define EXPORT_ACTION(TYPE, NAME, TEXT, TIP, ICON) \ action = actionCollection()->addAction(QStringLiteral(NAME), exportMapper, mapVoid); \ action->setText(TEXT); \ action->setToolTip(TIP); \ action->setIcon(ICON); \ exportMenu->addAction(action); \ exportMapper->setMapping(action, TYPE); EXPORT_ACTION(Export::TellicoXML, "file_export_xml", i18n("Export to XML..."), i18n("Export to a Tellico XML file"), QIcon::fromTheme(QStringLiteral("tellico"), QIcon(QStringLiteral(":/icons/tellico")))); EXPORT_ACTION(Export::TellicoZip, "file_export_zip", i18n("Export to Zip..."), i18n("Export to a Tellico Zip file"), QIcon::fromTheme(QStringLiteral("tellico"), QIcon(QStringLiteral(":/icons/tellico")))); EXPORT_ACTION(Export::HTML, "file_export_html", i18n("Export to HTML..."), i18n("Export to an HTML file"), mimeIcon("text/html")); EXPORT_ACTION(Export::CSV, "file_export_csv", i18n("Export to CSV..."), i18n("Export to a comma-separated values file"), mimeIcon("text/csv", "text/x-csv")); EXPORT_ACTION(Export::Alexandria, "file_export_alexandria", i18n("Export to Alexandria..."), i18n("Export to an Alexandria library"), QIcon::fromTheme(QStringLiteral("alexandria"), QIcon(QStringLiteral(":/icons/alexandria")))); EXPORT_ACTION(Export::Bibtex, "file_export_bibtex", i18n("Export to Bibtex..."), i18n("Export to a bibtex file"), mimeIcon("text/x-bibtex")); EXPORT_ACTION(Export::Bibtexml, "file_export_bibtexml", i18n("Export to Bibtexml..."), i18n("Export to a Bibtexml file"), mimeIcon("text/xml")); EXPORT_ACTION(Export::ONIX, "file_export_onix", i18n("Export to ONIX..."), i18n("Export to an ONIX file"), mimeIcon("text/xml")); EXPORT_ACTION(Export::GCstar, "file_export_gcstar", i18n("Export to GCstar..."), i18n("Export to a GCstar data file"), QIcon::fromTheme(QStringLiteral("gcstar"), QIcon(QStringLiteral(":/icons/gcstar")))); EXPORT_ACTION(Export::XSLT, "file_export_xslt", i18n("Export XSL Transform..."), i18n("Export using an XSL Transform"), mimeIcon("application/xslt+xml", "text/x-xslt")); #undef EXPORT_ACTION /************************************************* * Edit menu *************************************************/ KStandardAction::undo(Kernel::self()->commandHistory(), SLOT(undo()), actionCollection()); KStandardAction::redo(Kernel::self()->commandHistory(), SLOT(undo()), actionCollection()); action = KStandardAction::cut(this, SLOT(slotEditCut()), actionCollection()); action->setToolTip(i18n("Cut the selected text and puts it in the clipboard")); action = KStandardAction::copy(this, SLOT(slotEditCopy()), actionCollection()); action->setToolTip(i18n("Copy the selected text to the clipboard")); action = KStandardAction::paste(this, SLOT(slotEditPaste()), actionCollection()); action->setToolTip(i18n("Paste the clipboard contents")); action = KStandardAction::selectAll(this, SLOT(slotEditSelectAll()), actionCollection()); action->setToolTip(i18n("Select all the entries in the collection")); action = KStandardAction::deselect(this, SLOT(slotEditDeselect()), actionCollection()); action->setToolTip(i18n("Deselect all the entries in the collection")); action = actionCollection()->addAction(QStringLiteral("edit_search_internet"), this, SLOT(slotShowFetchDialog())); action->setText(i18n("Internet Search...")); action->setIconText(i18n("Search")); // find a better word for this? action->setIcon(QIcon::fromTheme(QStringLiteral("tools-wizard"))); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::Key_I); action->setToolTip(i18n("Search the internet...")); action = actionCollection()->addAction(QStringLiteral("filter_dialog"), this, SLOT(slotShowFilterDialog())); action->setText(i18n("Advanced &Filter...")); action->setIconText(i18n("Filter")); action->setIcon(QIcon::fromTheme(QStringLiteral("view-filter"))); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::Key_J); action->setToolTip(i18n("Filter the collection")); /************************************************* * Collection menu *************************************************/ m_newEntry = actionCollection()->addAction(QStringLiteral("coll_new_entry"), this, SLOT(slotNewEntry())); m_newEntry->setText(i18n("&New Entry...")); m_newEntry->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); m_newEntry->setIconText(i18n("New")); actionCollection()->setDefaultShortcut(m_newEntry, Qt::CTRL + Qt::Key_N); m_newEntry->setToolTip(i18n("Create a new entry")); m_editEntry = actionCollection()->addAction(QStringLiteral("coll_edit_entry"), this, SLOT(slotShowEntryEditor())); m_editEntry->setText(i18n("&Edit Entry...")); m_editEntry->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); actionCollection()->setDefaultShortcut(m_editEntry, Qt::CTRL + Qt::Key_E); m_editEntry->setToolTip(i18n("Edit the selected entries")); m_copyEntry = actionCollection()->addAction(QStringLiteral("coll_copy_entry"), Controller::self(), SLOT(slotCopySelectedEntries())); m_copyEntry->setText(i18n("D&uplicate Entry")); m_copyEntry->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); actionCollection()->setDefaultShortcut(m_copyEntry, Qt::CTRL + Qt::Key_Y); m_copyEntry->setToolTip(i18n("Copy the selected entries")); m_deleteEntry = actionCollection()->addAction(QStringLiteral("coll_delete_entry"), Controller::self(), SLOT(slotDeleteSelectedEntries())); m_deleteEntry->setText(i18n("&Delete Entry")); m_deleteEntry->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); actionCollection()->setDefaultShortcut(m_deleteEntry, Qt::CTRL + Qt::Key_D); m_deleteEntry->setToolTip(i18n("Delete the selected entries")); m_mergeEntry = actionCollection()->addAction(QStringLiteral("coll_merge_entry"), Controller::self(), SLOT(slotMergeSelectedEntries())); m_mergeEntry->setText(i18n("&Merge Entries")); m_mergeEntry->setIcon(QIcon::fromTheme(QStringLiteral("document-import"))); // CTRL+G is ambiguous, pick another // actionCollection()->setDefaultShortcut(m_mergeEntry, Qt::CTRL + Qt::Key_G); m_mergeEntry->setToolTip(i18n("Merge the selected entries")); m_mergeEntry->setEnabled(false); // gets enabled when more than 1 entry is selected m_checkOutEntry = actionCollection()->addAction(QStringLiteral("coll_checkout"), Controller::self(), SLOT(slotCheckOut())); m_checkOutEntry->setText(i18n("Check-&out...")); m_checkOutEntry->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up-double"))); m_checkOutEntry->setToolTip(i18n("Check-out the selected items")); m_checkInEntry = actionCollection()->addAction(QStringLiteral("coll_checkin"), Controller::self(), SLOT(slotCheckIn())); m_checkInEntry->setText(i18n("Check-&in")); m_checkInEntry->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down-double"))); m_checkInEntry->setToolTip(i18n("Check-in the selected items")); action = actionCollection()->addAction(QStringLiteral("coll_rename_collection"), this, SLOT(slotRenameCollection())); action->setText(i18n("&Rename Collection...")); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::Key_R); action->setToolTip(i18n("Rename the collection")); action = actionCollection()->addAction(QStringLiteral("coll_fields"), this, SLOT(slotShowCollectionFieldsDialog())); action->setText(i18n("Collection &Fields...")); action->setIconText(i18n("Fields")); action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-other"))); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::Key_U); action->setToolTip(i18n("Modify the collection fields")); action = actionCollection()->addAction(QStringLiteral("coll_reports"), this, SLOT(slotShowReportDialog())); action->setText(i18n("&Generate Reports...")); action->setIconText(i18n("Reports")); action->setIcon(QIcon::fromTheme(QStringLiteral("text-rdf"))); action->setToolTip(i18n("Generate collection reports")); action = actionCollection()->addAction(QStringLiteral("coll_convert_bibliography"), this, SLOT(slotConvertToBibliography())); action->setText(i18n("Convert to &Bibliography")); action->setIcon(QIcon(QLatin1String(":/icons/bibtex"))); action->setToolTip(i18n("Convert a book collection to a bibliography")); action = actionCollection()->addAction(QStringLiteral("coll_string_macros"), this, SLOT(slotShowStringMacroDialog())); action->setText(i18n("String &Macros...")); action->setIcon(QIcon::fromTheme(QStringLiteral("view-list-text"))); action->setToolTip(i18n("Edit the bibtex string macros")); action = actionCollection()->addAction(QStringLiteral("coll_key_manager"), this, SLOT(slotShowBibtexKeyDialog())); action->setText(i18n("Check for Duplicate Keys...")); action->setIcon(mimeIcon("text/x-bibtex")); action->setToolTip(i18n("Check for duplicate citation keys")); QSignalMapper* citeMapper = new QSignalMapper(this); connect(citeMapper, mappedInt, this, &MainWindow::slotCiteEntry); action = actionCollection()->addAction(QStringLiteral("cite_clipboard"), citeMapper, mapVoid); action->setText(i18n("Copy Bibtex to Cli&pboard")); action->setToolTip(i18n("Copy bibtex citations to the clipboard")); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste"))); citeMapper->setMapping(action, Cite::CiteClipboard); action = actionCollection()->addAction(QStringLiteral("cite_lyxpipe"), citeMapper, mapVoid); action->setText(i18n("Cite Entry in &LyX")); action->setToolTip(i18n("Cite the selected entries in LyX")); action->setIcon(QIcon::fromTheme(QStringLiteral("lyx"), QIcon(QLatin1String(":/icons/lyx")))); citeMapper->setMapping(action, Cite::CiteLyxpipe); m_updateMapper = new QSignalMapper(this); void (QSignalMapper::* mappedString)(const QString&) = &QSignalMapper::mapped; connect(m_updateMapper, mappedString, Controller::self(), &Controller::slotUpdateSelectedEntries); m_updateEntryMenu = new KActionMenu(i18n("&Update Entry"), this); m_updateEntryMenu->setIcon(QIcon::fromTheme(QStringLiteral("document-export"))); m_updateEntryMenu->setIconText(i18nc("Update Entry", "Update")); m_updateEntryMenu->setDelayed(false); actionCollection()->addAction(QStringLiteral("coll_update_entry"), m_updateEntryMenu); m_updateAll = actionCollection()->addAction(QStringLiteral("update_entry_all"), m_updateMapper, mapVoid); m_updateAll->setText(i18n("All Sources")); m_updateAll->setToolTip(i18n("Update entry data from all available sources")); m_updateMapper->setMapping(m_updateAll, QStringLiteral("_all")); /************************************************* * Settings menu *************************************************/ setStandardToolBarMenuEnabled(true); createStandardStatusBarAction(); m_lockLayout = new KDualAction(this); connect(m_lockLayout, &KDualAction::activeChanged, this, &MainWindow::slotToggleLayoutLock); m_lockLayout->setActiveText(i18n("Unlock Layout")); m_lockLayout->setActiveToolTip(i18n("Unlock the window's layout")); m_lockLayout->setActiveIcon(QIcon::fromTheme(QStringLiteral("object-unlocked"))); m_lockLayout->setInactiveText(i18n("Lock Layout")); m_lockLayout->setInactiveToolTip(i18n("Lock the window's layout")); m_lockLayout->setInactiveIcon(QIcon::fromTheme(QStringLiteral("object-locked"))); actionCollection()->addAction(QStringLiteral("lock_layout"), m_lockLayout); action = actionCollection()->addAction(QStringLiteral("reset_layout"), this, SLOT(slotResetLayout())); action->setText(i18n("Reset Layout")); action->setToolTip(i18n("Reset the window's layout")); action->setIcon(QIcon::fromTheme(QStringLiteral("resetview"))); m_toggleEntryEditor = new KToggleAction(i18n("Entry &Editor"), this); connect(m_toggleEntryEditor, &QAction::triggered, this, &MainWindow::slotToggleEntryEditor); m_toggleEntryEditor->setToolTip(i18n("Enable/disable the editor")); actionCollection()->addAction(QStringLiteral("toggle_edit_widget"), m_toggleEntryEditor); KStandardAction::preferences(this, SLOT(slotShowConfigDialog()), actionCollection()); /************************************************* * Help menu *************************************************/ KStandardAction::tipOfDay(this, SLOT(slotShowTipOfDay()), actionCollection()); /************************************************* * Short cuts *************************************************/ KStandardAction::fullScreen(this, SLOT(slotToggleFullScreen()), this, actionCollection()); KStandardAction::showMenubar(this, SLOT(slotToggleMenuBarVisibility()), actionCollection()); /************************************************* * Collection Toolbar *************************************************/ action = actionCollection()->addAction(QStringLiteral("change_entry_grouping_accel"), this, SLOT(slotGroupLabelActivated())); action->setText(i18n("Change Grouping")); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::Key_G); m_entryGrouping = new KSelectAction(i18n("&Group Selection"), this); m_entryGrouping->setToolTip(i18n("Change the grouping of the collection")); void (KSelectAction::* triggeredInt)(int) = &KSelectAction::triggered; connect(m_entryGrouping, triggeredInt, this, &MainWindow::slotChangeGrouping); actionCollection()->addAction(QStringLiteral("change_entry_grouping"), m_entryGrouping); action = actionCollection()->addAction(QStringLiteral("quick_filter_accel"), this, SLOT(slotFilterLabelActivated())); action->setText(i18n("Filter")); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::Key_F); m_quickFilter = new GUI::LineEdit(this); m_quickFilter->setPlaceholderText(i18n("Filter here...")); // same text as kdepim and amarok m_quickFilter->setClearButtonEnabled(true); // same as Dolphin text edit m_quickFilter->setMinimumWidth(150); m_quickFilter->setMaximumWidth(300); // want to update every time the filter text changes connect(m_quickFilter, &QLineEdit::textChanged, this, &MainWindow::slotQueueFilter); connect(m_quickFilter, &KLineEdit::clearButtonClicked, this, &MainWindow::slotClearFilter); m_quickFilter->installEventFilter(this); // intercept keyEvents QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(m_quickFilter); widgetAction->setText(i18n("Filter")); widgetAction->setToolTip(i18n("Filter the collection")); widgetAction->setProperty("isShortcutConfigurable", false); actionCollection()->addAction(QStringLiteral("quick_filter"), widgetAction); // final GUI setup is in initView() } #undef mimeIcon void MainWindow::initDocument() { MARK; Data::Document* doc = Data::Document::self(); Kernel::self()->resetHistory(); KConfigGroup config(KSharedConfig::openConfig(), "General Options"); doc->setLoadAllImages(config.readEntry("Load All Images", false)); // allow status messages from the document connect(doc, &Data::Document::signalStatusMsg, this, &MainWindow::slotStatusMsg); // do stuff that changes when the doc is modified connect(doc, &Data::Document::signalModified, this, &MainWindow::slotEnableModifiedActions); connect(doc, &Data::Document::signalCollectionAdded, Controller::self(), &Controller::slotCollectionAdded); connect(doc, &Data::Document::signalCollectionDeleted, Controller::self(), &Controller::slotCollectionDeleted); connect(Kernel::self()->commandHistory(), &QUndoStack::cleanChanged, doc, &Data::Document::slotSetClean); } void MainWindow::initView() { MARK; // initialize the image factory before the entry models are created ImageFactory::init(); m_entryView = new EntryView(this); connect(m_entryView, &EntryView::signalAction, this, &MainWindow::slotURLAction); m_entryView->view()->setWhatsThis(i18n("The Entry View shows a formatted view of the entry's contents.")); // trick to make sure the group views always extend along the entire left or right side // using QMainWindow::setCorner does not seem to work // https://wiki.qt.io/Technical_FAQ#Is_it_possible_for_either_the_left_or_right_dock_areas_to_have_full_height_of_their_side_rather_than_having_the_bottom_take_the_full_width.3F m_dummyWindow = new QMainWindow(this); m_dummyWindow->setCentralWidget(m_entryView->view()); m_dummyWindow->setWindowFlags(Qt::Widget); setCentralWidget(m_dummyWindow); m_collectionViewDock = new GUI::DockWidget(i18n("Collection View"), m_dummyWindow); m_collectionViewDock->setObjectName(QStringLiteral("collection_dock")); m_viewStack = new ViewStack(this); m_detailedView = m_viewStack->listView(); Controller::self()->addObserver(m_detailedView); m_detailedView->setWhatsThis(i18n("The Column View shows the value of multiple fields " "for each entry.")); connect(Data::Document::self(), &Data::Document::signalCollectionImagesLoaded, m_detailedView, &DetailedListView::slotRefreshImages); m_iconView = m_viewStack->iconView(); EntryIconModel* iconModel = new EntryIconModel(m_iconView); iconModel->setSourceModel(m_detailedView->model()); m_iconView->setModel(iconModel); Controller::self()->addObserver(m_iconView); m_iconView->setWhatsThis(i18n("The Icon View shows each entry in the collection or group using " "an icon, which may be an image in the entry.")); m_collectionViewDock->setWidget(m_viewStack); m_dummyWindow->addDockWidget(Qt::TopDockWidgetArea, m_collectionViewDock); actionCollection()->addAction(QStringLiteral("toggle_column_widget"), m_collectionViewDock->toggleViewAction()); m_groupViewDock = new GUI::DockWidget(i18n("Group View"), this); m_groupViewDock->setObjectName(QStringLiteral("group_dock")); m_groupViewDock->setAllowedAreas(Qt::DockWidgetAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea)); m_viewTabs = new GUI::TabWidget(this); m_viewTabs->setTabBarHidden(true); m_viewTabs->setDocumentMode(true); m_groupView = new GroupView(m_viewTabs); Controller::self()->addObserver(m_groupView); m_viewTabs->addTab(m_groupView, QIcon::fromTheme(QStringLiteral("folder")), i18n("Groups")); m_groupView->setWhatsThis(i18n("The Group View sorts the entries into groupings " "based on a selected field.")); m_groupViewDock->setWidget(m_viewTabs); addDockWidget(Qt::LeftDockWidgetArea, m_groupViewDock); actionCollection()->addAction(QStringLiteral("toggle_group_widget"), m_groupViewDock->toggleViewAction()); EntrySelectionModel* proxySelect = new EntrySelectionModel(m_iconView->model(), m_detailedView->selectionModel(), this); m_iconView->setSelectionModel(proxySelect); setMinimumWidth(MAIN_WINDOW_MIN_WIDTH); // setting up GUI now rather than in initActions setupGUI(Keys | ToolBar); createGUI(); } void MainWindow::initConnections() { // have to toggle the menu item if the dialog gets closed connect(m_editDialog, &QDialog::finished, this, &MainWindow::slotEditDialogFinished); EntrySelectionModel* proxySelect = static_cast(m_iconView->selectionModel()); connect(proxySelect, &EntrySelectionModel::entriesSelected, Controller::self(), &Controller::slotUpdateSelection); connect(proxySelect, &EntrySelectionModel::entriesSelected, m_editDialog, &EntryEditDialog::setContents); connect(proxySelect, &EntrySelectionModel::entriesSelected, m_entryView, &EntryView::showEntries); // let the group view call filters, too connect(m_groupView, &GroupView::signalUpdateFilter, this, &MainWindow::slotUpdateFilter); // use the EntrySelectionModel as a proxy so when entries get selected in the group view // the edit dialog and entry view are updated proxySelect->addSelectionProxy(m_groupView->selectionModel()); } void MainWindow::initFileOpen(bool nofile_) { MARK; slotInit(); // check to see if most recent file should be opened bool happyStart = false; if(!nofile_ && Config::reopenLastFile()) { // Config::lastOpenFile() is the full URL, protocol included QUrl lastFile(Config::lastOpenFile()); // empty string is actually ok, it gets handled if(!lastFile.isEmpty() && lastFile.isValid()) { slotFileOpen(lastFile); happyStart = true; } } if(!happyStart) { // the document is created with an initial book collection, continue with that Controller::self()->slotCollectionAdded(Data::Document::self()->collection()); m_fileSave->setEnabled(false); slotEnableOpenedActions(); slotEnableModifiedActions(false); slotEntryCount(); // tell the entry views and models that there are no images to load m_detailedView->slotRefreshImages(); } // show welcome text, even when opening an existing collection const int type = Kernel::self()->collectionType(); QString welcomeFile = DataFileRegistry::self()->locate(QStringLiteral("welcome.html")); QString text = FileHandler::readTextFile(QUrl::fromLocalFile(welcomeFile)); text.replace(QLatin1String("$FGCOLOR$"), Config::templateTextColor(type).name()); text.replace(QLatin1String("$BGCOLOR$"), Config::templateBaseColor(type).name()); text.replace(QLatin1String("$COLOR1$"), Config::templateHighlightedTextColor(type).name()); text.replace(QLatin1String("$COLOR2$"), Config::templateHighlightedBaseColor(type).name()); text.replace(QLatin1String("$IMGDIR$"), QUrl::fromLocalFile(ImageFactory::tempDir()).url()); text.replace(QLatin1String("$BANNER$"), i18n("Welcome to the Tellico Collection Manager")); text.replace(QLatin1String("$WELCOMETEXT$"), i18n("

Tellico is a tool for managing collections of books, " "videos, music, and whatever else you want to catalog.

" "

New entries can be added to your collection by " "entering data manually or by " "downloading data from " "various Internet sources.

")); m_entryView->showText(text); m_initialized = true; } // These are general options. // The options that can be changed in the "Configuration..." dialog // are taken care of by the ConfigDialog object. void MainWindow::saveOptions() { KConfigGroup config(KSharedConfig::openConfig(), "Main Window Options"); saveMainWindowSettings(config); config.writeEntry(QStringLiteral("Central Dock State"), m_dummyWindow->saveState()); Config::setShowEditWidget(m_toggleEntryEditor->isChecked()); // check any single dock widget, they all get locked together Config::setLockLayout(m_groupViewDock->isLocked()); KConfigGroup filesConfig(KSharedConfig::openConfig(), "Recent Files"); m_fileOpenRecent->saveEntries(filesConfig); if(!isNewDocument()) { Config::setLastOpenFile(Data::Document::self()->URL().url()); } Config::setViewWidget(m_viewStack->currentWidget()); // historical reasons // sorting by count was faked by sorting by phantom second column const int sortColumn = m_groupView->sortRole() == RowCountRole ? 1 : 0; Config::setGroupViewSortColumn(sortColumn); // ok to use SortColumn key, save semantics Config::setGroupViewSortAscending(m_groupView->sortOrder() == Qt::AscendingOrder); if(m_loanView) { const int sortColumn = m_loanView->sortRole() == RowCountRole ? 1 : 0; Config::setLoanViewSortAscending(sortColumn); // ok to use SortColumn key, save semantics Config::setLoanViewSortAscending(m_loanView->sortOrder() == Qt::AscendingOrder); } if(m_filterView) { const int sortColumn = m_filterView->sortRole() == RowCountRole ? 1 : 0; Config::setFilterViewSortAscending(sortColumn); // ok to use SortColumn key, save semantics Config::setFilterViewSortAscending(m_filterView->sortOrder() == Qt::AscendingOrder); } // this is used in the EntryEditDialog constructor, too KConfigGroup editDialogConfig(KSharedConfig::openConfig(), "Edit Dialog Options"); KWindowConfig::saveWindowSize(m_editDialog->windowHandle(), editDialogConfig); saveCollectionOptions(Data::Document::self()->collection()); Config::self()->save(); } void MainWindow::readCollectionOptions(Tellico::Data::CollPtr coll_) { if(!coll_) { myDebug() << "Bad, no collection in MainWindow::readCollectionOptions()"; return; } const QString configGroup = QStringLiteral("Options - %1").arg(CollectionFactory::typeName(coll_)); KConfigGroup group(KSharedConfig::openConfig(), configGroup); QString defaultGroup = coll_->defaultGroupField(); QString entryGroup; if(coll_->type() != Data::Collection::Base) { entryGroup = group.readEntry("Group By", defaultGroup); } else { QUrl url = Kernel::self()->URL(); for(int i = 0; i < Config::maxCustomURLSettings(); ++i) { QUrl u(group.readEntry(QStringLiteral("URL_%1").arg(i))); if(url == u) { entryGroup = group.readEntry(QStringLiteral("Group By_%1").arg(i), defaultGroup); break; } } // fall back to old setting if(entryGroup.isEmpty()) { entryGroup = group.readEntry("Group By", defaultGroup); } } if(entryGroup.isEmpty() || (!coll_->entryGroups().contains(entryGroup) && entryGroup != Data::Collection::s_peopleGroupName)) { entryGroup = defaultGroup; } m_groupView->setGroupField(entryGroup); QString entryXSLTFile = Config::templateName(coll_->type()); if(entryXSLTFile.isEmpty()) { entryXSLTFile = QStringLiteral("Fancy"); // should never happen, but just in case } m_entryView->setXSLTFile(entryXSLTFile + QLatin1String(".xsl")); // make sure the right combo element is selected slotUpdateCollectionToolBar(coll_); } void MainWindow::saveCollectionOptions(Tellico::Data::CollPtr coll_) { // don't save initial collection options, or empty collections if(!coll_ || coll_->entryCount() == 0 || isNewDocument()) { return; } int configIndex = -1; QString configGroup = QStringLiteral("Options - %1").arg(CollectionFactory::typeName(coll_)); KConfigGroup config(KSharedConfig::openConfig(), configGroup); QString groupName; if(m_entryGrouping->currentItem() > -1 && static_cast(coll_->entryGroups().count()) > m_entryGrouping->currentItem()) { if(m_entryGrouping->currentText() == (QLatin1Char('<') + i18n("People") + QLatin1Char('>'))) { groupName = Data::Collection::s_peopleGroupName; } else { groupName = Kernel::self()->fieldNameByTitle(m_entryGrouping->currentText()); } if(coll_->type() != Data::Collection::Base) { config.writeEntry("Group By", groupName); } } if(coll_->type() == Data::Collection::Base) { // all of this is to have custom settings on a per file basis QUrl url = Kernel::self()->URL(); QList urls = QList() << url; QStringList groupBys = QStringList() << groupName; for(int i = 0; i < Config::maxCustomURLSettings(); ++i) { QUrl u = config.readEntry(QStringLiteral("URL_%1").arg(i), QUrl()); QString g = config.readEntry(QStringLiteral("Group By_%1").arg(i), QString()); if(!u.isEmpty() && url != u) { urls.append(u); groupBys.append(g); } else if(!u.isEmpty()) { configIndex = i; } } int limit = qMin(urls.count(), Config::maxCustomURLSettings()); for(int i = 0; i < limit; ++i) { config.writeEntry(QStringLiteral("URL_%1").arg(i), urls[i].url()); config.writeEntry(QStringLiteral("Group By_%1").arg(i), groupBys[i]); } } m_detailedView->saveConfig(coll_, configIndex); } void MainWindow::readOptions() { KConfigGroup mainWindowConfig(KSharedConfig::openConfig(), "Main Window Options"); applyMainWindowSettings(mainWindowConfig); m_dummyWindow->restoreState(mainWindowConfig.readEntry(QStringLiteral("Central Dock State"), QByteArray())); m_viewStack->setCurrentWidget(Config::viewWidget()); m_iconView->setMaxAllowedIconWidth(Config::maxIconSize()); connect(toolBar(QStringLiteral("collectionToolBar")), &QToolBar::iconSizeChanged, this, &MainWindow::slotUpdateToolbarIcons); // initialize the recent file list KConfigGroup filesConfig(KSharedConfig::openConfig(), "Recent Files"); m_fileOpenRecent->loadEntries(filesConfig); // sort by count if column = 1 int sortRole = Config::groupViewSortColumn() == 0 ? static_cast(Qt::DisplayRole) : static_cast(RowCountRole); Qt::SortOrder sortOrder = Config::groupViewSortAscending() ? Qt::AscendingOrder : Qt::DescendingOrder; m_groupView->setSorting(sortOrder, sortRole); BibtexHandler::s_quoteStyle = Config::useBraces() ? BibtexHandler::BRACES : BibtexHandler::QUOTES; // Don't read any options for the edit dialog here, since it's not yet initialized. // Put them in init() } bool MainWindow::querySaveModified() { bool completed = true; if(Data::Document::self()->isModified()) { QString str = i18n("The current file has been modified.\n" "Do you want to save it?"); int want_save = KMessageBox::warningYesNoCancel(this, str, i18n("Unsaved Changes"), KStandardGuiItem::save(), KStandardGuiItem::discard()); switch(want_save) { case KMessageBox::Yes: completed = fileSave(); break; case KMessageBox::No: Data::Document::self()->setModified(false); completed = true; break; case KMessageBox::Cancel: default: completed = false; break; } } return completed; } bool MainWindow::queryClose() { // in case we're still loading the images, cancel that Data::Document::self()->cancelImageWriting(); const bool willClose = m_editDialog->queryModified() && querySaveModified(); if (willClose) { ImageFactory::clean(true); saveOptions(); } return willClose; } void MainWindow::slotFileNew(int type_) { slotStatusMsg(i18n("Creating new document...")); // close the fields dialog slotHideCollectionFieldsDialog(); if(m_editDialog->queryModified() && querySaveModified()) { // remove filter and loan tabs, they'll get re-added if needed if(m_filterView) { m_viewTabs->removeTab(m_viewTabs->indexOf(m_filterView)); Controller::self()->removeObserver(m_filterView); delete m_filterView; m_filterView = nullptr; } if(m_loanView) { m_viewTabs->removeTab(m_viewTabs->indexOf(m_loanView)); Controller::self()->removeObserver(m_loanView); delete m_loanView; m_loanView = nullptr; } m_viewTabs->setTabBarHidden(true); Data::Document::self()->newDocument(type_); Kernel::self()->resetHistory(); m_fileOpenRecent->setCurrentItem(-1); slotEnableOpenedActions(); slotEnableModifiedActions(false); m_newDocument = true; ImageFactory::clean(false); } StatusBar::self()->clearStatus(); } void MainWindow::slotFileOpen() { slotStatusMsg(i18n("Opening file...")); if(m_editDialog->queryModified() && querySaveModified()) { QString filter = i18n("Tellico Files") + QLatin1String(" (*.tc *.bc)"); filter += QLatin1String(";;"); filter += i18n("XML Files") + QLatin1String(" (*.xml)"); filter += QLatin1String(";;"); filter += i18n("All Files") + QLatin1String(" (*)"); // keyword 'open' QString fileClass; const QUrl startUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///open")), fileClass); QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open File"), startUrl, filter); if(!url.isEmpty() && url.isValid()) { slotFileOpen(url); if(url.isLocalFile()) { KRecentDirs::add(fileClass, url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); } } } StatusBar::self()->clearStatus(); } void MainWindow::slotFileOpen(const QUrl& url_) { slotStatusMsg(i18n("Opening file...")); // close the fields dialog slotHideCollectionFieldsDialog(); // there seems to be a race condition at start between slotInit() and initFileOpen() // which means the edit dialog might not have been created yet if((!m_editDialog || m_editDialog->queryModified()) && querySaveModified()) { if(openURL(url_)) { m_fileOpenRecent->addUrl(url_); m_fileOpenRecent->setCurrentItem(-1); } } StatusBar::self()->clearStatus(); } void MainWindow::slotFileOpenRecent(const QUrl& url_) { slotStatusMsg(i18n("Opening file...")); // close the fields dialog slotHideCollectionFieldsDialog(); if(m_editDialog->queryModified() && querySaveModified()) { if(!openURL(url_)) { m_fileOpenRecent->removeUrl(url_); m_fileOpenRecent->setCurrentItem(-1); } } else { // the QAction shouldn't be checked now m_fileOpenRecent->setCurrentItem(-1); } StatusBar::self()->clearStatus(); } void MainWindow::openFile(const QString& file_) { QUrl url(file_); if(!url.isEmpty() && url.isValid()) { slotFileOpen(url); } } bool MainWindow::openURL(const QUrl& url_) { MARK; // try to open document GUI::CursorSaver cs(Qt::WaitCursor); bool success = Data::Document::self()->openDocument(url_); if(success) { Kernel::self()->resetHistory(); m_quickFilter->clear(); slotEnableOpenedActions(); m_newDocument = false; slotEnableModifiedActions(Data::Document::self()->isModified()); // doc might add some stuff } else if(!m_initialized) { // special case on startup when openURL() is called with a command line argument // and that URL can't be opened. The window still needs to be initialized // the doc object is created with an initial book collection, continue with that Controller::self()->slotCollectionAdded(Data::Document::self()->collection()); m_fileSave->setEnabled(false); slotEnableOpenedActions(); slotEnableModifiedActions(false); slotEntryCount(); } // slotFileOpen(URL) gets called when opening files on the command line // so go ahead and make sure m_initialized is set. m_initialized = true; // remove filter and loan tabs, they'll get re-added if needed if(m_filterView && m_filterView->isEmpty()) { m_viewTabs->removeTab(m_viewTabs->indexOf(m_filterView)); Controller::self()->removeObserver(m_filterView); delete m_filterView; m_filterView = nullptr; } if(m_loanView && m_loanView->isEmpty()) { m_viewTabs->removeTab(m_viewTabs->indexOf(m_loanView)); Controller::self()->removeObserver(m_loanView); delete m_loanView; m_loanView = nullptr; } Controller::self()->hideTabs(); // does conditional check return success; } void MainWindow::slotFileSave() { fileSave(); } bool MainWindow::fileSave() { if(!m_editDialog->queryModified()) { return false; } slotStatusMsg(i18n("Saving file...")); bool ret = true; if(isNewDocument()) { ret = fileSaveAs(); } else { // special check: if there are more than 200 images AND the "Write Images In File" config key // is set, then warn user that performance may suffer, and write result if(Config::imageLocation() == Config::ImagesInFile && Config::askWriteImagesInFile() && Data::Document::self()->imageCount() > MAX_IMAGES_WARN_PERFORMANCE) { QString msg = i18n("

You are saving a file with many images, which causes Tellico to " "slow down significantly. Do you want to save the images separately in " "Tellico's data directory to improve performance?

Your choice can " "always be changed in the configuration dialog.

"); KGuiItem yes(i18n("Save Images Separately")); KGuiItem no(i18n("Save Images in File")); int res = KMessageBox::warningYesNo(this, msg, QString() /* caption */, yes, no); if(res == KMessageBox::No) { Config::setImageLocation(Config::ImagesInAppDir); } Config::setAskWriteImagesInFile(false); } GUI::CursorSaver cs(Qt::WaitCursor); if(Data::Document::self()->saveDocument(Data::Document::self()->URL())) { Kernel::self()->resetHistory(); m_newDocument = false; updateCaption(false); m_fileSave->setEnabled(false); // TODO: call a method of the model instead of the view here m_detailedView->resetEntryStatus(); } else { ret = false; } } StatusBar::self()->clearStatus(); return ret; } void MainWindow::slotFileSaveAs() { fileSaveAs(); } bool MainWindow::fileSaveAs() { if(!m_editDialog->queryModified()) { return false; } slotStatusMsg(i18n("Saving file with a new filename...")); QString filter = i18n("Tellico Files") + QLatin1String(" (*.tc *.bc)"); filter += QLatin1String(";;"); filter += i18n("All Files") + QLatin1String(" (*)"); // keyword 'open' QString fileClass; const QUrl startUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///open")), fileClass); const QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Save As"), startUrl, filter); if(url.isEmpty()) { StatusBar::self()->clearStatus(); return false; } if(url.isLocalFile()) { KRecentDirs::add(fileClass, url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); } bool ret = true; if(url.isValid()) { GUI::CursorSaver cs(Qt::WaitCursor); m_savingImageLocationChange = true; // Overwriting an existing file was already confirmed in QFileDialog::getSaveFileUrl() if(Data::Document::self()->saveDocument(url, true /* force */)) { Kernel::self()->resetHistory(); KRecentDocument::add(url); m_fileOpenRecent->addUrl(url); updateCaption(false); m_newDocument = false; m_fileSave->setEnabled(false); m_detailedView->resetEntryStatus(); } else { ret = false; } m_savingImageLocationChange = false; } StatusBar::self()->clearStatus(); return ret; } void MainWindow::slotFilePrint() { slotStatusMsg(i18n("Printing...")); bool printGrouped = Config::printGrouped(); bool printHeaders = Config::printFieldHeaders(); int imageWidth = Config::maxImageWidth(); int imageHeight = Config::maxImageHeight(); // If the collection is being filtered, warn the user if(m_detailedView->filter()) { QString str = i18n("The collection is currently being filtered to show a limited subset of " "the entries. Only the visible entries will be printed. Continue?"); int ret = KMessageBox::warningContinueCancel(this, str, QString(), KStandardGuiItem::print(), KStandardGuiItem::cancel(), QStringLiteral("WarnPrintVisible")); if(ret == KMessageBox::Cancel) { StatusBar::self()->clearStatus(); return; } } GUI::CursorSaver cs(Qt::WaitCursor); Export::HTMLExporter exporter(Data::Document::self()->collection()); // only print visible entries exporter.setEntries(m_detailedView->visibleEntries()); exporter.setXSLTFile(QStringLiteral("tellico-printing.xsl")); exporter.setPrintHeaders(printHeaders); exporter.setPrintGrouped(printGrouped); exporter.setGroupBy(Controller::self()->expandedGroupBy()); if(!printGrouped) { // the sort titles are only used if the entries are not grouped exporter.setSortTitles(Controller::self()->sortTitles()); } exporter.setColumns(m_detailedView->visibleColumns()); exporter.setMaxImageSize(imageWidth, imageHeight); slotStatusMsg(i18n("Processing document...")); if(Config::printFormatted()) { exporter.setOptions(Export::ExportUTF8 | Export::ExportFormatted); } else { exporter.setOptions(Export::ExportUTF8); } QString html = exporter.text(); if(html.isEmpty()) { XSLTError(); StatusBar::self()->clearStatus(); return; } // don't have busy cursor when showing the print dialog cs.restore(); // myDebug() << html; slotStatusMsg(i18n("Printing...")); doPrint(html); StatusBar::self()->clearStatus(); } void MainWindow::slotFileQuit() { slotStatusMsg(i18n("Exiting...")); close(); // will call queryClose() StatusBar::self()->clearStatus(); } void MainWindow::slotEditCut() { activateEditSlot("cut()"); } void MainWindow::slotEditCopy() { activateEditSlot("copy()"); } void MainWindow::slotEditPaste() { activateEditSlot("paste()"); } void MainWindow::activateEditSlot(const char* slot_) { // the edit widget is the only one that copies, cuts, and pastes QWidget* w; if(m_editDialog->isVisible()) { w = m_editDialog->focusWidget(); } else { w = qApp->focusWidget(); } if(w && w->isVisible()) { const QMetaObject* meta = w->metaObject(); const int idx = meta->indexOfSlot(slot_); if(idx > -1) { //myDebug() << "MainWindow invoking" << meta->method(idx).signature(); meta->method(idx).invoke(w, Qt::DirectConnection); } } } void MainWindow::slotEditSelectAll() { m_detailedView->selectAllVisible(); } void MainWindow::slotEditDeselect() { Controller::self()->slotUpdateSelection(Data::EntryList()); } void MainWindow::slotToggleEntryEditor() { if(m_toggleEntryEditor->isChecked()) { m_editDialog->show(); } else { m_editDialog->hide(); } } void MainWindow::slotShowConfigDialog() { if(!m_configDlg) { m_configDlg = new ConfigDialog(this); connect(m_configDlg, &ConfigDialog::signalConfigChanged, this, &MainWindow::slotHandleConfigChange); connect(m_configDlg, &QDialog::finished, this, &MainWindow::slotHideConfigDialog); } else { KWindowSystem::activateWindow(m_configDlg->winId()); } m_configDlg->show(); } void MainWindow::slotHideConfigDialog() { if(m_configDlg) { m_configDlg->hide(); m_configDlg->deleteLater(); m_configDlg = nullptr; } } void MainWindow::slotShowTipOfDay(bool force_/*=true*/) { KTipDialog::showTip(this, QStringLiteral("tellico/tellico.tips"), force_); } void MainWindow::slotStatusMsg(const QString& text_) { m_statusBar->setStatus(text_); } void MainWindow::slotClearStatus() { StatusBar::self()->clearStatus(); } void MainWindow::slotEntryCount() { Data::CollPtr coll = Data::Document::self()->collection(); if(!coll) { return; } int count = coll->entryCount(); QString text = i18n("Total entries: %1", count); int selectCount = Controller::self()->selectedEntries().count(); int filterCount = m_detailedView->visibleItems(); // if more than one book is selected, add the number of selected books if(filterCount < count && selectCount > 1) { text += QLatin1Char(' '); text += i18n("(%1 filtered; %2 selected)", filterCount, selectCount); } else if(filterCount < count) { text += QLatin1Char(' '); text += i18n("(%1 filtered)", filterCount); } else if(selectCount > 1) { text += QLatin1Char(' '); text += i18n("(%1 selected)", selectCount); } m_statusBar->setCount(text); } void MainWindow::slotEnableOpenedActions() { slotUpdateToolbarIcons(); updateCollectionActions(); // close the filter dialog when a new collection is opened slotHideFilterDialog(); slotStringMacroDialogFinished(); } void MainWindow::slotEnableModifiedActions(bool modified_ /*= true*/) { updateCaption(modified_); updateCollectionActions(); m_fileSave->setEnabled(modified_); } void MainWindow::slotHandleConfigChange() { const int imageLocation = Config::imageLocation(); const bool autoCapitalize = Config::autoCapitalization(); const bool autoFormat = Config::autoFormat(); const QStringList articles = Config::articleList(); const QStringList nocaps = Config::noCapitalizationList(); const QStringList suffixes = Config::nameSuffixList(); const QStringList prefixes = Config::surnamePrefixList(); m_configDlg->saveConfiguration(); // only modified if there are entries and image location is changed if(imageLocation != Config::imageLocation() && !Data::Document::self()->isEmpty()) { slotImageLocationChanged(); } if(autoCapitalize != Config::autoCapitalization() || autoFormat != Config::autoFormat() || articles != Config::articleList() || nocaps != Config::noCapitalizationList() || suffixes != Config::nameSuffixList() || prefixes != Config::surnamePrefixList()) { // invalidate all groups Data::Document::self()->collection()->invalidateGroups(); // refreshing the title causes the group view to refresh Controller::self()->slotRefreshField(Data::Document::self()->collection()->fieldByName(QStringLiteral("title"))); } QString entryXSLTFile = Config::templateName(Kernel::self()->collectionType()); m_entryView->setXSLTFile(entryXSLTFile + QLatin1String(".xsl")); } void MainWindow::slotUpdateCollectionToolBar(Tellico::Data::CollPtr coll_) { if(!coll_) { myWarning() << "no collection pointer!"; return; } QString current = m_groupView->groupBy(); if(current.isEmpty() || !coll_->entryGroups().contains(current)) { current = coll_->defaultGroupField(); } const QStringList groups = coll_->entryGroups(); if(groups.isEmpty()) { m_entryGrouping->clear(); return; } QMap groupMap; // use a map so they get sorted foreach(const QString& groupName, groups) { // special case for people "pseudo-group" if(groupName == Data::Collection::s_peopleGroupName) { groupMap.insert(groupName, QLatin1Char('<') + i18n("People") + QLatin1Char('>')); } else { groupMap.insert(groupName, coll_->fieldTitleByName(groupName)); } } const QStringList titles = groupMap.values(); if(titles == m_entryGrouping->items()) { // no need to update anything return; } const QStringList names = groupMap.keys(); int index = names.indexOf(current); if(index == -1) { current = names[0]; index = 0; } m_entryGrouping->setItems(titles); m_entryGrouping->setCurrentItem(index); // in case the current grouping field get modified to be non-grouping... m_groupView->setGroupField(current); // don't call slotChangeGrouping() since it adds an undo item // TODO::I have no idea how to get the combobox to update its size // this is the hackiest of hacks, taken from KXmlGuiWindow::saveNewToolbarConfig() // the window flickers as toolbar resizes, unavoidable? // crashes if removeCLient//addClient is called here, need to do later in event loop QTimer::singleShot(0, this, &MainWindow::guiFactoryReset); } void MainWindow::slotChangeGrouping() { const QString title = m_entryGrouping->currentText(); QString groupName = Kernel::self()->fieldNameByTitle(title); if(groupName.isEmpty()) { if(title == (QLatin1Char('<') + i18n("People") + QLatin1Char('>'))) { groupName = Data::Collection::s_peopleGroupName; } else { groupName = Data::Document::self()->collection()->defaultGroupField(); } } m_groupView->setGroupField(groupName); m_viewTabs->setCurrentWidget(m_groupView); } void MainWindow::slotShowReportDialog() { if(!m_reportDlg) { m_reportDlg = new ReportDialog(this); connect(m_reportDlg, &QDialog::finished, this, &MainWindow::slotHideReportDialog); } else { KWindowSystem::activateWindow(m_reportDlg->winId()); } m_reportDlg->show(); } void MainWindow::slotHideReportDialog() { if(m_reportDlg) { m_reportDlg->hide(); m_reportDlg->deleteLater(); m_reportDlg = nullptr; } } void MainWindow::doPrint(const QString& html_) { KHTMLPart w; // KHTMLPart printing was broken in KDE until KHTML 5.16 // see https://git.reviewboard.kde.org/r/125681/ const QString version = w.componentData().version(); const uint major = version.section(QLatin1Char('.'), 0, 0).toUInt(); const uint minor = version.section(QLatin1Char('.'), 1, 1).toUInt(); if(major == 5 && minor < 16) { myWarning() << "Printing is broken for KDE Frameworks < 5.16. Please upgrade"; return; } w.setJScriptEnabled(false); w.setJavaEnabled(false); w.setMetaRefreshEnabled(false); w.setPluginsEnabled(false); w.begin(Data::Document::self()->URL()); w.write(html_); w.end(); // the problem with doing my own layout is that the text gets truncated, both at the // top and at the bottom. Even adding the overlap parameter, there were problems. // KHTMLView takes care of that with a truncatedAt() parameter, but that's hidden in // the khtml::render_root class. So for now, just use the KHTMLView::print() method. w.view()->print(); } void MainWindow::XSLTError() { QString str = i18n("Tellico encountered an error in XSLT processing.") + QLatin1Char('\n'); str += i18n("Please check your installation."); Kernel::self()->sorry(str); } void MainWindow::slotShowFilterDialog() { if(!m_filterDlg) { m_filterDlg = new FilterDialog(FilterDialog::CreateFilter, this); // allow saving m_quickFilter->setEnabled(false); connect(m_filterDlg, &FilterDialog::signalCollectionModified, Data::Document::self(), &Data::Document::slotSetModified); connect(m_filterDlg, &FilterDialog::signalUpdateFilter, this, &MainWindow::slotUpdateFilter); connect(m_filterDlg, &QDialog::finished, this, &MainWindow::slotHideFilterDialog); } else { KWindowSystem::activateWindow(m_filterDlg->winId()); } m_filterDlg->setFilter(m_detailedView->filter()); m_filterDlg->show(); } void MainWindow::slotHideFilterDialog() { // m_quickFilter->blockSignals(false); m_quickFilter->setEnabled(true); if(m_filterDlg) { m_filterDlg->hide(); m_filterDlg->deleteLater(); m_filterDlg = nullptr; } } void MainWindow::slotQueueFilter() { if(m_dontQueueFilter) { return; } m_queuedFilters++; QTimer::singleShot(200, this, &MainWindow::slotCheckFilterQueue); } void MainWindow::slotCheckFilterQueue() { m_queuedFilters--; if(m_queuedFilters > 0) { return; } setFilter(m_quickFilter->text()); } void MainWindow::slotUpdateFilter(FilterPtr filter_) { // Can't just block signals because clear button won't show then m_dontQueueFilter = true; m_quickFilter->setText(QStringLiteral(" ")); // To be able to clear custom filter Controller::self()->slotUpdateFilter(filter_); m_dontQueueFilter = false; } void MainWindow::setFilter(const QString& text_) { QString text = text_.trimmed(); FilterPtr filter; if(!text.isEmpty()) { filter = new Filter(Filter::MatchAll); QString fieldName; // empty field name means match on any field // if the text contains '=' assume it's a field name or title if(text.indexOf(QLatin1Char('=')) > -1) { fieldName = text.section(QLatin1Char('='), 0, 0).trimmed(); text = text.section(QLatin1Char('='), 1).trimmed(); // check that the field name might be a title if(!Data::Document::self()->collection()->hasField(fieldName)) { fieldName = Data::Document::self()->collection()->fieldNameByTitle(fieldName); } } // if the text contains any non-word characters, assume it's a regexp // but \W in qt is letter, number, or '_', I want to be a bit less strict QRegExp rx(QLatin1String("[^\\w\\s-']")); if(rx.indexIn(text) == -1) { // split by whitespace, and add rules for each word const QStringList tokens = text.split(QRegExp(QLatin1String("\\s"))); foreach(const QString& token, tokens) { // an empty field string means check every field filter->append(new FilterRule(fieldName, token, FilterRule::FuncContains)); } } else { // if it isn't valid, hold off on applying the filter QRegExp tx(text); if(!tx.isValid()) { text = QRegExp::escape(text); tx.setPattern(text); } if(!tx.isValid()) { myDebug() << "invalid regexp:" << text; return; } filter->append(new FilterRule(fieldName, text, FilterRule::FuncRegExp)); } // also want to update the line edit in case the filter was set by DBUS if(m_quickFilter->text() != text_) { m_quickFilter->setText(text_); } } // only update filter if one exists or did exist if(filter || m_detailedView->filter()) { Controller::self()->slotUpdateFilter(filter); } } void MainWindow::slotShowCollectionFieldsDialog() { if(!m_collFieldsDlg) { m_collFieldsDlg = new CollectionFieldsDialog(Data::Document::self()->collection(), this); connect(m_collFieldsDlg, &QDialog::finished, this, &MainWindow::slotHideCollectionFieldsDialog); } else { KWindowSystem::activateWindow(m_collFieldsDlg->winId()); } m_collFieldsDlg->show(); } void MainWindow::slotHideCollectionFieldsDialog() { if(m_collFieldsDlg) { m_collFieldsDlg->hide(); m_collFieldsDlg->deleteLater(); m_collFieldsDlg = nullptr; } } void MainWindow::slotFileImport(int format_) { slotStatusMsg(i18n("Importing data...")); m_quickFilter->clear(); Import::Format format = static_cast(format_); bool checkURL = true; QUrl url; switch(ImportDialog::importTarget(format)) { case Import::File: { QString fileClass; const QUrl startUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///import")), fileClass); url = QFileDialog::getOpenFileUrl(this, i18n("Import File"), startUrl, ImportDialog::fileFilter(format)); KRecentDirs::add(fileClass, url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); } break; case Import::Dir: // TODO: allow remote audiofile importing { const QString fileClass(QStringLiteral("ImportDir")); QString dirName = ImportDialog::startDir(format); if(dirName.isEmpty()) { dirName = KRecentDirs::dir(fileClass); } url = QUrl::fromLocalFile(QFileDialog::getExistingDirectory(this, i18n("Import Directory"), dirName)); KRecentDirs::add(fileClass, url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); } break; case Import::None: default: checkURL = false; break; } if(checkURL) { bool ok = !url.isEmpty() && url.isValid() && QFile::exists(url.toLocalFile()); if(!ok) { StatusBar::self()->clearStatus(); return; } } importFile(format, QList() << url); StatusBar::self()->clearStatus(); } void MainWindow::slotFileExport(int format_) { slotStatusMsg(i18n("Exporting data...")); Export::Format format = static_cast(format_); ExportDialog dlg(format, Data::Document::self()->collection(), this); if(dlg.exec() == QDialog::Rejected) { StatusBar::self()->clearStatus(); return; } switch(ExportDialog::exportTarget(format)) { case Export::None: dlg.exportURL(); break; case Export::Dir: myDebug() << "ExportDir not implemented!"; break; case Export::File: { QString fileClass; const QUrl startUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///export")), fileClass); QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Export As"), startUrl, dlg.fileFilter()); if(url.isEmpty()) { StatusBar::self()->clearStatus(); return; } if(url.isValid()) { if(url.isLocalFile()) { KRecentDirs::add(fileClass, url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); } GUI::CursorSaver cs(Qt::WaitCursor); dlg.exportURL(url); } } break; } StatusBar::self()->clearStatus(); } void MainWindow::slotShowStringMacroDialog() { if(Data::Document::self()->collection()->type() != Data::Collection::Bibtex) { return; } if(!m_stringMacroDlg) { const Data::BibtexCollection* c = static_cast(Data::Document::self()->collection().data()); m_stringMacroDlg = new StringMapDialog(c->macroList(), this, false); m_stringMacroDlg->setWindowTitle(i18n("String Macros")); m_stringMacroDlg->setLabels(i18n("Macro"), i18n("String")); connect(m_stringMacroDlg, &QDialog::finished, this, &MainWindow::slotStringMacroDialogFinished); } else { KWindowSystem::activateWindow(m_stringMacroDlg->winId()); } m_stringMacroDlg->show(); } void MainWindow::slotStringMacroDialogFinished(int result_) { // no point in checking if collection is bibtex, as dialog would never have been created if(!m_stringMacroDlg) { return; } if(result_ == QDialog::Accepted) { static_cast(Data::Document::self()->collection().data())->setMacroList(m_stringMacroDlg->stringMap()); Data::Document::self()->setModified(true); } m_stringMacroDlg->hide(); m_stringMacroDlg->deleteLater(); m_stringMacroDlg = nullptr; } void MainWindow::slotShowBibtexKeyDialog() { if(Data::Document::self()->collection()->type() != Data::Collection::Bibtex) { return; } if(!m_bibtexKeyDlg) { m_bibtexKeyDlg = new BibtexKeyDialog(Data::Document::self()->collection(), this); connect(m_bibtexKeyDlg, &QDialog::finished, this, &MainWindow::slotHideBibtexKeyDialog); connect(m_bibtexKeyDlg, &BibtexKeyDialog::signalUpdateFilter, this, &MainWindow::slotUpdateFilter); } else { KWindowSystem::activateWindow(m_bibtexKeyDlg->winId()); } m_bibtexKeyDlg->show(); } void MainWindow::slotHideBibtexKeyDialog() { if(m_bibtexKeyDlg) { m_bibtexKeyDlg->deleteLater(); m_bibtexKeyDlg = nullptr; } } void MainWindow::slotNewEntry() { m_toggleEntryEditor->setChecked(true); slotToggleEntryEditor(); m_editDialog->slotHandleNew(); } void MainWindow::slotEditDialogFinished() { m_toggleEntryEditor->setChecked(false); } void MainWindow::slotShowEntryEditor() { m_toggleEntryEditor->setChecked(true); m_editDialog->show(); KWindowSystem::activateWindow(m_editDialog->winId()); } void MainWindow::slotConvertToBibliography() { // only book collections can be converted to bibtex Data::CollPtr coll = Data::Document::self()->collection(); if(!coll || coll->type() != Data::Collection::Book) { return; } GUI::CursorSaver cs; // need to make sure all images are transferred Data::Document::self()->loadAllImagesNow(); Data::CollPtr newColl = Data::BibtexCollection::convertBookCollection(coll); if(newColl) { m_newDocument = true; Kernel::self()->replaceCollection(newColl); m_fileOpenRecent->setCurrentItem(-1); slotUpdateToolbarIcons(); updateCollectionActions(); } else { myWarning() << "ERROR: no bibliography created!"; } } void MainWindow::slotCiteEntry(int action_) { StatusBar::self()->setStatus(i18n("Creating citations...")); Cite::ActionManager* man = Cite::ActionManager::self(); man->cite(static_cast(action_), Controller::self()->selectedEntries()); if(man->hasError()) { Kernel::self()->sorry(man->errorString()); } StatusBar::self()->clearStatus(); } void MainWindow::slotShowFetchDialog() { if(!m_fetchDlg) { m_fetchDlg = new FetchDialog(this); connect(m_fetchDlg, &QDialog::finished, this, &MainWindow::slotHideFetchDialog); connect(Controller::self(), &Controller::collectionAdded, m_fetchDlg, &FetchDialog::slotResetCollection); } else { KWindowSystem::activateWindow(m_fetchDlg->winId()); } m_fetchDlg->show(); } void MainWindow::slotHideFetchDialog() { if(m_fetchDlg) { m_fetchDlg->hide(); m_fetchDlg->deleteLater(); m_fetchDlg = nullptr; } } bool MainWindow::importFile(Tellico::Import::Format format_, const QUrl& url_, Tellico::Import::Action action_) { // try to open document GUI::CursorSaver cs(Qt::WaitCursor); bool failed = false; Data::CollPtr coll; if(!url_.isEmpty() && url_.isValid() && NetAccess::exists(url_, true, this)) { coll = ImportDialog::importURL(format_, url_); } else { Kernel::self()->sorry(i18n(errorLoad, url_.fileName())); failed = true; } if(!coll && !m_initialized) { // special case on startup when openURL() is called with a command line argument // and that URL can't be opened. The window still needs to be initialized // the doc object is created with an initial book collection, continue with that Controller::self()->slotCollectionAdded(Data::Document::self()->collection()); m_fileSave->setEnabled(false); slotEnableOpenedActions(); slotEnableModifiedActions(false); slotEntryCount(); m_fileOpenRecent->setCurrentItem(-1); m_initialized = true; failed = true; } else if(coll) { // this is rather dumb, but I'm too lazy to find the bug // if the document isn't initialized, then Tellico crashes // since Document::replaceCollection() ends up calling lots of stuff that isn't initialized if(!m_initialized) { Controller::self()->slotCollectionAdded(Data::Document::self()->collection()); m_initialized = true; } failed = !importCollection(coll, action_); } StatusBar::self()->clearStatus(); return !failed; // return true means success } bool MainWindow::exportCollection(Tellico::Export::Format format_, const QUrl& url_, bool filtered_) { if(!url_.isValid()) { myDebug() << "invalid URL:" << url_; return false; } GUI::CursorSaver cs; const Data::CollPtr coll = Data::Document::self()->collection(); if(!coll) { return false; } // only bibliographies can export to bibtex or bibtexml const bool isBibtex = (coll->type() == Data::Collection::Bibtex); if(!isBibtex && (format_ == Export::Bibtex || format_ == Export::Bibtexml)) { return false; } // only books and bibliographies can export to alexandria const bool isBook = (coll->type() == Data::Collection::Book); if(!isBibtex && !isBook && format_ == Export::Alexandria) { return false; } return ExportDialog::exportCollection(coll, filtered_ ? Controller::self()->visibleEntries() : coll->entries(), format_, url_); } bool MainWindow::showEntry(Data::ID id) { Data::EntryPtr entry = Data::Document::self()->collection()->entryById(id); if(entry) { m_entryView->showEntry(entry); } return entry; } void MainWindow::addFilterView() { if(m_filterView) { return; } m_filterView = new FilterView(m_viewTabs); Controller::self()->addObserver(m_filterView); m_viewTabs->insertTab(1, m_filterView, QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Filters")); m_filterView->setWhatsThis(i18n("The Filter View shows the entries which meet certain " "filter rules.")); connect(m_filterView, &FilterView::signalUpdateFilter, this, &MainWindow::slotUpdateFilter); // use the EntrySelectionModel as a proxy so when entries get selected in the filter view // the edit dialog and entry view are updated // TODO: consider using KSelectionProxyModel static_cast(m_iconView->selectionModel())->addSelectionProxy(m_filterView->selectionModel()); // sort by count if column = 1 int sortRole = Config::filterViewSortColumn() == 0 ? static_cast(Qt::DisplayRole) : static_cast(RowCountRole); Qt::SortOrder sortOrder = Config::filterViewSortAscending() ? Qt::AscendingOrder : Qt::DescendingOrder; m_filterView->setSorting(sortOrder, sortRole); } void MainWindow::addLoanView() { if(m_loanView) { return; } m_loanView = new LoanView(m_viewTabs); Controller::self()->addObserver(m_loanView); m_viewTabs->insertTab(2, m_loanView, QIcon::fromTheme(QStringLiteral("kaddressbook")), i18n("Loans")); m_loanView->setWhatsThis(i18n("The Loan View shows a list of all the people who " "have borrowed items from your collection.")); // use the EntrySelectionModel as a proxy so when entries get selected in the loan view // the edit dialog and entry view are updated // TODO: consider using KSelectionProxyModel static_cast(m_iconView->selectionModel())->addSelectionProxy(m_loanView->selectionModel()); // sort by count if column = 1 int sortRole = Config::loanViewSortColumn() == 0 ? static_cast(Qt::DisplayRole) : static_cast(RowCountRole); Qt::SortOrder sortOrder = Config::loanViewSortAscending() ? Qt::AscendingOrder : Qt::DescendingOrder; m_loanView->setSorting(sortOrder, sortRole); } void MainWindow::updateCaption(bool modified_) { QString caption; if(Data::Document::self()->collection()) { caption = Data::Document::self()->collection()->title(); } if(!m_newDocument) { if(!caption.isEmpty()) { caption += QLatin1String(" - "); } QUrl u = Data::Document::self()->URL(); if(u.isLocalFile()) { // for new files, the filename is set to Untitled in Data::Document if(u.fileName() == i18n(Tellico::untitledFilename)) { caption += u.fileName(); } else { caption += u.path(); } } else { caption += u.toDisplayString(); } } setCaption(caption, modified_); } void MainWindow::slotUpdateToolbarIcons() { // first change the icon for the menu item if(Kernel::self()->collectionType() == Data::Collection::Base) { m_newEntry->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); } else { m_newEntry->setIcon(QIcon(QLatin1String(":/icons/") + Kernel::self()->collectionTypeName())); } } void MainWindow::slotGroupLabelActivated() { // need entry grouping combo id foreach(QWidget* widget, m_entryGrouping->associatedWidgets()) { if(::qobject_cast(widget)) { QWidget* container = m_entryGrouping->requestWidget(widget); QComboBox* combo = ::qobject_cast(container); //krazy:exclude=qclasses if(combo) { combo->showPopup(); break; } } } } void MainWindow::slotFilterLabelActivated() { m_quickFilter->setFocus(); m_quickFilter->selectAll(); } void MainWindow::slotClearFilter() { m_quickFilter->clear(); slotQueueFilter(); } void MainWindow::slotRenameCollection() { Kernel::self()->renameCollection(); } void MainWindow::slotImageLocationMismatch() { // TODO: having a single image location mismatch should not be reason to completely save the whole document QTimer::singleShot(0, this, &MainWindow::slotImageLocationChanged); } void MainWindow::slotImageLocationChanged() { if(m_savingImageLocationChange) { return; } m_savingImageLocationChange = true; Data::Document::self()->slotSetModified(); KMessageBox::information(this, QLatin1String("") + i18n("Some images are not saved in the configured location. The current file " "must be saved and the images will be transferred to the new location.") + QLatin1String("")); fileSave(); m_savingImageLocationChange = false; } void MainWindow::updateCollectionActions() { if(!Data::Document::self()->collection()) { return; } stateChanged(QStringLiteral("collection_reset")); Data::Collection::Type type = Data::Document::self()->collection()->type(); stateChanged(QLatin1String("is_") + CollectionFactory::typeName(type)); Controller::self()->updateActions(); // special case when there are no available data sources if(m_fetchActions.isEmpty() && m_updateAll) { m_updateAll->setEnabled(false); } } void MainWindow::updateEntrySources() { unplugActionList(QStringLiteral("update_entry_actions")); foreach(QAction* action, m_fetchActions) { foreach(QWidget* widget, action->associatedWidgets()) { widget->removeAction(action); } m_updateMapper->removeMappings(action); } qDeleteAll(m_fetchActions); m_fetchActions.clear(); Fetch::FetcherVec vec = Fetch::Manager::self()->fetchers(Kernel::self()->collectionType()); foreach(Fetch::Fetcher::Ptr fetcher, vec) { QAction* action = new QAction(Fetch::Manager::fetcherIcon(fetcher), fetcher->source(), actionCollection()); action->setToolTip(i18n("Update entry data from %1", fetcher->source())); void (QAction::* triggeredBool)(bool) = &QAction::triggered; void (QSignalMapper::* mapVoid)() = &QSignalMapper::map; connect(action, triggeredBool, m_updateMapper, mapVoid); m_updateMapper->setMapping(action, fetcher->source()); m_fetchActions.append(action); } plugActionList(QStringLiteral("update_entry_actions"), m_fetchActions); } void MainWindow::importFile(Tellico::Import::Format format_, const QList& urls_) { QList urls = urls_; // update as DropHandler and Importer classes are updated if(urls_.count() > 1 && format_ != Import::Bibtex && format_ != Import::RIS && format_ != Import::CIW && format_ != Import::PDF) { QUrl u = urls_.front(); QString url = u.isLocalFile() ? u.path() : u.toDisplayString(); Kernel::self()->sorry(i18n("Tellico can only import one file of this type at a time. " "Only %1 will be imported.", url)); urls.clear(); urls += u; } ImportDialog dlg(format_, urls, this); if(dlg.exec() != QDialog::Accepted) { return; } // if edit dialog is saved ok and if replacing, then the doc is saved ok if(m_editDialog->queryModified() && (dlg.action() != Import::Replace || querySaveModified())) { GUI::CursorSaver cs(Qt::WaitCursor); Data::CollPtr coll = dlg.collection(); if(!coll) { if(!dlg.statusMessage().isEmpty()) { Kernel::self()->sorry(dlg.statusMessage()); } return; } importCollection(coll, dlg.action()); } } void MainWindow::importText(Tellico::Import::Format format_, const QString& text_) { if(text_.isEmpty()) { return; } Data::CollPtr coll = ImportDialog::importText(format_, text_); if(coll) { importCollection(coll, Import::Merge); } } bool MainWindow::importCollection(Tellico::Data::CollPtr coll_, Tellico::Import::Action action_) { bool failed = false; switch(action_) { case Import::Append: { // only append if match, but special case importing books into bibliographies Data::CollPtr c = Data::Document::self()->collection(); if(c->type() == coll_->type() || (c->type() == Data::Collection::Bibtex && coll_->type() == Data::Collection::Book)) { Kernel::self()->appendCollection(coll_); slotEnableModifiedActions(true); } else { Kernel::self()->sorry(i18n(errorAppendType)); failed = true; } } break; case Import::Merge: { // only merge if match, but special case importing books into bibliographies Data::CollPtr c = Data::Document::self()->collection(); if(c->type() == coll_->type() || (c->type() == Data::Collection::Bibtex && coll_->type() == Data::Collection::Book)) { Kernel::self()->mergeCollection(coll_); slotEnableModifiedActions(true); } else { Kernel::self()->sorry(i18n(errorMergeType)); failed = true; } } break; default: // replace Kernel::self()->replaceCollection(coll_); m_fileOpenRecent->setCurrentItem(-1); m_newDocument = true; slotEnableOpenedActions(); slotEnableModifiedActions(false); break; } // tell the entry views and models that there are no further images to load m_detailedView->slotRefreshImages(); return !failed; } void MainWindow::slotURLAction(const QUrl& url_) { Q_ASSERT(url_.scheme() == QLatin1String("tc")); QString actionName = url_.fileName(); QAction* action = this->action(actionName.toLatin1().constData()); if(action) { action->activate(QAction::Trigger); } else { myWarning() << "unknown action: " << actionName; } } bool MainWindow::eventFilter(QObject* obj_, QEvent* ev_) { if(ev_->type() == QEvent::KeyPress && obj_ == m_quickFilter) { switch(static_cast(ev_)->key()) { case Qt::Key_Escape: m_quickFilter->clear(); return true; } } return KXmlGuiWindow::eventFilter(obj_, ev_); } void MainWindow::slotToggleFullScreen() { Qt::WindowStates ws = windowState(); setWindowState((ws & Qt::WindowFullScreen) ? (ws & ~Qt::WindowFullScreen) : (ws | Qt::WindowFullScreen)); } void MainWindow::slotToggleMenuBarVisibility() { QMenuBar* mb = menuBar(); mb->isHidden() ? mb->show() : mb->hide(); } void MainWindow::slotToggleLayoutLock(bool lock_) { m_groupViewDock->setLocked(lock_); m_collectionViewDock->setLocked(lock_); } void MainWindow::slotResetLayout() { removeDockWidget(m_groupViewDock); addDockWidget(Qt::LeftDockWidgetArea, m_groupViewDock); m_groupViewDock->show(); m_dummyWindow->removeDockWidget(m_collectionViewDock); m_dummyWindow->addDockWidget(Qt::TopDockWidgetArea, m_collectionViewDock); m_collectionViewDock->show(); } void MainWindow::guiFactoryReset() { guiFactory()->removeClient(this); guiFactory()->reset(); guiFactory()->addClient(this); } diff --git a/src/tellicoui.rc b/src/tellicoui.rc index d2fd2320..2792add9 100644 --- a/src/tellicoui.rc +++ b/src/tellicoui.rc @@ -1,232 +1,233 @@ - + &File &New &Import + &Export &Collection &Update Entry &Bibliography &Show Views Main Toolbar Collection Toolbar diff --git a/src/translators/CMakeLists.txt b/src/translators/CMakeLists.txt index e93e9691..7d18cfe7 100644 --- a/src/translators/CMakeLists.txt +++ b/src/translators/CMakeLists.txt @@ -1,101 +1,102 @@ ########### next target ############### SET(translators_STAT_SRCS adsimporter.cpp alexandriaexporter.cpp alexandriaimporter.cpp amcimporter.cpp audiofileimporter.cpp bibtexexporter.cpp bibteximporter.cpp bibtexmlexporter.cpp bibtexmlimporter.cpp boardgamegeekimporter.cpp ciwimporter.cpp csvexporter.cpp csvimporter.cpp csvparser.cpp dataimporter.cpp deliciousimporter.cpp exporter.cpp filelistingimporter.cpp freedb_util.cpp freedbimporter.cpp gcstarexporter.cpp gcstarimporter.cpp goodreadsimporter.cpp griffithimporter.cpp grs1importer.cpp htmlexporter.cpp importer.cpp + librarythingimporter.cpp onixexporter.cpp pdfimporter.cpp referencerimporter.cpp risimporter.cpp tellico_xml.cpp tellicoimporter.cpp tellicoxmlexporter.cpp tellicoxmlhandler.cpp tellicozipexporter.cpp textimporter.cpp vinoxmlimporter.cpp xmlstatehandler.cpp xmphandler.cpp xsltexporter.cpp xslthandler.cpp xsltimporter.cpp ) add_library(translators STATIC ${translators_STAT_SRCS}) TARGET_LINK_LIBRARIES(translators collections tellicomodels core gui images utils ${TELLICO_BTPARSE_LIBS} ${TELLICO_CSV_LIBS} rtf2html-tellico ) TARGET_LINK_LIBRARIES(translators KF5::Archive KF5::JobWidgets KF5::Solid ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${LIBXSLT_EXSLT_LIBRARIES} ) IF( KF5Cddb_FOUND ) TARGET_LINK_LIBRARIES(translators KF5::Cddb) ELSEIF( Libkcddb_FOUND ) TARGET_LINK_LIBRARIES(translators KDE::Libkcddb::kcddb) ENDIF( KF5Cddb_FOUND) IF( Poppler_Qt5_FOUND ) TARGET_LINK_LIBRARIES(translators Poppler::Qt5) ENDIF( Poppler_Qt5_FOUND ) IF( KF5FileMetaData_FOUND ) TARGET_LINK_LIBRARIES(translators KF5::FileMetaData) ENDIF( KF5FileMetaData_FOUND ) IF( Exempi_FOUND ) TARGET_LINK_LIBRARIES(translators ${Exempi_LIBRARIES}) ENDIF( Exempi_FOUND ) IF( TAGLIB_FOUND ) TARGET_LINK_LIBRARIES(translators ${TAGLIB_LIBRARIES}) ENDIF( TAGLIB_FOUND ) IF( CDIO_FOUND ) TARGET_LINK_LIBRARIES(translators ${CDIO_LIBRARIES}) ENDIF( CDIO_FOUND ) ADD_DEPENDENCIES(translators tellico_config) ########### install files ############### INSTALL(FILES bibtex-translation.xml DESTINATION ${TELLICO_DATA_INSTALL_DIR}) diff --git a/src/translators/boardgamegeekimporter.cpp b/src/translators/boardgamegeekimporter.cpp index d2ece2fc..f5714d6c 100644 --- a/src/translators/boardgamegeekimporter.cpp +++ b/src/translators/boardgamegeekimporter.cpp @@ -1,277 +1,277 @@ /*************************************************************************** Copyright (C) 2014 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "boardgamegeekimporter.h" #include "../collections/boardgamecollection.h" #include "xslthandler.h" #include "tellicoimporter.h" #include "../core/filehandler.h" #include "../utils/datafileregistry.h" #include "../tellico_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { static const char* BGG_THING_URL = "http://boardgamegeek.com/xmlapi2/thing"; static const char* BGG_COLLECTION_URL = "http://boardgamegeek.com/xmlapi2/collection"; static int BGG_STEPSIZE = 25; } using Tellico::Import::BoardGameGeekImporter; BoardGameGeekImporter::BoardGameGeekImporter() : Import::Importer(), m_cancelled(false), m_widget(nullptr) , m_userEdit(nullptr), m_checkOwned(nullptr) { QString xsltFile = DataFileRegistry::self()->locate(QStringLiteral("boardgamegeek2tellico.xsl")); if(!xsltFile.isEmpty()) { m_xsltURL = QUrl::fromLocalFile(xsltFile); } else { myWarning() << "unable to find boardgamegeek2tellico.xsl!"; } KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("ImportOptions - BoardGameGeek")); m_user = config.readEntry("User ID"); m_ownedOnly = config.readEntry("Owned", false); } bool BoardGameGeekImporter::canImport(int type) const { return type == Data::Collection::BoardGame; } Tellico::Data::CollPtr BoardGameGeekImporter::collection() { if(m_coll) { return m_coll; } if(m_xsltURL.isEmpty() || !m_xsltURL.isValid()) { setStatusMessage(i18n("A valid XSLT file is needed to import the file.")); return Data::CollPtr(); } if(!m_widget) { myWarning() << "no widget!"; return Data::CollPtr(); } m_user = m_userEdit->text().trimmed(); if(m_user.isEmpty()) { setStatusMessage(i18n("A valid user ID must be entered.")); return Data::CollPtr(); } XSLTHandler handler(m_xsltURL); if(!handler.isValid()) { setStatusMessage(i18n("Tellico encountered an error in XSLT processing.")); return Data::CollPtr(); } m_ownedOnly = m_checkOwned->isChecked(); // first get the bgg id list QUrl u(QString::fromLatin1(BGG_COLLECTION_URL)); QUrlQuery q; q.addQueryItem(QStringLiteral("username"), m_user); q.addQueryItem(QStringLiteral("subtype"), QStringLiteral("boardgame")); q.addQueryItem(QStringLiteral("brief"), QStringLiteral("1")); if(m_ownedOnly) { q.addQueryItem(QStringLiteral("own"), QStringLiteral("1")); } u.setQuery(q); QStringList idList; QDomDocument dom = FileHandler::readXMLDocument(u, false, true); // could return HTTP 202 while the caching system generates the file // see http://boardgamegeek.com/thread/1188687/export-collections-has-been-updated-xmlapi-develop // also has a root node of message. Try 5 times, waiting by 2 seconds each time bool hasMessage = dom.documentElement().tagName() == QStringLiteral("message"); for(int loopCount = 0; hasMessage && loopCount < 5; ++loopCount) { // wait 2 seconds and try again QTime timer; timer.start(); while(timer.elapsed() < 2000) { QCoreApplication::processEvents(QEventLoop::AllEvents, 100); QThread::msleep(500); } dom = FileHandler::readXMLDocument(u, false, true); hasMessage = dom.documentElement().tagName() == QStringLiteral("message"); } if(hasMessage) { myDebug() << "BoardGameGeekImporter still has message and no collection"; #if 0 myWarning() << "Remove debug from boardgamegeekimporter.cpp"; QFile f(QStringLiteral("/tmp/bgg-message.xml")); if(f.open(QIODevice::WriteOnly)) { QTextStream t(&f); t.setCodec("UTF-8"); t << dom.toString(); } f.close(); #endif } QDomNodeList items = dom.documentElement().elementsByTagName(QStringLiteral("item")); for(int i = 0; i < items.count(); ++i) { if(!items.at(i).isElement()) { continue; } const QString id = items.at(i).toElement().attribute(QStringLiteral("objectid")); if(!id.isEmpty()) { idList += id; } } if(idList.isEmpty()) { myLog() << "No items found in BGG collection"; return Data::CollPtr(); } const bool showProgress = options() & ImportProgress; if(showProgress) { // use 10 as the amount for reading the ids emit signalTotalSteps(this, 10 + 100); emit signalProgress(this, 10); } m_coll = new Data::BoardGameCollection(true); for(int j = 0; j < idList.size() && !m_cancelled; j += BGG_STEPSIZE) { QStringList ids; const int maxSize = qMin(j+BGG_STEPSIZE, idList.size()); for(int k = j; k < maxSize; ++k) { ids += idList.at(k); } #if 0 const QString xmlData = text(ids); myWarning() << "Remove debug from boardgamegeekimporter.cpp"; QFile f(QStringLiteral("/tmp/test.xml")); if(f.open(QIODevice::WriteOnly)) { QTextStream t(&f); t.setCodec("UTF-8"); t << xmlData; } f.close(); #endif QString str = handler.applyStylesheet(text(ids)); // QString str = handler.applyStylesheet(xmlData); // myDebug() << str; #if 0 myWarning() << "Remove debug2 from boardgamegeekimporter.cpp"; QFile f2(QStringLiteral("/tmp/test.tc")); if(f2.open(QIODevice::WriteOnly)) { QTextStream t(&f2); t.setCodec("UTF-8"); t << str; } f2.close(); #endif Import::TellicoImporter imp(str); imp.setOptions(imp.options() ^ Import::ImportShowImageErrors); Data::CollPtr c = imp.collection(); if(!c) { continue; } // assume we always want the 3 extra fields defined in boardgamegeek2tellico.xsl if(!m_coll->hasField(QStringLiteral("bggid"))) { m_coll->addField(Data::FieldPtr(new Data::Field(*c->fieldByName(QStringLiteral("bggid"))))); m_coll->addField(Data::FieldPtr(new Data::Field(*c->fieldByName(QStringLiteral("boardgamegeek-link"))))); Data::FieldPtr f(new Data::Field(*c->fieldByName(QStringLiteral("artist")))); // also, let's assume that the artist field title should be illustrator instead of musician f->setTitle(i18nc("Comic Book Illustrator", "Artist")); m_coll->addField(f); } m_coll->addEntries(c->entries()); setStatusMessage(imp.statusMessage()); if(showProgress) { emit signalProgress(this, 10 + 100*maxSize/idList.size()); qApp->processEvents(); } } KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("ImportOptions - BoardGameGeek")); config.writeEntry("User ID", m_user); config.writeEntry("Owned", m_ownedOnly); if(m_cancelled) { m_coll = Data::CollPtr(); } return m_coll; } QWidget* BoardGameGeekImporter::widget(QWidget* parent_) { if(m_widget) { return m_widget; } m_widget = new QWidget(parent_); QVBoxLayout* l = new QVBoxLayout(m_widget); QGroupBox* gbox = new QGroupBox(i18n("BoardGameGeek Options"), m_widget); QFormLayout* lay = new QFormLayout(gbox); m_userEdit = new QLineEdit(gbox); m_userEdit->setText(m_user); m_checkOwned = new QCheckBox(i18n("Import owned items only"), gbox); m_checkOwned->setChecked(m_ownedOnly); - lay->addRow(i18n("User ID"), m_userEdit); + lay->addRow(i18n("User ID:"), m_userEdit); lay->addRow(m_checkOwned); l->addWidget(gbox); l->addStretch(1); return m_widget; } QString BoardGameGeekImporter::text(const QStringList& idList_) const { // myDebug() << idList_; QUrl u(QString::fromLatin1(BGG_THING_URL)); QUrlQuery q; q.addQueryItem(QStringLiteral("id"), idList_.join(QLatin1String(","))); q.addQueryItem(QStringLiteral("type"), QStringLiteral("boardgame,boardgameexpansion")); u.setQuery(q); // myDebug() << u; return FileHandler::readTextFile(u, true, true); } void BoardGameGeekImporter::slotCancel() { m_cancelled = true; } diff --git a/src/translators/goodreadsimporter.cpp b/src/translators/goodreadsimporter.cpp index 14196fa3..71fb4cfb 100644 --- a/src/translators/goodreadsimporter.cpp +++ b/src/translators/goodreadsimporter.cpp @@ -1,166 +1,166 @@ /*************************************************************************** Copyright (C) 2011 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #include "goodreadsimporter.h" #include "xslthandler.h" #include "tellicoimporter.h" #include "../core/filehandler.h" #include "../utils/datafileregistry.h" #include "../tellico_debug.h" #include #include #include #include #include #include #include #include #include #include namespace { static const char* GOODREADS_LIST_URL = "https://www.goodreads.com/review/list.xml"; static const char* GOODREADS_USER_URL = "https://www.goodreads.com/user/show.xml"; static const char* GOODREADS_API_KEY = "dpgbQvOWk0n4cwL32jQRA"; } using Tellico::Import::GoodreadsImporter; GoodreadsImporter::GoodreadsImporter() : Import::Importer(), m_widget(nullptr), m_userEdit(nullptr) { QString xsltFile = DataFileRegistry::self()->locate(QStringLiteral("goodreads2tellico.xsl")); if(!xsltFile.isEmpty()) { m_xsltURL = QUrl::fromLocalFile(xsltFile); } else { myWarning() << "unable to find goodreads2tellico.xsl!"; } KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("ImportOptions - Goodreads")); m_user = config.readEntry("User ID"); m_key = config.readEntry("Developer Key"); if(m_key.isEmpty()) { m_key = QLatin1String(GOODREADS_API_KEY); } } bool GoodreadsImporter::canImport(int type) const { return type == Data::Collection::Book; } Tellico::Data::CollPtr GoodreadsImporter::collection() { if(m_coll) { return m_coll; } if(m_xsltURL.isEmpty() || !m_xsltURL.isValid()) { setStatusMessage(i18n("A valid XSLT file is needed to import the file.")); return Data::CollPtr(); } if(!m_widget) { myWarning() << "no widget!"; return Data::CollPtr(); } m_user = m_userEdit->text().trimmed(); // if the user is not all digits, assume it's a user name and // convert it to a user id if(!QRegExp(QLatin1String("\\d+")).exactMatch(m_user)) { m_user = idFromName(m_user); } if(m_user.isEmpty()) { setStatusMessage(i18n("A valid user ID must be entered.")); return Data::CollPtr(); } XSLTHandler handler(m_xsltURL); if(!handler.isValid()) { setStatusMessage(i18n("Tellico encountered an error in XSLT processing.")); return Data::CollPtr(); } // myDebug() << text(); QString str = handler.applyStylesheet(text()); // myDebug() << str; Import::TellicoImporter imp(str); imp.setOptions(imp.options() ^ Import::ImportShowImageErrors); m_coll = imp.collection(); setStatusMessage(imp.statusMessage()); KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("ImportOptions - Goodreads")); config.writeEntry("User ID", m_user); config.writeEntry("Developer Key", m_key); return m_coll; } QWidget* GoodreadsImporter::widget(QWidget* parent_) { if(m_widget) { return m_widget; } m_widget = new QWidget(parent_); QVBoxLayout* l = new QVBoxLayout(m_widget); QGroupBox* gbox = new QGroupBox(i18n("Goodreads Options"), m_widget); QFormLayout* lay = new QFormLayout(gbox); m_userEdit = new QLineEdit(gbox); m_userEdit->setText(m_user); - lay->addRow(i18n("User ID"), m_userEdit); + lay->addRow(i18n("User ID:"), m_userEdit); l->addWidget(gbox); l->addStretch(1); return m_widget; } QString GoodreadsImporter::text() const { QUrl u(QString::fromLatin1(GOODREADS_LIST_URL)); QUrlQuery q; q.addQueryItem(QStringLiteral("v"), QStringLiteral("2")); q.addQueryItem(QStringLiteral("id"), m_user); q.addQueryItem(QStringLiteral("key"), m_key); u.setQuery(q); // myDebug() << u; return FileHandler::readTextFile(u, true /* quiet */, true); } QString GoodreadsImporter::idFromName(const QString& name_) const { QUrl u(QString::fromLatin1(GOODREADS_USER_URL)); QUrlQuery q; q.addQueryItem(QStringLiteral("username"), name_); q.addQueryItem(QStringLiteral("key"), m_key); u.setQuery(q); // myDebug() << u; QDomDocument dom = FileHandler::readXMLDocument(u, false /* process namespace */, true /* quiet */); return dom.documentElement().namedItem(QStringLiteral("user")) .namedItem(QStringLiteral("id")) .toElement() .text() .trimmed(); } diff --git a/src/translators/librarythingimporter.cpp b/src/translators/librarythingimporter.cpp new file mode 100644 index 00000000..9ca38674 --- /dev/null +++ b/src/translators/librarythingimporter.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + Copyright (C) 2019 Robby Stephenson + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + * * + ***************************************************************************/ + +#include "librarythingimporter.h" +#include "../collections/bookcollection.h" +#include "../core/filehandler.h" +#include "../utils/string_utils.h" +#include "../utils/isbnvalidator.h" +#include "../tellico_debug.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using Tellico::Import::LibraryThingImporter; + +LibraryThingImporter::LibraryThingImporter() : Import::Importer(), m_widget(nullptr), m_URLRequester(nullptr) { +} + +bool LibraryThingImporter::canImport(int type) const { + return type == Data::Collection::Book; +} + +Tellico::Data::CollPtr LibraryThingImporter::collection() { + if(m_coll) { + return m_coll; + } + + if(!m_widget) { + myWarning() << "no widget!"; + return Data::CollPtr(); + } + + QUrl jsonUrl = m_URLRequester->url(); + + if(jsonUrl.isEmpty() || !jsonUrl.isValid()) { + myDebug() << "Bad jsonUrl:" << jsonUrl; + return Data::CollPtr(); + } + + + QByteArray data = Tellico::FileHandler::readDataFile(jsonUrl, false /* quiet */); + QJsonParseError parseError; + QJsonDocument doc = QJsonDocument::fromJson(data, &parseError); + if(doc.isNull()) { + myDebug() << "Bad json data:" << parseError.errorString(); + return Data::CollPtr(); + } + + m_coll = new Data::BookCollection(true); + Data::EntryList entries; + QVariantMap map = doc.object().toVariantMap(); + QMapIterator i(map); + while (i.hasNext()) { + i.next(); + QVariantMap valueMap = i.value().toMap(); + Data::EntryPtr entry(new Data::Entry(m_coll)); + entry->setField(QStringLiteral("title"), mapValue(valueMap, "title")); + entry->setField(QStringLiteral("pub_year"), mapValue(valueMap, "date")); + entry->setField(QStringLiteral("keyword"), mapValue(valueMap, "tags")); + entry->setField(QStringLiteral("genre"), mapValue(valueMap, "genre")); + entry->setField(QStringLiteral("series"), mapValue(valueMap, "series")); + entry->setField(QStringLiteral("language"), mapValue(valueMap, "language")); + + QJsonArray authorArray = valueMap.value(QStringLiteral("authors")).toJsonArray(); + QStringList authors; + for(int i = 0; i < authorArray.size(); ++i) { + QVariantMap m = authorArray.at(i).toObject().toVariantMap(); + // TODO: read config option for author formatting? + // use first-lastname for now + authors += mapValue(m, "fl"); + } + entry->setField(QStringLiteral("author"), authors.join(FieldFormat::delimiterString())); + + QJsonArray formatArray = valueMap.value(QStringLiteral("format")).toJsonArray(); + for(int i = 0; i < formatArray.size(); ++i) { + QVariantMap m = formatArray.at(i).toObject().toVariantMap(); + const QString format = mapValue(m, "text"); + if(format == QLatin1String("Paperback")) { + entry->setField(QStringLiteral("binding"), i18n("Paperback")); + } else if(format == QLatin1String("Hardcover")) { + entry->setField(QStringLiteral("binding"), i18n("Hardback")); + } else { + // just in case there's a value there + entry->setField(QStringLiteral("binding"), format); + } + break; + } + + QString isbn = mapValue(valueMap, "originalisbn"); + ISBNValidator::staticFixup(isbn); + entry->setField(QStringLiteral("isbn"), isbn); + + // grab first set of digits + QRegularExpression digits(QStringLiteral("\\d+")); + QRegularExpressionMatch match = digits.match(mapValue(valueMap, "pages")); + if(match.hasMatch()) { + entry->setField(QStringLiteral("pages"), match.captured(0)); + } + entries += entry; + } + m_coll->addEntries(entries); + return m_coll; +} + +QWidget* LibraryThingImporter::widget(QWidget* parent_) { + if(m_widget) { + return m_widget; + } + m_widget = new QWidget(parent_); + QVBoxLayout* l = new QVBoxLayout(m_widget); + + QGroupBox* gbox = new QGroupBox(i18n("LibraryThing Options"), m_widget); + QFormLayout* lay = new QFormLayout(gbox); + + lay->addRow(new QLabel(i18n("Export your LibraryThing collection in " + "JSON format."), gbox)); + + m_URLRequester = new KUrlRequester(gbox); + // these are in the old KDE4 filter format, not the Qt5 format + QString filter = QLatin1String("*.json|") + i18n("JSON Files") + + QLatin1Char('\n') + + QLatin1String("*|") + i18n("All Files"); + m_URLRequester->setFilter(filter); + + lay->addRow(i18n("LibraryThing file:"), m_URLRequester); + + l->addWidget(gbox); + l->addStretch(1); + + return m_widget; +} diff --git a/src/translators/translators.h b/src/translators/librarythingimporter.h similarity index 65% copy from src/translators/translators.h copy to src/translators/librarythingimporter.h index 57353472..ea2f33d9 100644 --- a/src/translators/translators.h +++ b/src/translators/librarythingimporter.h @@ -1,92 +1,62 @@ /*************************************************************************** - Copyright (C) 2003-2009 Robby Stephenson + Copyright (C) 2019 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ -#ifndef TRANSLATORS_H -#define TRANSLATORS_H +#ifndef TELLICO_IMPORT_LIBRARYTHINGIMPORTER_H +#define TELLICO_IMPORT_LIBRARYTHINGIMPORTER_H + +#include "importer.h" + +class KUrlRequester; namespace Tellico { namespace Import { - enum Format { - TellicoXML = 0, - Bibtex, - Bibtexml, - CSV, - XSLT, - AudioFile, - MODS, - Alexandria, - FreeDB, - RIS, - GCstar, - FileListing, - GRS1, - AMC, - Griffith, - PDF, - Referencer, - Delicious, - Goodreads, - CIW, - VinoXML, - BoardGameGeek - }; - enum Action { - Replace, - Append, - Merge - }; +/** + * @author Robby Stephenson +*/ +class LibraryThingImporter : public Importer { +Q_OBJECT + +public: + /** + */ + LibraryThingImporter(); + + virtual Data::CollPtr collection() Q_DECL_OVERRIDE; + virtual bool canImport(int type) const Q_DECL_OVERRIDE; - enum Target { - None, - File, - Dir - }; - } + virtual QWidget* widget(QWidget* parent) Q_DECL_OVERRIDE; - namespace Export { - enum Format { - TellicoXML = 0, - TellicoZip, - Bibtex, - Bibtexml, - HTML, - CSV, - XSLT, - Text, - PilotDB, // Deprecated - Alexandria, - ONIX, - GCstar - }; +public Q_SLOTS: + void slotCancel() Q_DECL_OVERRIDE {} - enum Target { - None, - File, - Dir - }; - } -} +private: + Data::CollPtr m_coll; + QWidget* m_widget; + KUrlRequester* m_URLRequester; +}; + } // end namespace +} // end namespace #endif diff --git a/src/translators/translators.h b/src/translators/translators.h index 57353472..76210c81 100644 --- a/src/translators/translators.h +++ b/src/translators/translators.h @@ -1,92 +1,93 @@ /*************************************************************************** Copyright (C) 2003-2009 Robby Stephenson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ #ifndef TRANSLATORS_H #define TRANSLATORS_H namespace Tellico { namespace Import { enum Format { TellicoXML = 0, Bibtex, Bibtexml, CSV, XSLT, AudioFile, MODS, Alexandria, FreeDB, RIS, GCstar, FileListing, GRS1, AMC, Griffith, PDF, Referencer, Delicious, Goodreads, CIW, VinoXML, - BoardGameGeek + BoardGameGeek, + LibraryThing }; enum Action { Replace, Append, Merge }; enum Target { None, File, Dir }; } namespace Export { enum Format { TellicoXML = 0, TellicoZip, Bibtex, Bibtexml, HTML, CSV, XSLT, Text, PilotDB, // Deprecated Alexandria, ONIX, GCstar }; enum Target { None, File, Dir }; } } #endif diff --git a/tellico.xml b/tellico.xml index a7b3c1b7..f73b2506 100644 --- a/tellico.xml +++ b/tellico.xml @@ -1,37 +1,37 @@ Tellico Data File Tellico datoteka s podacima Fitxer de dades del Tellico Fitxer de dades del Tellico Datový soubor Tellico Tellico-datafil Tellico-Datendatei Tellico Data File Archivo de datos de Tellico Tellico andmefail Tellico-tiedosto Fichier de données de Tellico Ficheiro de datos de Tellico File de Datos de Tellico File dati di Tellico Tellico 데이터 파일 Tellico-gegevensbestand Tellico-fil Plik danych Tellico Ficheiro de Dados do Tellico Arquivo de dados do Tellico - файл данных Tellico + Файл данных Tellico Dátový súbor Tellico Podatkovna datoteka Tellico Tellico-datafil Tellico Veri Dosyası файл даних Tellico Tellico 数据文件 Tellico 資料檔