diff -Nru kbibtex-0.3/config/CMakeLists.txt kbibtex-0.4/config/CMakeLists.txt --- kbibtex-0.3/config/CMakeLists.txt 2011-06-02 09:38:00.000000000 +0000 +++ kbibtex-0.4/config/CMakeLists.txt 2011-11-20 20:36:54.000000000 +0000 @@ -1 +1 @@ -install( FILES fieldtypes.rc entrytypes.rc entrylayout.rc DESTINATION ${DATA_INSTALL_DIR}/kbibtex ) +install( FILES kbibtexrc DESTINATION ${CONFIG_INSTALL_DIR} ) diff -Nru kbibtex-0.3/config/entrylayout.rc kbibtex-0.4/config/entrylayout.rc --- kbibtex-0.3/config/entrylayout.rc 2011-06-02 09:38:00.000000000 +0000 +++ kbibtex-0.4/config/entrylayout.rc 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -[Tab1] -uiCaption=Title -columns=1 - -bibtexLabel1=title -uiLabel1=Title -fieldInputLayout1=SingleLine - -bibtexLabel2=booktitle -uiLabel2=Book Title -fieldInputLayout2=SingleLine - -bibtexLabel3=series -uiLabel3=Series -fieldInputLayout3=SingleLine - -[Tab2] -uiCaption=Author/Editor -iconName=user-identity -columns=2 - -bibtexLabel1=author -uiLabel1=Author -fieldInputLayout1=PersonList - -bibtexLabel2=editor -uiLabel2=Editor -fieldInputLayout2=PersonList - -[Tab3] -uiCaption=Publication -columns=2 - -bibtexLabel1=journal -uiLabel1=Journal -fieldInputLayout1=SingleLine - -bibtexLabel2=volume -uiLabel2=Volume -fieldInputLayout2=SingleLine - -bibtexLabel3=number -uiLabel3=Number/Issue -fieldInputLayout3=SingleLine - -bibtexLabel4=month -uiLabel4=Month -fieldInputLayout4=Month - -bibtexLabel5=year -uiLabel5=Year -fieldInputLayout5=SingleLine - -bibtexLabel6=pages -uiLabel6=Pages -fieldInputLayout6=SingleLine - -bibtexLabel7=edition -uiLabel7=Edition -fieldInputLayout7=SingleLine - -bibtexLabel8=chapter -uiLabel8=Chapter -fieldInputLayout8=SingleLine - -bibtexLabel9=organization -uiLabel9=Organization -fieldInputLayout9=SingleLine - -bibtexLabel10=publisher -uiLabel10=Publisher -fieldInputLayout10=SingleLine - -bibtexLabel11=school -uiLabel11=School -fieldInputLayout11=SingleLine - -bibtexLabel12=institution -uiLabel12=Institution -fieldInputLayout12=SingleLine - -bibtexLabel13=location -uiLabel13=Location -fieldInputLayout13=SingleLine - -bibtexLabel14=address -uiLabel14=Address -fieldInputLayout14=SingleLine - -bibtexLabel15=isbn -uiLabel15=ISBN -fieldInputLayout15=ISBN - -bibtexLabel16=issn -uiLabel16=ISSN -fieldInputLayout165=ISSN - -bibtexLabel17=howpublished -uiLabel17=How Published -fieldInputLayout17=SingleLine - -bibtexLabel18=crossref -uiLabel18=Cross-Reference -fieldInputLayout18=SingleLine - -[Tab4] -uiCaption=Misc -columns=1 - -bibtexLabel1=type -uiLabel1=Type -fieldInputLayout1=SingleLine - -bibtexLabel2=key -uiLabel2=Key -fieldInputLayout2=SingleLine - -bibtexLabel3=note -uiLabel3=Note -fieldInputLayout3=SingleLine - -bibtexLabel4=abstract -uiLabel4=Abstract -fieldInputLayout4=MultiLine - -bibtexLabel5=keywords -uiLabel5=Keywords -fieldInputLayout5=List - -bibtexLabel6=x-color -uiLabel6=Color -fieldInputLayout6=Color - diff -Nru kbibtex-0.3/config/entrytypes.rc kbibtex-0.4/config/entrytypes.rc --- kbibtex-0.3/config/entrytypes.rc 2011-06-02 09:38:00.000000000 +0000 +++ kbibtex-0.4/config/entrytypes.rc 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -[EntryType1] -UpperCamelCase=Article -Label=Journal Article - -[EntryType2] -UpperCamelCase=InProceedings -UpperCamelCaseAlt=Conference -Label=Publication in Conference Proceedings - -[EntryType3] -UpperCamelCase=Proceedings -Label=Conference or Workshop Proceedings - -[EntryType4] -UpperCamelCase=TechReport -Label=Technical Report - -[EntryType5] -UpperCamelCase=Misc -Label=Miscellaneous - -[EntryType6] -UpperCamelCase=Book -Label=Book - -[EntryType7] -UpperCamelCase=InBook -Label=Part of a Book - -[EntryType8] -UpperCamelCase=InCollection -Label=Part of a Book with own Title - -[EntryType9] -UpperCamelCase=PhDThesis -Label=PhD Thesis - -[EntryType10] -UpperCamelCase=MastersThesis -Label=Master's Thesis - -[EntryType11] -UpperCamelCase=Unpublished -Label=Unpublish Material - -[EntryType12] -UpperCamelCase=Manual -Label=Manual - -[EntryType13] -UpperCamelCase=Booklet diff -Nru kbibtex-0.3/config/fieldtypes.rc kbibtex-0.4/config/fieldtypes.rc --- kbibtex-0.3/config/fieldtypes.rc 2011-06-02 09:38:00.000000000 +0000 +++ kbibtex-0.4/config/fieldtypes.rc 1970-01-01 00:00:00.000000000 +0000 @@ -1,238 +0,0 @@ -[Column1] -UpperCamelCase=^type -Label=Type -DefaultWidth=5 - -[Column2] -UpperCamelCase=^id -Label=Identifier -DefaultWidth=6 - -[Column3] -UpperCamelCase=Title -Label=Title -DefaultWidth=14 -Visible=false -TypeFlags=Text;Reference;Source - -[Column4] -UpperCamelCase=Title -UpperCamelCaseAlt=BookTitle -Label=Title or Book Title -DefaultWidth=14 - -[Column5] -UpperCamelCase=Author -UpperCamelCaseAlt=Editor -Label=Author or Editor -DefaultWidth=7 - -[Column6] -UpperCamelCase=Author -Label=Author -DefaultWidth=7 -Visible=false -TypeFlags=Person;Reference - -[Column7] -UpperCamelCase=Editor -Label=Editor -DefaultWidth=7 -Visible=false -TypeFlags=Person;Reference - -[Column8] -UpperCamelCase=Month -Label=Month -DefaultWidth=3 -Visible=false -TypeFlags=Text;Reference;Source - -[Column9] -UpperCamelCase=Year -Label=Year -DefaultWidth=2 -TypeFlags=Text;Reference;Source - -[Column10] -UpperCamelCase=Journal -Label=Journal -DefaultWidth=4 -Visible=false -TypeFlags=Text;Reference;Source - -[Column11] -UpperCamelCase=Volume -Label=Volume -DefaultWidth=1 -Visible=false -TypeFlags=Text;Reference;Source - -[Column12] -UpperCamelCase=Number -Label=Number -DefaultWidth=1 -Visible=false -TypeFlags=Text;Reference;Source - -[Column13] -UpperCamelCase=ISSN -Label=ISSN -DefaultWidth=2 -Visible=false -TypeFlags=Text;Reference;Source - -[Column14] -UpperCamelCase=ISBN -Label=ISBN -DefaultWidth=2 -Visible=false -TypeFlags=Text;Reference;Source - -[Column15] -UpperCamelCase=ISBN -UpperCamelCaseAlt=ISSN -Label=ISBN or ISSN -DefaultWidth=2 -Visible=false - -[Column16] -UpperCamelCase=HowPublished -Label=How Published -DefaultWidth=5 -Visible=false -TypeFlags=Text;Reference;Source - -[Column17] -UpperCamelCase=Note -Label=Note -DefaultWidth=5 -Visible=false -TypeFlags=Text;Reference;Source - -[Column18] -UpperCamelCase=Abstract -Label=Abstract -DefaultWidth=7 -Visible=false -TypeFlags=Text;Reference;Source - -[Column19] -UpperCamelCase=Pages -Label=Pages -DefaultWidth=2 -Visible=true -TypeFlags=Text;Reference;Source - -[Column20] -UpperCamelCase=Publisher -Label=Publisher -DefaultWidth=5 -Visible=false -TypeFlags=Text;Reference;Source - -[Column21] -UpperCamelCase=Institution -Label=Institution -DefaultWidth=5 -Visible=false -TypeFlags=Text;Reference;Source - -[Column22] -UpperCamelCase=BookTitle -Label=Book Title -DefaultWidth=14 -Visible=false -TypeFlags=Text;Reference;Source - -[Column23] -UpperCamelCase=Series -Label=Series -DefaultWidth=12 -Visible=false -TypeFlags=Text;Reference;Source - -[Column24] -UpperCamelCase=Edition -Label=Edition -DefaultWidth=2 -Visible=false -TypeFlags=Text;Reference;Source - -[Column25] -UpperCamelCase=Chapter -Label=Chapter -DefaultWidth=1 -Visible=false -TypeFlags=Text;Reference;Source - -[Column26] -UpperCamelCase=Organization -Label=Organization -DefaultWidth=2 -Visible=false -TypeFlags=Text;Reference;Source - -[Column27] -UpperCamelCase=School -Label=School -DefaultWidth=2 -Visible=false -TypeFlags=Text;Reference;Source - -[Column28] -UpperCamelCase=Keywords -Label=Keywords -DefaultWidth=3 -Visible=false -TypeFlags=Keyword;Source - -[Column29] -UpperCamelCase=CrossRef -Label=Cross Reference -DefaultWidth=3 -Visible=false -TypeFlags=Verbatim - -[Column30] -UpperCamelCase=DOI -Label=DOI -DefaultWidth=1 -Visible=false -TypeFlags=Verbatim - -[Column31] -UpperCamelCase=URL -Label=URL -DefaultWidth=3 -Visible=false -TypeFlags=Verbatim - -[Column32] -UpperCamelCase=Address -Label=Address -DefaultWidth=3 -Visible=false -TypeFlags=Text;Reference;Source - -[Column33] -UpperCamelCase=Location -Label=Location -DefaultWidth=3 -Visible=false -TypeFlags=Text;Reference;Source - -[Column34] -UpperCamelCase=Type -Label=Type -DefaultWidth=2 -Visible=false -TypeFlags=Text;Reference;Source - -[Column35] -UpperCamelCase=Key -Label=Key -DefaultWidth=2 -Visible=false -TypeFlags=Text;Reference;Source - diff -Nru kbibtex-0.3/config/kbibtexrc kbibtex-0.4/config/kbibtexrc --- kbibtex-0.3/config/kbibtexrc 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/config/kbibtexrc 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,459 @@ +[EntryLayoutTab] +count=4 + +[EntryLayoutTab1] +uiCaption=Title +columns=1 +count=3 + +bibtexLabel1=title +uiLabel1=Title +fieldInputLayout1=SingleLine + +bibtexLabel2=booktitle +uiLabel2=Book Title +fieldInputLayout2=SingleLine + +bibtexLabel3=series +uiLabel3=Series +fieldInputLayout3=SingleLine + +[EntryLayoutTab2] +uiCaption=Author/Editor +iconName=user-identity +columns=2 +count=2 + +bibtexLabel1=author +uiLabel1=Author +fieldInputLayout1=PersonList + +bibtexLabel2=editor +uiLabel2=Editor +fieldInputLayout2=PersonList + +[EntryLayoutTab3] +uiCaption=Publication +columns=2 +count=18 + +bibtexLabel1=journal +uiLabel1=Journal +fieldInputLayout1=SingleLine + +bibtexLabel2=volume +uiLabel2=Volume +fieldInputLayout2=SingleLine + +bibtexLabel3=number +uiLabel3=Number/Issue +fieldInputLayout3=SingleLine + +bibtexLabel4=month +uiLabel4=Month +fieldInputLayout4=Month + +bibtexLabel5=year +uiLabel5=Year +fieldInputLayout5=SingleLine + +bibtexLabel6=pages +uiLabel6=Pages +fieldInputLayout6=SingleLine + +bibtexLabel7=edition +uiLabel7=Edition +fieldInputLayout7=SingleLine + +bibtexLabel8=chapter +uiLabel8=Chapter +fieldInputLayout8=SingleLine + +bibtexLabel9=organization +uiLabel9=Organization +fieldInputLayout9=SingleLine + +bibtexLabel10=publisher +uiLabel10=Publisher +fieldInputLayout10=SingleLine + +bibtexLabel11=school +uiLabel11=School +fieldInputLayout11=SingleLine + +bibtexLabel12=institution +uiLabel12=Institution +fieldInputLayout12=SingleLine + +bibtexLabel13=location +uiLabel13=Location +fieldInputLayout13=SingleLine + +bibtexLabel14=address +uiLabel14=Address +fieldInputLayout14=SingleLine + +bibtexLabel15=isbn +uiLabel15=ISBN +fieldInputLayout15=ISBN + +bibtexLabel16=issn +uiLabel16=ISSN +fieldInputLayout165=ISSN + +bibtexLabel17=howpublished +uiLabel17=How Published +fieldInputLayout17=SingleLine + +bibtexLabel18=crossref +uiLabel18=Cross-Reference +fieldInputLayout18=CrossRef + +[EntryLayoutTab4] +uiCaption=Misc +columns=1 +count=6 + +bibtexLabel1=type +uiLabel1=Type +fieldInputLayout1=SingleLine + +bibtexLabel2=key +uiLabel2=Key +fieldInputLayout2=SingleLine + +bibtexLabel3=note +uiLabel3=Note +fieldInputLayout3=SingleLine + +bibtexLabel4=abstract +uiLabel4=Abstract +fieldInputLayout4=MultiLine + +bibtexLabel5=keywords +uiLabel5=Keywords +fieldInputLayout5=KeywordList + +bibtexLabel6=x-color +uiLabel6=Color +fieldInputLayout6=Color + +[EntryType] +count=13 + +[EntryType1] +UpperCamelCase=Article +Label=Journal Article + +[EntryType2] +UpperCamelCase=InProceedings +UpperCamelCaseAlt=Conference +Label=Publication in Conference Proceedings + +[EntryType3] +UpperCamelCase=Proceedings +Label=Conference or Workshop Proceedings + +[EntryType4] +UpperCamelCase=TechReport +Label=Technical Report + +[EntryType5] +UpperCamelCase=Misc +Label=Miscellaneous + +[EntryType6] +UpperCamelCase=Book +Label=Book + +[EntryType7] +UpperCamelCase=InBook +Label=Part of a Book + +[EntryType8] +UpperCamelCase=InCollection +Label=Part of a Book with own Title + +[EntryType9] +UpperCamelCase=PhDThesis +Label=PhD Thesis + +[EntryType10] +UpperCamelCase=MastersThesis +Label=Master's Thesis + +[EntryType11] +UpperCamelCase=Unpublished +Label=Unpublish Material + +[EntryType12] +UpperCamelCase=Manual +Label=Manual + +[EntryType13] +UpperCamelCase=Booklet + +[Column] +count=36 + +[Column1] +UpperCamelCase=^type +Label=Type +DefaultWidth=5 + +[Column2] +UpperCamelCase=^id +Label=Identifier +DefaultWidth=6 + +[Column3] +UpperCamelCase=Title +Label=Title +DefaultWidth=14 +Visible=false +TypeFlags=Text;Reference;Source + +[Column4] +UpperCamelCase=Title +UpperCamelCaseAlt=BookTitle +Label=Title or Book Title +DefaultWidth=14 + +[Column5] +UpperCamelCase=Author +UpperCamelCaseAlt=Editor +Label=Author or Editor +DefaultWidth=7 + +[Column6] +UpperCamelCase=Author +Label=Author +DefaultWidth=7 +Visible=false +TypeFlags=Person;Reference + +[Column7] +UpperCamelCase=Editor +Label=Editor +DefaultWidth=7 +Visible=false +TypeFlags=Person;Reference + +[Column8] +UpperCamelCase=Month +Label=Month +DefaultWidth=3 +Visible=false +TypeFlags=Text;Reference;Source + +[Column9] +UpperCamelCase=Year +Label=Year +DefaultWidth=2 +TypeFlags=Text;Reference;Source + +[Column10] +UpperCamelCase=Journal +Label=Journal +DefaultWidth=4 +Visible=false +TypeFlags=Text;Reference;Source + +[Column11] +UpperCamelCase=Volume +Label=Volume +DefaultWidth=1 +Visible=false +TypeFlags=Text;Reference;Source + +[Column12] +UpperCamelCase=Number +Label=Number +DefaultWidth=1 +Visible=false +TypeFlags=Text;Reference;Source + +[Column13] +UpperCamelCase=ISSN +Label=ISSN +DefaultWidth=2 +Visible=false +TypeFlags=Text;Reference;Source + +[Column14] +UpperCamelCase=ISBN +Label=ISBN +DefaultWidth=2 +Visible=false +TypeFlags=Text;Reference;Source + +[Column15] +UpperCamelCase=ISBN +UpperCamelCaseAlt=ISSN +Label=ISBN or ISSN +DefaultWidth=2 +Visible=false + +[Column16] +UpperCamelCase=HowPublished +Label=How Published +DefaultWidth=5 +Visible=false +TypeFlags=Text;Reference;Source + +[Column17] +UpperCamelCase=Note +Label=Note +DefaultWidth=5 +Visible=false +TypeFlags=Text;Reference;Source + +[Column18] +UpperCamelCase=Abstract +Label=Abstract +DefaultWidth=7 +Visible=false +TypeFlags=Text;Reference;Source + +[Column19] +UpperCamelCase=Pages +Label=Pages +DefaultWidth=2 +Visible=true +TypeFlags=Text;Reference;Source + +[Column20] +UpperCamelCase=Publisher +Label=Publisher +DefaultWidth=5 +Visible=false +TypeFlags=Text;Reference;Source + +[Column21] +UpperCamelCase=Institution +Label=Institution +DefaultWidth=5 +Visible=false +TypeFlags=Text;Reference;Source + +[Column22] +UpperCamelCase=BookTitle +Label=Book Title +DefaultWidth=14 +Visible=false +TypeFlags=Text;Reference;Source + +[Column23] +UpperCamelCase=Series +Label=Series +DefaultWidth=12 +Visible=false +TypeFlags=Text;Reference;Source + +[Column24] +UpperCamelCase=Edition +Label=Edition +DefaultWidth=2 +Visible=false +TypeFlags=Text;Reference;Source + +[Column25] +UpperCamelCase=Chapter +Label=Chapter +DefaultWidth=1 +Visible=false +TypeFlags=Text;Reference;Source + +[Column26] +UpperCamelCase=Organization +Label=Organization +DefaultWidth=2 +Visible=false +TypeFlags=Text;Reference;Source + +[Column27] +UpperCamelCase=School +Label=School +DefaultWidth=2 +Visible=false +TypeFlags=Text;Reference;Source + +[Column28] +UpperCamelCase=Keywords +Label=Keywords +DefaultWidth=3 +Visible=false +TypeFlags=Keyword;Source + +[Column29] +UpperCamelCase=CrossRef +Label=Cross Reference +DefaultWidth=3 +Visible=false +TypeFlags=Verbatim + +[Column30] +UpperCamelCase=DOI +Label=DOI +DefaultWidth=1 +Visible=false +TypeFlags=Verbatim + +[Column31] +UpperCamelCase=URL +Label=URL +DefaultWidth=3 +Visible=false +TypeFlags=Verbatim + +[Column32] +UpperCamelCase=Address +Label=Address +DefaultWidth=3 +Visible=false +TypeFlags=Text;Reference;Source + +[Column33] +UpperCamelCase=Location +Label=Location +DefaultWidth=3 +Visible=false +TypeFlags=Text;Reference;Source + +[Column34] +UpperCamelCase=Type +Label=Type +DefaultWidth=2 +Visible=false +TypeFlags=Text;Reference;Source + +[Column35] +UpperCamelCase=Key +Label=Key +DefaultWidth=2 +Visible=false +TypeFlags=Text;Reference;Source + +[Column36] +UpperCamelCase=X-Color +Label=Color +DefaultWidth=2 +Visible=false +TypeFlags=Verbatim;Source + +[Reference Preview Docklet] +PreviewStyles=abbrv (bibtex2html)|abbrv|bibtex2html,acm (bibtex2html)|acm|bibtex2html,alpha (bibtex2html)|alpha|bibtex2html,apalike (bibtex2html)|apalike|bibtex2html,ieeetr (bibtex2html)|ieeetr|bibtex2html,plain (bibtex2html)|plain|bibtex2html,siam (bibtex2html)|siam|bibtex2html,unsrt (bibtex2html)|unsrt|bibtex2html,standard (XML/XSLT)|standard|xml,fancy (XML/XSLT)|fancy|xml,abstract-only (XML/XSLT)|abstractonly|xml + +[Search Engines Docklet] +ACMDigitalLibrary=false +Bibsonomy=true +GoogleScholar=true +IEEEXplore=false +PubMed=false +ScienceDirect=false +SpringerLink=false +arXivorg=false + +[MainWindow] +State=AAAA/wAAAAD9AAAAAwAAAAAAAAFLAAADY/wCAAAAA/sAAAAgAGQAbwBjAGsARABvAGMAdQBtAGUAbgB0AEwAaQBzAHQBAAAARQAAAOkAAAB+AQAABfsAAAAcAGQAbwBjAGsAUwBlAGEAcgBjAGgARgByAG8AbQEAAAExAAABPwAAAT8BAAAF/AAAAnMAAAE1AAAAngEAAB36AAAAAAEAAAAC+wAAACgAZABvAGMAawBSAGUAZgBlAHIAZQBuAGMAZQBQAHIAZQB2AGkAZQB3AQAAAAD/////AAAA4gEAAAX7AAAAGgBkAG8AYwBrAFYAYQBsAHUAZQBMAGkAcwB0AQAAAAAAAAFLAAAAqQEAAAUAAAABAAABOwAAA2P8AgAAAAH7AAAAHABkAG8AYwBrAFUAcgBsAFAAcgBlAHYAaQBlAHcBAAAARQAAA2MAAABiAQAABQAAAAMAAAJsAAABbPwBAAAAAvsAAAAeAGQAbwBjAGsAUgBlAHMAdQBsAHQAcwBGAHIAbwBtAQAAAU4AAAJsAAAAawEAAAX7AAAAHgBkAG8AYwBrAEUAbABlAG0AZQBuAHQARgByAG8AbQAAAAG8AAACZQAAAmUBAAAFAAACbAAAAfQAAAABAAAAAgAAAAEAAAAC/AAAAAEAAAACAAAAAQAAABYAbQBhAGkAbgBUAG8AbwBsAEIAYQByAQAAAAD/////AAAAAAAAAAA= +ToolBarsMovable=Disabled diff -Nru kbibtex-0.3/debian/changelog kbibtex-0.4/debian/changelog --- kbibtex-0.3/debian/changelog 2011-07-18 13:30:30.000000000 +0000 +++ kbibtex-0.4/debian/changelog 2011-12-15 02:44:02.000000000 +0000 @@ -1,3 +1,26 @@ +kbibtex (0.4-1) unstable; urgency=low + + * New upstream release. + * Add patch to disable -Wcast-align flag to prevent a (presumably) + false-positive warning to cause a FTBFS (Closes: #647030). + * Adjust debian/copyright to include a standalone paragraph for the GPL-2. + * Added patch to allow compiling with latest GCC (missing header includes). + + -- Michael Hanke Thu, 15 Dec 2011 03:43:34 +0100 + +kbibtex (0.4~beta1-1) unstable; urgency=low + + * New upstream release (Closes: #643751). Most search engines from the 0.2 + series are back. Does not modify 'localfile' fields in BibTeX entries + anymore (Closes: #644024). + * Adjust watch file for 0.4 series. + * Update debian/copyright and migrate to DEP5-compliant format. + * Added rudimentary git-buildpackage configuration. + * Add patch that removes an unnecessary xdg-open interpreter shebang from + kbibtex.desktop. + + -- Michael Hanke Sun, 02 Oct 2011 09:14:19 +0200 + kbibtex (0.3-1) unstable; urgency=low * First upstream release for KDE4 (Closes: #634255). A number of search diff -Nru kbibtex-0.3/debian/copyright kbibtex-0.4/debian/copyright --- kbibtex-0.3/debian/copyright 2011-07-18 12:25:23.000000000 +0000 +++ kbibtex-0.4/debian/copyright 2011-12-15 02:12:56.000000000 +0000 @@ -1,27 +1,36 @@ -This package was debianized by Michael Hanke on -Thu, 8 Sep 2005 19:33:07 +0200. - -It was downloaded from http://www.unix-ag.uni-kl.de/~fischer/kbibtex/ - -Copyright (C) 2004-2009 by Thomas Fischer - -License: - - This package 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) any later version. - - This package 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 package; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA. - -On Debian systems, the complete text of the GNU General -Public License can be found in `/usr/share/common-licenses/GPL-2'. +Format: http://dep.debian.net/deps/dep5/ +Upstream-Name: kbibtex +Upstream-Contact: Thomas Fischer +Source: http://www.unix-ag.uni-kl.de/~fischer/kbibtex/ + + +Files: * +Copyright: 2004-2011, Thomas Fischer +License: GPL-2+ + +Files: debian/* +Copyright: 2005-2011, Michael Hanke +License: GPL-2+ + +License: GPL-2+ + 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) any later + version. + . + 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 package; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301 USA + . + On Debian systems, the full text of the GNU General Public + License version 2 can be found in the file + `/usr/share/common-licenses/GPL-2'. diff -Nru kbibtex-0.3/debian/gbp.conf kbibtex-0.4/debian/gbp.conf --- kbibtex-0.3/debian/gbp.conf 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/debian/gbp.conf 2011-10-02 06:44:56.000000000 +0000 @@ -0,0 +1,5 @@ +[DEFAULT] +# the default branch for upstream sources: +upstream-branch = upstream +# the default branch for the debian patch: +debian-branch = master diff -Nru kbibtex-0.3/debian/patches/compiler_fixes kbibtex-0.4/debian/patches/compiler_fixes --- kbibtex-0.3/debian/patches/compiler_fixes 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/debian/patches/compiler_fixes 2011-12-15 02:32:32.000000000 +0000 @@ -0,0 +1,11 @@ +--- a/src/gui/preferences/settingsabstractwidget.h ++++ b/src/gui/preferences/settingsabstractwidget.h +@@ -24,6 +24,8 @@ + #include + + #include ++#include ++#include + + class KComboBox; + diff -Nru kbibtex-0.3/debian/patches/no_wcast-align kbibtex-0.4/debian/patches/no_wcast-align --- kbibtex-0.3/debian/patches/no_wcast-align 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/debian/patches/no_wcast-align 2011-12-15 02:08:34.000000000 +0000 @@ -0,0 +1,12 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 0342839..526299a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -6,6 +6,7 @@ set(MANDIR "share/man" CACHE STRING "Where to install manpages") + + find_package( Qt4 REQUIRED ) + find_package( KDE4 REQUIRED ) ++string(REPLACE "-Wcast-align" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + + # FIXME may have to be cleaned up a little bit + # Contributed by Jeremy Cribb diff -Nru kbibtex-0.3/debian/patches/remove_desktop_interpreter kbibtex-0.4/debian/patches/remove_desktop_interpreter --- kbibtex-0.3/debian/patches/remove_desktop_interpreter 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/debian/patches/remove_desktop_interpreter 2011-10-02 07:14:15.000000000 +0000 @@ -0,0 +1,12 @@ +From: Michael Hanke +Subject: Remove unnecessary interpreter line from desktop file. + +Without being executable it has no effect, and being executable is not necessary +for a desktop file. +--- a/src/program/kbibtex.desktop ++++ b/src/program/kbibtex.desktop +@@ -1,4 +1,3 @@ +-#!/usr/bin/env xdg-open + [Desktop Entry] + Encoding=UTF-8 + Name=KBibTeX diff -Nru kbibtex-0.3/debian/patches/series kbibtex-0.4/debian/patches/series --- kbibtex-0.3/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/debian/patches/series 2011-12-15 02:30:59.000000000 +0000 @@ -0,0 +1,3 @@ +compiler_fixes +remove_desktop_interpreter +no_wcast-align diff -Nru kbibtex-0.3/debian/rules kbibtex-0.4/debian/rules --- kbibtex-0.3/debian/rules 2011-07-18 12:25:23.000000000 +0000 +++ kbibtex-0.4/debian/rules 2011-10-02 07:09:53.000000000 +0000 @@ -8,3 +8,6 @@ dh_auto_configure -- \ -DPLUGIN_INSTALL_DIR:PATH=/usr/lib/kde4 \ -DLIB_INSTALL_DIR:PATH=/usr/lib/kbibtex + +override_dh_install: + dh_install diff -Nru kbibtex-0.3/debian/watch kbibtex-0.4/debian/watch --- kbibtex-0.3/debian/watch 2011-07-18 12:25:23.000000000 +0000 +++ kbibtex-0.4/debian/watch 2011-10-02 06:08:45.000000000 +0000 @@ -1,4 +1,4 @@ version=3 opts="passive,uversionmangle=s/-test/\~test/;s/-beta/\~beta/" \ -http://download.gna.org/kbibtex/0.3/kbibtex-(.*)\.tar\.bz2 +http://download.gna.org/kbibtex/0.4/kbibtex-(.*)\.tar\.bz2 diff -Nru kbibtex-0.3/mime/bibliography.xml kbibtex-0.4/mime/bibliography.xml --- kbibtex-0.3/mime/bibliography.xml 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/mime/bibliography.xml 2011-11-20 20:36:54.000000000 +0000 @@ -18,4 +18,10 @@ + + + + EndNote library + + diff -Nru kbibtex-0.3/src/CMakeLists.txt kbibtex-0.4/src/CMakeLists.txt --- kbibtex-0.3/src/CMakeLists.txt 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/CMakeLists.txt 2011-11-20 20:36:55.000000000 +0000 @@ -2,26 +2,50 @@ include( KDE4Defaults ) +# check if QtWebKit is available, which seems to be not available +# on e.g. RedHat Enterprise Linux 6 or Scientific Linux 6 +IF(QT_QTWEBKIT_FOUND) +message(STATUS "Found QtWebKit, enabling WebKit support") +add_definitions(-DHAVE_QTWEBKIT) +ELSE(QT_QTWEBKIT_FOUND) +message(STATUS "QtWebKit not found, disabling WebKit support") +ENDIF(QT_QTWEBKIT_FOUND) + + MARK_AS_ADVANCED(SVNVERSION_EXECUTABLE) FIND_PROGRAM(SVNVERSION_EXECUTABLE svnversion) IF(SVNVERSION_EXECUTABLE) +IF(WIN32) +# single \" in an echo statement makes problems under Windows; don't know why + ADD_CUSTOM_TARGET(svnversion ALL + cmake -E echo "#ifndef VERSION_H" > "${CMAKE_BINARY_DIR}/version.h.tmp" + COMMAND cmake -E echo "#define VERSION_H" >> "${CMAKE_BINARY_DIR}/version.h.tmp" + COMMAND cmake -E echo "const char *versionNumber = \"SVN unknown\";" >> "${CMAKE_BINARY_DIR}/version.h.tmp" + COMMAND cmake -E echo "#endif // VERSION_H" >> "${CMAKE_BINARY_DIR}/version.h.tmp" + COMMAND cmake -E copy_if_different "${CMAKE_BINARY_DIR}/version.h.tmp" "${CMAKE_BINARY_DIR}/version.h" + COMMAND cmake -E remove "${CMAKE_BINARY_DIR}/version.h.tmp" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + COMMENT "Updating ${CMAKE_BINARY_DIR}/version.h..." + VERBATIM) +ELSE(WIN32) ADD_CUSTOM_TARGET(svnversion ALL cmake -E echo "#ifndef VERSION_H" > "${CMAKE_BINARY_DIR}/version.h.tmp" COMMAND cmake -E echo "#define VERSION_H" >> "${CMAKE_BINARY_DIR}/version.h.tmp" COMMAND cmake -E echo_append "const char *versionNumber = \"SVN r" >> "${CMAKE_BINARY_DIR}/version.h.tmp" COMMAND svnversion -n ${CMAKE_CURRENT_SOURCE_DIR} >> "${CMAKE_BINARY_DIR}/version.h.tmp" - COMMAND cmake -E echo " (branch/0.3)\";" >> "${CMAKE_BINARY_DIR}/version.h.tmp" + COMMAND cmake -E echo "\";" >> "${CMAKE_BINARY_DIR}/version.h.tmp" COMMAND cmake -E echo "#endif // VERSION_H" >> "${CMAKE_BINARY_DIR}/version.h.tmp" COMMAND cmake -E copy_if_different "${CMAKE_BINARY_DIR}/version.h.tmp" "${CMAKE_BINARY_DIR}/version.h" COMMAND cmake -E remove "${CMAKE_BINARY_DIR}/version.h.tmp" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" COMMENT "Updating ${CMAKE_BINARY_DIR}/version.h..." VERBATIM) +ENDIF(WIN32) ELSE(SVNVERSION_EXECUTABLE) ADD_CUSTOM_TARGET(svnversion ALL cmake -E echo "#ifndef VERSION_H" > "${CMAKE_BINARY_DIR}/version.h.tmp" COMMAND cmake -E echo "#define VERSION_H" >> "${CMAKE_BINARY_DIR}/version.h.tmp" - COMMAND cmake -E echo "const char *versionNumber = \"0.3-beta1 (0.2.90)\";" >> "${CMAKE_BINARY_DIR}/version.h.tmp" + COMMAND cmake -E echo "const char *versionNumber = \"0.3\";" >> "${CMAKE_BINARY_DIR}/version.h.tmp" COMMAND cmake -E echo "#endif // VERSION_H" >> "${CMAKE_BINARY_DIR}/version.h.tmp" COMMAND cmake -E copy_if_different "${CMAKE_BINARY_DIR}/version.h.tmp" "${CMAKE_BINARY_DIR}/version.h" COMMAND cmake -E remove "${CMAKE_BINARY_DIR}/version.h.tmp" @@ -49,6 +73,7 @@ ) add_subdirectory( libkbibtexio ) +add_subdirectory( processing ) add_subdirectory( websearch ) add_subdirectory( gui ) add_subdirectory( program ) diff -Nru kbibtex-0.3/src/gui/bibtex/bibtexeditor.cpp kbibtex-0.4/src/gui/bibtex/bibtexeditor.cpp --- kbibtex-0.3/src/gui/bibtex/bibtexeditor.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/bibtexeditor.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -23,9 +23,9 @@ #include #include -#include #include #include +#include #include #include @@ -47,10 +47,16 @@ { private: ElementEditor *elementEditor; + static const QString configGroupNameWindowSize; + KConfigGroup configGroup; + public: ElementEditorDialog(QWidget *parent) : KDialog(parent), elementEditor(NULL) { - // nothing + /// restore window size + KSharedConfigPtr config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))); + configGroup = KConfigGroup(config, configGroupNameWindowSize); + restoreDialogSize(configGroup); } /** @@ -71,6 +77,10 @@ /// Will be triggered when closing the dialog /// given a re-implementation of closeEvent as above virtual void slotButtonClicked(int button) { + /// save window size of Ok is clicked + if (button == KDialog::Ok) + saveDialogSize(configGroup); + /// ignore button event if it is from the Cancel button /// and the user does not want to discard his/her changes if (button != KDialog::Cancel || allowedToClose()) @@ -79,6 +89,9 @@ private: bool allowedToClose() { + /// save window size + saveDialogSize(configGroup); + /// if there unapplied changes in the editor widget ... /// ... ask user for consent to discard changes ... /// only the allow to close this dialog @@ -86,15 +99,12 @@ } }; -BibTeXEditor::BibTeXEditor(QWidget *parent) - : BibTeXFileView(parent), m_isReadOnly(false), m_current(NULL) -{ - connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(itemActivated(QModelIndex))); -} +const QString ElementEditorDialog::configGroupNameWindowSize = QLatin1String("ElementEditorDialog"); -void BibTeXEditor::setModel(QAbstractItemModel * model) +BibTeXEditor::BibTeXEditor(const QString &name, QWidget *parent) + : BibTeXFileView(name, parent), m_isReadOnly(false), m_current(NULL), m_filterBar(NULL) { - BibTeXFileView::setModel(model); + connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(itemActivated(QModelIndex))); } void BibTeXEditor::viewCurrentElement() @@ -146,6 +156,7 @@ if (elementEditor.elementChanged()) { emit currentElementChanged(currentElement(), bibTeXModel()->bibTeXFile()); + emit selectedElementsChanged(); emit modified(); } } @@ -199,7 +210,6 @@ QTreeView::currentChanged(current, previous); // FIXME necessary? m_current = bibTeXModel()->element(sortFilterProxyModel()->mapToSource(current).row()); - emit currentElementChanged(m_current, bibTeXModel()->bibTeXFile()); } void BibTeXEditor::selectionChanged(const QItemSelection & selected, const QItemSelection & deselected) @@ -218,10 +228,16 @@ m_selection.removeOne(bibTeXModel()->element((*it).row())); } - emit selectedElementsChanged(); } +void BibTeXEditor::mouseReleaseEvent(QMouseEvent *event) +{ + QTreeView::mouseReleaseEvent(event); + /// delay notification about change of current item to allow drag'n'drop to work + emit currentElementChanged(m_current, bibTeXModel()->bibTeXFile()); +} + void BibTeXEditor::selectionDelete() { QModelIndexList mil = selectionModel()->selectedRows(); @@ -258,6 +274,17 @@ return NULL; } +void BibTeXEditor::setFilterBar(FilterBar *filterBar) +{ + m_filterBar = filterBar; +} + +void BibTeXEditor::setFilterBarFilter(SortFilterBibTeXFileModel::FilterQuery fq) +{ + if (m_filterBar != NULL) + m_filterBar->setFilter(fq); +} + void BibTeXEditor::mouseMoveEvent(QMouseEvent *event) { emit editorMouseEvent(event); diff -Nru kbibtex-0.3/src/gui/bibtex/bibtexeditor.h kbibtex-0.4/src/gui/bibtex/bibtexeditor.h --- kbibtex-0.3/src/gui/bibtex/bibtexeditor.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/bibtexeditor.h 2011-11-20 20:36:54.000000000 +0000 @@ -25,6 +25,7 @@ #include +#include #include #include @@ -37,9 +38,7 @@ { Q_OBJECT public: - BibTeXEditor(QWidget *parent); - - virtual void setModel(QAbstractItemModel * model); + BibTeXEditor(const QString &name, QWidget *parent); const QList& selectedElements() const; const Element* currentElement() const; @@ -50,6 +49,8 @@ ValueListModel *valueListModel(const QString &field); + void setFilterBar(FilterBar *filterBar); + signals: void selectedElementsChanged(); void currentElementChanged(Element*, const File *); @@ -69,12 +70,14 @@ void setSelectedElement(Element*); void selectionDelete(); void externalModification(); + void setFilterBarFilter(SortFilterBibTeXFileModel::FilterQuery); protected: bool m_isReadOnly; void currentChanged(const QModelIndex & current, const QModelIndex & previous); void selectionChanged(const QItemSelection & selected, const QItemSelection & deselected); + void mouseReleaseEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void dragEnterEvent(QDragEnterEvent *event); @@ -87,6 +90,7 @@ private: Element* m_current; QList m_selection; + FilterBar *m_filterBar; }; diff -Nru kbibtex-0.3/src/gui/bibtex/bibtexfilemodel.cpp kbibtex-0.4/src/gui/bibtex/bibtexfilemodel.cpp --- kbibtex-0.3/src/gui/bibtex/bibtexfilemodel.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/bibtexfilemodel.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -18,12 +18,14 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include + #include #include #include #include -#include +#include #include #include @@ -31,16 +33,24 @@ #include #include #include +#include #include "bibtexfilemodel.h" static const QRegExp curlyRegExp("[{}]+"); +const QString SortFilterBibTeXFileModel::configGroupName = QLatin1String("User Interface"); + +SortFilterBibTeXFileModel::SortFilterBibTeXFileModel(QObject * parent) + : QSortFilterProxyModel(parent), m_internalModel(NULL), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))) +{ + loadState(); +}; + void SortFilterBibTeXFileModel::setSourceModel(QAbstractItemModel *model) { QSortFilterProxyModel::setSourceModel(model); m_internalModel = dynamic_cast(model); - m_bibtexFields = BibTeXFields::self(); } BibTeXFileModel *SortFilterBibTeXFileModel::bibTeXSourceModel() @@ -52,40 +62,55 @@ { m_filterQuery = filterQuery; m_filterQuery.field = filterQuery.field.toLower(); /// required for comparison in filter code - invalidateFilter(); + invalidate(); } bool SortFilterBibTeXFileModel::lessThan(const QModelIndex & left, const QModelIndex & right) const { int column = left.column(); - if (column == right.column() && (m_bibtexFields->at(column).upperCamelCase == QLatin1String("Author") || m_bibtexFields->at(column).upperCamelCase == QLatin1String("Editor"))) { - /// special sorting for authors or editors: check all names, compare last and then first names + Q_ASSERT(left.column() == right.column()); ///< assume that we only sort by column + + BibTeXFields *bibtexFields = BibTeXFields::self(); + const FieldDescription &fd = bibtexFields->at(column); + + if (column == right.column() && (fd.upperCamelCase == QLatin1String("Author") || fd.upperCamelCase == QLatin1String("Editor"))) { + /// special sorting for authors or editors: check all names, + /// compare last and then first names + + /// first, check if two entries (and not e.g. comments) are to be compared Entry *entryA = dynamic_cast(m_internalModel->element(left.row())); Entry *entryB = dynamic_cast(m_internalModel->element(right.row())); if (entryA == NULL || entryB == NULL) return QSortFilterProxyModel::lessThan(left, right); - Value valueA = entryA->value(m_bibtexFields->at(column).upperCamelCase); - Value valueB = entryB->value(m_bibtexFields->at(column).upperCamelCase); + /// retrieve values of both cells + Value valueA = entryA->value(fd.upperCamelCase); + Value valueB = entryB->value(fd.upperCamelCase); if (valueA.isEmpty()) - valueA = entryA->value(m_bibtexFields->at(column).upperCamelCaseAlt); + valueA = entryA->value(fd.upperCamelCaseAlt); if (valueB.isEmpty()) - valueB = entryB->value(m_bibtexFields->at(column).upperCamelCaseAlt); + valueB = entryB->value(fd.upperCamelCaseAlt); + /// if either value is empty, use default implementation if (valueA.isEmpty() || valueB.isEmpty()) return QSortFilterProxyModel::lessThan(left, right); + /// compare each person in both values for (Value::Iterator itA = valueA.begin(), itB = valueB.begin(); itA != valueA.end() && itB != valueB.end(); ++itA, ++itB) { Person *personA = dynamic_cast(*itA); Person *personB = dynamic_cast(*itB); + /// not a Person object in value? fall back to default implementation if (personA == NULL || personB == NULL) return QSortFilterProxyModel::lessThan(left, right); + /// get both values' next persons' last names for comparison QString nameA = personA->lastName().replace(curlyRegExp, ""); QString nameB = personB->lastName().replace(curlyRegExp, ""); int cmp = QString::compare(nameA, nameB, Qt::CaseInsensitive); if (cmp < 0) return true; if (cmp > 0) return false; + /// if last names were inconclusive ... + /// get both values' next persons' first names for comparison nameA = personA->firstName().replace(curlyRegExp, ""); nameB = personB->firstName().replace(curlyRegExp, ""); cmp = QString::compare(nameA, nameB, Qt::CaseInsensitive); @@ -95,20 +120,43 @@ // TODO Check for suffix and prefix? } + /// comparison by names did not work (was not conclusive) + /// fall back to default implementation return QSortFilterProxyModel::lessThan(left, right); - } else + } else { + /// if comparing two numbers, do not perform lexicographical sorting (i.e. 13 < 2), + /// but numerical sorting instead (i.e. 13 > 2) + const QString textLeft = left.data(Qt::DisplayRole).toString(); + const QString textRight = right.data(Qt::DisplayRole).toString(); + bool okLeft = false, okRight = false; + int numberLeft = textLeft.toInt(&okLeft); + int numberRight = textRight.toInt(&okRight); + if (okLeft && okRight) + return numberLeft < numberRight; + + + /// everything else can be sorted by default implementation + /// (i.e. alphabetically or lexicographically) return QSortFilterProxyModel::lessThan(left, right); + } } bool SortFilterBibTeXFileModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const { Q_UNUSED(source_parent) - if (m_filterQuery.terms.isEmpty()) return true; /// empty filter query - Element *rowElement = m_internalModel->element(source_row); Q_ASSERT(rowElement != NULL); + /// check if showing comments is disabled + if (!m_showComments && typeid(*rowElement) == typeid(Comment)) + return false; + /// check if showing macros is disabled + if (!m_showMacros && typeid(*rowElement) == typeid(Macro)) + return false; + + if (m_filterQuery.terms.isEmpty()) return true; /// empty filter query + Entry *entry = dynamic_cast(rowElement); if (entry != NULL) { /// if current row contains an Entry ... @@ -122,7 +170,7 @@ if (m_filterQuery.field.isEmpty() || m_filterQuery.field == it.key().toLower()) { int i = 0; for (QStringList::ConstIterator itsl = m_filterQuery.terms.constBegin(); itsl != m_filterQuery.terms.constEnd(); ++itsl, ++i) { - bool contains = it.value().containsPattern(*itsl); + bool contains = (*itsl).isEmpty() ? true : it.value().containsPattern(*itsl); any |= contains; all[i] |= contains; } @@ -185,21 +233,36 @@ return false; } +void SortFilterBibTeXFileModel::loadState() +{ + KConfigGroup configGroup(config, configGroupName); + m_showComments = configGroup.readEntry(BibTeXFileModel::keyShowComments, BibTeXFileModel::defaultShowComments); + m_showMacros = configGroup.readEntry(BibTeXFileModel::keyShowMacros, BibTeXFileModel::defaultShowMacros); +} + +const QString BibTeXFileModel::keyShowComments = QLatin1String("showComments"); +const bool BibTeXFileModel::defaultShowComments = true; +const QString BibTeXFileModel::keyShowMacros = QLatin1String("showMacros"); +const bool BibTeXFileModel::defaultShowMacros = true; -const QRegExp BibTeXFileModel::whiteSpace = QRegExp("(\\s\\n\\r\\t)+"); BibTeXFileModel::BibTeXFileModel(QObject * parent) : QAbstractTableModel(parent), m_bibtexFile(NULL) { - m_bibtexFields = BibTeXFields::self(); -// TODO + /// load mapping from color value to label + KSharedConfigPtr config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))); + KConfigGroup configGroup(config, Preferences::groupColor); + QStringList colorCodes = configGroup.readEntry(Preferences::keyColorCodes, Preferences::defaultColorCodes); + QStringList colorLabels = configGroup.readEntry(Preferences::keyColorLabels, Preferences::defaultcolorLabels); + for (QStringList::ConstIterator itc = colorCodes.constBegin(), itl = colorLabels.constBegin(); itc != colorCodes.constEnd() && itl != colorLabels.constEnd(); ++itc, ++itl) { + colorToLabel.insert(*itc, *itl); + } } BibTeXFileModel::~BibTeXFileModel() { if (m_bibtexFile != NULL) delete m_bibtexFile; -// TODO } File *BibTeXFileModel::bibTeXFile() @@ -210,18 +273,11 @@ void BibTeXFileModel::setBibTeXFile(File *bibtexFile) { + // FIXME delete old m_bibtexFile before overwriting it? m_bibtexFile = bibtexFile; reset(); // TODO necessary here? } -/* -QModelIndex BibTeXFileModel::index(int row, int column, const QModelIndex & parent) const -{ - Q_UNUSED(parent) - return createIndex(row, column, (void*)NULL); // parent == QModelIndex() ? createIndex(row, column, (void*)NULL) : QModelIndex(); -} -*/ - QModelIndex BibTeXFileModel::parent(const QModelIndex & index) const { Q_UNUSED(index) @@ -240,7 +296,7 @@ int BibTeXFileModel::columnCount(const QModelIndex & /*parent*/) const { - return m_bibtexFields->count(); + return BibTeXFields::self()->count(); } QVariant BibTeXFileModel::data(const QModelIndex &index, int role) const @@ -254,22 +310,27 @@ return QVariant(); /// for now, only display data (no editing or icons etc) - if (role != Qt::DisplayRole && role != Qt::ToolTipRole && role != Qt::DecorationRole) + if (role != Qt::DisplayRole && role != Qt::ToolTipRole && role != Qt::BackgroundRole) return QVariant(); - if (index.row() < m_bibtexFile->count() && index.column() < m_bibtexFields->count()) { - QString raw = m_bibtexFields->at(index.column()).upperCamelCase; - QString rawAlt = m_bibtexFields->at(index.column()).upperCamelCaseAlt; + BibTeXFields *bibtexFields = BibTeXFields::self(); + if (index.row() < m_bibtexFile->count() && index.column() < bibtexFields->count()) { + const FieldDescription &fd = bibtexFields->at(index.column()); + QString raw = fd.upperCamelCase; + QString rawAlt = fd.upperCamelCaseAlt; Element* element = (*m_bibtexFile)[index.row()]; Entry* entry = dynamic_cast(element); /// if BibTeX entry has a "x-color" field, use that color to highlight row - if (role == Qt::DecorationRole) { - QString color; - if (index.column() != 0 || entry == NULL || (color = PlainTextValue::text(entry->value("x-color"), m_bibtexFile)) == "#000000" || color.isEmpty()) + if (role == Qt::BackgroundRole) { + QString colorName; + if (entry == NULL || (colorName = PlainTextValue::text(entry->value("x-color"), m_bibtexFile)) == "#000000" || colorName.isEmpty()) return QVariant(); - else - return QVariant(QColor(color)); + else { + QColor color(colorName); + color.setAlphaF(0.75); + return QVariant(color); + } } if (entry != NULL) { @@ -284,14 +345,18 @@ return QVariant(entry->type()); } else return QVariant(label); + } else if (raw.toLower() == Entry::ftColor) { + QString text = PlainTextValue::text(entry->value(raw), m_bibtexFile); + if (text.isEmpty()) return QVariant(); + QString colorText = colorToLabel[text]; + if (colorText.isEmpty()) return QVariant(text); + return QVariant(colorText); } else { if (entry->contains(raw)) { - QString text = PlainTextValue::text(entry->value(raw), m_bibtexFile); - text = text.replace(whiteSpace, " "); + const QString text = PlainTextValue::text(entry->value(raw), m_bibtexFile).simplified(); return QVariant(text); } else if (!rawAlt.isNull() && entry->contains(rawAlt)) { - QString text = PlainTextValue::text(entry->value(rawAlt), m_bibtexFile); - text = text.replace(whiteSpace, " "); + const QString text = PlainTextValue::text(entry->value(rawAlt), m_bibtexFile).simplified(); return QVariant(text); } else return QVariant(); @@ -304,8 +369,7 @@ else if (raw == "^type") return QVariant(i18n("Macro")); else if (raw == "Title") { - QString text = PlainTextValue::text(macro->value(), m_bibtexFile); - text = text.replace(whiteSpace, " "); + const QString text = PlainTextValue::text(macro->value(), m_bibtexFile).simplified(); return QVariant(text); } else return QVariant(); @@ -315,7 +379,7 @@ if (raw == "^type") return QVariant(i18n("Comment")); else if (raw == Entry::ftTitle) { - QString text = comment->text().replace(QRegExp("[\\s\\n\\r\\t]+"), " "); + const QString text = comment->text().simplified(); return QVariant(text); } else return QVariant(); @@ -325,8 +389,7 @@ if (raw == "^type") return QVariant(i18n("Preamble")); else if (raw == Entry::ftTitle) { - QString text = PlainTextValue::text(preamble->value(), m_bibtexFile); - text = text.replace(QRegExp("[\\s\\n\\r\\t]+"), " "); + const QString text = PlainTextValue::text(preamble->value(), m_bibtexFile).simplified(); return QVariant(text); } else return QVariant(); @@ -341,10 +404,10 @@ QVariant BibTeXFileModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section < 0 || section >= m_bibtexFields->count()) + const BibTeXFields *bibtexFields = BibTeXFields::self(); + if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section < 0 || section >= bibtexFields->count()) return QVariant(); - - return m_bibtexFields->at(section).label; + return bibtexFields->at(section).label; } Qt::ItemFlags BibTeXFileModel::flags(const QModelIndex &index) const diff -Nru kbibtex-0.3/src/gui/bibtex/bibtexfilemodel.h kbibtex-0.4/src/gui/bibtex/bibtexfilemodel.h --- kbibtex-0.3/src/gui/bibtex/bibtexfilemodel.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/bibtexfilemodel.h 2011-11-20 20:36:54.000000000 +0000 @@ -27,6 +27,8 @@ #include #include +#include + #include #include @@ -50,10 +52,7 @@ QString field; }; - SortFilterBibTeXFileModel(QObject * parent = 0) - : QSortFilterProxyModel(parent) { - m_internalModel = NULL; - }; + SortFilterBibTeXFileModel(QObject * parent = 0); virtual void setSourceModel(QAbstractItemModel *model); BibTeXFileModel *bibTeXSourceModel(); @@ -67,8 +66,13 @@ private: BibTeXFileModel *m_internalModel; - BibTeXFields *m_bibtexFields; SortFilterBibTeXFileModel::FilterQuery m_filterQuery; + + KSharedConfigPtr config; + static const QString configGroupName; + bool m_showComments, m_showMacros; + + void loadState(); }; @@ -78,20 +82,24 @@ class KBIBTEXGUI_EXPORT BibTeXFileModel : public QAbstractTableModel { public: + static const QString keyShowComments; + static const bool defaultShowComments; + static const QString keyShowMacros; + static const bool defaultShowMacros; + BibTeXFileModel(QObject * parent = 0); virtual ~BibTeXFileModel(); File *bibTeXFile(); - void setBibTeXFile(File *bibtexFile); + virtual void setBibTeXFile(File *bibtexFile); - //virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex & index) const; virtual bool hasChildren(const QModelIndex & parent = QModelIndex()) const; virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; virtual int columnCount(const QModelIndex & parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - Qt::ItemFlags flags(const QModelIndex &index) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual bool removeRow(int row, const QModelIndex & parent = QModelIndex()); bool removeRowList(const QList &rows); @@ -102,9 +110,7 @@ private: File *m_bibtexFile; - BibTeXFields *m_bibtexFields; - - static const QRegExp whiteSpace; + QMap colorToLabel; }; diff -Nru kbibtex-0.3/src/gui/bibtex/bibtexfileview.cpp kbibtex-0.4/src/gui/bibtex/bibtexfileview.cpp --- kbibtex-0.3/src/gui/bibtex/bibtexfileview.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/bibtexfileview.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -30,8 +31,9 @@ #include "bibtexfilemodel.h" #include "bibtexfileview.h" -BibTeXFileView::BibTeXFileView(QWidget * parent) - : QTreeView(parent), m_signalMapperBibTeXFields(new QSignalMapper(this)) +BibTeXFileView::BibTeXFileView(const QString &name, QWidget * parent) + : QTreeView(parent), m_name(name), m_signalMapperBibTeXFields(new QSignalMapper(this)), + config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupName(QLatin1String("BibTeXFileView")), configHeaderState(QLatin1String("HeaderState_%1")) { /// general visual appearance and behaviour setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -40,26 +42,35 @@ setFrameStyle(QFrame::NoFrame); setAlternatingRowColors(true); setAllColumnsShowFocus(true); + setRootIsDecorated(false); /// header appearance and behaviour header()->setClickable(true); header()->setSortIndicatorShown(true); header()->setSortIndicator(-1, Qt::AscendingOrder); connect(header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(sort(int, Qt::SortOrder))); + connect(header(), SIGNAL(sectionMoved(int, int, int)), this, SLOT(columnsChanged())); + connect(header(), SIGNAL(sectionResized(int, int, int)), this, SLOT(columnsChanged())); ///< FIXME columns get resized later on + connect(header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(columnsChanged())); header()->setContextMenuPolicy(Qt::ActionsContextMenu); + /// restore header appearance + KConfigGroup configGroup(config, configGroupName); + QByteArray headerState = configGroup.readEntry(configHeaderState.arg(m_name), QByteArray()); + headerDefault = header()->saveState(); + header()->restoreState(headerState); + /// build context menu for header to show/hide single columns - BibTeXFields *bibtexFields = BibTeXFields::self(); int col = 0; - for (BibTeXFields::Iterator it = bibtexFields->begin(); it != bibtexFields->end(); ++it, ++col) { - QString label = (*it).label; - KAction *action = new KAction(label, header()); + foreach(const FieldDescription &fd, *BibTeXFields::self()) { + KAction *action = new KAction(fd.label, header()); action->setData(col); action->setCheckable(true); - action->setChecked((*it).visible); + action->setChecked(fd.visible[m_name]); connect(action, SIGNAL(triggered()), m_signalMapperBibTeXFields, SLOT(map())); m_signalMapperBibTeXFields->setMapping(action, action); header()->addAction(action); + ++col; } connect(m_signalMapperBibTeXFields, SIGNAL(mapped(QObject*)), this, SLOT(headerActionToggled(QObject*))); @@ -74,18 +85,6 @@ header()->addAction(action); } -BibTeXFileView::~BibTeXFileView() -{ - BibTeXFields *bibtexFields = BibTeXFields::self(); - - for (int i = header()->count() - 1; i >= 0; --i) { - FieldDescription fd = bibtexFields->at(i); - fd.width = columnWidth(i); - bibtexFields->replace(i, fd); - } - bibtexFields->save(); -} - void BibTeXFileView::setModel(QAbstractItemModel * model) { QTreeView::setModel(model); @@ -97,6 +96,11 @@ Q_ASSERT(m_sortFilterProxyModel != NULL); m_bibTeXFileModel = dynamic_cast(m_sortFilterProxyModel->sourceModel()); } + + /// sort according to session + if (header()->isSortIndicatorShown()) + sort(header()->sortIndicatorSection(), header()->sortIndicatorOrder()); + Q_ASSERT(m_bibTeXFileModel != NULL); } @@ -110,23 +114,52 @@ return m_sortFilterProxyModel; } -void BibTeXFileView::resizeEvent(QResizeEvent */*event*/) +void BibTeXFileView::resizeEvent(QResizeEvent *) { - BibTeXFields *bibtexFields = BibTeXFields::self(); int sum = 0; - int widgetWidth = size().width() - verticalScrollBar()->size().width(); + int widgetWidth = size().width() - verticalScrollBar()->size().width() - 8; - for (BibTeXFields::Iterator it = bibtexFields->begin(); it != bibtexFields->end(); ++it) - if ((*it).visible) - sum += (*it).width; + foreach(const FieldDescription &fd, *BibTeXFields::self()) { + if (fd.visible[m_name]) + sum += fd.width[m_name]; + } + Q_ASSERT(sum > 0); int col = 0; - for (BibTeXFields::Iterator it = bibtexFields->begin(); it != bibtexFields->end(); ++it, ++col) { - setColumnWidth(col, (*it).width * widgetWidth / sum); - setColumnHidden(col, !((*it).visible)); + foreach(const FieldDescription &fd, *BibTeXFields::self()) { + setColumnWidth(col, fd.width[m_name] * widgetWidth / sum); + setColumnHidden(col, !fd.visible[m_name]); + ++col; } } +void BibTeXFileView::columnResized(int column, int oldSize, int newSize) +{ + syncBibTeXFields(); + QTreeView::columnResized(column, oldSize, newSize); +} + +void BibTeXFileView::syncBibTeXFields() +{ + int i = 0; + BibTeXFields *bibtexFields = BibTeXFields::self(); + foreach(const FieldDescription &origFd, *bibtexFields) { + FieldDescription newFd(origFd); + newFd.width[m_name] = newFd.visible[m_name] ? columnWidth(i) : 0; + bibtexFields->replace(i, newFd); + ++i; + } + bibtexFields->save(); +} + +void BibTeXFileView::columnsChanged() +{ + QByteArray headerState = header()->saveState(); + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(configHeaderState.arg(m_name), headerState); + config->sync(); +} + void BibTeXFileView::headerActionToggled(QObject *obj) { KAction *action = dynamic_cast(obj); @@ -136,17 +169,51 @@ if (!ok) return; BibTeXFields *bibtexFields = BibTeXFields::self(); - FieldDescription fd = bibtexFields->at(col); - fd.visible = action->isChecked(); - if (fd.width < 4) fd.width = width() / 10; + FieldDescription fd(bibtexFields->at(col)); + fd.visible[m_name] = action->isChecked(); + bibtexFields->replace(col, fd); ///< replace already here to make sum calculation below work + + /// accumulate column widths (needed below) + int sum = 0; + foreach(const FieldDescription &fd, *BibTeXFields::self()) { + if (fd.visible[m_name]) + sum += fd.width[m_name]; + } + if (sum == 0) { + /// no more columns left visible, therefore re-visibiling this column + action->setChecked(fd.visible[m_name] = true); + sum = 10; + } + if (fd.visible[m_name]) { + /// column just got visible, reset width + fd.width[m_name] = sum / 10; + } + bibtexFields->replace(col, fd); resizeEvent(NULL); + syncBibTeXFields(); } void BibTeXFileView::headerResetToDefaults() { - BibTeXFields::self()->resetToDefaults(); + BibTeXFields *bibtexFields = BibTeXFields::self(); + bibtexFields->resetToDefaults(m_name); + foreach(QAction *action, header()->actions()) { + bool ok = false; + int col = (int)action->data().toInt(&ok); + if (ok) { + FieldDescription fd = bibtexFields->at(col); + action->setChecked(fd.visible[m_name]); + } + } + + /// reset column ordering + header()->restoreState(headerDefault); + KConfigGroup configGroup(config, configGroupName); + configGroup.deleteEntry(configHeaderState.arg(m_name)); + config->sync(); + resizeEvent(NULL); } diff -Nru kbibtex-0.3/src/gui/bibtex/bibtexfileview.h kbibtex-0.4/src/gui/bibtex/bibtexfileview.h --- kbibtex-0.3/src/gui/bibtex/bibtexfileview.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/bibtexfileview.h 2011-11-20 20:36:54.000000000 +0000 @@ -36,22 +36,34 @@ { Q_OBJECT public: - BibTeXFileView(QWidget * parent = 0); - virtual ~BibTeXFileView(); + BibTeXFileView(const QString &name, QWidget *parent = 0); virtual void setModel(QAbstractItemModel * model); BibTeXFileModel *bibTeXModel(); QSortFilterProxyModel *sortFilterProxyModel(); protected: + const QString m_name; + void resizeEvent(QResizeEvent *event); +protected slots: + void columnResized(int column, int oldSize, int newSize); + private: QSignalMapper *m_signalMapperBibTeXFields; BibTeXFileModel *m_bibTeXFileModel; QSortFilterProxyModel *m_sortFilterProxyModel; + KSharedConfigPtr config; + const QString configGroupName; + const QString configHeaderState; + QByteArray headerDefault; + + void syncBibTeXFields(); + private slots: + void columnsChanged(); void headerActionToggled(QObject *action); void headerResetToDefaults(); void sort(int, Qt::SortOrder); diff -Nru kbibtex-0.3/src/gui/bibtex/clipboard.cpp kbibtex-0.4/src/gui/bibtex/clipboard.cpp --- kbibtex-0.3/src/gui/bibtex/clipboard.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/clipboard.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -33,6 +33,9 @@ #include #include "clipboard.h" +const QString Clipboard::keyCopyReferenceCommand = QLatin1String("copyReferenceCommand"); +const QString Clipboard::defaultCopyReferenceCommand = QLatin1String(""); + class Clipboard::ClipboardPrivate { private: @@ -41,9 +44,11 @@ public: BibTeXEditor *bibTeXEditor; QPoint previousPosition; + KSharedConfigPtr config; + const QString configGroupName; ClipboardPrivate(BibTeXEditor *be, Clipboard *p) - : parent(p), bibTeXEditor(be) { + : parent(p), bibTeXEditor(be), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupName(QLatin1String("General")) { // TODO } @@ -55,6 +60,7 @@ } FileExporterBibTeX exporter; + exporter.setEncoding(QLatin1String("latex")); QBuffer buffer(bibTeXEditor); buffer.open(QBuffer::WriteOnly); exporter.save(&buffer, file); @@ -133,7 +139,14 @@ if (!references.isEmpty()) { QClipboard *clipboard = QApplication::clipboard(); - clipboard->setText(references.join(",")); + QString text = references.join(","); + + KConfigGroup configGroup(d->config, d->configGroupName); + const QString copyReferenceCommand = configGroup.readEntry(keyCopyReferenceCommand, defaultCopyReferenceCommand); + if (!copyReferenceCommand.isEmpty()) + text = QString(QLatin1String("\\%1{%2}")).arg(copyReferenceCommand).arg(text); + + clipboard->setText(text); } } @@ -159,9 +172,7 @@ mimeData->setData("text/plain", data); drag->setMimeData(mimeData); - Qt::DropAction dropAction = drag->exec(Qt::CopyAction); - kDebug() << "dropAction = " << dropAction; - // Q_ASSERT_X(dropAction == Qt::CopyAction, "void Clipboard::editorMouseEvent(QMouseEvent *event)", "Drag'n'drop is not the expected copy operation"); + drag->exec(Qt::CopyAction); } d->previousPosition = event->pos(); diff -Nru kbibtex-0.3/src/gui/bibtex/clipboard.h kbibtex-0.4/src/gui/bibtex/clipboard.h --- kbibtex-0.3/src/gui/bibtex/clipboard.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/clipboard.h 2011-11-20 20:36:54.000000000 +0000 @@ -21,7 +21,7 @@ #ifndef KBIBTEX_GUI_CLIPBOARD_H #define KBIBTEX_GUI_CLIPBOARD_H -#include "kbibtexio_export.h" +#include "kbibtexgui_export.h" #include @@ -32,11 +32,14 @@ class BibTeXEditor; -class KBIBTEXIO_EXPORT Clipboard : public QObject +class KBIBTEXGUI_EXPORT Clipboard : public QObject { Q_OBJECT public: + static const QString keyCopyReferenceCommand; + static const QString defaultCopyReferenceCommand; + Clipboard(BibTeXEditor *bibTeXEditor); public slots: diff -Nru kbibtex-0.3/src/gui/bibtex/findduplicatesui.cpp kbibtex-0.4/src/gui/bibtex/findduplicatesui.cpp --- kbibtex-0.3/src/gui/bibtex/findduplicatesui.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/findduplicatesui.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,570 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "bibtexeditor.h" +#include "bibtexfilemodel.h" +#include "findduplicatesui.h" +#include "findduplicates.h" +#include "bibtexentries.h" + +const int FieldNameRole = Qt::UserRole + 101; + +const int maxFieldsCount = 1024; + +/** + * Model to hold alternative values as visualized in the RadioTreeView + */ +class AlternativesItemModel : public QAbstractItemModel +{ +private: + /// marker to memorize in an index's internal id that it is a top-level index + static const quint32 noParentInternalId; + + /// parent widget, needed to get font from (for text in italics) + QTreeView *p; + + EntryClique *currentClique; + +public: + AlternativesItemModel(QTreeView *parent) + : QAbstractItemModel(parent), p(parent), currentClique(NULL) { + // nothing + } + + void setCurrentClique(EntryClique *currentClique) { + this->currentClique = currentClique; + } + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const { + if (parent == QModelIndex()) + return createIndex(row, column, noParentInternalId); + else if (parent.parent() == QModelIndex()) + return createIndex(row, column, parent.row()); + return QModelIndex(); + } + + QModelIndex parent(const QModelIndex &index) const { + if (index.internalId() >= noParentInternalId) + return QModelIndex(); + else + return createIndex(index.internalId(), 0, noParentInternalId); + } + + int rowCount(const QModelIndex &parent = QModelIndex()) const { + if (currentClique == NULL) + return 0; + + if (parent == QModelIndex()) { + /// top-level index, check how many lists of lists of alternatives exist + return currentClique->fieldCount(); + } else if (parent.parent() == QModelIndex()) { + /// first, find the map of alternatives for this chosen field name (see parent) + QString fieldName = parent.data(FieldNameRole).toString(); + QList alt = currentClique->values(fieldName); + /// second, return number of alternatives for list of alternatives + /// plus one for an "else" option + return alt.count() + (fieldName.startsWith('^') || fieldName == Entry::ftKeywords || fieldName == Entry::ftUrl ? 0 : 1); + } + + return 0; + } + + int columnCount(const QModelIndex &) const { + /// only one column in use + return 1; + } + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const { + Q_UNUSED(section) + Q_UNUSED(orientation) + + if (role == Qt::DisplayRole) + return i18n("Alternatives"); + return QVariant(); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { + if (index.parent() == QModelIndex()) { + /// top-level elements showing field names like "Title", "Authors", etc + const QString fieldName = currentClique->fieldList().at(index.row()).toLower(); + switch (role) { + case FieldNameRole: + /// plain-and-simple field name (all lower case) + return fieldName; + case Qt::ToolTipRole: + case Qt::DisplayRole: + /// nicely formatted field names for visual representation + if (fieldName == QLatin1String("^id")) + return i18n("Identifier"); + else if (fieldName == QLatin1String("^type")) + return i18n("Type"); + else + return BibTeXEntries::self()->format(fieldName, KBibTeX::cUpperCamelCase); + case IsRadioRole: + /// this is not to be a radio widget + return QVariant::fromValue(false); + case Qt::FontRole: + if (fieldName.startsWith('^')) { + QFont f = p->font(); + f.setItalic(true); + return f; + } + } + } else if (index.parent().parent() == QModelIndex()) { + /// second-level entries for alternatives + + /// start with determining which list of alternatives actually to use + QString fieldName = index.parent().data(FieldNameRole).toString(); + QList values = currentClique->values(fieldName); + + switch (role) { + case Qt::ToolTipRole: + case Qt::DisplayRole: + if (index.row() < values.count()) { + QString text = PlainTextValue::text(values.at(index.row())); + if (fieldName == QLatin1String("^type")) { + BibTeXEntries *be = BibTeXEntries::self(); + text = be->format(text, KBibTeX::cUpperCamelCase); + } + + /// textual representation of the alternative's value + return text; + } else + /// add an "else" option + return i18n("None of the above"); + case Qt::FontRole: + /// for the "else" option, make font italic + if (index.row() >= values.count()) { + QFont f = p->font(); + f.setItalic(true); + return f; + } + case Qt::CheckStateRole: { + if (fieldName != Entry::ftKeywords && fieldName != Entry::ftUrl) + return QVariant(); + + QList chosenValues = currentClique->chosenValues(fieldName); + QString text = PlainTextValue::text(values.at(index.row())); + foreach(Value value, chosenValues) { + if (PlainTextValue::text(value) == text) + return Qt::Checked; + } + + return Qt::Unchecked; + } + case RadioSelectedRole: { + if (fieldName == Entry::ftKeywords || fieldName == Entry::ftUrl) + return QVariant::fromValue(false); + + /// return selection status (true or false) for this alternative + Value chosen = currentClique->chosenValue(fieldName); + if (chosen.isEmpty()) + return QVariant::fromValue(index.row() >= values.count()); + else if (index.row() < values.count()) { + QString chosenPlainText = PlainTextValue::text(chosen); + QString rowPlainText = PlainTextValue::text(values[index.row()]); + return QVariant::fromValue(chosenPlainText == rowPlainText); + } + return QVariant::fromValue(false); + } + case IsRadioRole: + /// this is to be a radio widget + return QVariant::fromValue(fieldName != Entry::ftKeywords && fieldName != Entry::ftUrl); + } + } + + return QVariant(); + } + + bool setData(const QModelIndex & index, const QVariant & value, int role = RadioSelectedRole) { + if (index.parent() != QModelIndex()) { + QString fieldName = index.parent().data(FieldNameRole).toString(); + if (role == RadioSelectedRole && value.canConvert() && value.toBool() == true) { + /// start with determining which list of alternatives actually to use + QList values = currentClique->values(fieldName); + + /// store which alternative was selected + if (index.row() < values.count()) + currentClique->setChosenValue(fieldName, values[index.row()]); + else { + Value v; + currentClique->setChosenValue(fieldName, v); + } + + /// update view on neighbouring alternatives + emit dataChanged(index.sibling(0, 0), index.sibling(rowCount(index.parent()), 0)); + + return true; + } else if (role == Qt::CheckStateRole && (fieldName == Entry::ftKeywords || fieldName == Entry::ftUrl)) { + bool ok; + int checkState = value.toInt(&ok); + if (ok) { + QList values = currentClique->values(fieldName); + if (checkState == Qt::Checked) + currentClique->setChosenValue(fieldName, values[index.row()], EntryClique::AddValue); + else if (checkState == Qt::Unchecked) + currentClique->setChosenValue(fieldName, values[index.row()], EntryClique::RemoveValue); + + return true; + } + } + } + + return false; + } + + bool hasChildren(const QModelIndex & parent = QModelIndex()) const { + /// depth-two tree + return parent == QModelIndex() || parent.parent() == QModelIndex(); + } + + virtual Qt::ItemFlags flags(const QModelIndex &index) const { + Qt::ItemFlags f = QAbstractItemModel::flags(index); + if (index.parent() != QModelIndex()) { + QString fieldName = index.parent().data(FieldNameRole).toString(); + if (fieldName == Entry::ftKeywords || fieldName == Entry::ftUrl) + f |= Qt::ItemIsUserCheckable; + } + return f; + } +}; + +const quint32 AlternativesItemModel::noParentInternalId = 0xffffff; + + +class CheckableBibTeXFileModel : public BibTeXFileModel +{ +private: + QList cl; + int currentClique; + QTreeView *tv; + +public: + CheckableBibTeXFileModel(QList &cliqueList, QTreeView *treeView, QObject *parent = NULL) + : BibTeXFileModel(parent), cl(cliqueList), currentClique(0), tv(treeView) { + // nothing + } + + void setCurrentClique(EntryClique *currentClique) { + this->currentClique = cl.indexOf(currentClique); + } + + virtual QVariant data(const QModelIndex &index, int role) const { + if (role == Qt::CheckStateRole && index.column() == 1) { + Entry *entry = dynamic_cast(element(index.row())); + Q_ASSERT_X(entry != NULL, "CheckableBibTeXFileModel::data", "entry is NULL"); + if (entry != NULL) { + QList entryList = cl[currentClique]->entryList(); + if (entryList.contains(entry)) + return cl[currentClique]->isEntryChecked(entry) ? Qt::Checked : Qt::Unchecked; + } + } + + return BibTeXFileModel::data(index, role); + } + + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) { + bool ok; + int checkState = value.toInt(&ok); + Q_ASSERT_X(ok, "CheckableBibTeXFileModel::setData", QString("Could not convert value " + value.toString()).toAscii()); + if (ok && role == Qt::CheckStateRole && index.column() == 1) { + Entry *entry = dynamic_cast(element(index.row())); + if (entry != NULL) { + QList entryList = cl[currentClique]->entryList(); + if (entryList.contains(entry)) { + EntryClique *ec = cl[currentClique]; + ec->setEntryChecked(entry, checkState == Qt::Checked); + cl[currentClique] = ec; + emit dataChanged(index, index); + tv->reset(); + return true; + } + } + } + + return false; + } + + virtual Qt::ItemFlags flags(const QModelIndex &index) const { + Qt::ItemFlags f = BibTeXFileModel::flags(index); + if (index.column() == 1) + f |= Qt::ItemIsUserCheckable; + return f; + } +}; + + +class FilterIdBibTeXFileModel : public QSortFilterProxyModel +{ +private: + CheckableBibTeXFileModel *internalModel; + EntryClique* currentClique; + +public: + FilterIdBibTeXFileModel(QObject *parent = NULL) + : QSortFilterProxyModel(parent), internalModel(NULL), currentClique(NULL) { + // nothing + } + + void setCurrentClique(EntryClique* currentClique) { + Q_ASSERT(internalModel != NULL); + internalModel->setCurrentClique(currentClique); + this->currentClique = currentClique; + invalidate(); + } + + void setSourceModel(QAbstractItemModel *model) { + QSortFilterProxyModel::setSourceModel(model); + internalModel = dynamic_cast(model); + Q_ASSERT(internalModel != NULL); + } + + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { + Q_UNUSED(source_parent) + + if (internalModel != NULL && currentClique != NULL) { + Entry *entry = dynamic_cast(internalModel->element(source_row)); + if (entry != NULL) { + QList entryList = currentClique->entryList(); + if (entryList.contains(entry)) return true; + } + } + return false; + } +}; + + +class MergeWidget::MergeWidgetPrivate +{ +private: + MergeWidget *p; + +public: + File *file; + BibTeXEditor *editor; + KPushButton *buttonNext, *buttonPrev; + QLabel *labelWhichClique; + static const char *whichCliqueText; + + CheckableBibTeXFileModel *model; + FilterIdBibTeXFileModel *filterModel; + + RadioButtonTreeView *alternativesView; + AlternativesItemModel *alternativesItemModel; + + int currentClique; + QList cl; + + MergeWidgetPrivate(MergeWidget *parent, QList &cliqueList) + : p(parent), currentClique(0), cl(cliqueList) { + // nothing + } + + void setupGUI() { + p->setMinimumSize(p->fontMetrics().xHeight()*96, p->fontMetrics().xHeight()*64); + p->setBaseSize(p->fontMetrics().xHeight()*128, p->fontMetrics().xHeight()*96); + + QBoxLayout *layout = new QVBoxLayout(p); + + QLabel *label = new QLabel(i18n("Select your duplicates"), p); + layout->addWidget(label); + + QSplitter *splitter = new QSplitter(Qt::Vertical, p); + layout->addWidget(splitter); + + editor = new BibTeXEditor(QLatin1String("MergeWidget"), splitter); + editor->setReadOnly(true); + + alternativesView = new RadioButtonTreeView(splitter); + + model = new CheckableBibTeXFileModel(cl, alternativesView, p); + model->setBibTeXFile(new File(*file)); + + QBoxLayout *containerLayout = new QHBoxLayout(); + layout->addLayout(containerLayout); + containerLayout->addStretch(10); + labelWhichClique = new QLabel(p); + containerLayout->addWidget(labelWhichClique); + buttonPrev = new KPushButton(KIcon("go-previous"), i18n("Previous"), p); + containerLayout->addWidget(buttonPrev, 1); + buttonNext = new KPushButton(KIcon("go-next"), i18n("Next"), p); + containerLayout->addWidget(buttonNext, 1); + + filterModel = new FilterIdBibTeXFileModel(p); + filterModel->setSourceModel(model); + alternativesItemModel = new AlternativesItemModel(alternativesView); + + showCurrentClique(); + + connect(buttonPrev, SIGNAL(clicked()), p, SLOT(previousClique())); + connect(buttonNext, SIGNAL(clicked()), p, SLOT(nextClique())); + + connect(editor, SIGNAL(doubleClicked(QModelIndex)), editor, SLOT(viewCurrentElement())); + } + + void showCurrentClique() { + EntryClique *ec = cl[currentClique]; + filterModel->setCurrentClique(ec); + alternativesItemModel->setCurrentClique(ec); + editor->setModel(filterModel); + alternativesView->setModel(alternativesItemModel); + editor->reset(); + alternativesView->reset(); + alternativesView->expandAll(); + + buttonNext->setEnabled(currentClique >= 0 && currentClique < cl.count() - 1); + buttonPrev->setEnabled(currentClique > 0); + labelWhichClique->setText(i18n(whichCliqueText, currentClique + 1, cl.count())); + } + +}; + +const char* MergeWidget::MergeWidgetPrivate::whichCliqueText = "Showing clique %1 of %2."; + +MergeWidget::MergeWidget(File *file, QList &cliqueList, QWidget *parent) + : QWidget(parent), d(new MergeWidgetPrivate(this, cliqueList)) +{ + d->file = file; + d->setupGUI(); +} + +void MergeWidget::previousClique() +{ + if (d->currentClique > 0) { + --d->currentClique; + d->showCurrentClique(); + } +} + +void MergeWidget::nextClique() +{ + if (d->currentClique >= 0 && d->currentClique < d->cl.count() - 1) { + ++d->currentClique; + d->showCurrentClique(); + } +} + + +class FindDuplicatesUI::FindDuplicatesUIPrivate +{ +private: + FindDuplicatesUI *p; + +public: + KParts::Part *part; + BibTeXEditor *editor; + + FindDuplicatesUIPrivate(FindDuplicatesUI *parent, KParts::Part *kpart, BibTeXEditor *bibTeXEditor) + : p(parent), part(kpart), editor(bibTeXEditor) { + // nothing + } +}; + +FindDuplicatesUI::FindDuplicatesUI(KParts::Part *part, BibTeXEditor *bibTeXEditor) + : QObject(), d(new FindDuplicatesUIPrivate(this, part, bibTeXEditor)) +{ + KAction *newAction = new KAction(KIcon("tab-duplicate"), i18n("Find Duplicates"), this); + part->actionCollection()->addAction(QLatin1String("findduplicates"), newAction); + connect(newAction, SIGNAL(triggered()), this, SLOT(slotFindDuplicates())); +#if KDE_VERSION_MINOR >= 4 + part->replaceXMLFile(KStandardDirs::locate("appdata", "findduplicatesui.rc"), KStandardDirs::locateLocal("appdata", "findduplicatesui.rc"), true); +#endif +} + +void FindDuplicatesUI::slotFindDuplicates() +{ + // FIXME move to settings + //bool ok = false; + //int sensitivity = KInputDialog::getInteger(i18n("Sensitivity"), i18n("Enter a value for sensitivity.\n\nLow values (close to 0) require very similar entries for duplicate detection, larger values (close to 10000) are more likely to count entries as duplicates.\n\nPlease provide feedback to the developers if you have a suggestion for a better default value than 4000."), 4000, 0, 10000, 10, &ok, d->part->widget()); + //if (!ok) sensitivity = 4000; + int sensitivity = 4000; + + KDialog dlg(d->part->widget()); + FindDuplicates fd(&dlg, sensitivity); + File *file = d->editor->bibTeXModel()->bibTeXFile(); + bool deleteFileLater = false; + + int rowCount = d->editor->selectedElements().count() / d->editor->model()->columnCount(); + if (rowCount > 1 && rowCount < d->editor->model()->rowCount() && KMessageBox::questionYesNo(d->part->widget(), i18n("Multiple elements are selected. Do you want to search for duplicates only within the selection or in the whole document?"), i18n("Search only in selection?"), KGuiItem(i18n("Only in selection")), KGuiItem(i18n("Whole document"))) == KMessageBox::Yes) { + QModelIndexList mil = d->editor->selectionModel()->selectedRows(); + file = new File(); + deleteFileLater = true; + for (QModelIndexList::ConstIterator it = mil.constBegin(); it != mil.constEnd(); ++it) { + file->append(d->editor->bibTeXModel()->element(d->editor->sortFilterProxyModel()->mapToSource(*it).row())); + } + } + + QList cliques; + bool gotCanceled = fd.findDuplicateEntries(file, cliques); + if (gotCanceled) { + if (deleteFileLater) delete file; + return; + } + + if (cliques.isEmpty()) { + KMessageBox::information(d->part->widget(), i18n("No duplicates have been found."), i18n("No duplicates found")); + } else { + MergeWidget mw(d->editor->bibTeXModel()->bibTeXFile(), cliques, &dlg); + dlg.setMainWidget(&mw); + + if (dlg.exec() == QDialog::Accepted) { + MergeDuplicates md(&dlg); + file = d->editor->bibTeXModel()->bibTeXFile(); + if (md.mergeDuplicateEntries(cliques, file)) { + d->editor->bibTeXModel()->setBibTeXFile(file); + } + } + + while (!cliques.isEmpty()) { + EntryClique *ec = cliques.first(); + cliques.removeFirst(); + delete ec; + } + + d->editor->externalModification(); + } + + if (deleteFileLater) delete file; +} diff -Nru kbibtex-0.3/src/gui/bibtex/findduplicatesui.h kbibtex-0.4/src/gui/bibtex/findduplicatesui.h --- kbibtex-0.3/src/gui/bibtex/findduplicatesui.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/findduplicatesui.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,79 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_FINDDUPLICATES_H +#define KBIBTEX_GUI_FINDDUPLICATES_H + +#include "kbibtexgui_export.h" + +#include +#include + +namespace KParts +{ +class Part; +} +class KXMLGUIClient; +class KPushButton; + +class BibTeXEditor; +class EntryClique; +class File; + +class RadioButtonTreeView; +class AlternativesItemModel; +class CheckableBibTeXFileModel; +class FilterIdBibTeXFileModel; + +class MergeWidget : public QWidget +{ + Q_OBJECT + +public: + MergeWidget(File *file, QList &cliques, QWidget *parent); + void showCurrentClique(); + +private slots: + void previousClique(); + void nextClique(); + +private: + class MergeWidgetPrivate; + MergeWidgetPrivate *d; +}; + + +class KBIBTEXGUI_EXPORT FindDuplicatesUI : public QObject +{ + Q_OBJECT + +public: + FindDuplicatesUI(KParts::Part *part, BibTeXEditor *bibTeXEditor); + // TODO + +private slots: + void slotFindDuplicates(); + +private: + class FindDuplicatesUIPrivate; + FindDuplicatesUIPrivate *d; +}; + +#endif // KBIBTEX_GUI_FINDDUPLICATES_H diff -Nru kbibtex-0.3/src/gui/bibtex/findduplicatesui.rc kbibtex-0.4/src/gui/bibtex/findduplicatesui.rc --- kbibtex-0.3/src/gui/bibtex/findduplicatesui.rc 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/bibtex/findduplicatesui.rc 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff -Nru kbibtex-0.3/src/gui/CMakeLists.txt kbibtex-0.4/src/gui/CMakeLists.txt --- kbibtex-0.3/src/gui/CMakeLists.txt 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/CMakeLists.txt 2011-11-20 20:36:54.000000000 +0000 @@ -11,6 +11,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/widgets ${CMAKE_CURRENT_SOURCE_DIR}/widgets ${CMAKE_CURRENT_SOURCE_DIR}/config + ${CMAKE_CURRENT_SOURCE_DIR}/../processing/ ${CMAKE_CURRENT_SOURCE_DIR}/../libkbibtexio/ ${CMAKE_CURRENT_SOURCE_DIR}/../libkbibtexio/config ) @@ -19,7 +20,9 @@ field/fieldinput.cpp field/fieldlineedit.cpp field/fieldlistedit.cpp + field/colorlabelwidget.cpp bibtex/bibtexeditor.cpp + bibtex/findduplicatesui.cpp bibtex/clipboard.cpp bibtex/bibtexfilemodel.cpp bibtex/bibtexfileview.cpp @@ -27,13 +30,23 @@ element/elementwidgets.cpp widgets/menulineedit.cpp widgets/filterbar.cpp + widgets/radiobuttontreeview.cpp config/entrylayout.cpp + preferences/kbibtexpreferencesdialog.cpp + preferences/settingsgeneralwidget.cpp + preferences/settingsglobalkeywordswidget.cpp + preferences/settingscolorlabelwidget.cpp + preferences/settingsuserinterfacewidget.cpp + preferences/settingsfileexporterbibtexwidget.cpp + preferences/settingsfileexporterpdfpswidget.cpp + preferences/settingsfileexporterwidget.cpp + preferences/settingsabstractwidget.cpp valuelistmodel.cpp ) add_definitions( -DMAKE_KBIBTEXGUI_LIB ) -# debug area for KBibTeX's IO library +# debug area for KBibTeX's GUI library add_definitions(-DKDE_DEFAULT_DEBUG_AREA=101012) kde4_add_library( kbibtexgui SHARED ${kbibtexgui_LIB_SRCS} ) @@ -42,9 +55,11 @@ ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${KDE4_KDEUI_LIBS} - ${KDE4_KIO_LIBS} - kbibtexio + ${KDE4_KIO_LIBS} + kbibtexio + kbibtexproc ) -install(TARGETS kbibtexgui DESTINATION ${LIB_INSTALL_DIR}) +install( TARGETS kbibtexgui RUNTIME DESTINATION bin LIBRARY DESTINATION ${LIB_INSTALL_DIR} ) +install( FILES bibtex/findduplicatesui.rc DESTINATION ${DATA_INSTALL_DIR}/kbibtex ) diff -Nru kbibtex-0.3/src/gui/config/entrylayout.cpp kbibtex-0.4/src/gui/config/entrylayout.cpp --- kbibtex-0.3/src/gui/config/entrylayout.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/config/entrylayout.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -17,6 +17,7 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ + #include #include #include @@ -33,15 +34,13 @@ public: EntryLayout *p; - KConfig *systemDefaultsConfig; - KSharedConfigPtr userConfig; + KSharedConfigPtr config; static EntryLayout *singleton; EntryLayoutPrivate(EntryLayout *parent) - : p(parent) { - systemDefaultsConfig = new KConfig(KStandardDirs::locate("appdata", "entrylayout.rc"), KConfig::SimpleConfig); - userConfig = KSharedConfig::openConfig(KStandardDirs::locateLocal("appdata", "entrylayout.rc"), KConfig::SimpleConfig); + : p(parent), config(KSharedConfig::openConfig("kbibtexrc")) { + // nothing } static QString convert(KBibTeX::FieldInputType fil) { @@ -52,6 +51,8 @@ case KBibTeX::Month : return QLatin1String("Month"); case KBibTeX::Color : return QLatin1String("Color"); case KBibTeX::PersonList : return QLatin1String("PersonList"); + case KBibTeX::KeywordList : return QLatin1String("KeywordList"); + case KBibTeX::CrossRef : return QLatin1String("CrossRef"); default: return QLatin1String("SingleLine"); } } @@ -69,13 +70,13 @@ return KBibTeX::Color; else if (text == QLatin1String("PersonList")) return KBibTeX::PersonList; + else if (text == QLatin1String("KeywordList")) + return KBibTeX::KeywordList; + else if (text == QLatin1String("CrossRef")) + return KBibTeX::CrossRef; else return KBibTeX::SingleLine; } - - ~EntryLayoutPrivate() { - delete systemDefaultsConfig; - } }; EntryLayout *EntryLayout::EntryLayoutPrivate::singleton = NULL; @@ -101,23 +102,28 @@ void EntryLayout::load() { clear(); - for (int tab = 1; tab < entryLayoutMaxTabCount; ++tab) { - QString groupName = QString("Tab%1").arg(tab); - KConfigGroup usercg(d->userConfig, groupName); - KConfigGroup systemcg(d->systemDefaultsConfig, groupName); + + QString groupName = QLatin1String("EntryLayoutTab"); + KConfigGroup configGroup(d->config, groupName); + int tabCount = qMin(configGroup.readEntry("count", 0), entryLayoutMaxTabCount); + + for (int tab = 1; tab <= tabCount; ++tab) { + QString groupName = QString("EntryLayoutTab%1").arg(tab); + KConfigGroup configGroup(d->config, groupName); EntryTabLayout etl; - etl.uiCaption = systemcg.readEntry("uiCaption", ""); - etl.iconName = systemcg.readEntry("iconName", "entry"); - etl.columns = systemcg.readEntry("columns", 1); + etl.uiCaption = configGroup.readEntry("uiCaption", ""); + etl.iconName = configGroup.readEntry("iconName", "entry"); + etl.columns = configGroup.readEntry("columns", 1); if (etl.uiCaption.isEmpty()) continue; - for (int field = 1; field < entryLayoutMaxFieldPerTabCount; ++field) { + int fieldCount = qMin(configGroup.readEntry("count", 0), entryLayoutMaxFieldPerTabCount); + for (int field = 1; field <= fieldCount; ++field) { SingleFieldLayout sfl; - sfl.bibtexLabel = systemcg.readEntry(QString("bibtexLabel%1").arg(field), ""); - sfl.uiLabel = systemcg.readEntry(QString("uiLabel%1").arg(field), ""); - sfl.fieldInputLayout = EntryLayoutPrivate::convert(systemcg.readEntry(QString("fieldInputLayout%1").arg(field), "SingleLine")); + sfl.bibtexLabel = configGroup.readEntry(QString("bibtexLabel%1").arg(field), ""); + sfl.uiLabel = configGroup.readEntry(QString("uiLabel%1").arg(field), ""); + sfl.fieldInputLayout = EntryLayoutPrivate::convert(configGroup.readEntry(QString("fieldInputLayout%1").arg(field), "SingleLine")); if (sfl.bibtexLabel.isEmpty() || sfl.uiLabel.isEmpty()) continue; @@ -125,33 +131,50 @@ } append(etl); } + + if (isEmpty()) kWarning() << "List of entry layouts is empty"; } void EntryLayout::save() { - /* - int col = 1; - for (Iterator it = begin(); it != end(); ++it, ++col) { - QString groupName = QString("Column%1").arg(col); - KConfigGroup usercg(d->userConfig, groupName); - FieldDescription &fd = *it; - usercg.writeEntry("Width", fd.width); - usercg.writeEntry("Visible", fd.visible); + int tabCount = 0; + foreach(EntryTabLayout etl, *this) { + ++tabCount; + QString groupName = QString("EntryLayoutTab%1").arg(tabCount); + KConfigGroup configGroup(d->config, groupName); + + configGroup.writeEntry("uiCaption", etl.uiCaption); + configGroup.writeEntry("iconName", etl.iconName); + configGroup.writeEntry("columns", etl.columns); + + int fieldCount = 0; + foreach(SingleFieldLayout sfl, etl.singleFieldLayouts) { + ++fieldCount; + configGroup.writeEntry(QString("bibtexLabel%1").arg(fieldCount), sfl.bibtexLabel); + configGroup.writeEntry(QString("uiLabel%1").arg(fieldCount), sfl.uiLabel); + configGroup.writeEntry(QString("fieldInputLayout%1").arg(fieldCount), EntryLayoutPrivate::convert(sfl.fieldInputLayout)); + } + configGroup.writeEntry("count", fieldCount); } - d->userConfig->sync(); - */ + QString groupName = QLatin1String("EntryLayoutTab"); + KConfigGroup configGroup(d->config, groupName); + configGroup.writeEntry("count", tabCount); + + d->config->sync(); } void EntryLayout::resetToDefaults() { - /* - for (int col = 1; col < bibTeXFieldsMaxColumnCount; ++col) { - QString groupName = QString("Column%1").arg(col); - KConfigGroup usercg(d->userConfig, groupName); - usercg.deleteGroup(); + QString groupName = QLatin1String("EntryLayoutTab"); + KConfigGroup configGroup(d->config, groupName); + configGroup.deleteGroup(); + + for (int tab = 1; tab < entryLayoutMaxTabCount; ++tab) { + QString groupName = QString("EntryLayoutTab%1").arg(tab); + KConfigGroup configGroup(d->config, groupName); + configGroup.deleteGroup(); } - */ load(); } diff -Nru kbibtex-0.3/src/gui/element/elementeditor.cpp kbibtex-0.4/src/gui/element/elementeditor.cpp --- kbibtex-0.3/src/gui/element/elementeditor.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/element/elementeditor.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -42,6 +42,8 @@ #include "elementwidgets.h" #include "elementeditor.h" +#define testNullDelete(a) {if ((a)!=NULL) delete (a); (a)=NULL;} + class ElementEditor::ElementEditorPrivate { private: @@ -62,6 +64,10 @@ ElementEditorPrivate(Element *m, const File *f, ElementEditor *parent) : element(m), file(f), p(parent), previousWidget(NULL), referenceWidget(NULL), sourceWidget(NULL), elementChanged(false), elementUnapplied(false) { + internalEntry = NULL; + internalMacro = NULL; + internalComment = NULL; + internalPreamble = NULL; createGUI(); } @@ -175,10 +181,11 @@ (*it)->setModified(false); } - internalEntry = NULL; - internalMacro = NULL; - internalComment = NULL; - internalPreamble = NULL; + testNullDelete(internalEntry); + testNullDelete(internalEntry); + testNullDelete(internalMacro); + testNullDelete(internalComment); + testNullDelete(internalPreamble); const Entry *e = dynamic_cast(element); if (e != NULL) { internalEntry = new Entry(*e); @@ -224,9 +231,9 @@ Q_ASSERT(temp != NULL); previousWidget->apply(temp); - if (isSourceWidget) referenceWidget->apply(temp); + if (isSourceWidget && referenceWidget != NULL) referenceWidget->apply(temp); newWidget->reset(temp); - if (dynamic_cast(previousWidget) != NULL) + if (referenceWidget != NULL && dynamic_cast(previousWidget) != NULL) referenceWidget->reset(temp); } previousWidget = newWidget; @@ -260,7 +267,7 @@ Value crossRefVal = entry.value(Entry::ftCrossRef); if (!crossRefVal.isEmpty() && file != NULL) { crossRefStr = PlainTextValue::text(crossRefVal, file); - const Element *crossRefDest = file->containsKey(crossRefStr); + const Element *crossRefDest = file->containsKey(crossRefStr, File::etEntry); if (crossRefDest != NULL && typeid(*crossRefDest) == typeid(Entry)) dummyFile << new Entry(*dynamic_cast(crossRefDest)); else @@ -425,6 +432,16 @@ return d->elementUnapplied; } +int ElementEditor::currentTab() +{ + return d->tab->currentIndex(); +} + +void ElementEditor::setCurrentTab(int tabIndex) +{ + d->tab->setCurrentIndex(tabIndex); +} + void ElementEditor::tabChanged() { d->switchTo(d->tab->currentWidget()); diff -Nru kbibtex-0.3/src/gui/element/elementeditor.h kbibtex-0.4/src/gui/element/elementeditor.h --- kbibtex-0.3/src/gui/element/elementeditor.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/element/elementeditor.h 2011-11-20 20:36:54.000000000 +0000 @@ -37,10 +37,14 @@ public: ElementEditor(const Element *element, const File *file, QWidget *parent); ElementEditor(Element *element, const File *file, QWidget *parent); + void setReadOnly(bool isReadOnly = true); bool elementChanged(); bool elementUnapplied(); + int currentTab(); + void setCurrentTab(int tabIndex); + signals: void modified(bool); diff -Nru kbibtex-0.3/src/gui/element/elementwidgets.cpp kbibtex-0.4/src/gui/element/elementwidgets.cpp --- kbibtex-0.3/src/gui/element/elementwidgets.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/element/elementwidgets.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -27,13 +27,15 @@ #include #include #include +#include #include #include #include #include #include -#include +#include +#include #include #include @@ -45,6 +47,7 @@ #include #include #include +#include #include "elementwidgets.h" static const unsigned int interColumnSpace = 16; @@ -72,6 +75,10 @@ setModified(true); } +const QString ElementWidget::keyElementWidgetLayout = QLatin1String("ElementWidgetLayout"); +const Qt::Orientation ElementWidget::defaultElementWidgetLayout = Qt::Horizontal; + + EntryConfiguredWidget::EntryConfiguredWidget(EntryTabLayout &entryTabLayout, QWidget *parent) : ElementWidget(parent), etl(entryTabLayout) { @@ -102,14 +109,16 @@ if (entry == NULL) return false; /// clear all widgets - for (QMap::Iterator it = bibtexKeyToWidget.begin(); it != bibtexKeyToWidget.end(); ++it) + for (QMap::Iterator it = bibtexKeyToWidget.begin(); it != bibtexKeyToWidget.end(); ++it) { it.value()->clear(); + it.value()->setFile(m_file); + } for (Entry::ConstIterator it = entry->constBegin(); it != entry->constEnd(); ++it) { const QString key = it.key().toLower(); if (bibtexKeyToWidget.contains(key)) { FieldInput *fieldInput = bibtexKeyToWidget[key]; - fieldInput->setFile(m_file); + fieldInput->setElement(element); fieldInput->reset(it.value()); } } @@ -135,6 +144,24 @@ return KIcon(etl.iconName); } +void EntryConfiguredWidget::setFile(const File *file) +{ + if (file != NULL) + for (QMap::Iterator it = bibtexKeyToWidget.begin(); it != bibtexKeyToWidget.end(); ++it) { + /// list of unique values for same field + QStringList list = file->uniqueEntryValuesList(it.key()); + /// for crossref fields, add all entries' ids + if (it.key().toLower() == Entry::ftCrossRef) + list.append(file->allKeys(File::etEntry)); + /// add macro keys + list.append(file->allKeys(File::etMacro)); + + it.value()->setCompletionItems(list); + } + + ElementWidget::setFile(file); +} + bool EntryConfiguredWidget::canEdit(const Element *element) { return typeid(*element) == typeid(Entry); @@ -142,47 +169,77 @@ void EntryConfiguredWidget::createGUI() { + /// retrieve information from settings if labels should be + /// above widgets or on the left side + KSharedConfigPtr config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))); + const QString configGroupName(QLatin1String("User Interface")); + KConfigGroup configGroup(config, configGroupName); + Qt::Orientation layoutOrientation = (Qt::Orientation)configGroup.readEntry(keyElementWidgetLayout, (int)defaultElementWidgetLayout); + QGridLayout *gridLayout = new QGridLayout(this); - BibTeXFields *bf = BibTeXFields::self(); + const BibTeXFields *bf = BibTeXFields::self(); + /// determine how many rows a column should have int mod = etl.singleFieldLayouts.size() / etl.columns; if (etl.singleFieldLayouts.size() % etl.columns > 0) ++mod; + if (layoutOrientation == Qt::Vertical) mod *= 2; int row = 0, col = 0; - for (QList::ConstIterator sflit = etl.singleFieldLayouts.constBegin(); sflit != etl.singleFieldLayouts.constEnd(); ++sflit) { + foreach(const SingleFieldLayout &sfl, etl.singleFieldLayouts) { + /// add extra space between "columns" of labels and widgets if (row == 0 && col > 1) gridLayout->setColumnMinimumWidth(col - 1, interColumnSpace); - const FieldDescription *fd = bf->find((*sflit).bibtexLabel); - KBibTeX::TypeFlags typeFlags = fd == NULL ? KBibTeX::tfSource : fd->typeFlags; - KBibTeX::TypeFlag preferredTypeFlag = fd == NULL ? KBibTeX::tfSource : fd->preferredTypeFlag; - FieldInput *fieldInput = new FieldInput((*sflit).fieldInputLayout, preferredTypeFlag, typeFlags, this); - bibtexKeyToWidget.insert((*sflit).bibtexLabel, fieldInput); + /// create an editing widget for this field + const FieldDescription &fd = bf->find(sfl.bibtexLabel); + KBibTeX::TypeFlags typeFlags = fd.isNull() ? KBibTeX::tfSource : fd.typeFlags; + KBibTeX::TypeFlag preferredTypeFlag = fd.isNull() ? KBibTeX::tfSource : fd.preferredTypeFlag; + FieldInput *fieldInput = new FieldInput(sfl.fieldInputLayout, preferredTypeFlag, typeFlags, this); + fieldInput->setFieldKey(sfl.bibtexLabel); + bibtexKeyToWidget.insert(sfl.bibtexLabel, fieldInput); connect(fieldInput, SIGNAL(modified()), this, SLOT(gotModified())); - bool isVerticallyMinimumExpaning = (*sflit).fieldInputLayout == KBibTeX::MultiLine || (*sflit).fieldInputLayout == KBibTeX::List || (*sflit).fieldInputLayout == KBibTeX::PersonList; - - QLabel *label = new QLabel((*sflit).uiLabel + ":", this); + /// create a label next to the editing widget + QLabel *label = new QLabel(QString("%1:").arg(sfl.uiLabel), this); label->setBuddy(fieldInput); - gridLayout->addWidget(label, row, col, 1, 1, (isVerticallyMinimumExpaning ? Qt::AlignTop : Qt::AlignVCenter) | Qt::AlignRight); - gridLayout->addWidget(fieldInput, row, col + 1, 1, 1); + /// position both label and editing widget according to set layout + bool isVerticallyMinimumExpaning = sfl.fieldInputLayout == KBibTeX::MultiLine || sfl.fieldInputLayout == KBibTeX::List || sfl.fieldInputLayout == KBibTeX::PersonList || sfl.fieldInputLayout == KBibTeX::KeywordList; + gridLayout->addWidget(label, row, col, 1, 1, (isVerticallyMinimumExpaning ? Qt::AlignTop : Qt::AlignVCenter) | (layoutOrientation == Qt::Horizontal ? Qt::AlignRight : Qt::AlignLeft)); + if (layoutOrientation == Qt::Horizontal) ++col; + else ++row; + gridLayout->addWidget(fieldInput, row, col, 1, 1); gridLayout->setRowStretch(row, isVerticallyMinimumExpaning ? 1000 : 0); + /// move position counter ++row; + if (layoutOrientation == Qt::Horizontal) --col; + + /// check if column is full if (row >= mod) { - gridLayout->setColumnStretch(col, 1); - gridLayout->setColumnStretch(col + 1, 1000); + /// set columns' stretch + if (layoutOrientation == Qt::Horizontal) { + gridLayout->setColumnStretch(col, 1); + gridLayout->setColumnStretch(col + 1, 1000); + } else + gridLayout->setColumnStretch(col, 1000); + /// update/reset position in layout row = 0; - col += 3; + col += layoutOrientation == Qt::Horizontal ? 3 : 2; } } + /// fill tab's bottom with space gridLayout->setRowStretch(mod, 1); + + /// set last column's stretch if (row > 0) { - gridLayout->setColumnStretch(col, 1); - gridLayout->setColumnStretch(col + 1, 1000); + if (layoutOrientation == Qt::Horizontal) { + gridLayout->setColumnStretch(col, 1); + gridLayout->setColumnStretch(col + 1, 1000); + } else + gridLayout->setColumnStretch(col, 1000); } } @@ -319,6 +376,7 @@ { QVBoxLayout *layout = new QVBoxLayout(this); fileList = new FieldInput(KBibTeX::UrlList, KBibTeX::tfVerbatim, KBibTeX::tfVerbatim, this); + fileList->setFieldKey(QLatin1String("^external")); layout->addWidget(fileList); connect(fileList, SIGNAL(modified()), this, SLOT(gotModified())); } @@ -406,6 +464,7 @@ for (Value::ConstIterator it = value.constBegin(); it != value.constEnd(); ++it) combinedValue.append(*it); } + fileList->setElement(element); fileList->setFile(m_file); fileList->reset(combinedValue); @@ -579,46 +638,55 @@ void OtherFieldsWidget::createGUI() { + /// retrieve information from settings if labels should be + /// above widgets or on the left side + KSharedConfigPtr config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))); + const QString configGroupName(QLatin1String("User Interface")); + KConfigGroup configGroup(config, configGroupName); + Qt::Orientation layoutOrientation = (Qt::Orientation)configGroup.readEntry(keyElementWidgetLayout, (int)defaultElementWidgetLayout); + QGridLayout *layout = new QGridLayout(this); - layout->setColumnStretch(0, 0); - layout->setColumnStretch(1, 1); + /// set row and column stretches based on chosen layout + layout->setColumnStretch(0, layoutOrientation == Qt::Horizontal ? 0 : 1); + layout->setColumnStretch(1, layoutOrientation == Qt::Horizontal ? 1 : 0); layout->setColumnStretch(2, 0); layout->setRowStretch(0, 0); - layout->setRowStretch(1, 1); + layout->setRowStretch(layoutOrientation == Qt::Horizontal ? 1 : 3, 1); layout->setRowStretch(2, 0); - layout->setRowStretch(3, 0); - layout->setRowStretch(4, 1); + layout->setRowStretch(layoutOrientation == Qt::Horizontal ? 3 : 6, 0); + layout->setRowStretch(layoutOrientation == Qt::Horizontal ? 4 : 7, 1); QLabel *label = new QLabel(i18n("Name:"), this); - layout->addWidget(label, 0, 0, 1, 1); + layout->addWidget(label, layoutOrientation == Qt::Horizontal ? 0 : 0, layoutOrientation == Qt::Horizontal ? 0 : 0, 1, 1, (layoutOrientation == Qt::Horizontal ? Qt::AlignRight : Qt::AlignLeft)); + fieldName = new KLineEdit(this); - layout->addWidget(fieldName, 0, 1, 1, 1); + layout->addWidget(fieldName, layoutOrientation == Qt::Horizontal ? 0 : 1, layoutOrientation == Qt::Horizontal ? 1 : 0, 1, 1); label->setBuddy(fieldName); buttonAddApply = new KPushButton(KIcon("list-add"), i18n("Add"), this); buttonAddApply->setEnabled(false); - layout->addWidget(buttonAddApply, 0, 2, 1, 1); + layout->addWidget(buttonAddApply, layoutOrientation == Qt::Horizontal ? 0 : 1, layoutOrientation == Qt::Horizontal ? 2 : 1, 1, 1); label = new QLabel(i18n("Content:"), this); - layout->addWidget(label, 1, 0, 1, 1); + layout->addWidget(label, layoutOrientation == Qt::Horizontal ? 1 : 2, layoutOrientation == Qt::Horizontal ? 0 : 0, 1, 1, (layoutOrientation == Qt::Horizontal ? Qt::AlignRight : Qt::AlignLeft)); fieldContent = new FieldInput(KBibTeX::MultiLine, KBibTeX::tfSource, KBibTeX::tfSource, this); - layout->addWidget(fieldContent, 1, 1, 1, 2); + layout->addWidget(fieldContent, layoutOrientation == Qt::Horizontal ? 1 : 3, layoutOrientation == Qt::Horizontal ? 1 : 0, 1, 2); label->setBuddy(fieldContent); label = new QLabel(i18n("List:"), this); - layout->addWidget(label, 2, 0, 3, 1); + layout->addWidget(label, layoutOrientation == Qt::Horizontal ? 2 : 4, layoutOrientation == Qt::Horizontal ? 0 : 0, 1, 1, (layoutOrientation == Qt::Horizontal ? Qt::AlignRight : Qt::AlignLeft)); + otherFieldsList = new QTreeWidget(this); - QStringList header; - header << i18n("Key") << i18n("Value"); - otherFieldsList->setHeaderLabels(header); - layout->addWidget(otherFieldsList, 2, 1, 3, 1); + otherFieldsList->setHeaderLabels(QStringList() << i18n("Key") << i18n("Value")); + layout->addWidget(otherFieldsList, layoutOrientation == Qt::Horizontal ? 2 : 5, layoutOrientation == Qt::Horizontal ? 1 : 0, 3, 1); label->setBuddy(otherFieldsList); + buttonDelete = new KPushButton(KIcon("list-remove"), i18n("Delete"), this); buttonDelete->setEnabled(false); - layout->addWidget(buttonDelete, 2, 2, 1, 1); + layout->addWidget(buttonDelete, layoutOrientation == Qt::Horizontal ? 2 : 5, layoutOrientation == Qt::Horizontal ? 2 : 1, 1, 1); buttonOpen = new KPushButton(KIcon("document-open"), i18n("Open"), this); buttonOpen->setEnabled(false); - layout->addWidget(buttonOpen, 3, 2, 1, 1); + layout->addWidget(buttonOpen, layoutOrientation == Qt::Horizontal ? 3 : 6, layoutOrientation == Qt::Horizontal ? 2 : 1, 1, 1); connect(otherFieldsList, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(listElementExecuted(QTreeWidgetItem*, int))); connect(otherFieldsList, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(listCurrentChanged(QTreeWidgetItem*, QTreeWidgetItem*))); @@ -788,6 +856,26 @@ } +class SourceWidget::SourceWidgetTextEdit : public QTextEdit +{ +public: + SourceWidgetTextEdit(QWidget *parent) + : QTextEdit(parent) { + // nothing + } + +protected: + virtual void dropEvent(QDropEvent *event) { + FileImporterBibTeX importer; + FileExporterBibTeX exporter; + const File *file = importer.fromString(event->mimeData()->text()); + if (file->count() == 1) + document()->setPlainText(exporter.toString(file->first())); + else + QTextEdit::dropEvent(event); + } +}; + SourceWidget::SourceWidget(QWidget *parent) : ElementWidget(parent) { @@ -816,6 +904,13 @@ if (readMacro != NULL && macro != NULL) { macro->operator =(*readMacro); result = true; + } else { + Preamble *preamble = dynamic_cast(element); + Preamble *readPreamble = dynamic_cast(file->first()); + if (readPreamble != NULL && preamble != NULL) { + preamble->operator =(*readPreamble); + result = true; + } } } } @@ -831,6 +926,7 @@ disconnect(sourceEdit, SIGNAL(textChanged()), this, SLOT(gotModified())); FileExporterBibTeX exporter; + exporter.setEncoding(QLatin1String("utf-8")); QBuffer textBuffer; textBuffer.open(QIODevice::WriteOnly); bool result = exporter.save(&textBuffer, element, NULL); @@ -877,7 +973,7 @@ layout->setRowStretch(0, 1); layout->setRowStretch(1, 0); - sourceEdit = new QTextEdit(this); + sourceEdit = new SourceWidgetTextEdit(this); layout->addWidget(sourceEdit, 0, 0, 1, 3); sourceEdit->document()->setDefaultFont(KGlobalSettings::fixedFont()); sourceEdit->setTabStopWidth(QFontMetrics(sourceEdit->font()).averageCharWidth() * 4); diff -Nru kbibtex-0.3/src/gui/element/elementwidgets.h kbibtex-0.4/src/gui/element/elementwidgets.h --- kbibtex-0.3/src/gui/element/elementwidgets.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/element/elementwidgets.h 2011-11-20 20:36:54.000000000 +0000 @@ -30,7 +30,6 @@ #include -class QTextEdit; class QTreeWidget; class QTreeWidgetItem; @@ -48,6 +47,9 @@ Q_OBJECT public: + static const QString keyElementWidgetLayout; + static const Qt::Orientation defaultElementWidgetLayout; + ElementWidget(QWidget *parent); virtual bool apply(Element *element) const = 0; virtual bool reset(const Element *element) = 0; @@ -59,7 +61,7 @@ bool isModified() const; void setModified(bool); - void setFile(const File *file) { + virtual void setFile(const File *file) { m_file = file; } @@ -98,6 +100,8 @@ QString label(); KIcon icon(); + virtual void setFile(const File *file); + static bool canEdit(const Element *element); }; @@ -221,7 +225,8 @@ Q_OBJECT private: - QTextEdit *sourceEdit; + class SourceWidgetTextEdit; + SourceWidgetTextEdit *sourceEdit; QString originalText; void createGUI(); diff -Nru kbibtex-0.3/src/gui/field/colorlabelwidget.cpp kbibtex-0.4/src/gui/field/colorlabelwidget.cpp --- kbibtex-0.3/src/gui/field/colorlabelwidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/field/colorlabelwidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,197 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "colorlabelwidget.h" + +const int ColorRole = Qt::UserRole + 521; + +class ColorLabelComboBoxModel : public QAbstractItemModel +{ +public: + struct ColorLabelPair { + QColor color; + QString label; + }; + + QList colorLabelPairs; + QColor userColor; + + KSharedConfigPtr config; + + ColorLabelComboBoxModel(QObject *p = NULL) + : QAbstractItemModel(p), userColor(Qt::black), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))) { + KConfigGroup configGroup(config, Preferences::groupColor); + QStringList colorCodes = configGroup.readEntry(Preferences::keyColorCodes, Preferences::defaultColorCodes); + QStringList colorLabels = configGroup.readEntry(Preferences::keyColorLabels, Preferences::defaultcolorLabels); + + for (QStringList::ConstIterator itc = colorCodes.constBegin(), itl = colorLabels.constBegin(); itc != colorCodes.constEnd() && itl != colorLabels.constEnd(); ++itc, ++itl) { + ColorLabelPair clp; + clp.color = QColor(*itc); + clp.label = *itl; + colorLabelPairs << clp; + } + } + + QModelIndex index(int row, int column, const QModelIndex&) const { + return createIndex(row, column); + } + + QModelIndex parent(const QModelIndex & = QModelIndex()) const { + return QModelIndex(); + } + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const { + return parent == QModelIndex() ? 2 + colorLabelPairs.count() : 0; + } + + virtual int columnCount(const QModelIndex & = QModelIndex()) const { + return 1; + } + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { + if (role == ColorRole) { + if (index.row() == 0) + return Qt::black; + else if (index.row() == rowCount() - 1) + return userColor; + else + return colorLabelPairs[index.row()-1].color; + } else if (role == Qt::FontRole && (index.row() == 0 || index.row() == rowCount() - 1)) { + QFont font; + font.setItalic(true); + return font; + } else if (role == Qt::DecorationRole && index.row() > 0 && (index.row() < rowCount() - 1 || userColor != Qt::black)) { + QColor color = data(index, ColorRole).value(); + return ColorLabelWidget::createSolidIcon(color); + } else if (role == Qt::DisplayRole) + if (index.row() == 0) + return i18n("No color"); + else if (index.row() == rowCount() - 1) + return i18n("User-defined color"); + else + return colorLabelPairs[index.row()-1].label; + else + return QVariant(); + } + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const { + if (section != 0 || orientation != Qt::Horizontal || role != Qt::DisplayRole) + return QVariant(); + + return i18n("Color & Label"); + } + + void setColor(const QColor &newColor) { + userColor = newColor; + const QModelIndex idx = index(rowCount() - 1, 0, QModelIndex()); + emit dataChanged(idx, idx); + } +}; + +class ColorLabelWidget::ColorLabelWidgetPrivate +{ +private: + ColorLabelWidget *parent; + +public: + ColorLabelComboBoxModel *model; + + ColorLabelWidgetPrivate(ColorLabelWidget *p) + : parent(p) { + // nothing + } +}; + +ColorLabelWidget::ColorLabelWidget(QWidget *parent) + : KComboBox(false, parent), d(new ColorLabelWidgetPrivate(this)) +{ + d->model = new ColorLabelComboBoxModel(this); + setModel(d->model); + connect(this, SIGNAL(activated(int)), this, SLOT(slotActivated(int))); +} + +bool ColorLabelWidget::reset(const Value& value) +{ + int i = 0; + VerbatimText *verbatimText = NULL; + if (value.count() == 1 && (verbatimText = dynamic_cast(value.first())) != NULL) { + const QColor color = QColor(verbatimText->text()); + for (; i < d->model->rowCount(); ++i) + if (d->model->data(d->model->index(i, 0, QModelIndex()), ColorRole).value() == color) + break; + + if (i >= d->model->rowCount()) { + d->model->userColor = color; + i = d->model->rowCount() - 1; + } + } + setCurrentIndex(i); + + return true; +} + +bool ColorLabelWidget::apply(Value& value) const +{ + QColor color = d->model->data(d->model->index(currentIndex(), 0, QModelIndex()), ColorRole).value(); + value.clear(); + if (color != Qt::black) { + VerbatimText *verbatimText = new VerbatimText(color.name()); + value << verbatimText; + } + return true; +} + +void ColorLabelWidget::setReadOnly(bool isReadOnly) +{ + setEnabled(!isReadOnly); +} + +void ColorLabelWidget::slotActivated(int index) +{ + if (index == count() - 1) { + QColor dialogColor = d->model->userColor; + if (KColorDialog::getColor(dialogColor, this) == KColorDialog::Accepted) + d->model->setColor(dialogColor); + } + + emit modified(); +} + +QPixmap ColorLabelWidget::createSolidIcon(const QColor &color) +{ + QFontMetrics fm = QFontMetrics(QFont()); + int h = fm.height() - 4; + QPixmap pm(h, h); + QPainter painter(&pm); + painter.setPen(color); + painter.setBrush(QBrush(color)); + painter.drawRect(0, 0, h, h); + return pm; +} diff -Nru kbibtex-0.3/src/gui/field/colorlabelwidget.h kbibtex-0.4/src/gui/field/colorlabelwidget.h --- kbibtex-0.3/src/gui/field/colorlabelwidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/field/colorlabelwidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,57 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_COLORLABELWIDGET_H +#define KBIBTEX_GUI_COLORLABELWIDGET_H + +#include + +#include + +#include + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT ColorLabelWidget : public KComboBox +{ + Q_OBJECT + +public: + ColorLabelWidget(QWidget *parent = NULL); + + bool reset(const Value& value); + bool apply(Value& value) const; + void setReadOnly(bool); + + static QPixmap createSolidIcon(const QColor &color); + +signals: + void modified(); + +private slots: + void slotActivated(int); + +private: + class ColorLabelWidgetPrivate; + ColorLabelWidget::ColorLabelWidgetPrivate *d; +}; + +#endif // KBIBTEX_GUI_COLORLABELWIDGET_H diff -Nru kbibtex-0.3/src/gui/field/fieldinput.cpp kbibtex-0.4/src/gui/field/fieldinput.cpp --- kbibtex-0.3/src/gui/field/fieldinput.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/field/fieldinput.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -22,16 +22,17 @@ #include #include #include -#include -#include #include #include #include -#include +#include +#include +#include #include #include +#include #include "fieldinput.h" class FieldInput::FieldInputPrivate @@ -40,20 +41,17 @@ FieldInput *p; FieldLineEdit *fieldLineEdit; FieldListEdit *fieldListEdit; - KColorButton *colorButton; - KPushButton *predefColorButton; - KPushButton *resetColorButton; - QWidget *colorWidget; - QMenu *colorMenu; - QSignalMapper *colorSignalMapper; + ColorLabelWidget *colorWidget; public: KBibTeX::FieldInputType fieldInputType; KBibTeX::TypeFlags typeFlags; KBibTeX::TypeFlag preferredTypeFlag; + const File *bibtexFile; + const Element *element; FieldInputPrivate(FieldInput *parent) - : p(parent), fieldLineEdit(NULL), fieldListEdit(NULL), colorButton(NULL), colorWidget(NULL) { + : p(parent), fieldLineEdit(NULL), fieldListEdit(NULL), colorWidget(NULL), bibtexFile(NULL), element(NULL) { // TODO } @@ -87,35 +85,18 @@ monthSelector->setMenu(monthMenu); } break; + case KBibTeX::CrossRef: { + fieldLineEdit = new FieldLineEdit(preferredTypeFlag, typeFlags, false, p); + layout->addWidget(fieldLineEdit); + KPushButton *referenceSelector = new KPushButton(KIcon("flag-gree"), ""); ///< find better icon + referenceSelector->setToolTip(i18n("Select an existing entry")); + fieldLineEdit->prependWidget(referenceSelector); + connect(referenceSelector, SIGNAL(clicked()), p, SLOT(selectCrossRef())); + } + break; case KBibTeX::Color: { - colorWidget = new QWidget(p); - QBoxLayout *boxLayout = new QHBoxLayout(colorWidget); - boxLayout->setMargin(0); - predefColorButton = new KPushButton(KIcon("color-picker-white"), i18n("Predefined colors"), colorWidget); - boxLayout->addWidget(predefColorButton, 0); - colorButton = new KColorButton(colorWidget); - boxLayout->addWidget(colorButton, 0); + colorWidget = new ColorLabelWidget(p); layout->addWidget(colorWidget, 0); - resetColorButton = new KPushButton(KIcon("edit-clear-locationbar-rtl"), i18n("Reset"), colorWidget); - layout->addWidget(resetColorButton, 0, Qt::AlignLeft); - connect(resetColorButton, SIGNAL(clicked()), p, SLOT(resetColor())); - - colorSignalMapper = new QSignalMapper(predefColorButton); - connect(colorSignalMapper, SIGNAL(mapped(QString)), p, SLOT(setColor(QString))); - colorMenu = new QMenu(predefColorButton); - predefColorButton->setMenu(colorMenu); - - // TODO: Make it configurable - - QAction *action = colorAction(i18n("Important"), "#cc3300"); - colorMenu->addAction(action); - colorSignalMapper->setMapping(action, "#cc3300"); - connect(action, SIGNAL(triggered()), colorSignalMapper, SLOT(map())); - - action = colorAction(i18n("Read"), "#009966"); - colorMenu->addAction(action); - colorSignalMapper->setMapping(action, "#009966"); - connect(action, SIGNAL(triggered()), colorSignalMapper, SLOT(map())); } break; case KBibTeX::PersonList: @@ -126,22 +107,16 @@ fieldListEdit = new UrlListEdit(p); layout->addWidget(fieldListEdit); break; + case KBibTeX::KeywordList: + fieldListEdit = new KeywordListEdit(p); + layout->addWidget(fieldListEdit); + break; default: fieldLineEdit = new FieldLineEdit(preferredTypeFlag, typeFlags, false, p); layout->addWidget(fieldLineEdit); } - enalbeModifiedSignal(); - } - - QAction *colorAction(const QString&label, const QString &color) { - int h = predefColorButton->fontMetrics().height() - 4; - QPixmap pm(h, h); - QPainter painter(&pm); - painter.setPen(QColor(color)); - painter.setBrush(QBrush(painter.pen().color())); - painter.drawRect(0, 0, h, h); - return new QAction(KIcon(pm), label, p); + enableModifiedSignal(); } void clear() { @@ -150,7 +125,7 @@ fieldLineEdit->setText(""); else if (fieldListEdit != NULL) fieldListEdit->clear(); - enalbeModifiedSignal(); + enableModifiedSignal(); } bool reset(const Value& value) { @@ -164,17 +139,10 @@ else if (fieldListEdit != NULL) result = fieldListEdit->reset(value); else if (colorWidget != NULL) { - disconnect(colorButton, SIGNAL(changed(QColor)), p, SIGNAL(modified())); - - VerbatimText *verbatimText = NULL; - if (value.count() == 1 && (verbatimText = dynamic_cast(value.first())) != NULL) - colorButton->setColor(QColor(verbatimText->text())); - else - p->resetColor(); - result = true; + result = colorWidget->reset(value); } - enalbeModifiedSignal(); + enableModifiedSignal(); return result; } @@ -185,13 +153,7 @@ else if (fieldListEdit != NULL) result = fieldListEdit->apply(value); else if (colorWidget != NULL) { - value.clear(); - const QString colorName = colorButton->color().name(); - if (!(colorButton->color() == QColor(Qt::black)) && colorName != QLatin1String("#000000")) { // FIXME test looks redundant - VerbatimText *verbatimText = new VerbatimText(colorName); - value << verbatimText; - } - result = true; + result = colorWidget->apply(value); } return result; } @@ -204,21 +166,67 @@ } void setFile(const File *file) { + bibtexFile = file; if (fieldLineEdit != NULL) fieldLineEdit->setFile(file); if (fieldListEdit != NULL) fieldListEdit->setFile(file); } - void enalbeModifiedSignal() { + void setElement(const Element *element) { + this->element = element; + if (fieldLineEdit != NULL) + fieldLineEdit->setElement(element); + if (fieldListEdit != NULL) + fieldListEdit->setElement(element); + } + + void setFieldKey(const QString &fieldKey) { + if (fieldLineEdit != NULL) + fieldLineEdit->setFieldKey(fieldKey); + if (fieldListEdit != NULL) + fieldListEdit->setFieldKey(fieldKey); + } + + void setCompletionItems(const QStringList &items) { + if (fieldLineEdit != NULL) + fieldLineEdit->setCompletionItems(items); + if (fieldListEdit != NULL) + fieldListEdit->setCompletionItems(items); + } + + void selectCrossRef() { + Q_ASSERT(fieldLineEdit != NULL); + if (bibtexFile == NULL) return; + + /// create a standard input dialog with a list of all keys (ids of entries) + bool ok = false; + QStringList list = bibtexFile->allKeys(File::etEntry); + list.sort(); + + /// remove own id + const Entry *entry = dynamic_cast(element); + if (entry != NULL) list.removeOne(entry->id()); + + QString crossRef = KInputDialog::getItem(i18n("Select Cross Reference"), i18n("Select the cross reference to another entry:"), list, 0, false, &ok, p); + + if (ok && !crossRef.isEmpty()) { + /// insert selected cross reference into edit widget + VerbatimText *verbatimText = new VerbatimText(crossRef); + Value value; + value.append(verbatimText); + reset(value); + } + } + + + void enableModifiedSignal() { if (fieldLineEdit != NULL) connect(fieldLineEdit, SIGNAL(textChanged(QString)), p, SIGNAL(modified())); if (fieldListEdit != NULL) connect(fieldListEdit, SIGNAL(modified()), p, SIGNAL(modified())); - if (colorButton != NULL) { - connect(resetColorButton, SIGNAL(clicked()), p, SIGNAL(modified())); - connect(colorButton, SIGNAL(changed(QColor)), p, SIGNAL(modified())); - connect(colorSignalMapper, SIGNAL(mapped(int)), p, SIGNAL(modified())); + if (colorWidget != NULL) { + connect(colorWidget, SIGNAL(modified()), p, SIGNAL(modified())); } // TODO } @@ -228,10 +236,8 @@ disconnect(fieldLineEdit, SIGNAL(textChanged(QString)), p, SIGNAL(modified())); if (fieldListEdit != NULL) disconnect(fieldListEdit, SIGNAL(modified()), p, SIGNAL(modified())); - if (colorButton != NULL) { - disconnect(resetColorButton, SIGNAL(clicked()), p, SIGNAL(modified())); - disconnect(colorButton, SIGNAL(changed(QColor)), p, SIGNAL(modified())); - disconnect(colorSignalMapper, SIGNAL(mapped(int)), p, SIGNAL(modified())); + if (colorWidget != NULL) { + disconnect(colorWidget, SIGNAL(modified()), p, SIGNAL(modified())); } // TODO } @@ -271,26 +277,30 @@ d->setFile(file); } -void FieldInput::setMonth(int month) +void FieldInput::setElement(const Element *element) { - MacroKey *macro = new MacroKey(KBibTeX::MonthsTriple[month-1]); - Value value; - value.append(macro); - reset(value); + d->setElement(element); } -void FieldInput::setColor(const QString&color) +void FieldInput::setFieldKey(const QString &fieldKey) { - VerbatimText *verbatimText = new VerbatimText(color); - Value value; - value.append(verbatimText); - reset(value); + d->setFieldKey(fieldKey); } -void FieldInput::resetColor() +void FieldInput::setCompletionItems(const QStringList &items) +{ + d->setCompletionItems(items); +} + +void FieldInput::setMonth(int month) { - VerbatimText *verbatimText = new VerbatimText(QLatin1String("#000000")); + MacroKey *macro = new MacroKey(KBibTeX::MonthsTriple[month-1]); Value value; - value.append(verbatimText); + value.append(macro); reset(value); } + +void FieldInput::selectCrossRef() +{ + d->selectCrossRef(); +} diff -Nru kbibtex-0.3/src/gui/field/fieldinput.h kbibtex-0.4/src/gui/field/fieldinput.h --- kbibtex-0.3/src/gui/field/fieldinput.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/field/fieldinput.h 2011-11-20 20:36:54.000000000 +0000 @@ -25,6 +25,8 @@ #include #include +class Element; + /** @author Thomas Fischer */ @@ -42,14 +44,16 @@ void setReadOnly(bool isReadOnly); void setFile(const File *file); + void setElement(const Element *element); + void setFieldKey(const QString &fieldKey); + void setCompletionItems(const QStringList &items); signals: void modified(); private slots: void setMonth(int month); - void setColor(const QString&); - void resetColor(); + void selectCrossRef(); private: class FieldInputPrivate; diff -Nru kbibtex-0.3/src/gui/field/fieldlineedit.cpp kbibtex-0.4/src/gui/field/fieldlineedit.cpp --- kbibtex-0.3/src/gui/field/fieldlineedit.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/field/fieldlineedit.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include @@ -33,6 +35,8 @@ #include #include #include +#include +#include #include #include @@ -54,14 +58,19 @@ QSignalMapper *menuTypesSignalMapper; KPushButton *buttonOpenUrl; + KSharedConfigPtr config; + const QString configGroupNameGeneral; + QString personNameFormatting; + public: QMenu *menuTypes; KBibTeX::TypeFlag typeFlag; KUrl urlToOpen; const File *file; + QString fieldKey; FieldLineEditPrivate(KBibTeX::TypeFlag ptf, KBibTeX::TypeFlags tf, FieldLineEdit *p) - : parent(p), preferredTypeFlag(ptf), typeFlags(tf), file(NULL) { + : parent(p), preferredTypeFlag(ptf), typeFlags(tf), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupNameGeneral(QLatin1String("General")), file(NULL) { menuTypes = new QMenu(i18n("Types"), parent); menuTypesSignalMapper = new QSignalMapper(parent); setupMenu(); @@ -69,6 +78,7 @@ buttonOpenUrl = new KPushButton(KIcon("document-open-remote"), "", parent); buttonOpenUrl->setVisible(false); + buttonOpenUrl->setProperty("isConst", true); parent->appendWidget(buttonOpenUrl); connect(buttonOpenUrl, SIGNAL(clicked()), parent, SLOT(slotOpenUrl())); @@ -77,6 +87,15 @@ Value value; typeFlag = determineTypeFlag(value, preferredTypeFlag, typeFlags); updateGUI(typeFlag); + + KConfigGroup configGroup(config, configGroupNameGeneral); + personNameFormatting = configGroup.readEntry(Person::keyPersonNameFormatting, Person::defaultPersonNameFormatting); + } + + ~FieldLineEditPrivate() { + delete menuTypes; + delete menuTypesSignalMapper; + delete buttonOpenUrl; } bool reset(const Value& value) { @@ -103,13 +122,7 @@ } else { const Person *person = dynamic_cast(first); if (typeFlag == KBibTeX::tfPerson && person != NULL) { - text = person->lastName(); - QString temp = person->firstName(); - if (!temp.isEmpty()) text.prepend(temp + " "); - temp = person->suffix(); - if (!temp.isEmpty()) text.append(", " + temp); - temp = person->prefix(); - if (!temp.isEmpty()) text.prepend(temp + " "); + text = Person::transcribePersonName(person, personNameFormatting); result = true; } else { const MacroKey *macroKey = dynamic_cast(first); @@ -364,11 +377,11 @@ void updateURL(const QString &text) { QList urls; - FileInfo::urlsInText(text, true, file != NULL && file->property(File::Url).value().isValid() ? file->property(File::Url).value().directory() : QString::null, urls); - if (urls.isEmpty()) - urlToOpen = KUrl(); - else + FileInfo::urlsInText(text, true, file != NULL && file->property(File::Url).toUrl().isValid() ? KUrl(file->property(File::Url).toUrl()).directory() : QString::null, urls); + if (!urls.isEmpty() && urls.first().isValid()) urlToOpen = urls.first(); + else + urlToOpen = KUrl(); /// set special "open URL" button visible if URL (or file or DOI) found buttonOpenUrl->setVisible(urlToOpen.isValid()); @@ -386,6 +399,13 @@ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); setObjectName(QLatin1String("FieldLineEdit")); setMenu(d->menuTypes); + setChildAcceptDrops(false); + setAcceptDrops(true); +} + +FieldLineEdit::~FieldLineEdit() +{ + delete d; } bool FieldLineEdit::apply(Value& value) const @@ -406,7 +426,6 @@ void FieldLineEdit::slotTypeChanged(int newTypeFlagInt) { KBibTeX::TypeFlag newTypeFlag = (KBibTeX::TypeFlag)newTypeFlagInt; - kDebug() << "new type is " << BibTeXFields::typeFlagToString(newTypeFlag); Value value; d->apply(value); @@ -423,6 +442,16 @@ d->file = file; } +void FieldLineEdit::setElement(const Element *element) +{ + Q_UNUSED(element) +} + +void FieldLineEdit::setFieldKey(const QString &fieldKey) +{ + d->fieldKey = fieldKey; +} + void FieldLineEdit::slotOpenUrl() { d->openUrl(); @@ -432,3 +461,41 @@ { d->textChanged(text); } + +void FieldLineEdit::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasFormat("text/plain") || event->mimeData()->hasFormat("text/x-bibtex")) + event->acceptProposedAction(); +} + +void FieldLineEdit::dropEvent(QDropEvent *event) +{ + const QString clipboardText = event->mimeData()->text(); + if (clipboardText.isEmpty()) return; + + const File *file = NULL; + if (!d->fieldKey.isEmpty() && clipboardText.startsWith("@")) { + FileImporterBibTeX importer; + file = importer.fromString(clipboardText); + const Entry *entry = (file != NULL && file->count() == 1) ? dynamic_cast(file->first()) : NULL; + if (entry != NULL && d->fieldKey == Entry::ftCrossRef) { + /// handle drop on crossref line differently (use dropped entry's id) + Value v; + v.append(new VerbatimText(entry->id())); + reset(v); + emit textChanged(entry->id()); + return; + } else if (entry != NULL && entry->contains(d->fieldKey)) { + /// case for "normal" fields like for journal, pages, ... + reset(entry->value(d->fieldKey)); + emit textChanged(text()); + return; + } + } + + if (file == NULL || file->count() == 0) { + /// fall-back case: just copy whole text into edit widget + setText(clipboardText); + emit textChanged(clipboardText); + } +} diff -Nru kbibtex-0.3/src/gui/field/fieldlineedit.h kbibtex-0.4/src/gui/field/fieldlineedit.h --- kbibtex-0.3/src/gui/field/fieldlineedit.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/field/fieldlineedit.h 2011-11-20 20:36:54.000000000 +0000 @@ -31,6 +31,8 @@ class QMenu; class QSignalMapper; +class Element; + /** @author Thomas Fischer */ @@ -40,12 +42,19 @@ public: FieldLineEdit(KBibTeX::TypeFlag preferredTypeFlag, KBibTeX::TypeFlags typeFlags, bool isMultiLine = false, QWidget *parent = NULL); + ~FieldLineEdit(); bool reset(const Value& value); bool apply(Value& value) const; virtual void setReadOnly(bool); void setFile(const File *file); + void setElement(const Element *element); + void setFieldKey(const QString &fieldKey); + +protected: + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); private: bool m_incompleteRepresentation; diff -Nru kbibtex-0.3/src/gui/field/fieldlistedit.cpp kbibtex-0.4/src/gui/field/fieldlistedit.cpp --- kbibtex-0.3/src/gui/field/fieldlistedit.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/field/fieldlistedit.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -24,12 +24,16 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include #include #include #include @@ -53,9 +57,11 @@ QBoxLayout *pushButtonContainerLayout; KPushButton *addLineButton; const File *file; + QString fieldKey; QWidget *container; QScrollArea *scrollArea; bool m_isReadOnly; + QStringList completionItems; FieldListEditProtected(KBibTeX::TypeFlag ptf, KBibTeX::TypeFlags tf, FieldListEdit *parent) : p(parent), innerSpacing(4), preferredTypeFlag(ptf), typeFlags(tf), file(NULL), m_isReadOnly(false) { @@ -65,6 +71,12 @@ setupGUI(); } + ~FieldListEditProtected() { + delete smRemove; + delete smGoUp; + delete smGoDown; + } + void setupGUI() { QBoxLayout *outerLayout = new QVBoxLayout(p); outerLayout->setMargin(0); @@ -124,6 +136,7 @@ FieldLineEdit *addFieldLineEdit() { FieldLineEdit *le = new FieldLineEdit(preferredTypeFlag, typeFlags, false, container); le->setFile(file); + le->setAcceptDrops(false); le->setReadOnly(m_isReadOnly); le->setInnerWidgetsTransparency(true); layout->insertWidget(layout->count() - 2, le); @@ -147,6 +160,8 @@ connect(goUp, SIGNAL(clicked()), smGoUp, SLOT(map())); smGoUp->setMapping(goUp, le); + connect(le, SIGNAL(textChanged(QString)), p, SIGNAL(modified())); + return le; } @@ -197,6 +212,13 @@ : QWidget(parent), d(new FieldListEditProtected(preferredTypeFlag, typeFlags, this)) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + setMinimumSize(fontMetrics().averageCharWidth() * 30, fontMetrics().averageCharWidth() * 10); + setAcceptDrops(true); +} + +FieldListEdit::~FieldListEdit() +{ + delete d; } bool FieldListEdit::reset(const Value& value) @@ -247,14 +269,76 @@ d->file = file; } +void FieldListEdit::setElement(const Element *element) +{ + Q_UNUSED(element) +} + +void FieldListEdit::setFieldKey(const QString &fieldKey) +{ + d->fieldKey = fieldKey; +} + +void FieldListEdit::setCompletionItems(const QStringList &items) +{ + d->completionItems = items; + for (QList::Iterator it = d->lineEditList.begin(); it != d->lineEditList.end(); ++it) + (*it)->setCompletionItems(items); +} + void FieldListEdit::addButton(KPushButton *button) { d->addButton(button); } +void FieldListEdit::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasFormat("text/plain") || event->mimeData()->hasFormat("text/x-bibtex")) + event->acceptProposedAction(); +} + +void FieldListEdit::dropEvent(QDropEvent *event) +{ + const QString clipboardText = event->mimeData()->text(); + if (clipboardText.isEmpty()) return; + + const File *file = NULL; + if (!d->fieldKey.isEmpty() && clipboardText.startsWith("@")) { + FileImporterBibTeX importer; + file = importer.fromString(clipboardText); + const Entry *entry = (file != NULL && file->count() == 1) ? dynamic_cast(file->first()) : NULL; + + if (entry != NULL && d->fieldKey == QLatin1String("^external")) { + /// handle "external" list differently + QList urlList = FileInfo::entryUrls(entry, KUrl(file->property(File::Url).toString())); + Value v; + foreach(const KUrl &url, urlList) { + v.append(new VerbatimText(url.pathOrUrl())); + } + reset(v); + emit modified(); + return; + } else if (entry != NULL && entry->contains(d->fieldKey)) { + /// case for "normal" lists like for authors, editors, ... + reset(entry->value(d->fieldKey)); + emit modified(); + return; + } + } + + if (file == NULL || file->count() == 0) { + /// fall-back case: single field line edit with text + d->removeAllFieldLineEdits(); + FieldLineEdit *fle = d->addFieldLineEdit(); + fle->setText(clipboardText); + emit modified(); + } +} + void FieldListEdit::lineAdd(Value *value) { FieldLineEdit *le = d->addFieldLineEdit(); + le->setCompletionItems(d->completionItems); if (value != NULL) le->reset(*value); } @@ -262,6 +346,7 @@ void FieldListEdit::lineAdd() { FieldLineEdit *newEdit = d->addFieldLineEdit(); + newEdit->setCompletionItems(d->completionItems); QSize size(d->container->width(), d->recommendedHeight()); d->container->resize(size); newEdit->setFocus(Qt::ShortcutFocusReason); @@ -365,3 +450,82 @@ FieldListEdit::setReadOnly(isReadOnly); m_addLocalFile->setEnabled(!isReadOnly); } + + +const QString KeywordListEdit::keyGlobalKeywordList = QLatin1String("globalKeywordList"); + +KeywordListEdit::KeywordListEdit(QWidget *parent) + : FieldListEdit(KBibTeX::tfKeyword, KBibTeX::tfKeyword | KBibTeX::tfSource, parent), m_config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), m_configGroupName(QLatin1String("Global Keywords")) +{ + m_addKeyword = new KPushButton(KIcon("list-add"), i18n("Add Keywords"), this); + addButton(m_addKeyword); + connect(m_addKeyword, SIGNAL(clicked()), this, SLOT(slotAddKeyword())); + connect(m_addKeyword, SIGNAL(clicked()), this, SIGNAL(modified())); +} + +void KeywordListEdit::slotAddKeyword() +{ + /// fetch stored, global keywords + KConfigGroup configGroup(m_config, m_configGroupName); + QStringList keywords = configGroup.readEntry(KeywordListEdit::keyGlobalKeywordList, QStringList()); + + /// use a map for case-insensitive sorting of strings + /// (recommended by Qt's documentation) + QMap forCaseInsensitiveSorting; + /// insert all stored, global keywords + foreach(const QString &keyword, keywords) + forCaseInsensitiveSorting.insert(keyword.toLower(), keyword); + /// insert all unique keywords used in this file + foreach(const QString &keyword, m_keywordsFromFile) + forCaseInsensitiveSorting.insert(keyword.toLower(), keyword); + /// re-create string list from map's values + keywords = forCaseInsensitiveSorting.values(); + + bool ok = false; + QStringList newKeywordList = KInputDialog::getItemList(i18n("Add Keywords"), i18n("Select keywords to add:"), keywords, QStringList(), true, &ok, this); + if (ok) { + foreach(const QString &newKeywordText, newKeywordList) { + Value *value = new Value(); + ValueItem *vi = new Keyword(newKeywordText); + value->append(vi); + lineAdd(value); + } + } +} + +void KeywordListEdit::setReadOnly(bool isReadOnly) +{ + FieldListEdit::setReadOnly(isReadOnly); + m_addKeyword->setEnabled(!isReadOnly); +} + +void KeywordListEdit::setFile(const File *file) +{ + if (file == NULL) + m_keywordsFromFile.clear(); + else + m_keywordsFromFile = file->uniqueEntryValuesSet(Entry::ftKeywords); + + FieldListEdit::setFile(file); +} + +void KeywordListEdit::setCompletionItems(const QStringList &items) +{ + /// fetch stored, global keywords + KConfigGroup configGroup(m_config, m_configGroupName); + QStringList keywords = configGroup.readEntry(KeywordListEdit::keyGlobalKeywordList, QStringList()); + + /// use a map for case-insensitive sorting of strings + /// (recommended by Qt's documentation) + QMap forCaseInsensitiveSorting; + /// insert all stored, global keywords + foreach(const QString &keyword, keywords) + forCaseInsensitiveSorting.insert(keyword.toLower(), keyword); + /// insert all unique keywords used in this file + foreach(const QString &keyword, items) + forCaseInsensitiveSorting.insert(keyword.toLower(), keyword); + /// re-create string list from map's values + keywords = forCaseInsensitiveSorting.values(); + + FieldListEdit::setCompletionItems(keywords); +} diff -Nru kbibtex-0.3/src/gui/field/fieldlistedit.h kbibtex-0.4/src/gui/field/fieldlistedit.h --- kbibtex-0.3/src/gui/field/fieldlistedit.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/field/fieldlistedit.h 2011-11-20 20:36:54.000000000 +0000 @@ -21,14 +21,21 @@ #define KBIBTEX_GUI_FIELDLISTEDIT_H #include +#include + +#include #include #include class QCheckBox; +class QDropEvent; +class QDragEnterEvent; class KPushButton; +class Element; + /** @author Thomas Fischer */ @@ -38,13 +45,17 @@ public: FieldListEdit(KBibTeX::TypeFlag preferredTypeFlag, KBibTeX::TypeFlags typeFlags, QWidget *parent = NULL); + ~FieldListEdit(); virtual bool reset(const Value& value); virtual bool apply(Value& value) const; void clear(); virtual void setReadOnly(bool isReadOnly); - void setFile(const File *file); + virtual void setFile(const File *file); + virtual void setElement(const Element *element); + virtual void setFieldKey(const QString &fieldKey); + virtual void setCompletionItems(const QStringList &items); signals: void modified(); @@ -52,6 +63,8 @@ protected: void addButton(KPushButton *button); void lineAdd(Value *value); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *); private slots: void lineAdd(); @@ -102,4 +115,31 @@ KPushButton *m_addLocalFile; }; + +/** +@author Thomas Fischer +*/ +class KeywordListEdit : public FieldListEdit +{ + Q_OBJECT + +public: + static const QString keyGlobalKeywordList; + + KeywordListEdit(QWidget *parent = NULL); + + virtual void setReadOnly(bool isReadOnly); + virtual void setFile(const File *file); + virtual void setCompletionItems(const QStringList &items); + +private slots: + void slotAddKeyword(); + +private: + KSharedConfigPtr m_config; + const QString m_configGroupName; + KPushButton *m_addKeyword; + QSet m_keywordsFromFile; +}; + #endif // KBIBTEX_GUI_FIELDLISTEDIT_H diff -Nru kbibtex-0.3/src/gui/preferences/kbibtexpreferencesdialog.cpp kbibtex-0.4/src/gui/preferences/kbibtexpreferencesdialog.cpp --- kbibtex-0.3/src/gui/preferences/kbibtexpreferencesdialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/kbibtexpreferencesdialog.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,156 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include + +#include +#include + +#include "settingsgeneralwidget.h" +#include "settingsglobalkeywordswidget.h" +#include "settingsfileexporterbibtexwidget.h" +#include "settingsfileexporterpdfpswidget.h" +#include "settingsfileexporterwidget.h" +#include "settingscolorlabelwidget.h" +#include "settingsuserinterfacewidget.h" +#include "kbibtexpreferencesdialog.h" + +class KBibTeXPreferencesDialog::KBibTeXPreferencesDialogPrivate +{ +private: + KBibTeXPreferencesDialog *p; + KComboBox *listOfEncodings; + QSet settingWidgets; + +public: + KBibTeXPreferencesDialogPrivate(KBibTeXPreferencesDialog *parent) + : p(parent) { + // nothing + } + + void addPages() { + SettingsAbstractWidget *settingsWidget = new SettingsGeneralWidget(p); + settingWidgets.insert(settingsWidget); + KPageWidgetItem *pageGlobal = p->addPage(settingsWidget, i18n("General")); + pageGlobal->setIcon(KIcon("kbibtex")); + connect(settingsWidget, SIGNAL(changed()), p, SLOT(gotChanged())); + + settingsWidget = new SettingsGlobalKeywordsWidget(p); + settingWidgets.insert(settingsWidget); + KPageWidgetItem *page = p->addSubPage(pageGlobal, settingsWidget, i18n("Keywords")); + page->setIcon(KIcon("checkbox")); // TODO find better icon + connect(settingsWidget, SIGNAL(changed()), p, SLOT(gotChanged())); + + settingsWidget = new SettingsColorLabelWidget(p); + settingWidgets.insert(settingsWidget); + page = p->addSubPage(pageGlobal, settingsWidget, i18n("Color & Labels")); + page->setIcon(KIcon("preferences-desktop-color")); + connect(settingsWidget, SIGNAL(changed()), p, SLOT(gotChanged())); + + settingsWidget = new SettingsUserInterfaceWidget(p); + settingWidgets.insert(settingsWidget); + page = p->addPage(settingsWidget, i18n("User Interface")); + page->setIcon(KIcon("user-identity")); + connect(settingsWidget, SIGNAL(changed()), p, SLOT(gotChanged())); + + settingsWidget = new SettingsFileExporterWidget(p); + settingWidgets.insert(settingsWidget); + KPageWidgetItem *pageSaving = p->addPage(settingsWidget, i18n("Saving and Exporting")); + pageSaving->setIcon(KIcon("document-save")); + connect(settingsWidget, SIGNAL(changed()), p, SLOT(gotChanged())); + + settingsWidget = new SettingsFileExporterBibTeXWidget(p); + settingWidgets.insert(settingsWidget); + page = p->addSubPage(pageSaving, settingsWidget, i18n("BibTeX")); + page->setIcon(KIcon("text-x-bibtex")); + connect(settingsWidget, SIGNAL(changed()), p, SLOT(gotChanged())); + + settingsWidget = new SettingsFileExporterPDFPSWidget(p); + settingWidgets.insert(settingsWidget); + page = p->addSubPage(pageSaving, settingsWidget, i18n("PDF, Postscript, and RTF")); + page->setIcon(KIcon("application-pdf")); + connect(settingsWidget, SIGNAL(changed()), p, SLOT(gotChanged())); + } + + void loadState() { + foreach(SettingsAbstractWidget *settingsWidget, settingWidgets) { + settingsWidget->loadState(); + } + } + + void saveState() { + foreach(SettingsAbstractWidget *settingsWidget, settingWidgets) { + settingsWidget->saveState(); + } + } + + void resetToDefaults() { + foreach(SettingsAbstractWidget *settingsWidget, settingWidgets) { + settingsWidget->resetToDefaults(); + } + } +}; + +KBibTeXPreferencesDialog::KBibTeXPreferencesDialog(QWidget *parent, Qt::WFlags flags) + : KPageDialog(parent, flags), d(new KBibTeXPreferencesDialogPrivate(this)) +{ + setFaceType(KPageDialog::Tree); + setWindowTitle(i18n("Preferences")); + setButtons(Default | Reset | Ok | Apply | Cancel); + setDefaultButton(Ok); + enableButtonApply(false); + setModal(true); + showButtonSeparator(true); + + connect(this, SIGNAL(applyClicked()), this, SLOT(apply())); + connect(this, SIGNAL(okClicked()), this, SLOT(ok())); + connect(this, SIGNAL(defaultClicked()), this, SLOT(resetToDefaults())); + connect(this, SIGNAL(resetClicked()), this, SLOT(reset())); + + d->addPages(); +} + +void KBibTeXPreferencesDialog::apply() +{ + enableButtonApply(false); + d->saveState(); +} + +void KBibTeXPreferencesDialog::reset() +{ + enableButtonApply(false); + d->loadState(); +} + +void KBibTeXPreferencesDialog::ok() +{ + apply(); + accept(); +} + +void KBibTeXPreferencesDialog::resetToDefaults() +{ + d->resetToDefaults(); +} + +void KBibTeXPreferencesDialog::gotChanged() +{ + enableButtonApply(true); +} diff -Nru kbibtex-0.3/src/gui/preferences/kbibtexpreferencesdialog.h kbibtex-0.4/src/gui/preferences/kbibtexpreferencesdialog.h --- kbibtex-0.3/src/gui/preferences/kbibtexpreferencesdialog.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/kbibtexpreferencesdialog.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,50 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_PREFERENCESDIALOG_H +#define KBIBTEX_GUI_PREFERENCESDIALOG_H + +#include + +#include + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT KBibTeXPreferencesDialog : public KPageDialog +{ + Q_OBJECT + +public: + KBibTeXPreferencesDialog(QWidget *parent, Qt::WFlags flags = 0); + +private: + class KBibTeXPreferencesDialogPrivate; + KBibTeXPreferencesDialogPrivate *d; + +private slots: + void apply(); + void reset(); + void ok(); + void resetToDefaults(); + void gotChanged(); +}; + +#endif // KBIBTEX_GUI_PREFERENCESDIALOG_H diff -Nru kbibtex-0.3/src/gui/preferences/settingsabstractwidget.cpp kbibtex-0.4/src/gui/preferences/settingsabstractwidget.cpp --- kbibtex-0.3/src/gui/preferences/settingsabstractwidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsabstractwidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,105 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include + +#include +#include + +#include "settingsabstractwidget.h" + +ItalicTextItemModel::ItalicTextItemModel(QObject *parent) + : QAbstractItemModel(parent) +{ + // nothing +} + +void ItalicTextItemModel::addItem(const QString &a, const QString &b) +{ + m_data.append(QPair(a, b)); +} + +QVariant ItalicTextItemModel::data(const QModelIndex & index, int role) const +{ + if (index.row() < 0 || index.row() >= m_data.count()) + return QVariant(); + + if (role == Qt::FontRole) { + QFont font; + if (m_data[index.row()].second.isEmpty()) + font.setItalic(true); + return font; + } else if (role == Qt::DisplayRole) { + return m_data[index.row()].first; + } else if (role == Qt::UserRole) { + return m_data[index.row()].second; + } + + return QVariant(); +} + +QModelIndex ItalicTextItemModel::index(int row, int column, const QModelIndex&) const +{ + return createIndex(row, column); +} + +QModelIndex ItalicTextItemModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +int ItalicTextItemModel::rowCount(const QModelIndex &) const +{ + return m_data.count(); +} + +int ItalicTextItemModel::columnCount(const QModelIndex &) const +{ + return 1; +} + + +SettingsAbstractWidget::SettingsAbstractWidget(QWidget *parent) + : QWidget(parent) +{ + // nothing +} + +void SettingsAbstractWidget::selectValue(KComboBox *comboBox, const QString &value, int role) +{ + bool foundLine = false; + QAbstractItemModel *model = comboBox->model(); + int row = 0; + QModelIndex index; + const QString lowerValue = value.toLower(); + while (row < model->rowCount() && (index = model->index(row, 0, QModelIndex())) != QModelIndex()) { + QString line = model->data(index, role).toString(); + if (line.toLower() == lowerValue) { + comboBox->setCurrentIndex(row); + foundLine = true; + break; + } + ++row; + } + + if (!foundLine) + kWarning() << "No line in combobox" << comboBox->objectName() << "matched" << value; +} + diff -Nru kbibtex-0.3/src/gui/preferences/settingsabstractwidget.h kbibtex-0.4/src/gui/preferences/settingsabstractwidget.h --- kbibtex-0.3/src/gui/preferences/settingsabstractwidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsabstractwidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,69 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_SETTINGSABSTRACTWIDGET_H +#define KBIBTEX_GUI_SETTINGSABSTRACTWIDGET_H + +#include + +#include + +class KComboBox; + +class ItalicTextItemModel : public QAbstractItemModel +{ +public: + ItalicTextItemModel(QObject *parent = NULL); + + void addItem(const QString &a, const QString &b); + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, const QModelIndex&) const; + QModelIndex parent(const QModelIndex &) const; + int rowCount(const QModelIndex &) const; + int columnCount(const QModelIndex &) const; + +private: + QList > m_data; +}; + + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT SettingsAbstractWidget : public QWidget +{ + Q_OBJECT + +public: + SettingsAbstractWidget(QWidget *parent); + +signals: + void changed(); + +public slots: + virtual void loadState() = 0; + virtual void saveState() = 0; + virtual void resetToDefaults() = 0; + +protected: + void selectValue(KComboBox *comboBox, const QString &value, int role = Qt::DisplayRole); +}; + +#endif // KBIBTEX_GUI_SETTINGSABSTRACTWIDGET_H diff -Nru kbibtex-0.3/src/gui/preferences/settingscolorlabelwidget.cpp kbibtex-0.4/src/gui/preferences/settingscolorlabelwidget.cpp --- kbibtex-0.3/src/gui/preferences/settingscolorlabelwidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingscolorlabelwidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,411 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "file.h" +#include +#include "settingscolorlabelwidget.h" + +class ColorLabelSettingsDelegate : public QStyledItemDelegate +{ +public: + ColorLabelSettingsDelegate(QWidget *parent = NULL) + : QStyledItemDelegate(parent) { + // nothing + } + + virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const { + if (index.column() == 0) + return new KColorButton(parent); + else + return new KLineEdit(parent); + } + + void setEditorData(QWidget *editor, const QModelIndex &index) const { + if (index.column() == 0) { + KColorButton *colorButton = qobject_cast(editor); + colorButton->setColor(index.model()->data(index, Qt::EditRole).value()); + } else { + KLineEdit *lineEdit = qobject_cast(editor); + lineEdit->setText(index.model()->data(index, Qt::EditRole).toString()); + } + } + + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { + if (index.column() == 0) { + KColorButton *colorButton = qobject_cast(editor); + if (colorButton->color() != Qt::black) + model->setData(index, colorButton->color(), Qt::EditRole); + } else { + KLineEdit *lineEdit = qobject_cast(editor); + if (!lineEdit->text().isEmpty()) + model->setData(index, lineEdit->text(), Qt::EditRole); + } + } + + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const { + QSize hint = QStyledItemDelegate::sizeHint(option, index); + QFontMetrics fm = QFontMetrics(QFont()); + hint.setHeight(qMax(hint.height(), fm.xHeight()*6)); + return hint; + } +}; + + +ColorLabelSettingsModel::ColorLabelSettingsModel(QObject *parent) + : QAbstractItemModel(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))) +{ + loadState(); +} + +int ColorLabelSettingsModel::rowCount(const QModelIndex &parent) const +{ + return parent == QModelIndex() ? colorLabelPairs.count() : 0; +} + +int ColorLabelSettingsModel::columnCount(const QModelIndex &parent) const +{ + return parent == QModelIndex() ? 2 : 0; +} + +QModelIndex ColorLabelSettingsModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row >= 0 && row <= colorLabelPairs.count() - 1 && column >= 0 && column <= 1 && parent == QModelIndex()) + return createIndex(row, column, row); + else return QModelIndex(); +} + +QModelIndex ColorLabelSettingsModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +QVariant ColorLabelSettingsModel::data(const QModelIndex &index, int role) const +{ + if (index == QModelIndex() || index.row() < 0 || index.row() >= colorLabelPairs.count()) + return QVariant(); + + if ((role == Qt::DisplayRole || role == Qt::EditRole) && index.column() == 1) + return colorLabelPairs[index.row()].label; + else if ((role == Qt::DecorationRole || role == Qt::EditRole) && index.column() == 0) + return colorLabelPairs[index.row()].color; + + return QVariant(); +} + +bool ColorLabelSettingsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role == Qt::EditRole) { + if (index.column() == 0 && value.canConvert()) { + QColor color = value.value(); + if (color != Qt::black) { + colorLabelPairs[index.row()].color = color; + emit modified(); + return true; + } + } else if (index.column() == 1 && value.canConvert()) { + QString text = value.value(); + if (!text.isEmpty()) { + colorLabelPairs[index.row()].label = text; + emit modified(); + return true; + } + } + } + return false; +} + +Qt::ItemFlags ColorLabelSettingsModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags result = QAbstractItemModel::flags(index); + result |= Qt::ItemIsEditable; + return result; +} + +QVariant ColorLabelSettingsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return QVariant(); + + switch (section) { + case 0:return i18n("Color"); + case 1:return i18n("Label"); + default: return QVariant(); + } +} + +void ColorLabelSettingsModel::loadState() +{ + KConfigGroup configGroup(config, Preferences::groupColor); + QStringList colorCodes = configGroup.readEntry(Preferences::keyColorCodes, Preferences::defaultColorCodes); + QStringList colorLabels = configGroup.readEntry(Preferences::keyColorLabels, Preferences::defaultcolorLabels); + + colorLabelPairs.clear(); + for (QStringList::ConstIterator itc = colorCodes.constBegin(), itl = colorLabels.constBegin(); itc != colorCodes.constEnd() && itl != colorLabels.constEnd(); ++itc, ++itl) { + ColorLabelPair clp; + clp.color = QColor(*itc); + clp.label = *itl; + colorLabelPairs << clp; + } +} + +void ColorLabelSettingsModel::saveState() +{ + QStringList colorCodes, colorLabels; + foreach(const ColorLabelPair &clp, colorLabelPairs) { + colorCodes << clp.color.name(); + colorLabels << clp.label; + } + + KConfigGroup configGroup(config, Preferences::groupColor); + configGroup.writeEntry(Preferences::keyColorCodes, colorCodes); + configGroup.writeEntry(Preferences::keyColorLabels, colorLabels); + config->sync(); +} + +void ColorLabelSettingsModel::resetToDefaults() +{ + colorLabelPairs.clear(); + for (QStringList::ConstIterator itc = Preferences::defaultColorCodes.constBegin(), itl = Preferences::defaultcolorLabels.constBegin(); itc != Preferences::defaultColorCodes.constEnd() && itl != Preferences::defaultcolorLabels.constEnd(); ++itc, ++itl) { + ColorLabelPair clp; + clp.color = QColor(*itc); + clp.label = *itl; + colorLabelPairs << clp; + } + emit modified(); +} + +bool ColorLabelSettingsModel::containsLabel(const QString &label) +{ + foreach(const ColorLabelPair &clp, colorLabelPairs) { + if (clp.label == label) + return true; + } + return false; +} + +void ColorLabelSettingsModel::addColorLabel(const QColor &color, const QString &label) +{ + ColorLabelPair clp; + clp.color = color; + clp.label = label; + colorLabelPairs << clp; + QModelIndex idxA = index(colorLabelPairs.count() - 1, 0, QModelIndex()); + QModelIndex idxB = index(colorLabelPairs.count() - 1, 1, QModelIndex()); + emit dataChanged(idxA, idxB); + emit modified(); +} + +void ColorLabelSettingsModel::removeColorLabel(int row) +{ + if (row >= 0 && row < colorLabelPairs.count()) { + colorLabelPairs.removeAt(row); + QModelIndex idxA = index(row, 0, QModelIndex()); + QModelIndex idxB = index(colorLabelPairs.count() - 1, 1, QModelIndex()); + emit dataChanged(idxA, idxB); + emit modified(); + } +} + + +class SettingsColorLabelWidget::SettingsColorLabelWidgetPrivate +{ +private: + SettingsColorLabelWidget *p; + ColorLabelSettingsDelegate *delegate; + + KSharedConfigPtr config; + +public: + ColorLabelSettingsModel *model; + KPushButton *buttonRemove; + QTreeView *view; + + SettingsColorLabelWidgetPrivate(SettingsColorLabelWidget *parent) + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))) { + // nothing + } + + void loadState() { + model->loadState(); + } + + void saveState() { + model->saveState(); + } + + void resetToDefaults() { + model->resetToDefaults(); + } + + void setupGUI() { + QGridLayout *layout = new QGridLayout(p); + layout->setMargin(0); + + view = new QTreeView(p); + layout->addWidget(view, 0, 0, 3, 1); + view->setRootIsDecorated(false); + connect(view, SIGNAL(pressed(QModelIndex)), p, SLOT(enableRemoveButton())); + + model = new ColorLabelSettingsModel(view); + view->setModel(model); + connect(model, SIGNAL(modified()), p, SIGNAL(changed())); + + delegate = new ColorLabelSettingsDelegate(view); + view->setItemDelegate(delegate); + + KPushButton *buttonAdd = new KPushButton(KIcon("list-add"), i18n("Add ..."), p); + layout->addWidget(buttonAdd, 0, 1, 1, 1); + connect(buttonAdd, SIGNAL(clicked()), p, SLOT(addColorDialog())); + + buttonRemove = new KPushButton(KIcon("list-remove"), i18n("Remove"), p); + layout->addWidget(buttonRemove, 1, 1, 1, 1); + buttonRemove->setEnabled(false); + connect(buttonRemove, SIGNAL(clicked()), p, SLOT(removeColor())); + } +}; + + +SettingsColorLabelWidget::SettingsColorLabelWidget(QWidget *parent) + : SettingsAbstractWidget(parent), d(new SettingsColorLabelWidgetPrivate(this)) +{ + d->setupGUI(); +} + +void SettingsColorLabelWidget::loadState() +{ + d->loadState(); +} + +void SettingsColorLabelWidget::saveState() +{ + d->saveState(); +} + +void SettingsColorLabelWidget::resetToDefaults() +{ + d->resetToDefaults(); +} + +void SettingsColorLabelWidget::addColorDialog() +{ + bool ok = false; + QString newLabel = KInputDialog::getText(i18n("New Label"), i18n("Enter a new label:"), QLatin1String(""), &ok, this); + if (ok && !d->model->containsLabel(newLabel)) { + QColor color = Qt::red; + if (KColorDialog::getColor(color, this) == KColorDialog::Accepted && color != Qt::black) + d->model->addColorLabel(Qt::red, newLabel); + } +} + +void SettingsColorLabelWidget::removeColor() +{ + int row = d->view->selectionModel()->selectedIndexes().first().row(); + d->model->removeColorLabel(row); + d->buttonRemove->setEnabled(false); +} + +void SettingsColorLabelWidget::enableRemoveButton() +{ + d->buttonRemove->setEnabled(!d->view->selectionModel()->selectedIndexes().isEmpty()); +} + + +ColorLabelContextMenu::ColorLabelContextMenu(BibTeXEditor *widget) + : QObject(widget), m_tv(widget) +{ + QSignalMapper *sm = new QSignalMapper(this); + connect(sm, SIGNAL(mapped(QString)), this, SLOT(colorActivated(QString))); + + m_menu = new KActionMenu(KIcon("preferences-desktop-color"), i18n("Color"), widget); + widget->addAction(m_menu); + + KSharedConfigPtr config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))); + KConfigGroup configGroup(config, Preferences::groupColor); + QStringList colorCodes = configGroup.readEntry(Preferences::keyColorCodes, Preferences::defaultColorCodes); + QStringList colorLabels = configGroup.readEntry(Preferences::keyColorLabels, Preferences::defaultcolorLabels); + for (QStringList::ConstIterator itc = colorCodes.constBegin(), itl = colorLabels.constBegin(); itc != colorCodes.constEnd() && itl != colorLabels.constEnd(); ++itc, ++itl) { + KAction *action = new KAction(KIcon(ColorLabelWidget::createSolidIcon(*itc)), *itl, m_menu); + m_menu->addAction(action); + sm->setMapping(action, *itc); + connect(action, SIGNAL(triggered()), sm, SLOT(map())); + } + + KAction *action = new KAction(m_menu); + action->setSeparator(true); + m_menu->addAction(action); + + action = new KAction(i18n("No color"), m_menu); + m_menu->addAction(action); + sm->setMapping(action, QLatin1String("#000000")); + connect(action, SIGNAL(triggered()), sm, SLOT(map())); +} + +void ColorLabelContextMenu::setEnabled(bool enabled) +{ + m_menu->setEnabled(enabled); +} + +void ColorLabelContextMenu::colorActivated(const QString &colorString) +{ + SortFilterBibTeXFileModel *sfbfm = dynamic_cast(m_tv->model()); + Q_ASSERT(sfbfm != NULL); + BibTeXFileModel *model = sfbfm->bibTeXSourceModel(); + Q_ASSERT(model != NULL); + File *file = model->bibTeXFile(); + Q_ASSERT(file != NULL); + + bool modifying = false; + QModelIndexList list = m_tv->selectionModel()->selectedIndexes(); + foreach(const QModelIndex &index, list) { + if (index.column() == 1) { + Element *element = file->at(index.row()); + Entry *entry = dynamic_cast(element); + if (entry != NULL) { + entry->remove(Entry::ftColor); + if (colorString != QLatin1String("#000000")) { + Value v; + v.append(new VerbatimText(colorString)); + entry->insert(Entry::ftColor, v); + } + modifying = true; + } + } + } + + if (modifying) + m_tv->externalModification(); +} diff -Nru kbibtex-0.3/src/gui/preferences/settingscolorlabelwidget.h kbibtex-0.4/src/gui/preferences/settingscolorlabelwidget.h --- kbibtex-0.3/src/gui/preferences/settingscolorlabelwidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingscolorlabelwidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,123 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_SETTINGSCOLORLABELWIDGET_H +#define KBIBTEX_GUI_SETTINGSCOLORLABELWIDGET_H + +#include + +#include + +#include + +#include "settingsabstractwidget.h" + +class KActionMenu; + +class BibTeXEditor; + +/** +@author Thomas Fischer +*/ +class ColorLabelSettingsModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + ColorLabelSettingsModel(QObject *parent); + + virtual int rowCount(const QModelIndex &parent) const; + virtual int columnCount(const QModelIndex &parent) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent) const; + virtual QModelIndex parent(const QModelIndex &) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role); + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; + + void loadState(); + void saveState(); + void resetToDefaults(); + + bool containsLabel(const QString &label); + void addColorLabel(const QColor &color, const QString &label); + void removeColorLabel(int row); + +signals: + void modified(); + +private: + struct ColorLabelPair { + QColor color; + QString label; + }; + + QList colorLabelPairs; + KSharedConfigPtr config; + +}; + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT SettingsColorLabelWidget : public SettingsAbstractWidget +{ + Q_OBJECT + +public: + SettingsColorLabelWidget(QWidget *parent); + +public slots: + void loadState(); + void saveState(); + void resetToDefaults(); + +private slots: + void addColorDialog(); + void removeColor(); + void enableRemoveButton(); + +private: + class SettingsColorLabelWidgetPrivate; + SettingsColorLabelWidgetPrivate *d; +}; + + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT ColorLabelContextMenu : public QObject +{ + Q_OBJECT + +public: + ColorLabelContextMenu(BibTeXEditor *widget); + + void setEnabled(bool); + +private slots: + void colorActivated(const QString &colorString); + +private: + BibTeXEditor *m_tv; + KActionMenu *m_menu; +}; + +#endif // KBIBTEX_GUI_SETTINGSCOLORLABELWIDGET_H diff -Nru kbibtex-0.3/src/gui/preferences/settingsfileexporterbibtexwidget.cpp kbibtex-0.4/src/gui/preferences/settingsfileexporterbibtexwidget.cpp --- kbibtex-0.3/src/gui/preferences/settingsfileexporterbibtexwidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsfileexporterbibtexwidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,222 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "settingsfileexporterbibtexwidget.h" + +#define createDelimiterString(a, b) (QString("%1%2%3").arg(a).arg(QChar(8230)).arg(b)) + + +class SettingsFileExporterBibTeXWidget::SettingsFileExporterBibTeXWidgetPrivate +{ +private: + SettingsFileExporterBibTeXWidget *p; + + KComboBox *comboBoxEncodings; + KComboBox *comboBoxStringDelimiters; + KComboBox *comboBoxQuoteComment; + KComboBox *comboBoxKeywordCasing; + QCheckBox *checkBoxProtectCasing; + KComboBox *comboBoxPersonNameFormatting; + const Person dummyPerson; + + KSharedConfigPtr config; + const QString configGroupName; + +public: + + SettingsFileExporterBibTeXWidgetPrivate(SettingsFileExporterBibTeXWidget *parent) + : p(parent), dummyPerson(Person(i18n("John"), i18n("Doe"), i18n("Jr."))), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupName(QLatin1String("FileExporterBibTeX")) { + // nothing + } + + void loadState() { + KConfigGroup configGroup(config, configGroupName); + QString encoding = configGroup.readEntry(FileExporterBibTeX::keyEncoding, FileExporterBibTeX::defaultEncoding); + p->selectValue(comboBoxEncodings, encoding); + QString stringDelimiter = configGroup.readEntry(FileExporterBibTeX::keyStringDelimiter, FileExporterBibTeX::defaultStringDelimiter); + p->selectValue(comboBoxStringDelimiters, createDelimiterString(stringDelimiter[0], stringDelimiter[1])); + FileExporterBibTeX::QuoteComment quoteComment = (FileExporterBibTeX::QuoteComment)configGroup.readEntry(FileExporterBibTeX::keyQuoteComment, (int)FileExporterBibTeX::defaultQuoteComment); + comboBoxQuoteComment->setCurrentIndex((int)quoteComment); + KBibTeX::Casing keywordCasing = (KBibTeX::Casing)configGroup.readEntry(FileExporterBibTeX::keyKeywordCasing, (int)FileExporterBibTeX::defaultKeywordCasing); + comboBoxKeywordCasing->setCurrentIndex((int)keywordCasing); + checkBoxProtectCasing->setChecked(configGroup.readEntry(FileExporterBibTeX::keyProtectCasing, FileExporterBibTeX::defaultProtectCasing)); + QString personNameFormatting = configGroup.readEntry(Person::keyPersonNameFormatting, ""); + p->selectValue(comboBoxPersonNameFormatting, personNameFormatting, Qt::UserRole); + } + + void saveState() { + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(FileExporterBibTeX::keyEncoding, comboBoxEncodings->currentText()); + QString stringDelimiter = comboBoxStringDelimiters->currentText(); + configGroup.writeEntry(FileExporterBibTeX::keyStringDelimiter, QString(stringDelimiter[0]) + stringDelimiter[stringDelimiter.length()-1]); + FileExporterBibTeX::QuoteComment quoteComment = (FileExporterBibTeX::QuoteComment)comboBoxQuoteComment->currentIndex(); + configGroup.writeEntry(FileExporterBibTeX::keyQuoteComment, (int)quoteComment); + KBibTeX::Casing keywordCasing = (KBibTeX::Casing)comboBoxKeywordCasing->currentIndex(); + configGroup.writeEntry(FileExporterBibTeX::keyKeywordCasing, (int)keywordCasing); + configGroup.writeEntry(FileExporterBibTeX::keyProtectCasing, checkBoxProtectCasing->isChecked()); + configGroup.writeEntry(Person::keyPersonNameFormatting, comboBoxPersonNameFormatting->itemData(comboBoxPersonNameFormatting->currentIndex())); + + config->sync(); + } + + void resetToDefaults() { + p->selectValue(comboBoxEncodings, FileExporterBibTeX::defaultEncoding); + p->selectValue(comboBoxStringDelimiters, createDelimiterString(FileExporterBibTeX::defaultStringDelimiter[0], FileExporterBibTeX::defaultStringDelimiter[1])); + comboBoxQuoteComment->setCurrentIndex((int)FileExporterBibTeX::defaultQuoteComment); + comboBoxKeywordCasing->setCurrentIndex((int)FileExporterBibTeX::defaultKeywordCasing); + checkBoxProtectCasing->setChecked(FileExporterBibTeX::defaultProtectCasing); + comboBoxPersonNameFormatting->setCurrentIndex(0); + } + + void setupGUI() { + QFormLayout *layout = new QFormLayout(p); + + comboBoxEncodings = new KComboBox(false, p); + comboBoxEncodings->setObjectName("comboBoxEncodings"); + layout->addRow(i18n("Encoding:"), comboBoxEncodings); + comboBoxEncodings->addItem(QLatin1String("LaTeX")); + comboBoxEncodings->insertSeparator(1); + comboBoxEncodings->addItems(IConvLaTeX::encodings()); + connect(comboBoxEncodings, SIGNAL(currentIndexChanged(int)), p, SIGNAL(changed())); + + comboBoxStringDelimiters = new KComboBox(false, p); + comboBoxStringDelimiters->setObjectName("comboBoxStringDelimiters"); + layout->addRow(i18n("String Delimiters:"), comboBoxStringDelimiters); + comboBoxStringDelimiters->addItem(createDelimiterString('"', '"')); + comboBoxStringDelimiters->addItem(createDelimiterString('{', '}')); + comboBoxStringDelimiters->addItem(createDelimiterString('(', ')')); + connect(comboBoxStringDelimiters, SIGNAL(currentIndexChanged(int)), p, SIGNAL(changed())); + + comboBoxQuoteComment = new KComboBox(false, p); + layout->addRow(i18n("Comment Quoting:"), comboBoxQuoteComment); + comboBoxQuoteComment->addItem(i18n("None")); + comboBoxQuoteComment->addItem(i18n("@comment{%1}", QChar(8230))); + comboBoxQuoteComment->addItem(i18n("%{%1}", QChar(8230))); + connect(comboBoxQuoteComment, SIGNAL(currentIndexChanged(int)), p, SIGNAL(changed())); + + comboBoxKeywordCasing = new KComboBox(false, p); + layout->addRow(i18n("Keyword Casing:"), comboBoxKeywordCasing); + comboBoxKeywordCasing->addItem(i18n("lowercase")); + comboBoxKeywordCasing->addItem(i18n("Initial capital")); + comboBoxKeywordCasing->addItem(i18n("UpperCamelCase")); + comboBoxKeywordCasing->addItem(i18n("lowerCamelCase")); + comboBoxKeywordCasing->addItem(i18n("UPPERCASE")); + connect(comboBoxKeywordCasing, SIGNAL(currentIndexChanged(int)), p, SIGNAL(changed())); + + checkBoxProtectCasing = new QCheckBox(i18n("Protect Titles")); + layout->addRow(i18n("Protect Casing?"), checkBoxProtectCasing); + connect(checkBoxProtectCasing, SIGNAL(toggled(bool)), p, SIGNAL(changed())); + + comboBoxPersonNameFormatting = new KComboBox(false, p); + comboBoxPersonNameFormatting->setObjectName("comboBoxPersonNameFormatting"); + layout->addRow(i18n("Person Names Formatting:"), comboBoxPersonNameFormatting); + ItalicTextItemModel *itim = new ItalicTextItemModel(); + itim->addItem(i18n("Use global settings"), QString("")); + itim->addItem(Person::transcribePersonName(&dummyPerson, QLatin1String("<%f ><%l>")), QString("<%f ><%l>")); + itim->addItem(Person::transcribePersonName(&dummyPerson, QLatin1String("<%l><, %f>")), QString("<%l><, %f>")); + comboBoxPersonNameFormatting->setModel(itim); + connect(comboBoxPersonNameFormatting, SIGNAL(currentIndexChanged(int)), p, SIGNAL(changed())); + } + + void loadProperties(File *file) { + if (file->hasProperty(File::Encoding)) { + QString encoding = file->property(File::Encoding).toString(); + p->selectValue(comboBoxEncodings, encoding); + } + if (file->hasProperty(File::StringDelimiter)) { + QString stringDelimiter = file->property(File::StringDelimiter).toString(); + p->selectValue(comboBoxStringDelimiters, createDelimiterString(stringDelimiter[0], stringDelimiter[1])); + } + if (file->hasProperty(File::QuoteComment)) { + FileExporterBibTeX::QuoteComment quoteComment = (FileExporterBibTeX::QuoteComment)file->property(File::QuoteComment).toInt(); + comboBoxQuoteComment->setCurrentIndex((int)quoteComment); + } + if (file->hasProperty(File::KeywordCasing)) { + KBibTeX::Casing keywordCasing = (KBibTeX::Casing)file->property(File::KeywordCasing).toInt(); + comboBoxKeywordCasing->setCurrentIndex((int)keywordCasing); + } + if (file->hasProperty(File::ProtectCasing)) + checkBoxProtectCasing->setChecked(file->property(File::QuoteComment).toBool()); + if (file->hasProperty(File::NameFormatting)) + p->selectValue(comboBoxPersonNameFormatting, file->property(File::NameFormatting).toString(), Qt::UserRole); + } + + void saveProperties(File *file) { + file->setProperty(File::Encoding, comboBoxEncodings->currentText()); + QString stringDelimiter = comboBoxStringDelimiters->currentText(); + file->setProperty(File::StringDelimiter, QString(stringDelimiter[0]) + stringDelimiter[stringDelimiter.length()-1]); + FileExporterBibTeX::QuoteComment quoteComment = (FileExporterBibTeX::QuoteComment)comboBoxQuoteComment->currentIndex(); + file->setProperty(File::QuoteComment, (int)quoteComment); + KBibTeX::Casing keywordCasing = (KBibTeX::Casing)comboBoxKeywordCasing->currentIndex(); + file->setProperty(File::KeywordCasing, (int)keywordCasing); + file->setProperty(File::ProtectCasing, checkBoxProtectCasing->isChecked()); + file->setProperty(File::NameFormatting, comboBoxPersonNameFormatting->itemData(comboBoxPersonNameFormatting->currentIndex())); + } +}; + + +SettingsFileExporterBibTeXWidget::SettingsFileExporterBibTeXWidget(QWidget *parent) + : SettingsAbstractWidget(parent), d(new SettingsFileExporterBibTeXWidgetPrivate(this)) +{ + d->setupGUI(); + d->loadState(); +} + +SettingsFileExporterBibTeXWidget::SettingsFileExporterBibTeXWidget(File *file, QWidget *parent) + : SettingsAbstractWidget(parent), d(new SettingsFileExporterBibTeXWidgetPrivate(this)) +{ + d->setupGUI(); + d->loadState(); + d->loadProperties(file); +} + +void SettingsFileExporterBibTeXWidget::loadState() +{ + d->loadState(); +} + +void SettingsFileExporterBibTeXWidget::saveState() +{ + d->saveState(); +} + +void SettingsFileExporterBibTeXWidget::saveProperties(File *file) +{ + d->saveProperties(file); +} + +void SettingsFileExporterBibTeXWidget::resetToDefaults() +{ + d->resetToDefaults(); +} diff -Nru kbibtex-0.3/src/gui/preferences/settingsfileexporterbibtexwidget.h kbibtex-0.4/src/gui/preferences/settingsfileexporterbibtexwidget.h --- kbibtex-0.3/src/gui/preferences/settingsfileexporterbibtexwidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsfileexporterbibtexwidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,53 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_SETTINGSFILEEXPORTERBIBTEXWIDGET_H +#define KBIBTEX_GUI_SETTINGSFILEEXPORTERBIBTEXWIDGET_H + +#include + +#include "settingsabstractwidget.h" + +class File; + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT SettingsFileExporterBibTeXWidget : public SettingsAbstractWidget +{ + Q_OBJECT + +public: + SettingsFileExporterBibTeXWidget(QWidget *parent); + SettingsFileExporterBibTeXWidget(File *file, QWidget *parent); + + void saveProperties(File *file); + +public slots: + void loadState(); + void saveState(); + void resetToDefaults(); + +private: + class SettingsFileExporterBibTeXWidgetPrivate; + SettingsFileExporterBibTeXWidgetPrivate *d; +}; + +#endif // KBIBTEX_GUI_SETTINGSFILEEXPORTERBIBTEXWIDGET_H diff -Nru kbibtex-0.3/src/gui/preferences/settingsfileexporterpdfpswidget.cpp kbibtex-0.4/src/gui/preferences/settingsfileexporterpdfpswidget.cpp --- kbibtex-0.3/src/gui/preferences/settingsfileexporterpdfpswidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsfileexporterpdfpswidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,114 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include "fileexportertoolchain.h" +#include "settingsfileexporterpdfpswidget.h" + +class SettingsFileExporterPDFPSWidget::SettingsFileExporterPDFPSWidgetPrivate +{ +private: + SettingsFileExporterPDFPSWidget *p; + + KComboBox *comboBoxBabelLanguage; + KComboBox *comboBoxBibliographyStyle; + + KSharedConfigPtr config; + static const QString configGroupName; + +public: + + SettingsFileExporterPDFPSWidgetPrivate(SettingsFileExporterPDFPSWidget *parent) + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))) { + // nothing + } + + void loadState() { + KConfigGroup configGroup(config, configGroupName); + QString babelLanguage = configGroup.readEntry(FileExporterToolchain::keyBabelLanguage, FileExporterToolchain::defaultBabelLanguage); + p->selectValue(comboBoxBabelLanguage, babelLanguage); + QString bibliographyStyle = configGroup.readEntry(FileExporterToolchain::keyBibliographyStyle, FileExporterToolchain::defaultBibliographyStyle); + p->selectValue(comboBoxBibliographyStyle, bibliographyStyle); + } + + void saveState() { + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(FileExporterToolchain::keyBabelLanguage, comboBoxBabelLanguage->lineEdit()->text()); + configGroup.writeEntry(FileExporterToolchain::keyBibliographyStyle, comboBoxBibliographyStyle->lineEdit()->text()); + config->sync(); + } + + void resetToDefaults() { + p->selectValue(comboBoxBabelLanguage, FileExporterToolchain::defaultBabelLanguage); + p->selectValue(comboBoxBibliographyStyle, FileExporterToolchain::defaultBibliographyStyle); + } + + void setupGUI() { + QFormLayout *layout = new QFormLayout(p); + + comboBoxBabelLanguage = new KComboBox(true, p); + comboBoxBabelLanguage->setObjectName("comboBoxBabelLanguage"); + layout->addRow(i18n("Language for 'babel':"), comboBoxBabelLanguage); + comboBoxBabelLanguage->addItem(QLatin1String("english")); + comboBoxBabelLanguage->addItem(QLatin1String("ngerman")); + comboBoxBabelLanguage->addItem(QLatin1String("swedish")); + connect(comboBoxBabelLanguage->lineEdit(), SIGNAL(textChanged(QString)), p, SIGNAL(changed())); + + comboBoxBibliographyStyle = new KComboBox(true, p); + comboBoxBibliographyStyle->setObjectName("comboBoxBibliographyStyle"); + layout->addRow(i18n("Bibliography style:"), comboBoxBibliographyStyle); + comboBoxBibliographyStyle->addItem(QLatin1String("abbrv")); + comboBoxBibliographyStyle->addItem(QLatin1String("alpha")); + comboBoxBibliographyStyle->addItem(QLatin1String("plain")); + comboBoxBibliographyStyle->addItem(QLatin1String("dcu")); + connect(comboBoxBibliographyStyle->lineEdit(), SIGNAL(textChanged(QString)), p, SIGNAL(changed())); + } +}; + +const QString SettingsFileExporterPDFPSWidget::SettingsFileExporterPDFPSWidgetPrivate::configGroupName = QLatin1String("FileExporterPDFPS"); + +SettingsFileExporterPDFPSWidget::SettingsFileExporterPDFPSWidget(QWidget *parent) + : SettingsAbstractWidget(parent), d(new SettingsFileExporterPDFPSWidgetPrivate(this)) +{ + d->setupGUI(); + d->loadState(); +} + +void SettingsFileExporterPDFPSWidget::loadState() +{ + d->loadState(); +} + +void SettingsFileExporterPDFPSWidget::saveState() +{ + d->saveState(); +} + +void SettingsFileExporterPDFPSWidget::resetToDefaults() +{ + d->resetToDefaults(); +} diff -Nru kbibtex-0.3/src/gui/preferences/settingsfileexporterpdfpswidget.h kbibtex-0.4/src/gui/preferences/settingsfileexporterpdfpswidget.h --- kbibtex-0.3/src/gui/preferences/settingsfileexporterpdfpswidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsfileexporterpdfpswidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,48 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_SETTINGSFILEEXPORTERPDFPSWIDGET_H +#define KBIBTEX_GUI_SETTINGSFILEEXPORTERPDFPSWIDGET_H + +#include + +#include "settingsabstractwidget.h" + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT SettingsFileExporterPDFPSWidget : public SettingsAbstractWidget +{ + Q_OBJECT + +public: + SettingsFileExporterPDFPSWidget(QWidget *parent); + +public slots: + void loadState(); + void saveState(); + void resetToDefaults(); + +private: + class SettingsFileExporterPDFPSWidgetPrivate; + SettingsFileExporterPDFPSWidgetPrivate *d; +}; + +#endif // KBIBTEX_GUI_SETTINGSFILEEXPORTERPDFPSWIDGET_H diff -Nru kbibtex-0.3/src/gui/preferences/settingsfileexporterwidget.cpp kbibtex-0.4/src/gui/preferences/settingsfileexporterwidget.cpp --- kbibtex-0.3/src/gui/preferences/settingsfileexporterwidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsfileexporterwidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,159 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "settingsfileexporterwidget.h" + +class SettingsFileExporterWidget::SettingsFileExporterWidgetPrivate +{ +private: + SettingsFileExporterWidget *p; + + KComboBox *comboBoxPaperSize; + QMap paperSizeLabelToName; + + KComboBox *comboBoxCopyReferenceCmd; + static const QString citeCmdToLabel; + + KSharedConfigPtr config; + const QString configGroupNameGeneral, configGroupNameLyX; + +public: + KLineEdit *lineEditLyXServerPipeName; + + SettingsFileExporterWidgetPrivate(SettingsFileExporterWidget *parent) + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), + configGroupNameGeneral(QLatin1String("General")), configGroupNameLyX(QLatin1String("LyX")) { + paperSizeLabelToName.insert(i18n("A4"), QLatin1String("a4")); + paperSizeLabelToName.insert(i18n("Letter"), QLatin1String("letter")); + paperSizeLabelToName.insert(i18n("Legal"), QLatin1String("legal")); + } + + void loadState() { + KConfigGroup configGroup(config, configGroupNameGeneral); + const QString paperSizeName = configGroup.readEntry(FileExporter::keyPaperSize, FileExporter::defaultPaperSize); + p->selectValue(comboBoxPaperSize, paperSizeLabelToName.key(paperSizeName)); + + const QString copyReferenceCommand = configGroup.readEntry(Clipboard::keyCopyReferenceCommand, Clipboard::defaultCopyReferenceCommand); + p->selectValue(comboBoxCopyReferenceCmd, copyReferenceCommand.isEmpty() ? QString("") : copyReferenceCommand, Qt::UserRole); + + configGroup = KConfigGroup(config, configGroupNameLyX); + lineEditLyXServerPipeName->setText(configGroup.readEntry(LyX::keyLyXServerPipeName, LyX::defaultLyXServerPipeName)); + } + + void saveState() { + KConfigGroup configGroup(config, configGroupNameGeneral); + const QString paperSizeName = paperSizeLabelToName.value(comboBoxPaperSize->currentText(), FileExporter::defaultPaperSize); + configGroup.writeEntry(FileExporter::keyPaperSize, paperSizeName); + + const QString copyReferenceCommand = comboBoxCopyReferenceCmd->itemData(comboBoxCopyReferenceCmd->currentIndex()).toString(); + configGroup.writeEntry(Clipboard::keyCopyReferenceCommand, copyReferenceCommand); + + configGroup = KConfigGroup(config, configGroupNameLyX); + configGroup.writeEntry(LyX::keyLyXServerPipeName, lineEditLyXServerPipeName->text()); + config->sync(); + } + + void resetToDefaults() { + p->selectValue(comboBoxPaperSize, paperSizeLabelToName[FileExporter::defaultPaperSize]); + p->selectValue(comboBoxCopyReferenceCmd, QString(""), Qt::UserRole); + lineEditLyXServerPipeName->setText(LyX::defaultLyXServerPipeName); + } + + void setupGUI() { + QFormLayout *layout = new QFormLayout(p); + + comboBoxPaperSize = new KComboBox(false, p); + comboBoxPaperSize->setObjectName("comboBoxPaperSize"); + layout->addRow(i18n("Paper Size:"), comboBoxPaperSize); + QStringList paperSizeLabelToNameKeys = paperSizeLabelToName.keys(); + paperSizeLabelToNameKeys.sort(); + foreach(QString labelText, paperSizeLabelToNameKeys) { + comboBoxPaperSize->addItem(labelText, paperSizeLabelToName[labelText]); + } + connect(comboBoxPaperSize, SIGNAL(currentIndexChanged(int)), p, SIGNAL(changed())); + + comboBoxCopyReferenceCmd = new KComboBox(false, p); + comboBoxCopyReferenceCmd->setObjectName("comboBoxCopyReferenceCmd"); + layout->addRow(i18n("Command for \"Copy Reference\":"), comboBoxCopyReferenceCmd); + ItalicTextItemModel *itim = new ItalicTextItemModel(); + itim->addItem(i18n("No command"), QString("")); + const QStringList citeCommands = QStringList() << QLatin1String("cite") << QLatin1String("citealt") << QLatin1String("citeauthor") << QLatin1String("citeauthor*") << QLatin1String("citeyear") << QLatin1String("citeyearpar") << QLatin1String("shortcite") << QLatin1String("citet") << QLatin1String("citet*") << QLatin1String("citep") << QLatin1String("citep*"); // TODO more + foreach(const QString &citeCommand, citeCommands) { + itim->addItem(citeCmdToLabel.arg(citeCommand), citeCommand); + } + comboBoxCopyReferenceCmd->setModel(itim); + connect(comboBoxCopyReferenceCmd, SIGNAL(currentIndexChanged(int)), p, SIGNAL(changed())); + + QBoxLayout *boxLayout = new QHBoxLayout(); + boxLayout->setMargin(0); + lineEditLyXServerPipeName = new KLineEdit(p); + lineEditLyXServerPipeName->setReadOnly(true); + boxLayout->addWidget(lineEditLyXServerPipeName); + KPushButton *buttonBrowseForPipeName = new KPushButton(KIcon("network-connect"), "", p); + boxLayout->addWidget(buttonBrowseForPipeName); + layout->addRow(i18n("LyX Server Pipe"), boxLayout); + connect(buttonBrowseForPipeName, SIGNAL(clicked()), p, SLOT(selectPipeName())); + connect(lineEditLyXServerPipeName, SIGNAL(textChanged(QString)), p, SIGNAL(changed())); + } +}; + +const QString SettingsFileExporterWidget::SettingsFileExporterWidgetPrivate::citeCmdToLabel = QLatin1String("\\%1{") + QChar(0x2026) + QChar('}'); + +SettingsFileExporterWidget::SettingsFileExporterWidget(QWidget *parent) + : SettingsAbstractWidget(parent), d(new SettingsFileExporterWidgetPrivate(this)) +{ + d->setupGUI(); + d->loadState(); +} + +void SettingsFileExporterWidget::loadState() +{ + d->loadState(); +} + +void SettingsFileExporterWidget::saveState() +{ + d->saveState(); +} + +void SettingsFileExporterWidget::resetToDefaults() +{ + d->resetToDefaults(); +} + +void SettingsFileExporterWidget::selectPipeName() +{ + QString pipeName = KFileDialog::getOpenFileName(QDir::homePath(), QLatin1String("inode/fifo"), this, i18n("Select LyX Server Pipe")); + if (!pipeName.isEmpty()) + d->lineEditLyXServerPipeName->setText(pipeName); +} diff -Nru kbibtex-0.3/src/gui/preferences/settingsfileexporterwidget.h kbibtex-0.4/src/gui/preferences/settingsfileexporterwidget.h --- kbibtex-0.3/src/gui/preferences/settingsfileexporterwidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsfileexporterwidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,52 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_SETTINGSFILEEXPORTERWIDGET_H +#define KBIBTEX_GUI_SETTINGSFILEEXPORTERWIDGET_H + +#include + +#include "settingsabstractwidget.h" + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT SettingsFileExporterWidget : public SettingsAbstractWidget +{ + Q_OBJECT + +public: + SettingsFileExporterWidget(QWidget *parent); + +public slots: + void loadState(); + void saveState(); + void resetToDefaults(); + +private: + class SettingsFileExporterWidgetPrivate; + SettingsFileExporterWidgetPrivate *d; + +private slots: + void selectPipeName(); +}; + + +#endif // KBIBTEX_GUI_SETTINGSFILEEXPORTERWIDGET_H diff -Nru kbibtex-0.3/src/gui/preferences/settingsgeneralwidget.cpp kbibtex-0.4/src/gui/preferences/settingsgeneralwidget.cpp --- kbibtex-0.3/src/gui/preferences/settingsgeneralwidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsgeneralwidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,101 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include "settingsgeneralwidget.h" + +class SettingsGeneralWidget::SettingsGeneralWidgetPrivate +{ +private: + SettingsGeneralWidget *p; + + KComboBox *comboBoxPersonNameFormatting; + const Person dummyPerson; + QString restartRequiredMsg; + + KSharedConfigPtr config; + const QString configGroupName; + +public: + + SettingsGeneralWidgetPrivate(SettingsGeneralWidget *parent) + : p(parent), dummyPerson(Person(i18n("John"), i18n("Doe"), i18n("Jr."))), restartRequiredMsg(i18n("Changing this option requires a restart to take effect.")), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupName(QLatin1String("General")) { + // nothing + } + + void loadState() { + KConfigGroup configGroup(config, configGroupName); + QString personNameFormatting = configGroup.readEntry(Person::keyPersonNameFormatting, Person::defaultPersonNameFormatting); + p->selectValue(comboBoxPersonNameFormatting, Person::transcribePersonName(&dummyPerson, personNameFormatting)); + } + + void saveState() { + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(Person::keyPersonNameFormatting, comboBoxPersonNameFormatting->itemData(comboBoxPersonNameFormatting->currentIndex())); + config->sync(); + } + + void resetToDefaults() { + p->selectValue(comboBoxPersonNameFormatting, Person::transcribePersonName(&dummyPerson, Person::defaultPersonNameFormatting)); + } + + void setupGUI() { + QFormLayout *layout = new QFormLayout(p); + + comboBoxPersonNameFormatting = new KComboBox(false, p); + layout->addRow(i18n("Person Names Formatting:"), comboBoxPersonNameFormatting); + const QStringList formattingOptions = QStringList() << QLatin1String("<%f ><%l><, %s>") << QLatin1String("<%l><, %f><, %s>"); + foreach(const QString &formattingOption, formattingOptions) { + comboBoxPersonNameFormatting->addItem(Person::transcribePersonName(&dummyPerson, formattingOption), formattingOption); + } + comboBoxPersonNameFormatting->setToolTip(restartRequiredMsg); + connect(comboBoxPersonNameFormatting, SIGNAL(currentIndexChanged(int)), p, SIGNAL(changed())); + } +}; + + +SettingsGeneralWidget::SettingsGeneralWidget(QWidget *parent) + : SettingsAbstractWidget(parent), d(new SettingsGeneralWidgetPrivate(this)) +{ + d->setupGUI(); + d->loadState(); +} + +void SettingsGeneralWidget::loadState() +{ + d->loadState(); +} + +void SettingsGeneralWidget::saveState() +{ + d->saveState(); +} + +void SettingsGeneralWidget::resetToDefaults() +{ + d->resetToDefaults(); +} diff -Nru kbibtex-0.3/src/gui/preferences/settingsgeneralwidget.h kbibtex-0.4/src/gui/preferences/settingsgeneralwidget.h --- kbibtex-0.3/src/gui/preferences/settingsgeneralwidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsgeneralwidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,50 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_SETTINGSGENERALWIDGET_H +#define KBIBTEX_GUI_SETTINGSGENERALWIDGET_H + +#include + +#include "settingsabstractwidget.h" + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT SettingsGeneralWidget : public SettingsAbstractWidget +{ + Q_OBJECT + +public: + SettingsGeneralWidget(QWidget *parent); + +public slots: + void loadState(); + void saveState(); + void resetToDefaults(); + +private: + class SettingsGeneralWidgetPrivate; + SettingsGeneralWidgetPrivate *d; +}; + + +#endif // KBIBTEX_GUI_SETTINGSGENERALWIDGET_H + diff -Nru kbibtex-0.3/src/gui/preferences/settingsglobalkeywordswidget.cpp kbibtex-0.4/src/gui/preferences/settingsglobalkeywordswidget.cpp --- kbibtex-0.3/src/gui/preferences/settingsglobalkeywordswidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsglobalkeywordswidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,135 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "settingsglobalkeywordswidget.h" + +class SettingsGlobalKeywordsWidget::SettingsGlobalKeywordsWidgetPrivate +{ +private: + SettingsGlobalKeywordsWidget *p; + + KSharedConfigPtr config; + const QString configGroupName; + +public: + QListView *listViewKeywords; + QStringListModel stringListModel; + KPushButton *buttonRemove; + + SettingsGlobalKeywordsWidgetPrivate(SettingsGlobalKeywordsWidget *parent) + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupName(QLatin1String("Global Keywords")) { + // nothing + } + + void loadState() { + KConfigGroup configGroup(config, configGroupName); + QStringList keywordList = configGroup.readEntry(KeywordListEdit::keyGlobalKeywordList, QStringList()); + stringListModel.setStringList(keywordList); + } + + void saveState() { + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(KeywordListEdit::keyGlobalKeywordList, stringListModel.stringList()); + config->sync(); + } + + void resetToDefaults() { + /// ignored, you don't want to delete all your keywords ... + } + + void setupGUI() { + QGridLayout *layout = new QGridLayout(p); + layout->setMargin(0); + + listViewKeywords = new QListView(p); + layout->addWidget(listViewKeywords, 0, 0, 3, 1); + listViewKeywords->setModel(&stringListModel); + connect(listViewKeywords, SIGNAL(pressed(QModelIndex)), p, SLOT(enableRemoveButton())); + + KPushButton *buttonAdd = new KPushButton(KIcon("list-add"), i18n("Add ..."), p); + layout->addWidget(buttonAdd, 0, 1, 1, 1); + connect(buttonAdd, SIGNAL(clicked()), p, SLOT(addKeywordDialog())); + + buttonRemove = new KPushButton(KIcon("list-remove"), i18n("Remove"), p); + layout->addWidget(buttonRemove, 1, 1, 1, 1); + buttonRemove->setEnabled(false); + connect(buttonRemove, SIGNAL(clicked()), p, SLOT(removeKeyword())); + } +}; + + +SettingsGlobalKeywordsWidget::SettingsGlobalKeywordsWidget(QWidget *parent) + : SettingsAbstractWidget(parent), d(new SettingsGlobalKeywordsWidgetPrivate(this)) +{ + d->setupGUI(); + d->loadState(); +} + +void SettingsGlobalKeywordsWidget::loadState() +{ + d->loadState(); +} + +void SettingsGlobalKeywordsWidget::saveState() +{ + d->saveState(); +} + +void SettingsGlobalKeywordsWidget::resetToDefaults() +{ + d->resetToDefaults(); +} + +void SettingsGlobalKeywordsWidget::addKeywordDialog() +{ + bool ok = false; + QString newKeyword = KInputDialog::getText(i18n("New Keyword"), i18n("Enter a new keyword:"), QLatin1String(""), &ok, this); + if (ok && !d->stringListModel.stringList().contains(newKeyword)) { + QStringList list = d->stringListModel.stringList(); + list.append(newKeyword); + list.sort(); + d->stringListModel.setStringList(list); + } +} + +void SettingsGlobalKeywordsWidget::removeKeyword() +{ + QString keyword = d->stringListModel.data(d->listViewKeywords->selectionModel()->selectedIndexes().first(), Qt::DisplayRole).toString(); + QStringList list = d->stringListModel.stringList(); + list.removeOne(keyword); + d->stringListModel.setStringList(list); + d->buttonRemove->setEnabled(false); +} + +void SettingsGlobalKeywordsWidget::enableRemoveButton() +{ + d->buttonRemove->setEnabled(!d->listViewKeywords->selectionModel()->selectedIndexes().isEmpty()); +} diff -Nru kbibtex-0.3/src/gui/preferences/settingsglobalkeywordswidget.h kbibtex-0.4/src/gui/preferences/settingsglobalkeywordswidget.h --- kbibtex-0.3/src/gui/preferences/settingsglobalkeywordswidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsglobalkeywordswidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,55 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_SETTINGSGLOBALKEYWORDSWIDGET_H +#define KBIBTEX_GUI_SETTINGSGLOBALKEYWORDSWIDGET_H + +#include + +#include "settingsabstractwidget.h" + +class File; + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT SettingsGlobalKeywordsWidget : public SettingsAbstractWidget +{ + Q_OBJECT + +public: + SettingsGlobalKeywordsWidget(QWidget *parent); + +public slots: + void loadState(); + void saveState(); + void resetToDefaults(); + +private slots: + void addKeywordDialog(); + void removeKeyword(); + void enableRemoveButton(); + +private: + class SettingsGlobalKeywordsWidgetPrivate; + SettingsGlobalKeywordsWidgetPrivate *d; +}; + +#endif // KBIBTEX_GUI_SETTINGSGLOBALKEYWORDSWIDGET_H diff -Nru kbibtex-0.3/src/gui/preferences/settingsuserinterfacewidget.cpp kbibtex-0.4/src/gui/preferences/settingsuserinterfacewidget.cpp --- kbibtex-0.3/src/gui/preferences/settingsuserinterfacewidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsuserinterfacewidget.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,109 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include +#include "bibtexfilemodel.h" +#include "settingsuserinterfacewidget.h" + +class SettingsUserInterfaceWidget::SettingsUserInterfaceWidgetPrivate +{ +private: + SettingsUserInterfaceWidget *p; + + QCheckBox *checkBoxShowComments; + QCheckBox *checkBoxShowMacros; + QCheckBox *checkBoxLabelsAboveWidget; + + KSharedConfigPtr config; + static const QString configGroupName; + +public: + SettingsUserInterfaceWidgetPrivate(SettingsUserInterfaceWidget *parent) + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))) { + } + + void loadState() { + KConfigGroup configGroup(config, configGroupName); + checkBoxShowComments->setChecked(configGroup.readEntry(BibTeXFileModel::keyShowComments, BibTeXFileModel::defaultShowComments)); + checkBoxShowMacros->setChecked(configGroup.readEntry(BibTeXFileModel::keyShowMacros, BibTeXFileModel::defaultShowMacros)); + checkBoxLabelsAboveWidget->setChecked((Qt::Orientation)(configGroup.readEntry(ElementWidget::keyElementWidgetLayout, (int)ElementWidget::defaultElementWidgetLayout)) == Qt::Vertical); + } + + void saveState() { + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(BibTeXFileModel::keyShowComments, checkBoxShowComments->isChecked()); + configGroup.writeEntry(BibTeXFileModel::keyShowMacros, checkBoxShowMacros->isChecked()); + configGroup.writeEntry(ElementWidget::keyElementWidgetLayout, (int)(checkBoxLabelsAboveWidget->isChecked() ? Qt::Vertical : Qt::Horizontal)); + config->sync(); + } + + void resetToDefaults() { + checkBoxShowComments->setChecked(BibTeXFileModel::defaultShowComments); + checkBoxShowMacros->setChecked(BibTeXFileModel::defaultShowMacros); + checkBoxLabelsAboveWidget->setChecked(ElementWidget::defaultElementWidgetLayout == Qt::Vertical); + } + + void setupGUI() { + QFormLayout *layout = new QFormLayout(p); + + checkBoxShowComments = new QCheckBox(p); + layout->addRow(i18n("Show Comments:"), checkBoxShowComments); + connect(checkBoxShowComments, SIGNAL(toggled(bool)), p, SIGNAL(changed())); + checkBoxShowMacros = new QCheckBox(p); + layout->addRow(i18n("Show Macros:"), checkBoxShowMacros); + connect(checkBoxShowMacros, SIGNAL(toggled(bool)), p, SIGNAL(changed())); + checkBoxLabelsAboveWidget = new QCheckBox(i18n("Labels above edit widgets"), p); + layout->addRow(i18n("Entry Editor:"), checkBoxLabelsAboveWidget); + connect(checkBoxLabelsAboveWidget, SIGNAL(toggled(bool)), p, SIGNAL(changed())); + } +}; + +const QString SettingsUserInterfaceWidget::SettingsUserInterfaceWidgetPrivate::configGroupName = QLatin1String("User Interface"); + + +SettingsUserInterfaceWidget::SettingsUserInterfaceWidget(QWidget *parent) + : SettingsAbstractWidget(parent), d(new SettingsUserInterfaceWidgetPrivate(this)) +{ + d->setupGUI(); + d->loadState(); +} + +void SettingsUserInterfaceWidget::loadState() +{ + d->loadState(); +} + +void SettingsUserInterfaceWidget::saveState() +{ + d->saveState(); +} + +void SettingsUserInterfaceWidget::resetToDefaults() +{ + d->resetToDefaults(); +} diff -Nru kbibtex-0.3/src/gui/preferences/settingsuserinterfacewidget.h kbibtex-0.4/src/gui/preferences/settingsuserinterfacewidget.h --- kbibtex-0.3/src/gui/preferences/settingsuserinterfacewidget.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/preferences/settingsuserinterfacewidget.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,49 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_SETTINGSUSERINTERFACEWIDGET_H +#define KBIBTEX_GUI_SETTINGSUSERINTERFACEWIDGET_H + +#include + +#include "settingsabstractwidget.h" + +/** +@author Thomas Fischer +*/ +class KBIBTEXGUI_EXPORT SettingsUserInterfaceWidget : public SettingsAbstractWidget +{ + Q_OBJECT + +public: + SettingsUserInterfaceWidget(QWidget *parent); + +public slots: + void loadState(); + void saveState(); + void resetToDefaults(); + +private: + class SettingsUserInterfaceWidgetPrivate; + SettingsUserInterfaceWidgetPrivate *d; +}; + + +#endif // KBIBTEX_GUI_SETTINGSUSERINTERFACEWIDGET_H diff -Nru kbibtex-0.3/src/gui/valuelistmodel.cpp kbibtex-0.4/src/gui/valuelistmodel.cpp --- kbibtex-0.3/src/gui/valuelistmodel.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/valuelistmodel.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -22,71 +22,311 @@ #include #include - +#include +#include +#include +#include + +#include +#include +#include #include #include #include +#include +#include +#include +#include +#include +#include #include #include +#include #include "valuelistmodel.h" -static QRegExp ignoredInSorting("[{}\\]+"); +const int CountRole = Qt::UserRole + 611; + +QWidget *ValueListDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &sovi, const QModelIndex &index) const +{ + if (index.column() == 0) { + const FieldDescription &fd = BibTeXFields::self()->find(m_fieldName); + FieldLineEdit *fieldLineEdit = new FieldLineEdit(fd.preferredTypeFlag, fd.typeFlags, false, parent); + fieldLineEdit->setAutoFillBackground(true); + return fieldLineEdit; + } else + return QStyledItemDelegate::createEditor(parent, sovi, index); +} + +void ValueListDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + if (index.column() == 0) { + FieldLineEdit *fieldLineEdit = qobject_cast(editor); + if (fieldLineEdit != NULL) + fieldLineEdit->reset(index.model()->data(index, Qt::EditRole).value()); + } +} + +void ValueListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + FieldLineEdit *fieldLineEdit = qobject_cast(editor); + if (fieldLineEdit != NULL) { + Value v; + fieldLineEdit->apply(v); + if (v.count() == 1) /// field should contain exactly one value item (no zero, not two or more) + model->setData(index, QVariant::fromValue(v)); + } +} + +QSize ValueListDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + QSize size = QStyledItemDelegate::sizeHint(option, index); + size.setHeight(qMax(size.height(), option.fontMetrics.height()*3 / 2)); // TODO calculate height better + return size; +} + +void ValueListDelegate::commitAndCloseEditor() +{ + QLineEdit *editor = qobject_cast(sender()); + emit commitData(editor); + emit closeEditor(editor); +} + +void ValueListDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 *noTextOption = qstyleoption_cast(option); + QStyledItemDelegate::initStyleOption(noTextOption, index); + if (option->decorationPosition != QStyleOptionViewItem::Top) { + /// remove text from style (do not draw text) + noTextOption->text.clear(); + } + +} + +void ValueListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + /// code heavily inspired by kdepimlibs-4.6.3/akonadi/collectionstatisticsdelegate.cpp + + /// save painter's state, restored before leaving this function + painter->save(); + + /// first, paint the basic, but without the text. We remove the text + /// in initStyleOption(), which gets called by QStyledItemDelegate::paint(). + QStyledItemDelegate::paint(painter, option, index); + + /// now, we retrieve the correct style option by calling intiStyleOption from + /// the superclass. + QStyleOptionViewItemV4 option4 = option; + QStyledItemDelegate::initStyleOption(&option4, index); + QString field = option4.text; + + /// now calculate the rectangle for the text + QStyle *s = m_parent->style(); + const QWidget *widget = option4.widget; + const QRect textRect = s->subElementRect(QStyle::SE_ItemViewItemText, &option4, widget); + + if (option.state & QStyle::State_Selected) { + /// selected lines are drawn with different color + painter->setPen(option.palette.highlightedText().color()); + } + + /// count will be empty unless only one column is shown + const QString count = index.column() == 0 && index.model()->columnCount() == 1 ? QString(QLatin1String(" (%1)")).arg(index.data(CountRole).toInt()) : QLatin1String(""); + + /// squeeze the folder text if it is to big and calculate the rectangles + /// where the folder text and the unread count will be drawn to + QFontMetrics fm(painter->fontMetrics()); + int countWidth = fm.width(count); + int fieldWidth = fm.width(field); + if (countWidth + fieldWidth > textRect.width()) { + /// text plus count is too wide for column, cut text and insert "..." + field = fm.elidedText(field, Qt::ElideRight, textRect.width() - countWidth); + fieldWidth = fm.width(field); + } + + /// determine rects to draw field + int top = textRect.top() + (textRect.height() - fm.height()) / 2; + QRect fieldRect = textRect; + QRect countRect = textRect; + fieldRect.setTop(top); + fieldRect.setHeight(fm.height()); + + if (m_parent->header()->visualIndex(index.column()) == 0) { + /// left-align text + fieldRect.setLeft(fieldRect.left() + 4); ///< hm, indent necessary? + fieldRect.setRight(fieldRect.left() + fieldWidth); + } else { + /// right-align text + fieldRect.setRight(fieldRect.right() - 4); ///< hm, indent necessary? + fieldRect.setLeft(fieldRect.right() - fieldWidth); ///< hm, indent necessary? + } + + /// draw field name + painter->drawText(fieldRect, Qt::AlignLeft, field); + + if (!count.isEmpty()) { + /// determine rects to draw count + countRect.setTop(top); + countRect.setHeight(fm.height()); + countRect.setLeft(fieldRect.right()); + + /// use bold font + QFont font = painter->font(); + font.setBold(true); + painter->setFont(font); + /// determine color for count number + const QColor countColor = (option.state & QStyle::State_Selected) ? KColorScheme(QPalette::Active, KColorScheme::Selection).foreground(KColorScheme::LinkText).color() : KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::LinkText).color(); + painter->setPen(countColor); + + /// draw count + painter->drawText(countRect, Qt::AlignLeft, count); + } + + /// restore painter's state + painter->restore(); +} + +static QRegExp ignoredInSorting("[{}\\\\]+"); ValueListModel::ValueListModel(const File *bibtexFile, const QString &fieldName, QObject *parent) - : QAbstractTableModel(parent), file(bibtexFile), fName(fieldName.toLower()) + : QAbstractTableModel(parent), file(bibtexFile), fName(fieldName.toLower()), showCountColumn(true), sortBy(SortByText) { + /// load mapping from color value to label + KSharedConfigPtr config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))); + KConfigGroup configGroup(config, Preferences::groupColor); + QStringList colorCodes = configGroup.readEntry(Preferences::keyColorCodes, Preferences::defaultColorCodes); + QStringList colorLabels = configGroup.readEntry(Preferences::keyColorLabels, Preferences::defaultcolorLabels); + for (QStringList::ConstIterator itc = colorCodes.constBegin(), itl = colorLabels.constBegin(); itc != colorCodes.constEnd() && itl != colorLabels.constEnd(); ++itc, ++itl) { + colorToLabel.insert(*itc, *itl); + } + updateValues(); } int ValueListModel::rowCount(const QModelIndex & parent) const { - return parent == QModelIndex() ? sortedValues.count() : 0; + return parent == QModelIndex() ? values.count() : 0; } int ValueListModel::columnCount(const QModelIndex & parent) const { - return parent == QModelIndex() ? 2 : 0; + return parent == QModelIndex() ? (showCountColumn ? 2 : 1) : 0; } QVariant ValueListModel::data(const QModelIndex & index, int role) const { - if (index.row() >= sortedValues.count() || index.column() >= 2) + if (index.row() >= values.count() || index.column() >= 2) return QVariant(); - if (role == Qt::DisplayRole) { - if (index.column() == 0) - return QVariant(sortedValues[index.row()]); - else - return QVariant(valueToCount[sortedValues[index.row()]]); - } else if (role == Qt::TextAlignmentRole) { - if (index.column() == 0) - return QVariant(Qt::AlignLeft); - else - return QVariant(Qt::AlignRight); - } else if (role == SortRole) { + if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { if (index.column() == 0) { - QString buffer = sortedValues[index.row()]; + if (fName == Entry::ftColor) { + QString text = values[index.row()].text; + if (text.isEmpty()) return QVariant(); + QString colorText = colorToLabel[text]; + if (colorText.isEmpty()) return QVariant(text); + return QVariant(colorText); + } else + return QVariant(values[index.row()].text); + } else + return QVariant(values[index.row()].count); + } else if (role == SortRole) { + if ((showCountColumn && index.column() == 0) || (!showCountColumn && sortBy == SortByText)) { + QString buffer = values[index.row()].sortBy.isNull() ? values[index.row()].text : values[index.row()].sortBy; return QVariant(buffer.replace(ignoredInSorting, "")); } else - return QVariant(valueToCount[sortedValues[index.row()]]); - } else + return QVariant(values[index.row()].count); + } else if (role == SearchTextRole) { + return QVariant(values[index.row()].text); + } else if (role == Qt::EditRole) + return QVariant::fromValue(values[index.row()].value); + else if (role == CountRole) + return QVariant(values[index.row()].count); + else return QVariant(); } +bool ValueListModel::setData(const QModelIndex & index, const QVariant &value, int role) +{ + if (role == Qt::EditRole && index.column() == 0) { + Value newValue = value.value(); /// nice names ... ;-) + QString origText = data(index, Qt::DisplayRole).toString(); + if (fName == Entry::ftColor) { + QString color = colorToLabel.key(origText); + if (!color.isEmpty()) origText = color; + } + int index = indexOf(origText); Q_ASSERT(index >= 0); + const QString newText = PlainTextValue::text(newValue); + kDebug() << "replacing" << origText << "with" << newText << "for field" << fName; + + foreach(Element *element, *file) { + Entry *entry = dynamic_cast< Entry*>(element); + if (entry != NULL) { + for (Entry::Iterator eit = entry->begin(); eit != entry->end(); ++eit) { + QString key = eit.key().toLower(); + if (key == fName) { + const QString valueFullText = PlainTextValue::text(eit.value()); + if (valueFullText == origText) + entry->insert(key, newValue); + else { + for (Value::Iterator vit = eit.value().begin(); vit != eit.value().end(); ++vit) { + const QString valueItemText = PlainTextValue::text(*(*vit)); + if (valueItemText == origText) { + vit = eit.value().erase(vit); + vit = eit.value().insert(vit, newValue.first()); + } + } + } + break; + } + } + } + } + + values[index].text = newText; + values[index].value = newValue; + const Person *person = dynamic_cast(newValue.first()); + values[index].sortBy = person == NULL ? QString::null : person->lastName() + QLatin1String(" ") + person->firstName(); + reset(); + + return true; + } + return false; +} + +Qt::ItemFlags ValueListModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags result = QAbstractTableModel::flags(index); + /// make first column editable + if (index.column() == 0) + result |= Qt::ItemIsEditable; + return result; +} + QVariant ValueListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section >= 2 || orientation != Qt::Horizontal || role != Qt::DisplayRole) return QVariant(); - else if (section == 0) + else if ((section == 0 && columnCount() == 2) || (columnCount() == 1 && sortBy == SortByText)) return QVariant(i18n("Value")); else return QVariant(i18n("Count")); } +void ValueListModel::setShowCountColumn(bool showCountColumn) +{ + this->showCountColumn = showCountColumn; + reset(); +} + +void ValueListModel::setSortBy(SortBy sortBy) +{ + this->sortBy = sortBy; + reset(); +} + void ValueListModel::updateValues() { - sortedValues.clear(); - valueToCount.clear(); + values.clear(); for (File::ConstIterator fit = file->constBegin(); fit != file->constEnd(); ++fit) { const Entry *entry = dynamic_cast(*fit); @@ -97,6 +337,8 @@ insertValue(eit.value()); break; } + if (eit.value().isEmpty()) + kWarning() << "value for key" << key << "in entry" << entry->id() << "is empty"; } } } @@ -104,26 +346,49 @@ void ValueListModel::insertValue(const Value &value) { - for (Value::ConstIterator it = value.constBegin(); it != value.constEnd(); ++it) { - if (typeid(*(*it)) == typeid(Person)) { - const Person *person = static_cast(*it); - QString text = person->lastName(); - if (!person->firstName().isEmpty()) text.append(", " + person->firstName()); - // TODO: other parts of name - if (valueToCount.contains(text)) - valueToCount[text] = valueToCount[text] + 1; - else { - valueToCount[text] = 1; - sortedValues << text; - } + foreach(ValueItem *item, value) { + const QString text = PlainTextValue::text(*item, file); + if (text.isEmpty()) continue; ///< skip empty values + + int index = indexOf(text); + if (index < 0) { + /// previously unknown text + ValueLine newValueLine; + newValueLine.text = text; + newValueLine.count = 1; + Value v; + v.append(item); + newValueLine.value = v; + + /// memorize sorting criterium: + /// * for persons, use last name first + /// * in any case, use lower case + const Person *person = dynamic_cast(item); + newValueLine.sortBy = person == NULL ? text.toLower() : person->lastName().toLower() + QLatin1String(" ") + person->firstName().toLower(); + + values << newValueLine; } else { - const QString text = PlainTextValue::text(*(*it), file); - if (valueToCount.contains(text)) - valueToCount[text] = valueToCount[text] + 1; - else { - valueToCount[text] = 1; - sortedValues << text; - } + ++values[index].count; } } } + +int ValueListModel::indexOf(const QString &text) +{ + QString color; + QString cmpText = text; + if (fName == Entry::ftColor && !(color = colorToLabel.key(text, QLatin1String(""))).isEmpty()) + cmpText = color; + if (cmpText.isEmpty()) + kWarning() << "Should never happen"; + + int i = 0; + /// this is really slow for large data sets: O(n^2) + /// maybe use a hash table instead? + foreach(const ValueLine &valueLine, values) { + if (valueLine.text == cmpText) + return i; + ++i; + } + return -1; +} diff -Nru kbibtex-0.3/src/gui/valuelistmodel.h kbibtex-0.4/src/gui/valuelistmodel.h --- kbibtex-0.3/src/gui/valuelistmodel.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/valuelistmodel.h 2011-11-20 20:36:54.000000000 +0000 @@ -22,30 +22,79 @@ #define KBIBTEX_PROGRAM_VALUELISTMODEL_H #include +#include +#include #include static const int SortRole = Qt::UserRole + 113; +static const int SearchTextRole = Qt::UserRole + 114; -class ValueListModel : public QAbstractTableModel +class KBIBTEXGUI_EXPORT ValueListDelegate : public QStyledItemDelegate { + Q_OBJECT + private: + QString m_fieldName; + QTreeView *m_parent; + +public: + ValueListDelegate(QTreeView *parent = NULL) + : QStyledItemDelegate(parent), m_fieldName(QString::null), m_parent(parent) {} + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const; + void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const; + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + + void setFieldName(const QString &fieldName) { + m_fieldName = fieldName; + } + +private slots: + void commitAndCloseEditor(); +}; + +class KBIBTEXGUI_EXPORT ValueListModel : public QAbstractTableModel +{ +public: + enum SortBy { SortByText, SortByCount }; + +private: + struct ValueLine { + QString text; + QString sortBy; + Value value; + int count; + }; + const File *file; const QString fName; - QStringList sortedValues; - QMap valueToCount; + QList values; + QMap colorToLabel; + bool showCountColumn; + SortBy sortBy; public: ValueListModel(const File *bibtexFile, const QString &fieldName, QObject *parent); - int rowCount(const QModelIndex & parent = QModelIndex()) const; - int columnCount(const QModelIndex & parent = QModelIndex()) const; - QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex & parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void setShowCountColumn(bool showCountColumn); + void setSortBy(SortBy sortBy); private: void updateValues(); void insertValue(const Value &value); + int indexOf(const QString &text); + QString htmlize(const QString &text) const; }; diff -Nru kbibtex-0.3/src/gui/widgets/filterbar.cpp kbibtex-0.4/src/gui/widgets/filterbar.cpp --- kbibtex-0.3/src/gui/widgets/filterbar.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/widgets/filterbar.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -21,10 +21,13 @@ #include #include #include +#include #include #include #include +#include +#include #include "filterbar.h" #include "bibtexfields.h" @@ -35,19 +38,31 @@ FilterBar *p; public: + KSharedConfigPtr config; + const QString configGroupName; + KComboBox *comboBoxFilterText; + const int maxNumStoredFilterTexts; KComboBox *comboBoxCombination; KComboBox *comboBoxField; + QTimer *filterUpdateTimer; FilterBarPrivate(FilterBar *parent) - : p(parent) { - // nothing + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), + configGroupName(QLatin1String("Filter Bar")), maxNumStoredFilterTexts(12), filterUpdateTimer(new QTimer(parent)) { + connect(filterUpdateTimer, SIGNAL(timeout()), p, SLOT(timerTriggered())); } void clearFilter() { + comboBoxCombination->blockSignals(true); + comboBoxField->blockSignals(true); + comboBoxFilterText->lineEdit()->setText(""); comboBoxCombination->setCurrentIndex(0); comboBoxField->setCurrentIndex(0); + + comboBoxCombination->blockSignals(false); + comboBoxField->blockSignals(false); } SortFilterBibTeXFileModel::FilterQuery filter() { @@ -64,13 +79,67 @@ } void setFilter(SortFilterBibTeXFileModel::FilterQuery fq) { + comboBoxCombination->blockSignals(true); + comboBoxField->blockSignals(true); + comboBoxCombination->setCurrentIndex(fq.combination == SortFilterBibTeXFileModel::AnyTerm ? 0 : (fq.terms.count() < 2 ? 2 : 1)); comboBoxFilterText->lineEdit()->setText(fq.terms.join(" ")); - for (int idx = 0; idx < comboBoxField->count();++idx) - if (fq.field == comboBoxField->itemText(idx) || comboBoxField->itemData(idx, Qt::UserRole).toString() == fq.field) { + for (int idx = 0; idx < comboBoxField->count(); ++idx) { + const QString lower = fq.field.toLower(); + if (lower == comboBoxField->itemText(idx).toLower() || comboBoxField->itemData(idx, Qt::UserRole).toString().toLower() == lower) { comboBoxField->setCurrentIndex(idx); break; } + } + + comboBoxCombination->blockSignals(false); + comboBoxField->blockSignals(false); + } + + void addCompletionString(const QString &text) { + KConfigGroup configGroup(config, configGroupName); + + /// Previous searches are stored as a string list, where each individual + /// string starts with 12 characters for the date and time when this + /// search was used. Starting from the 13th character (12th, if you + /// start counting from 0) the user's input is stored. + /// This approach has several advantages: It does not require a more + /// complex data structure, can easily read and written using + /// KConfigGroup's functions, and can be sorted lexicographically/ + /// chronologically using QStringList's sort. + /// Disadvantage is that string fragments have to be managed manually. + QStringList completionListDate = configGroup.readEntry(QLatin1String("PreviousSearches"), QStringList()); + for (QStringList::Iterator it = completionListDate.begin(); it != completionListDate.end();) + if ((*it).mid(12) == text) + it = completionListDate.erase(it); + else + ++it; + completionListDate << (QDateTime::currentDateTime().toString("yyyyMMddhhmm") + text); + + /// after sorting, discard all but the maxNumStoredFilterTexts most + /// recent user-entered filter texts + completionListDate.sort(); + while (completionListDate.count() > maxNumStoredFilterTexts) + completionListDate.removeFirst(); + + configGroup.writeEntry(QLatin1String("PreviousSearches"), completionListDate); + config->sync(); + + /// add user-entered filter text to combobox's drop-down list + if (!comboBoxFilterText->contains(text)) + comboBoxFilterText->addItem(text); + } + + void storeComboBoxStatus() { + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(QLatin1String("CurrentCombination"), comboBoxCombination->currentIndex()); + configGroup.writeEntry(QLatin1String("CurrentField"), comboBoxField->currentIndex()); + config->sync(); + } + + void resetFilterUpdateTimer() { + filterUpdateTimer->stop(); + filterUpdateTimer->start(500); } }; @@ -94,7 +163,7 @@ d->comboBoxFilterText->setEditable(true); QFontMetrics metrics(d->comboBoxFilterText->font()); d->comboBoxFilterText->setMinimumWidth(metrics.width(QLatin1String("AIWaiw"))*7); - KLineEdit *lineEdit = dynamic_cast(d->comboBoxFilterText->lineEdit()); + KLineEdit *lineEdit = static_cast(d->comboBoxFilterText->lineEdit()); lineEdit->setClearButtonShown(true); d->comboBoxCombination = new KComboBox(false, this); @@ -109,15 +178,25 @@ d->comboBoxField->addItem(i18n("every field"), QVariant()); d->comboBoxField->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - BibTeXFields *bibTeXFiles = BibTeXFields::self(); - for (BibTeXFields::Iterator it = bibTeXFiles->begin(); it != bibTeXFiles->end(); ++it) - if ((*it).upperCamelCaseAlt.isEmpty()) - d->comboBoxField->addItem((*it).label, (*it).upperCamelCase); + foreach(const FieldDescription &fd, *BibTeXFields::self()) { + if (fd.upperCamelCaseAlt.isEmpty()) + d->comboBoxField->addItem(fd.label, fd.upperCamelCase); + } - connect(d->comboBoxFilterText->lineEdit(), SIGNAL(returnPressed()), this, SLOT(widgetsChanged())); + connect(d->comboBoxFilterText->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(lineeditTextChanged())); + connect(d->comboBoxFilterText->lineEdit(), SIGNAL(returnPressed()), this, SLOT(lineeditReturnPressed())); connect(lineEdit, SIGNAL(clearButtonClicked()), this, SLOT(clearFilter())); - connect(d->comboBoxCombination, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetsChanged())); - connect(d->comboBoxField, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetsChanged())); + connect(d->comboBoxCombination, SIGNAL(currentIndexChanged(int)), this, SLOT(comboboxStatusChanged())); + connect(d->comboBoxField, SIGNAL(currentIndexChanged(int)), this, SLOT(comboboxStatusChanged())); + + /// restore history on filter texts + /// see addCompletionString for more detailed explanation + KConfigGroup configGroup(d->config, QLatin1String("FilterBar")); + QStringList completionListDate = configGroup.readEntry(QLatin1String("PreviousSearches"), QStringList()); + for (QStringList::Iterator it = completionListDate.begin(); it != completionListDate.end(); ++it) + d->comboBoxFilterText->addItem((*it).mid(12)); + d->comboBoxCombination->setCurrentIndex(configGroup.readEntry("CurrentCombination", 0)); + d->comboBoxField->setCurrentIndex(configGroup.readEntry("CurrentField", 0)); } void FilterBar::clearFilter() @@ -137,7 +216,28 @@ return d->filter(); } -void FilterBar::widgetsChanged() +void FilterBar::lineeditTextChanged() +{ + d->resetFilterUpdateTimer(); +} + +void FilterBar::comboboxStatusChanged() +{ + d->filterUpdateTimer->stop(); + d->storeComboBoxStatus(); + emit filterChanged(d->filter()); +} + +void FilterBar::lineeditReturnPressed() +{ + d->filterUpdateTimer->stop(); + /// only store text in auto-completion if user pressed enter + d->addCompletionString(d->comboBoxFilterText->lineEdit()->text()); + emit filterChanged(d->filter()); +} + +void FilterBar::timerTriggered() { + /// timer used for a delayed filter update emit filterChanged(d->filter()); } diff -Nru kbibtex-0.3/src/gui/widgets/filterbar.h kbibtex-0.4/src/gui/widgets/filterbar.h --- kbibtex-0.3/src/gui/widgets/filterbar.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/widgets/filterbar.h 2011-11-20 20:36:54.000000000 +0000 @@ -49,7 +49,10 @@ FilterBarPrivate *d; private slots: - void widgetsChanged(); + void lineeditTextChanged(); + void comboboxStatusChanged(); + void lineeditReturnPressed(); + void timerTriggered(); }; #endif // KBIBTEX_GUI_FILTERBAR_H diff -Nru kbibtex-0.3/src/gui/widgets/menulineedit.cpp kbibtex-0.4/src/gui/widgets/menulineedit.cpp --- kbibtex-0.3/src/gui/widgets/menulineedit.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/widgets/menulineedit.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -80,9 +80,11 @@ appendWidget(m_singleLineEditText); hLayout->setStretchFactor(m_singleLineEditText, 100); m_singleLineEditText->setClearButtonShown(true); - connect(m_singleLineEditText, SIGNAL(textChanged(QString)), p, SIGNAL(textChanged(QString))); m_singleLineEditText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + m_singleLineEditText->setCompletionMode(KGlobalSettings::CompletionPopupAuto); + m_singleLineEditText->completionObject()->setIgnoreCase(true); p->setFocusProxy(m_singleLineEditText); + connect(m_singleLineEditText, SIGNAL(textChanged(QString)), p, SIGNAL(textChanged(QString))); } p->setFocusPolicy(Qt::StrongFocus); // FIXME improve focus handling @@ -117,7 +119,7 @@ m_singleLineEditText->setReadOnly(isReadOnly); else if (m_multiLineEditText == w) m_multiLineEditText->setReadOnly(isReadOnly); - else + else if (!w->property("isConst").isValid() && !w->property("isConst").toBool()) w->setEnabled(!isReadOnly); } @@ -136,6 +138,11 @@ d->setupUI(); } +MenuLineEdit::~MenuLineEdit() +{ + delete d; +} + void MenuLineEdit::setMenu(QMenu *menu) { d->m_pushButtonType->setMenu(menu); @@ -186,6 +193,14 @@ d->m_pushButtonType->setToolTip(text); } +void MenuLineEdit::setChildAcceptDrops(bool acceptDrops) +{ + if (d->m_singleLineEditText != NULL) + d->m_singleLineEditText->setAcceptDrops(acceptDrops); + if (d->m_multiLineEditText != NULL) + d->m_multiLineEditText->setAcceptDrops(acceptDrops); +} + void MenuLineEdit::prependWidget(QWidget *widget) { d->prependWidget(widget); @@ -210,6 +225,12 @@ return false; } +void MenuLineEdit::setCompletionItems(const QStringList &items) +{ + if (d->m_singleLineEditText != NULL) + d->m_singleLineEditText->completionObject()->setItems(items); +} + void MenuLineEdit::focusInEvent(QFocusEvent *) { if (d->m_singleLineEditText != NULL) diff -Nru kbibtex-0.3/src/gui/widgets/menulineedit.h kbibtex-0.4/src/gui/widgets/menulineedit.h --- kbibtex-0.3/src/gui/widgets/menulineedit.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/gui/widgets/menulineedit.h 2011-11-20 20:36:54.000000000 +0000 @@ -36,6 +36,7 @@ public: MenuLineEdit(bool isMultiLine, QWidget *parent); + ~MenuLineEdit(); void setMenu(QMenu *menu); virtual void setReadOnly(bool); @@ -44,12 +45,14 @@ void setIcon(const KIcon & icon); void setFont(const QFont & font); void setButtonToolTip(const QString &); + void setChildAcceptDrops(bool acceptDrops); void prependWidget(QWidget *widget); void appendWidget(QWidget *widget); void setInnerWidgetsTransparency(bool makeInnerWidgetsTransparent); bool isModified() const; + void setCompletionItems(const QStringList &items); protected: virtual void focusInEvent(QFocusEvent *event); diff -Nru kbibtex-0.3/src/gui/widgets/radiobuttontreeview.cpp kbibtex-0.4/src/gui/widgets/radiobuttontreeview.cpp --- kbibtex-0.3/src/gui/widgets/radiobuttontreeview.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/widgets/radiobuttontreeview.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,115 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include + +#include + +#include "radiobuttontreeview.h" + + +/** + * @author Thomas Fischer + */ +class RadioButtonItemDelegate : public QStyledItemDelegate +{ +public: + RadioButtonItemDelegate(QObject *p) + : QStyledItemDelegate(p) { + // nothing + } + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { + if (index.data(IsRadioRole).canConvert() && index.data(IsRadioRole).value()) { + /// determine size and spacing of radio buttons in current style + int radioButtonWidth = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &option); + int spacing = QApplication::style()->pixelMetric(QStyle::PM_RadioButtonLabelSpacing, &option); + + /// draw default appearance (text, highlighting) shifted to the left + QStyleOptionViewItem myOption = option; + int left = myOption.rect.left(); + myOption.rect.setLeft(left + spacing + radioButtonWidth); + QStyledItemDelegate::paint(painter, myOption, index); + + /// draw radio button in the open space + myOption.rect.setLeft(left); + myOption.rect.setWidth(radioButtonWidth); + if (index.data(RadioSelectedRole).canConvert()) { + /// change radio button's visual appearance if selected or not + bool radioButtonSelected = index.data(RadioSelectedRole).value(); + myOption.state |= radioButtonSelected ? QStyle::State_On : QStyle::State_Off; + } + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorRadioButton, &myOption, painter); + } else + QStyledItemDelegate::paint(painter, option, index); + } + + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const { + QSize s = QStyledItemDelegate::sizeHint(option, index); + if (index.data(IsRadioRole).value()) { + /// determine size of radio buttons in current style + int radioButtonHeight = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight, &option); + /// ensure that line is tall enough to draw radio button + s.setHeight(qMax(s.height(), radioButtonHeight)); + } + return s; + } +}; + + +RadioButtonTreeView::RadioButtonTreeView(QWidget *parent) + : QTreeView(parent) +{ + setItemDelegate(new RadioButtonItemDelegate(this)); +} + +void RadioButtonTreeView::mouseReleaseEvent(QMouseEvent *event) +{ + QModelIndex index = indexAt(event->pos()); + if (index.data(IsRadioRole).value()) { + /// clicking on an alternative's item in tree view should select alternative + switchRadioFlag(index); + event->accept(); + } else + QTreeView::mouseReleaseEvent(event); +} + +void RadioButtonTreeView::keyReleaseEvent(QKeyEvent *event) +{ + QModelIndex index = currentIndex(); + if (index.data(IsRadioRole).value() && event->key() == Qt::Key_Space) { + /// pressing space on an alternative's item in tree view should select alternative + switchRadioFlag(index); + event->accept(); + } else + QTreeView::keyReleaseEvent(event); +} + +void RadioButtonTreeView::switchRadioFlag(QModelIndex &index) +{ + const int maxRow = 1024; + const int col = index.column(); + for (int row = 0; row < maxRow; ++row) { + const QModelIndex &sib = index.sibling(row, col); + model()->setData(sib, QVariant::fromValue(sib == index), RadioSelectedRole); + } +} diff -Nru kbibtex-0.3/src/gui/widgets/radiobuttontreeview.h kbibtex-0.4/src/gui/widgets/radiobuttontreeview.h --- kbibtex-0.3/src/gui/widgets/radiobuttontreeview.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/gui/widgets/radiobuttontreeview.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,61 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_GUI_RADIOBUTTONTREEVIEW_H +#define KBIBTEX_GUI_RADIOBUTTONTREEVIEW_H + +#include +#include + +class QMouseEvent; +class QKeyEvent; + +static const int RadioSelectedRole = Qt::UserRole + 102; +static const int IsRadioRole = Qt::UserRole + 103; + +/** + * @author Thomas Fischer + * + * This class is a refinement of QTreeView, as it adds support + * for radio buttons for elements in the view. + * To use this view, set RadioButtonItemDelegate as the item delegate + * and use a model that respondes to the roles IsRadioRole and + * RadioSelectedRole. The role IsRadioRole returns a boolean value + * packed in a QVariant if a QModelIndex should have a radio button, + * RadioSelectedRole is boolean value as well, determining if a + * radio button is selected or not. + * This class will take care that if a QModelIndex receives a mouse + * click or a space key press, RadioSelectedRole will be set true for + * this QModelIndex and all sibling indices will be set to false. + */ +class RadioButtonTreeView : public QTreeView +{ +public: + RadioButtonTreeView(QWidget *parent); + +protected: + void mouseReleaseEvent(QMouseEvent *event); + void keyReleaseEvent(QKeyEvent *event); + +private: + void switchRadioFlag(QModelIndex &index); +}; + +#endif // KBIBTEX_GUI_RADIOBUTTONTREEVIEW_H diff -Nru kbibtex-0.3/src/kbibtexnamespace.h kbibtex-0.4/src/kbibtexnamespace.h --- kbibtex-0.3/src/kbibtexnamespace.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/kbibtexnamespace.h 2011-11-20 20:36:55.000000000 +0000 @@ -26,11 +26,11 @@ { enum Casing { - cLowerCase, - cInitialCapital, - cUpperCamelCase, - cLowerCamelCase, - cUpperCase + cLowerCase = 0, + cInitialCapital = 1, + cUpperCamelCase = 2, + cLowerCamelCase = 3, + cUpperCase = 4 }; enum FieldInputType { @@ -41,7 +41,9 @@ Month = 5, Color = 6, PersonList = 7, - UrlList = 8 + UrlList = 8, + KeywordList = 9, + CrossRef = 10 }; enum TypeFlag { @@ -69,4 +71,13 @@ } +/** + * Poor man's variant of a text-squeezing function. + * Effect is similar as observed in KSqueezedTextLabel: + * If the text is longer as n characters, the middle part + * will be cut away and replaced by "..." to get a + * string of max n characters. + */ +#define squeeze_text(text, n) ((text).length()<=(n)?(text):(text).left((n)/2-1)+QLatin1String("...")+(text).right((n)/2-2)) + #endif // KBIBTEX_NAMESPACE_H diff -Nru kbibtex-0.3/src/libkbibtexio/CMakeLists.txt kbibtex-0.4/src/libkbibtexio/CMakeLists.txt --- kbibtex-0.3/src/libkbibtexio/CMakeLists.txt 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/CMakeLists.txt 2011-11-20 20:36:55.000000000 +0000 @@ -47,6 +47,7 @@ kde4_add_library( kbibtexio SHARED ${kbibtexio_LIB_SRCS} ) +IF(WIN32) target_link_libraries( kbibtexio ${QT_QTCORE_LIBRARY} ${KDE4_KDECORE_LIBS} @@ -54,7 +55,17 @@ ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${POPPLER_QT4_LIBRARIES} + iconv ) +ELSE(WIN32) +target_link_libraries( kbibtexio + ${QT_QTCORE_LIBRARY} + ${KDE4_KDECORE_LIBS} + ${KDE4_KIO_LIBS} + ${LIBXML2_LIBRARIES} + ${LIBXSLT_LIBRARIES} + ${POPPLER_QT4_LIBRARIES} +) +ENDIF(WIN32) -install(TARGETS kbibtexio DESTINATION ${LIB_INSTALL_DIR}) - +install(TARGETS kbibtexio RUNTIME DESTINATION bin LIBRARY DESTINATION ${LIB_INSTALL_DIR}) diff -Nru kbibtex-0.3/src/libkbibtexio/config/bibtexentries.cpp kbibtex-0.4/src/libkbibtexio/config/bibtexentries.cpp --- kbibtex-0.3/src/libkbibtexio/config/bibtexentries.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/config/bibtexentries.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -18,67 +18,87 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include -#include #include #include -#include #include #include #include "bibtexentries.h" -static const int BibTeXEntriesMax = 256; +bool operator==(const EntryDescription &a, const EntryDescription &b) +{ + return a.upperCamelCase == b.upperCamelCase; +} +uint qHash(const EntryDescription &a) +{ + return qHash(a.upperCamelCase); +} + +static const int entryTypeMaxCount = 256; class BibTeXEntries::BibTeXEntriesPrivate { public: BibTeXEntries *p; - KConfig *systemDefaultsConfig; - KSharedConfigPtr userConfig; + KSharedConfigPtr config; static BibTeXEntries *singleton; BibTeXEntriesPrivate(BibTeXEntries *parent) - : p(parent) { - systemDefaultsConfig = new KConfig(KStandardDirs::locate("appdata", "entrytypes.rc"), KConfig::SimpleConfig); - userConfig = KSharedConfig::openConfig(KStandardDirs::locateLocal("appdata", "entrytypes.rc"), KConfig::SimpleConfig); - } - - ~BibTeXEntriesPrivate() { - delete systemDefaultsConfig; + : p(parent), config(KSharedConfig::openConfig("kbibtexrc")) { + // nothing } void load() { -// TODO p->clear(); - // TODO: Dummy implementation EntryDescription ed; - for (int col = 1; col < BibTeXEntriesMax; ++col) { + QString groupName = QLatin1String("EntryType"); + KConfigGroup configGroup(config, groupName); + int typeCount = qMin(configGroup.readEntry("count", 0), entryTypeMaxCount); + + for (int col = 1; col <= typeCount; ++col) { QString groupName = QString("EntryType%1").arg(col); - KConfigGroup usercg(userConfig, groupName); - KConfigGroup systemcg(systemDefaultsConfig, groupName); + KConfigGroup configGroup(config, groupName); - ed.upperCamelCase = systemcg.readEntry("UpperCamelCase", ""); - ed.upperCamelCase = usercg.readEntry("UpperCamelCase", ed.upperCamelCase); + ed.upperCamelCase = configGroup.readEntry("UpperCamelCase", ""); if (ed.upperCamelCase.isEmpty()) continue; - ed.upperCamelCaseAlt = systemcg.readEntry("UpperCamelCaseAlt", ""); - ed.upperCamelCaseAlt = usercg.readEntry("UpperCamelCaseAlt", ed.upperCamelCaseAlt); - ed.label = systemcg.readEntry("Label", ed.upperCamelCase);; - ed.label = usercg.readEntry("Label", ed.label);; + ed.upperCamelCaseAlt = configGroup.readEntry("UpperCamelCaseAlt", ""); + ed.label = configGroup.readEntry("Label", ed.upperCamelCase);; p->append(ed); } + + if (p->isEmpty()) kWarning() << "List of entry descriptions is empty"; } + + void save() { + int typeCount = 0; + foreach(EntryDescription ed, *p) { + ++typeCount; + QString groupName = QString("EntryType%1").arg(typeCount); + KConfigGroup configGroup(config, groupName); + + configGroup.writeEntry("UpperCamelCase", ed.upperCamelCase); + configGroup.writeEntry("UpperCamelCaseAlt", ed.upperCamelCaseAlt); + configGroup.writeEntry("Label", ed.label); + } + + QString groupName = QLatin1String("EntryType"); + KConfigGroup configGroup(config, groupName); + configGroup.writeEntry("count", typeCount); + + config->sync(); + } + }; BibTeXEntries *BibTeXEntries::BibTeXEntriesPrivate::singleton = NULL; BibTeXEntries::BibTeXEntries() - : d(new BibTeXEntriesPrivate(this)) + : QList(), d(new BibTeXEntriesPrivate(this)) { d->load(); } @@ -109,7 +129,7 @@ for (ConstIterator it = begin(); it != end(); ++it) { /// configuration file uses camel-case QString itName = (*it).upperCamelCase.toLower(); - if (itName == iName && (*it).upperCamelCase == QString::null) { + if (itName == iName && !(*it).upperCamelCase.isEmpty()) { iName = (*it).upperCamelCase; break; } @@ -123,7 +143,7 @@ for (ConstIterator it = begin(); it != end(); ++it) { /// configuration file uses camel-case QString itName = (*it).upperCamelCase.toLower(); - if (itName == iName && (*it).upperCamelCase == QString::null) { + if (itName == iName && !(*it).upperCamelCase.isEmpty()) { iName = (*it).upperCamelCase; break; } diff -Nru kbibtex-0.3/src/libkbibtexio/config/bibtexentries.h kbibtex-0.4/src/libkbibtexio/config/bibtexentries.h --- kbibtex-0.3/src/libkbibtexio/config/bibtexentries.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/config/bibtexentries.h 2011-11-20 20:36:55.000000000 +0000 @@ -31,6 +31,9 @@ QString label; } EntryDescription; +bool operator==(const EntryDescription &a, const EntryDescription &b); +uint qHash(const EntryDescription &a); + /** @author Thomas Fischer */ diff -Nru kbibtex-0.3/src/libkbibtexio/config/bibtexfields.cpp kbibtex-0.4/src/libkbibtexio/config/bibtexfields.cpp --- kbibtex-0.3/src/libkbibtexio/config/bibtexfields.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/config/bibtexfields.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -18,8 +18,6 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include -#include #include #include #include @@ -27,83 +25,125 @@ #include "bibtexfields.h" +bool operator==(const FieldDescription &a, const FieldDescription &b) +{ + return a.upperCamelCase == b.upperCamelCase; +} +uint qHash(const FieldDescription &a) +{ + return qHash(a.upperCamelCase); +} + static const int bibTeXFieldsMaxColumnCount = 256; +const FieldDescription FieldDescription::null; + class BibTeXFields::BibTeXFieldsPrivate { public: BibTeXFields *p; - KConfig *systemDefaultsConfig; - KSharedConfigPtr userConfig; + KSharedConfigPtr config; static BibTeXFields *singleton; BibTeXFieldsPrivate(BibTeXFields *parent) - : p(parent) { - systemDefaultsConfig = new KConfig(KStandardDirs::locate("appdata", "fieldtypes.rc"), KConfig::SimpleConfig); - userConfig = KSharedConfig::openConfig(KStandardDirs::locateLocal("appdata", "fieldtypes.rc"), KConfig::SimpleConfig); - } - - ~BibTeXFieldsPrivate() { - delete systemDefaultsConfig; + : p(parent), config(KSharedConfig::openConfig("kbibtexrc")) { + // nothing } void load() { - unsigned int sumWidth = 0; - FieldDescription fd; - p->clear(); - for (int col = 1; col < bibTeXFieldsMaxColumnCount; ++col) { + + QString groupName = QLatin1String("Column"); + KConfigGroup configGroup(config, groupName); + int columnCount = qMin(configGroup.readEntry("count", 0), bibTeXFieldsMaxColumnCount); + const QStringList defaultTreeViewNames = QStringList() << QLatin1String("SearchResults") << QLatin1String("Main") << QLatin1String("MergeWidget"); + QStringList treeViewNames = configGroup.readEntry("treeViewNames", defaultTreeViewNames); + + for (int col = 1; col <= columnCount; ++col) { + FieldDescription fd; + QString groupName = QString("Column%1").arg(col); - KConfigGroup usercg(userConfig, groupName); - KConfigGroup systemcg(systemDefaultsConfig, groupName); + KConfigGroup configGroup(config, groupName); - fd.upperCamelCase = systemcg.readEntry("UpperCamelCase", ""); - fd.upperCamelCase = usercg.readEntry("UpperCamelCase", fd.upperCamelCase); - if (fd.upperCamelCase.isEmpty()) continue; + fd.upperCamelCase = configGroup.readEntry("UpperCamelCase", ""); + if (fd.upperCamelCase.isEmpty()) + continue; - fd.upperCamelCaseAlt = systemcg.readEntry("UpperCamelCaseAlt", ""); - fd.upperCamelCaseAlt = usercg.readEntry("UpperCamelCaseAlt", fd.upperCamelCaseAlt); + fd.upperCamelCaseAlt = configGroup.readEntry("UpperCamelCaseAlt", ""); if (fd.upperCamelCaseAlt.isEmpty()) fd.upperCamelCaseAlt = QString::null; - fd.label = systemcg.readEntry("Label", fd.upperCamelCase); - fd.label = usercg.readEntry("Label", fd.label); + fd.label = configGroup.readEntry("Label", fd.upperCamelCase); - fd.defaultWidth = systemcg.readEntry("DefaultWidth", sumWidth / col); - fd.defaultWidth = usercg.readEntry("DefaultWidth", fd.defaultWidth); + fd.defaultWidth = configGroup.readEntry("DefaultWidth", 10); + bool defaultVisible = configGroup.readEntry("Visible", true); - fd.width = systemcg.readEntry("Width", fd.defaultWidth); - fd.width = usercg.readEntry("Width", fd.width); - sumWidth += fd.width; - - fd.visible = systemcg.readEntry("Visible", true); - fd.visible = usercg.readEntry("Visible", fd.visible); + foreach(const QString &treeViewName, treeViewNames) { + fd.width.insert(treeViewName, configGroup.readEntry("Width_" + treeViewName, fd.defaultWidth)); + fd.visible.insert(treeViewName, configGroup.readEntry("Visible_" + treeViewName, defaultVisible)); + } - QString typeFlags = systemcg.readEntry("TypeFlags", "Source"); - typeFlags = usercg.readEntry("TypeFlags", typeFlags); + QString typeFlags = configGroup.readEntry("TypeFlags", "Source"); fd.typeFlags = typeFlagsFromString(typeFlags); QString preferredTypeFlag = typeFlags.split(';').first(); fd.preferredTypeFlag = typeFlagFromString(preferredTypeFlag); p->append(fd); } + + if (p->isEmpty()) kWarning() << "List of field descriptions is empty after load()"; + } + + void save() { + if (p->isEmpty()) kWarning() << "List of field descriptions is empty before save()"; + + QStringList treeViewNames; + int columnCount = 0; + foreach(const FieldDescription &fd, *p) { + ++columnCount; + QString groupName = QString("Column%1").arg(columnCount); + KConfigGroup configGroup(config, groupName); + + foreach(const QString &treeViewName, fd.width.keys()) { + configGroup.writeEntry("Width_" + treeViewName, fd.width[treeViewName]); + configGroup.writeEntry("Visible_" + treeViewName, fd.visible[treeViewName]); + } + QString typeFlagsString = fd.typeFlags == fd.preferredTypeFlag ? "" : ";" + typeFlagsToString(fd.typeFlags); + typeFlagsString.prepend(typeFlagToString(fd.preferredTypeFlag)); + configGroup.writeEntry("TypeFlags", typeFlagsString); + + if (treeViewNames.isEmpty()) + treeViewNames.append(fd.width.keys()); + } + + QString groupName = QLatin1String("Column"); + KConfigGroup configGroup(config, groupName); + configGroup.writeEntry("count", columnCount); + configGroup.writeEntry("treeViewNames", treeViewNames); + + config->sync(); + } + + void resetToDefaults(const QString &treeViewName) { + for (int col = 1; col < bibTeXFieldsMaxColumnCount; ++col) { + QString groupName = QString("Column%1").arg(col); + KConfigGroup configGroup(config, groupName); + configGroup.deleteEntry("Width_" + treeViewName); + configGroup.deleteEntry("Visible_" + treeViewName); + } + load(); } }; BibTeXFields *BibTeXFields::BibTeXFieldsPrivate::singleton = NULL; BibTeXFields::BibTeXFields() - : d(new BibTeXFieldsPrivate(this)) + : QList(), d(new BibTeXFieldsPrivate(this)) { d->load(); } -BibTeXFields::~BibTeXFields() -{ - delete d; -} - BibTeXFields* BibTeXFields::self() { if (BibTeXFieldsPrivate::singleton == NULL) @@ -113,31 +153,12 @@ void BibTeXFields::save() { - return; // FIXME avoid saving config for now ... - int col = 1; - for (Iterator it = begin(); it != end(); ++it, ++col) { - QString groupName = QString("Column%1").arg(col); - KConfigGroup usercg(d->userConfig, groupName); - FieldDescription &fd = *it; - usercg.writeEntry("Width", fd.width); - usercg.writeEntry("Visible", fd.visible); - QString typeFlagsString = fd.typeFlags == fd.preferredTypeFlag ? "" : ";" + typeFlagsToString(fd.typeFlags); - typeFlagsString.prepend(typeFlagToString(fd.preferredTypeFlag)); - usercg.writeEntry("TypeFlags", typeFlagsString); - } - - d->userConfig->sync(); -} - -void BibTeXFields::resetToDefaults() -{ - for (int col = 1; col < bibTeXFieldsMaxColumnCount; ++col) { - QString groupName = QString("Column%1").arg(col); - KConfigGroup usercg(d->userConfig, groupName); - usercg.deleteGroup(); - } + d->save(); +} - d->load(); +void BibTeXFields::resetToDefaults(const QString &treeViewName) +{ + d->resetToDefaults(treeViewName); } QString BibTeXFields::format(const QString& name, KBibTeX::Casing casing) const @@ -151,7 +172,7 @@ iName[0] = iName[0].toUpper(); return iName; case KBibTeX::cLowerCamelCase: { - for (ConstIterator it = begin(); it != end(); ++it) { + for (ConstIterator it = constBegin(); it != constEnd(); ++it) { /// configuration file uses camel-case QString itName = (*it).upperCamelCase.toLower(); if (itName == iName && (*it).upperCamelCaseAlt == QString::null) { @@ -165,7 +186,7 @@ return iName; } case KBibTeX::cUpperCamelCase: { - for (ConstIterator it = begin(); it != end(); ++it) { + for (ConstIterator it = constBegin(); it != constEnd(); ++it) { /// configuration file uses camel-case QString itName = (*it).upperCamelCase.toLower(); if (itName == iName && (*it).upperCamelCaseAlt == QString::null) { @@ -182,16 +203,14 @@ return name; } -const FieldDescription* BibTeXFields::find(const QString &name) const +const FieldDescription& BibTeXFields::find(const QString &name) const { - const FieldDescription* result = NULL; - const QString iName = name.toLower(); for (ConstIterator it = constBegin(); it != constEnd(); ++it) { - if ((*it).upperCamelCase.toLower() == iName && (result == NULL || (*it).upperCamelCaseAlt == QString::null)) - result = &(*it); + if ((*it).upperCamelCase.toLower() == iName && (*it).upperCamelCaseAlt.isEmpty()) + return *it; } - return result; + return FieldDescription::null; } KBibTeX::TypeFlag BibTeXFields::typeFlagFromString(const QString &typeFlagString) diff -Nru kbibtex-0.3/src/libkbibtexio/config/bibtexfields.h kbibtex-0.4/src/libkbibtexio/config/bibtexfields.h --- kbibtex-0.3/src/libkbibtexio/config/bibtexfields.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/config/bibtexfields.h 2011-11-20 20:36:55.000000000 +0000 @@ -28,16 +28,34 @@ #include -typedef struct { +struct FieldDescription { QString upperCamelCase; QString upperCamelCaseAlt; QString label; KBibTeX::TypeFlags typeFlags; KBibTeX::TypeFlag preferredTypeFlag; - int width; + QMap width; int defaultWidth; - bool visible; -} FieldDescription; + QMap visible; + + FieldDescription() + : upperCamelCase(QString::null), upperCamelCaseAlt(QString::null), label(QString::null), defaultWidth(0) { /* nothing */ } + + FieldDescription(const FieldDescription &other) + : upperCamelCase(other.upperCamelCase), upperCamelCaseAlt(other.upperCamelCaseAlt), label(other.label), typeFlags(other.typeFlags), preferredTypeFlag(other.preferredTypeFlag), defaultWidth(other.defaultWidth) { + foreach(const QString &key, other.width.keys()) width.insert(key, other.width[key]); + foreach(const QString &key, other.visible.keys()) visible.insert(key, other.visible[key]); + } + + bool isNull() const { + return upperCamelCase.isNull() && label.isNull(); + } + + static const FieldDescription null; +}; + +bool operator==(const FieldDescription &a, const FieldDescription &b); +uint qHash(const FieldDescription &a); /** @author Thomas Fischer @@ -45,11 +63,9 @@ class KBIBTEXIO_EXPORT BibTeXFields : public QList { public: - virtual ~BibTeXFields(); - static BibTeXFields *self(); void save(); - void resetToDefaults(); + void resetToDefaults(const QString &treeViewName); /** * Change the casing of a given field name to one of the predefine formats. @@ -61,7 +77,7 @@ static QString typeFlagToString(KBibTeX::TypeFlag typeFlag); static QString typeFlagsToString(KBibTeX::TypeFlags typeFlags); - const FieldDescription* find(const QString &name) const; + const FieldDescription& find(const QString &name) const; protected: BibTeXFields(); diff -Nru kbibtex-0.3/src/libkbibtexio/encoderlatex.cpp kbibtex-0.4/src/libkbibtexio/encoderlatex.cpp --- kbibtex-0.3/src/libkbibtexio/encoderlatex.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/encoderlatex.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -498,6 +498,7 @@ ++it; (*cur).append('$').append(*it); it = intermediate.erase(it); + --it; } else ++it; } @@ -592,6 +593,7 @@ ++it; (*cur).append('$').append(*it); it = intermediate.erase(it); + --it; } else ++it; } @@ -668,6 +670,23 @@ return text; } +QString EncoderLaTeX::convertToPlainAscii(const QString &text) const +{ + QString internalText = text; + + for (int i = 0; i < modcharmappingdatalatexcount; ++i) { + QChar c = QChar(modcharmappingdatalatex[i].unicode); + if (internalText.indexOf(c) >= 0) + internalText = internalText.replace(c, QString(modcharmappingdatalatex[i].letter)); + } + for (int i = 0; i < commandmappingdatalatexcount; ++i) { + QChar c = QChar(commandmappingdatalatex[i].unicode); + if (internalText.indexOf(c) >= 0) + internalText = internalText.replace(c, QString(commandmappingdatalatex[i].letters)); + } + + return internalText; +} EncoderLaTeX* EncoderLaTeX::currentEncoderLaTeX() { @@ -684,19 +703,3 @@ encoderLaTeX = NULL; } } - -QString& EncoderLaTeX::convertToPlainAscii(QString &text) -{ - for (int i = 0; i < modcharmappingdatalatexcount; ++i) { - QChar c = QChar(modcharmappingdatalatex[i].unicode); - if (text.indexOf(c) >= 0) - text = text.replace(c, QString(modcharmappingdatalatex[i].letter)); - } - for (int i = 0; i < commandmappingdatalatexcount; ++i) { - QChar c = QChar(commandmappingdatalatex[i].unicode); - if (text.indexOf(c) >= 0) - text = text.replace(c, QString(commandmappingdatalatex[i].letters)); - } - - return text; -} diff -Nru kbibtex-0.3/src/libkbibtexio/encoderlatex.h kbibtex-0.4/src/libkbibtexio/encoderlatex.h --- kbibtex-0.3/src/libkbibtexio/encoderlatex.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/encoderlatex.h 2011-11-20 20:36:55.000000000 +0000 @@ -40,7 +40,7 @@ QString encode(const QString &text); QString encode(const QString &text, const QChar &replace); QString& decomposedUTF8toLaTeX(QString &text); - QString& convertToPlainAscii(QString &text); + QString convertToPlainAscii(const QString &text) const; static EncoderLaTeX *currentEncoderLaTeX(); static void deleteCurrentEncoderLaTeX(); diff -Nru kbibtex-0.3/src/libkbibtexio/encoderxml.cpp kbibtex-0.4/src/libkbibtexio/encoderxml.cpp --- kbibtex-0.3/src/libkbibtexio/encoderxml.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/encoderxml.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -17,6 +17,8 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ + +#include #include #include @@ -37,6 +39,8 @@ }; static const int charmappingdataxmlcount = sizeof(charmappingdataxml) / sizeof(charmappingdataxml[ 0 ]) ; +static const QStringList backslashSymbols = QStringList() << QLatin1String("\\&") << QLatin1String("\\%") << QLatin1String("\\_"); + /** * Private class to store internal variables that should not be visible * in the interface as defined in the header file. @@ -47,7 +51,7 @@ struct CharMappingItem { QRegExp regExp; QChar unicode; - QString latex; + QString xml; }; QList charMapping; @@ -57,7 +61,7 @@ CharMappingItem charMappingItem; charMappingItem.regExp = QRegExp(charmappingdataxml[ i ].regexp); charMappingItem.unicode = QChar(charmappingdataxml[ i ].unicode); - charMappingItem.latex = QString(charmappingdataxml[ i ].latex); + charMappingItem.xml = QString(charmappingdataxml[ i ].latex); charMapping.append(charMappingItem); } } @@ -83,13 +87,13 @@ result.replace((*it).regExp, (*it).unicode); /** - * Find and replace all characters written as hexadecimal number - */ + * Find and replace all characters written as hexadecimal number + */ int p = -1; while ((p = result.indexOf("&#x", p + 1)) >= 0) { int p2 = result.indexOf(";", p + 1); if (p2 < 0) break; - bool ok = FALSE; + bool ok = false; int hex = result.mid(p + 3, p2 - p - 3).toInt(&ok, 16); if (ok && hex > 0) result.replace(result.mid(p, p2 - p + 1), QChar(hex)); @@ -102,12 +106,24 @@ while ((p = result.indexOf("&#", p + 1)) >= 0) { int p2 = result.indexOf(";", p + 1); if (p2 < 0) break; - bool ok = FALSE; + bool ok = false; int dec = result.mid(p + 2, p2 - p - 2).toInt(&ok, 10); if (ok && dec > 0) result.replace(result.mid(p, p2 - p + 1), QChar(dec)); } + /// Replace special symbols with backslash-encoded variant (& --> \&) + foreach(const QString &backslashSymbol, backslashSymbols) { + int p = -1; + while ((p = result.indexOf(backslashSymbol[1], p + 1)) >= 0) { + if (p == 0 || result[p-1] != QChar('\\')) { + /// replace only symbols which have no backslash on their right + result = result.left(p) + QChar('\\') + result.mid(p); + ++p; + } + } + } + return result; } @@ -116,7 +132,12 @@ QString result = text; for (QList::ConstIterator it = d->charMapping.begin(); it != d->charMapping.end(); ++it) - result.replace((*it).unicode, (*it).latex); + result.replace((*it).unicode, (*it).xml); + + /// Replace backlash-encoded symbols with plain text (\& --> &) + foreach(const QString &backslashSymbol, backslashSymbols) { + result.replace(backslashSymbol, backslashSymbol[1]); + } return result; } diff -Nru kbibtex-0.3/src/libkbibtexio/entry.cpp kbibtex-0.4/src/libkbibtexio/entry.cpp --- kbibtex-0.3/src/libkbibtexio/entry.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/entry.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -51,6 +51,7 @@ const QLatin1String Entry::ftSeries = QLatin1String("series"); const QLatin1String Entry::ftTitle = QLatin1String("title"); const QLatin1String Entry::ftUrl = QLatin1String("url"); +const QLatin1String Entry::ftUrlDate = QLatin1String("urldate"); const QLatin1String Entry::ftVolume = QLatin1String("volume"); const QLatin1String Entry::ftYear = QLatin1String("year"); @@ -89,7 +90,7 @@ Entry::~Entry() { - // nothing + clear(); } Entry& Entry::operator= (const Entry & other) @@ -180,7 +181,7 @@ Entry *result = new Entry(original); QString crossRef = PlainTextValue::text(original.value(QLatin1String("crossref")), bibTeXfile); - const Entry *crossRefEntry = dynamic_cast((bibTeXfile != NULL) ? bibTeXfile->containsKey(crossRef) : NULL); + const Entry *crossRefEntry = dynamic_cast((bibTeXfile != NULL) ? bibTeXfile->containsKey(crossRef, File::etEntry) : NULL); if (crossRefEntry != NULL) { /// copy all fields from crossref'ed entry to new entry which do not (yet) exist in the new entry for (Entry::ConstIterator it = crossRefEntry->constBegin(); it != crossRefEntry->constEnd(); ++it) diff -Nru kbibtex-0.3/src/libkbibtexio/entry.h kbibtex-0.4/src/libkbibtexio/entry.h --- kbibtex-0.3/src/libkbibtexio/entry.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/entry.h 2011-11-20 20:36:55.000000000 +0000 @@ -90,6 +90,8 @@ static const QLatin1String ftTitle; /** Representation of the BibTeX field key "url" */ static const QLatin1String ftUrl; + /** Representation of the BibLaTeX field key "urldate" */ + static const QLatin1String ftUrlDate; /** Representation of the BibTeX field key "volume" */ static const QLatin1String ftVolume; /** Representation of the BibTeX field key "year" */ diff -Nru kbibtex-0.3/src/libkbibtexio/file.cpp kbibtex-0.4/src/libkbibtexio/file.cpp --- kbibtex-0.3/src/libkbibtexio/file.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/file.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -34,6 +34,11 @@ const QString File::Url = QLatin1String("Url"); const QString File::Encoding = QLatin1String("Encoding"); +const QString File::StringDelimiter = QLatin1String("StringDelimiter"); +const QString File::QuoteComment = QLatin1String("QuoteComment"); +const QString File::KeywordCasing = QLatin1String("KeywordCasing"); +const QString File::ProtectCasing = QLatin1String("ProtectCasing"); +const QString File::NameFormatting = QLatin1String("NameFormatting"); class File::FilePrivate { @@ -44,7 +49,7 @@ QMap properties; FilePrivate(File *parent) - : p(parent) { + : p(parent) { // TODO } }; @@ -55,20 +60,32 @@ // nothing } -File::~File() +File::File(const File &other) + : QList(other), d(new FilePrivate(this)) { // nothing } -const Element *File::containsKey(const QString &key) const +File::~File() +{ + // FIXME: at some point elements have to be deleted + // maybe use QSharedPointer? + //while (!isEmpty()) { + // Element *e = first(); + // removeFirst(); + // delete e; + //} +} + +const Element *File::containsKey(const QString &key, ElementTypes elementTypes) const { for (ConstIterator it = begin(); it != end(); ++it) { - const Entry* entry = dynamic_cast(*it); + const Entry* entry = elementTypes.testFlag(etEntry) ? dynamic_cast(*it) : NULL; if (entry != NULL) { if (entry->id() == key) return entry; } else { - const Macro* macro = dynamic_cast(*it); + const Macro* macro = elementTypes.testFlag(etMacro) ? dynamic_cast(*it) : NULL; if (macro != NULL) { if (macro->key() == key) return macro; @@ -79,16 +96,16 @@ return NULL; } -QStringList File::allKeys() const +QStringList File::allKeys(ElementTypes elementTypes) const { QStringList result; - for (ConstIterator it = begin(); it != end(); ++it) { - const Entry* entry = dynamic_cast(*it); + foreach(const Element *element, *this) { + const Entry* entry = elementTypes.testFlag(etEntry) ? dynamic_cast(element) : NULL; if (entry != NULL) result.append(entry->id()); else { - const Macro* macro = dynamic_cast(*it); + const Macro* macro = elementTypes.testFlag(etMacro) ? dynamic_cast(element) : NULL; if (macro != NULL) result.append(macro->key()); } @@ -97,6 +114,31 @@ return result; } +QSet File::uniqueEntryValuesSet(const QString &fieldName) const +{ + QSet valueSet; + const QString lcFieldName = fieldName.toLower(); + + foreach(const Element *element, *this) { + const Entry* entry = dynamic_cast(element); + if (entry != NULL) + for (Entry::ConstIterator it = entry->constBegin(); it != entry->constEnd(); ++it) + if (it.key().toLower() == lcFieldName) + foreach(const ValueItem *valueItem, it.value()) + valueSet.insert(PlainTextValue::text(*valueItem, this)); + } + + return valueSet; +} + +QStringList File::uniqueEntryValuesList(const QString &fieldName) const +{ + QSet valueSet = uniqueEntryValuesSet(fieldName); + QStringList list = valueSet.toList(); + list.sort(); + return list; +} + void File::setProperty(const QString &key, const QVariant &value) { d->properties.insert(key, value); diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterbibtex2html.cpp kbibtex-0.4/src/libkbibtexio/fileexporterbibtex2html.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporterbibtex2html.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterbibtex2html.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -59,7 +59,7 @@ args << "-debug"; /// verbose mode (to find incorrect BibTeX entries) args << bibTeXFilename; - bool result = p->runProcess("bibtex2html", args, errorLog) && p->writeFileToIODevice(outputFilename, iodevice); + bool result = p->runProcess("bibtex2html", args, errorLog) && p->writeFileToIODevice(outputFilename, iodevice, errorLog); return result; } @@ -107,7 +107,8 @@ QFile output(d->bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter * bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX * bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("utf-8")); result = bibtexExporter->save(&output, bibtexfile, errorLog); output.close(); delete bibtexExporter; @@ -125,7 +126,8 @@ QFile output(d->bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter * bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX * bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("utf-8")); result = bibtexExporter->save(&output, element, errorLog); output.close(); delete bibtexExporter; diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterbibtex.cpp kbibtex-0.4/src/libkbibtexio/fileexporterbibtex.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporterbibtex.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterbibtex.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -17,6 +17,7 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ + #include #include @@ -48,45 +49,18 @@ #define encodercheck(encoder, text) ((encoder)?(encoder)->encode((text)):(text)) -class ExportWidget : public QWidget -{ -public: - KComboBox *listOfEncodings; - static const QString defaultEncoding; +const QString FileExporterBibTeX::keyEncoding = QLatin1String("encoding"); +const QString FileExporterBibTeX::defaultEncoding = QLatin1String("LaTeX"); +const QString FileExporterBibTeX::keyStringDelimiter = QLatin1String("stringDelimiter"); +const QString FileExporterBibTeX::defaultStringDelimiter = QLatin1String("\"\""); +const QString FileExporterBibTeX::keyQuoteComment = QLatin1String("quoteComment"); +const FileExporterBibTeX::QuoteComment FileExporterBibTeX::defaultQuoteComment = FileExporterBibTeX::qcNone; +const QString FileExporterBibTeX::keyKeywordCasing = QLatin1String("keywordCasing"); +const KBibTeX::Casing FileExporterBibTeX::defaultKeywordCasing = KBibTeX::cLowerCase; +const QString FileExporterBibTeX::keyProtectCasing = QLatin1String("protectCasing"); +const bool FileExporterBibTeX::defaultProtectCasing = true; - ExportWidget(QWidget *parent) - : QWidget(parent) { - QFormLayout *layout = new QFormLayout(this); - setLayout(layout); - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - listOfEncodings = new KComboBox(true, this); - layout->addRow(i18n("Encoding:"), listOfEncodings); - - listOfEncodings->addItem(QLatin1String("LaTeX")); - listOfEncodings->insertSeparator(1); - listOfEncodings->addItems(IConvLaTeX::encodings()); - setProposedEncoding(defaultEncoding); - } - - void setProposedEncoding(const QString &proposedEncoding) { - QAbstractItemModel *model = listOfEncodings->model(); - int row = 0; - QModelIndex index; - const QString lowerPE = proposedEncoding.toLower(); - while ((index = model->index(row, 0, QModelIndex())) != QModelIndex()) { - QString line = model->data(index).toString(); - if (line.toLower() == lowerPE) { - listOfEncodings->lineEdit()->setText(line); - listOfEncodings->setCurrentIndex(row); - break; - } - ++row; - } - } -}; - -const QString ExportWidget::defaultEncoding = QLatin1String("LaTeX"); +FileExporterBibTeX *FileExporterBibTeX::staticFileExporterBibTeX = NULL; class FileExporterBibTeX::FileExporterBibTeXPrivate { @@ -98,24 +72,47 @@ QChar stringCloseDelimiter; KBibTeX::Casing keywordCasing; QuoteComment quoteComment; - QString encoding; + QString encoding, forcedEncoding; bool protectCasing; + QString personNameFormatting; bool cancelFlag; IConvLaTeX *iconvLaTeX; + KSharedConfigPtr config; + const QString configGroupName, configGroupNameGeneral; FileExporterBibTeXPrivate(FileExporterBibTeX *parent) - : p(parent), stringOpenDelimiter(QChar('"')), stringCloseDelimiter(QChar('"')), keywordCasing(KBibTeX::cLowerCase), quoteComment(qcNone), encoding(QLatin1String("latex")), protectCasing(false), cancelFlag(false) { - iconvLaTeX = new IConvLaTeX(encoding == QLatin1String("latex") ? QLatin1String("us-ascii") : encoding); + : p(parent), cancelFlag(false), iconvLaTeX(NULL), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupName("FileExporterBibTeX"), configGroupNameGeneral("General") { + forcedEncoding = QString::null; + loadState(); } ~FileExporterBibTeXPrivate() { delete iconvLaTeX; } + void loadState() { + KConfigGroup configGroup(config, configGroupName); + encoding = configGroup.readEntry(p->keyEncoding, p->defaultEncoding); + QString stringDelimiter = configGroup.readEntry(p->keyStringDelimiter, p->defaultStringDelimiter); + stringOpenDelimiter = stringDelimiter[0]; + stringCloseDelimiter = stringDelimiter[1]; + keywordCasing = (KBibTeX::Casing)configGroup.readEntry(p->keyKeywordCasing, (int)p->defaultKeywordCasing); + quoteComment = (QuoteComment)configGroup.readEntry(p->keyQuoteComment, (int)p->defaultQuoteComment); + protectCasing = configGroup.readEntry(p->keyProtectCasing, p->defaultProtectCasing); + personNameFormatting = configGroup.readEntry(Person::keyPersonNameFormatting, ""); + + if (personNameFormatting.isEmpty()) { + /// no person name formatting is specified for BibTeX, fall back to general setting + KConfigGroup configGroupGeneral(config, configGroupNameGeneral); + personNameFormatting = configGroupGeneral.readEntry(Person::keyPersonNameFormatting, Person::defaultPersonNameFormatting); + } + } + bool writeEntry(QIODevice* iodevice, const Entry& entry) { BibTeXEntries *be = BibTeXEntries::self(); BibTeXFields *bf = BibTeXFields::self(); + /// write start of a entry (entry type and id) in plain ASCII iodevice->putChar('@'); iodevice->write(be->format(entry.type(), keywordCasing).toAscii().data()); iodevice->putChar('{'); @@ -124,9 +121,16 @@ for (Entry::ConstIterator it = entry.begin(); it != entry.end(); ++it) { const QString key = it.key(); Value value = it.value(); - QString text = valueToBibTeX(value, key, leUTF8); - if (text.isEmpty()) kWarning() << "Value for field " << key << " is empty" << endl; - if (protectCasing && dynamic_cast(value.first()) != NULL && (key == Entry::ftTitle || key == Entry::ftBookTitle || key == Entry::ftSeries)) + if (value.isEmpty()) continue; ///< ignore empty key-value pairs + + QString text = p->internalValueToBibTeX(value, key, leUTF8); + if (text.isEmpty()) { + /// ignore empty key-value pairs + kWarning() << "Value for field " << key << " is empty" << endl; + continue; + } + + if (protectCasing && typeid(*value.first()) == typeid(PlainText) && (key == Entry::ftTitle || key == Entry::ftBookTitle || key == Entry::ftSeries)) addProtectiveCasing(text); iodevice->putChar(','); @@ -149,7 +153,7 @@ bool writeMacro(QIODevice* iodevice, const Macro& macro) { BibTeXEntries *be = BibTeXEntries::self(); - QString text = valueToBibTeX(macro.value(), QString::null, leUTF8); + QString text = p->internalValueToBibTeX(macro.value(), QString::null, leUTF8); if (protectCasing) addProtectiveCasing(text); @@ -205,7 +209,7 @@ iodevice->putChar('@'); iodevice->write(be->format(QLatin1String("Preamble"), keywordCasing).toAscii().data()); iodevice->putChar('{'); - iodevice->write(iconvLaTeX->encode(valueToBibTeX(preamble.value(), QString::null, leUTF8))); + iodevice->write(iconvLaTeX->encode(p->internalValueToBibTeX(preamble.value(), QString::null, leUTF8))); iodevice->putChar('}'); iodevice->putChar('\n'); iodevice->putChar('\n'); @@ -233,8 +237,42 @@ if (addBrackets) text.insert(1, '{').insert(text.length(), '}'); } + + void applyEncoding(QString& encoding) { + encoding = encoding.isEmpty() ? QLatin1String("latex") : encoding.toLower(); + delete iconvLaTeX; + iconvLaTeX = new IConvLaTeX(encoding == QLatin1String("latex") ? QLatin1String("us-ascii") : encoding); + } + + bool requiresPersonQuoting(const QString &text, bool isLastName) { + if (isLastName && !text.contains(" ")) + /** Last name contains NO spaces, no quoting necessary */ + return false; + else if (!isLastName && !text.contains(" and ")) + /** First name contains no " and " no quoting necessary */ + return false; + else if (isLastName && !text.isEmpty() && text[0].isLower()) + /** Last name starts with lower-case character (von, van, de, ...) */ + // FIXME does not work yet + return false; + else if (text[0] != '{' || text[text.length() - 1] != '}') + /** as either last name contains spaces or first name contains " and " and there is no protective quoting yet, there must be a protective quoting added */ + return true; + + int bracketCounter = 0; + for (int i = text.length() - 1; i >= 0; --i) { + if (text[i] == '{') + ++bracketCounter; + else if (text[i] == '}') + --bracketCounter; + if (bracketCounter == 0 && i > 0) + return true; + } + return false; + } }; + FileExporterBibTeX::FileExporterBibTeX() : FileExporter(), d(new FileExporterBibTeXPrivate(this)) { @@ -243,43 +281,12 @@ FileExporterBibTeX::~FileExporterBibTeX() { -// nothing + delete d; } -void FileExporterBibTeX::setEncoding(const QString& encoding) +void FileExporterBibTeX::setEncoding(const QString &encoding) { - QString normalizedEncoding = encoding.isNull() ? QLatin1String("latex") : encoding.toLower(); - if (normalizedEncoding != d->encoding) { - d->encoding = normalizedEncoding; - delete d->iconvLaTeX; - d->iconvLaTeX = new IConvLaTeX(d->encoding == QLatin1String("latex") ? QLatin1String("us-ascii") : encoding); - } -} - -QString FileExporterBibTeX::encoding() const -{ - return d->encoding; -} - -void FileExporterBibTeX::setStringDelimiters(const QChar& stringOpenDelimiter, const QChar& stringCloseDelimiter) -{ - d->stringOpenDelimiter = stringOpenDelimiter; - d->stringCloseDelimiter = stringCloseDelimiter; -} - -void FileExporterBibTeX::setKeywordCasing(KBibTeX::Casing keywordCasing) -{ - d->keywordCasing = keywordCasing; -} - -void FileExporterBibTeX::setQuoteComment(QuoteComment quoteComment) -{ - d->quoteComment = quoteComment; -} - -void FileExporterBibTeX::setProtectCasing(bool protectCasing) -{ - d->protectCasing = protectCasing; + d->forcedEncoding = encoding; } bool FileExporterBibTeX::save(QIODevice* iodevice, const File* bibtexfile, QStringList * /*errorLog*/) @@ -314,7 +321,7 @@ Comment *comment = dynamic_cast(*it); QString commentText = QString::null; /** check if this file requests a special encoding */ - if (comment == NULL || !comment->text().startsWith("x-kbibtex-encoding=")) + if (comment == NULL || !comment->text().startsWith("x-kbibtex-")) remainingList.append(*it); } } @@ -324,15 +331,33 @@ int totalElements = (int) bibtexfile->count(); int currentPos = 0; - QString effectiveEncoding = ExportWidget::defaultEncoding; - if (!d->encoding.isEmpty()) - effectiveEncoding = d->encoding; + loadState(); if (bibtexfile->hasProperty(File::Encoding)) - effectiveEncoding = bibtexfile->property(File::Encoding).toString(); - setEncoding(effectiveEncoding); - - if (effectiveEncoding != QLatin1String("latex")) - parameterCommentsList << new Comment("x-kbibtex-encoding=" + effectiveEncoding, true); + d->encoding = bibtexfile->property(File::Encoding).toString(); + if (!d->forcedEncoding.isEmpty()) + d->encoding = d->forcedEncoding; + d->applyEncoding(d->encoding); + if (bibtexfile->hasProperty(File::StringDelimiter)) { + QString stringDelimiter = bibtexfile->property(File::StringDelimiter).toString(); + d->stringOpenDelimiter = stringDelimiter[0]; + d->stringCloseDelimiter = stringDelimiter[1]; + } + if (bibtexfile->hasProperty(File::QuoteComment)) + d->quoteComment = (QuoteComment)bibtexfile->property(File::QuoteComment).toInt(); + if (bibtexfile->hasProperty(File::KeywordCasing)) + d->keywordCasing = (KBibTeX::Casing)bibtexfile->property(File::KeywordCasing).toInt(); + if (bibtexfile->hasProperty(File::ProtectCasing)) + d->protectCasing = bibtexfile->property(File::ProtectCasing).toBool(); + if (bibtexfile->hasProperty(File::NameFormatting)) { + /// if the user set "use global default", this property is an empty string + /// in this case, keep default value + const QString buffer = bibtexfile->property(File::NameFormatting).toString(); + d->personNameFormatting = buffer.isEmpty() ? d->personNameFormatting : buffer; + } + + if (d->encoding != QLatin1String("latex")) + parameterCommentsList << new Comment("x-kbibtex-encoding=" + d->encoding, true); + parameterCommentsList << new Comment("x-kbibtex-personnameformatting=" + d->personNameFormatting, true); /** before anything else, write parameter comments */ for (QList::ConstIterator it = parameterCommentsList.begin(); it != parameterCommentsList.end() && result && !d->cancelFlag; it++) { @@ -377,6 +402,11 @@ { bool result = false; + loadState(); + if (!d->forcedEncoding.isEmpty()) + d->encoding = d->forcedEncoding; + d->applyEncoding(d->encoding); + const Entry *entry = dynamic_cast(element); if (entry != NULL) result |= d->writeEntry(iodevice, *entry); @@ -404,31 +434,16 @@ d->cancelFlag = true; } -void FileExporterBibTeX::showExportDialog(QWidget *parent, File *bibtexfile) const +QString FileExporterBibTeX::valueToBibTeX(const Value& value, const QString& key, UseLaTeXEncoding useLaTeXEncoding) { - Q_ASSERT(bibtexfile != NULL); - - KDialog dialog(parent); - dialog.setButtons(KDialog::Ok); - - /// default encoding is LaTeX - QString proposedEncoding = ExportWidget::defaultEncoding; - /// check encoding set to this exporter - if (!d->encoding.isEmpty()) - proposedEncoding = d->encoding; - /// encoding as stored in the File has highest precendence - if (bibtexfile->hasProperty(File::Encoding)) - proposedEncoding = bibtexfile->property(File::Encoding).toString(); - - ExportWidget exportWidget(&dialog); - exportWidget.setProposedEncoding(proposedEncoding); - dialog.setMainWidget(&exportWidget); - - if (dialog.exec() == QDialog::Accepted) - bibtexfile->setProperty(File::Encoding, exportWidget.listOfEncodings->lineEdit()->text()); + if (staticFileExporterBibTeX == NULL) + staticFileExporterBibTeX = new FileExporterBibTeX(); + else + staticFileExporterBibTeX->loadState(); + return staticFileExporterBibTeX->internalValueToBibTeX(value, key, useLaTeXEncoding); } -QString FileExporterBibTeX::valueToBibTeX(const Value& value, const QString& key, UseLaTeXEncoding useLaTeXEncoding) +QString FileExporterBibTeX::internalValueToBibTeX(const Value& value, const QString& key, UseLaTeXEncoding useLaTeXEncoding) { if (value.isEmpty()) return ""; @@ -437,11 +452,13 @@ QString result = ""; bool isOpen = false; + /// variable to memorize which closing delimiter to use + QChar stringCloseDelimiter = d->stringCloseDelimiter; const ValueItem* prev = NULL; for (QList::ConstIterator it = value.begin(); it != value.end(); ++it) { const MacroKey *macroKey = dynamic_cast(*it); if (macroKey != NULL) { - if (isOpen) result.append('}'); + if (isOpen) result.append(stringCloseDelimiter); isOpen = false; if (!result.isEmpty()) result.append(" # "); result.append(macroKey->text()); @@ -449,82 +466,145 @@ } else { const PlainText *plainText = dynamic_cast(*it); if (plainText != NULL) { + QString textBody = encodercheck(encoder, escapeLaTeXChars(plainText->text())); if (!isOpen) { if (!result.isEmpty()) result.append(" # "); - result.append('{'); + if (textBody.contains("\"")) { + /// fall back to {...} delimiters if text contains quotation marks + result.append("{"); + stringCloseDelimiter = '}'; + } else { + result.append(d->stringOpenDelimiter); + stringCloseDelimiter = d->stringCloseDelimiter; + } } else if (prev != NULL && typeid(*prev) == typeid(PlainText)) result.append(' '); else if (prev != NULL && typeid(*prev) == typeid(Person)) { /// handle "et al." i.e. "and others" result.append(" and "); - } else - result.append("} # {"); + } else { + result.append(stringCloseDelimiter).append(" # "); + + if (textBody.contains("\"")) { + /// fall back to {...} delimiters if text contains quotation marks + result.append("{"); + stringCloseDelimiter = '}'; + } else { + result.append(d->stringOpenDelimiter); + stringCloseDelimiter = d->stringCloseDelimiter; + } + } isOpen = true; - result.append(encodercheck(encoder, escapeLaTeXChars(plainText->text()))); + result.append(textBody); prev = plainText; } else { const VerbatimText *verbatimText = dynamic_cast(*it); if (verbatimText != NULL) { + QString textBody = verbatimText->text(); if (!isOpen) { if (!result.isEmpty()) result.append(" # "); - result.append('{'); + if (textBody.contains("\"")) { + /// fall back to {...} delimiters if text contains quotation marks + result.append("{"); + stringCloseDelimiter = '}'; + } else { + result.append(d->stringOpenDelimiter); + stringCloseDelimiter = d->stringCloseDelimiter; + } } else if (prev != NULL && typeid(*prev) == typeid(VerbatimText)) { if (key.toLower().startsWith(Entry::ftUrl) || key.toLower().startsWith(Entry::ftLocalFile) || key.toLower().startsWith(Entry::ftDOI)) result.append("; "); else result.append(' '); - } else - result.append("} # {"); + } else { + result.append(stringCloseDelimiter).append(" # "); + + if (textBody.contains("\"")) { + /// fall back to {...} delimiters if text contains quotation marks + result.append("{"); + stringCloseDelimiter = '}'; + } else { + result.append(d->stringOpenDelimiter); + stringCloseDelimiter = d->stringCloseDelimiter; + } + } isOpen = true; - result.append(verbatimText->text()); + result.append(textBody); prev = verbatimText; } else { const Person *person = dynamic_cast(*it); if (person != NULL) { + QString firstName = person->firstName(); + if (!firstName.isEmpty() && d->requiresPersonQuoting(firstName, false)) + firstName = firstName.prepend("{").append("}"); + + QString lastName = person->lastName(); + if (!lastName.isEmpty() && d->requiresPersonQuoting(lastName, true)) + lastName = lastName.prepend("{").append("}"); + + // TODO: Prefix and suffix + + QString thisName = Person::transcribePersonName(d->personNameFormatting, firstName, lastName); + if (!isOpen) { if (!result.isEmpty()) result.append(" # "); - result.append('{'); + if (thisName.contains("\"")) { + /// fall back to {...} delimiters if text contains quotation marks + result.append("{"); + stringCloseDelimiter = '}'; + } else { + result.append(d->stringOpenDelimiter); + stringCloseDelimiter = d->stringCloseDelimiter; + } } else if (prev != NULL && typeid(*prev) == typeid(Person)) result.append(" and "); - else - result.append("} # {"); - isOpen = true; - - QString thisName = ""; - QString v = person->firstName(); - if (!v.isEmpty()) { - bool requiresQuoting = requiresPersonQuoting(v, false); - if (requiresQuoting) thisName.append("{"); - thisName.append(v); - if (requiresQuoting) thisName.append("}"); - thisName.append(" "); - } + else { + result.append(stringCloseDelimiter).append(" # "); - v = person->lastName(); - if (!v.isEmpty()) { - bool requiresQuoting = requiresPersonQuoting(v, false); - if (requiresQuoting) thisName.append("{"); - thisName.append(v); - if (requiresQuoting) thisName.append("}"); + if (thisName.contains("\"")) { + /// fall back to {...} delimiters if text contains quotation marks + result.append("{"); + stringCloseDelimiter = '}'; + } else { + result.append(d->stringOpenDelimiter); + stringCloseDelimiter = d->stringCloseDelimiter; + } } - - // TODO: Prefix and suffix + isOpen = true; result.append(encodercheck(encoder, escapeLaTeXChars(thisName))); prev = person; } else { const Keyword *keyword = dynamic_cast(*it); if (keyword != NULL) { + QString textBody = encodercheck(encoder, escapeLaTeXChars(keyword->text())); if (!isOpen) { if (!result.isEmpty()) result.append(" # "); - result.append('{'); + if (textBody.contains("\"")) { + /// fall back to {...} delimiters if text contains quotation marks + result.append("{"); + stringCloseDelimiter = '}'; + } else { + result.append(d->stringOpenDelimiter); + stringCloseDelimiter = d->stringCloseDelimiter; + } } else if (prev != NULL && typeid(*prev) == typeid(Keyword)) result.append("; "); - else - result.append("} # {"); + else { + result.append(stringCloseDelimiter).append(" # "); + + if (textBody.contains("\"")) { + /// fall back to {...} delimiters if text contains quotation marks + result.append("{"); + stringCloseDelimiter = '}'; + } else { + result.append(d->stringOpenDelimiter); + stringCloseDelimiter = d->stringCloseDelimiter; + } + } isOpen = true; - result.append(encodercheck(encoder, escapeLaTeXChars(keyword->text()))); + result.append(textBody); prev = keyword; } } @@ -534,7 +614,7 @@ prev = *it; } - if (isOpen) result.append('}'); + if (isOpen) result.append(stringCloseDelimiter); return result; } @@ -552,35 +632,47 @@ QString FileExporterBibTeX::escapeLaTeXChars(const QString &text) { + /// Regular expression to match dollar signs that are not escaped (i.e. not \$). + /// Store character in front of dollar sign in cap(1). + const QRegExp regExpMathSeparator = QRegExp(QLatin1String("(^|[^\\\\])\\$")); + /// Regular expression for characters to escape for LaTeX + const QRegExp regExpEscape("[^\\\\][&#_%]"); + /// Status whether in math mode or not (as determined by dollar signs) + bool inMathMode = false; + /// Resulting text QString result = text; - const QRegExp regExpEscape("[^\\\\][&#_%]"); - int p = -1; - while ((p = regExpEscape.indexIn(result, p + 1)) != -1) - result = result.left(p + 1) + '\\' + result.mid(p + 1); + int m1 = -1, m2 = -1; + /// For each dollar sign (= switch into or out of math mode) ... + while ((m1 = regExpMathSeparator.indexIn(result, m2 + 1)) >= 0) { + /// Compensate for character in front of dollar sign + int cl = regExpMathSeparator.cap(1).length(); + m1 += cl; + + if (!inMathMode) { + /// If not in math mode, make special characters LaTeX-safe + int p = m2; + while ((p = regExpEscape.indexIn(result, p + 1)) >= 0 && p < m1) { + result = result.left(p + 1) + '\\' + result.mid(p + 1); + ++m1; + } + } + /// Toggle math mode + inMathMode = !inMathMode; + + m2 = m1; + } + + if (m1 == -1 && !inMathMode) { + int p = m2; + while ((p = regExpEscape.indexIn(result, p + 1)) >= 0) + result = result.left(p + 1) + '\\' + result.mid(p + 1); + } + return result; } -bool FileExporterBibTeX::requiresPersonQuoting(const QString &text, bool isLastName) +void FileExporterBibTeX::loadState() { - if (isLastName && !text.contains(" ")) - /** Last name contains NO spaces, no quoting necessary */ - return FALSE; - else if (!isLastName && !text.contains(" and ")) - /** First name contains no " and " no quoting necessary */ - return FALSE; - else if (text[0] != '{' || text[text.length()-1] != '}') - /** as either last name contains spaces or first name contains " and " and there is no protective quoting yet, there must be a protective quoting added */ - return TRUE; - - int bracketCounter = 0; - for (int i = text.length() - 1; i >= 0; --i) { - if (text[i] == '{') - ++bracketCounter; - else if (text[i] == '}') - --bracketCounter; - if (bracketCounter == 0 && i > 0) - return TRUE; - } - return false; + d->loadState(); } diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterbibtex.h kbibtex-0.4/src/libkbibtexio/fileexporterbibtex.h --- kbibtex-0.3/src/libkbibtexio/fileexporterbibtex.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterbibtex.h 2011-11-20 20:36:55.000000000 +0000 @@ -42,27 +42,31 @@ { public: enum UseLaTeXEncoding {leUTF8, leLaTeX}; - enum QuoteComment {qcNone, qcCommand, qcPercentSign}; + enum QuoteComment {qcNone = 0, qcCommand = 1, qcPercentSign = 2}; + + static const QString keyEncoding; + static const QString defaultEncoding; + + static const QString keyStringDelimiter; + static const QString defaultStringDelimiter; + + static const QString keyQuoteComment; + static const FileExporterBibTeX::QuoteComment defaultQuoteComment; + + static const QString keyKeywordCasing; + static const KBibTeX::Casing defaultKeywordCasing; + + static const QString keyProtectCasing; + static const bool defaultProtectCasing; FileExporterBibTeX(); ~FileExporterBibTeX(); - /** - * Set the encoding when saving a BibTeX file. - * Important: The File object's "Encoding" property has precendence over this setting. - */ - void setEncoding(const QString& encoding); - QString encoding() const; - void setStringDelimiters(const QChar& stringOpenDelimiter, const QChar& stringCloseDelimiter); - void setKeywordCasing(KBibTeX::Casing keywordCasing); - void setQuoteComment(QuoteComment quoteComment); - void setProtectCasing(bool protectCasing); + void setEncoding(const QString &encoding); bool save(QIODevice* iodevice, const File* bibtexfile, QStringList *errorLog = NULL); bool save(QIODevice* iodevice, const Element* element, QStringList *errorLog = NULL); - virtual void showExportDialog(QWidget *parent, File *bibtexfile) const; - static QString valueToBibTeX(const Value& value, const QString& fieldType = QString::null, UseLaTeXEncoding useLaTeXEncoding = leLaTeX); static QString elementToString(const Element* element); @@ -70,11 +74,15 @@ void cancel(); private: - static bool requiresPersonQuoting(const QString &text, bool isLastName); static QString escapeLaTeXChars(const QString &text); class FileExporterBibTeXPrivate; FileExporterBibTeXPrivate *d; + + QString internalValueToBibTeX(const Value& value, const QString& fieldType = QString::null, UseLaTeXEncoding useLaTeXEncoding = leLaTeX); + void loadState(); + + static FileExporterBibTeX *staticFileExporterBibTeX; }; #endif diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterblg.cpp kbibtex-0.4/src/libkbibtexio/fileexporterblg.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporterblg.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterblg.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -48,7 +48,8 @@ QFile output(m_bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter* bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX* bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("utf-8")); result = bibtexExporter->save(&output, bibtexfile, errorLog); bibtexExporter->save(ioDevice, bibtexfile, NULL); output.close(); @@ -67,7 +68,8 @@ QFile output(m_bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter * bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX * bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("utf-8")); result = bibtexExporter->save(&output, element, errorLog); bibtexExporter->save(ioDevice, element, NULL); output.close(); @@ -111,7 +113,8 @@ ts << "\\documentclass{article}\n"; ts << "\\usepackage[T1]{fontenc}\n"; ts << "\\usepackage[utf8]{inputenc}\n"; - ts << "\\usepackage[" << m_latexLanguage << "]{babel}\n"; + if (kpsewhich("babel.sty")) + ts << "\\usepackage[" << m_latexLanguage << "]{babel}\n"; if (kpsewhich("hyperref.sty")) ts << "\\usepackage[pdfproducer={KBibTeX: http://home.gna.org/kbibtex/},pdftex]{hyperref}\n"; else if (kpsewhich("url.sty")) diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporter.cpp kbibtex-0.4/src/libkbibtexio/fileexporter.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporter.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporter.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -19,6 +19,12 @@ ***************************************************************************/ #include "fileexporter.h" +#include +#include + +const QString FileExporter::keyPaperSize = QLatin1String("paperSize"); +const QString FileExporter::defaultPaperSize = QLatin1String("a4"); + FileExporter::FileExporter() : QObject() { // nothing @@ -29,4 +35,32 @@ // nothing } -// #include "fileexporter.moc" +QString FileExporter::toString(const Element* element) +{ + QBuffer buffer; + buffer.open(QBuffer::WriteOnly); + if (save(&buffer, element)) { + buffer.close(); + if (buffer.open(QBuffer::ReadOnly)) { + QTextStream ts(&buffer); + return ts.readAll(); + } + } + + return QString::null; +} + +QString FileExporter::toString(const File* bibtexfile) +{ + QBuffer buffer; + buffer.open(QBuffer::WriteOnly); + if (save(&buffer, bibtexfile)) { + buffer.close(); + if (buffer.open(QBuffer::ReadOnly)) { + QTextStream ts(&buffer); + return ts.readAll(); + } + } + + return QString::null; +} diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporter.h kbibtex-0.4/src/libkbibtexio/fileexporter.h --- kbibtex-0.3/src/libkbibtexio/fileexporter.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporter.h 2011-11-20 20:36:55.000000000 +0000 @@ -37,35 +37,18 @@ Q_OBJECT public: + static const QString keyPaperSize; + static const QString defaultPaperSize; + FileExporter(); ~FileExporter(); + QString toString(const Element* element); + QString toString(const File* bibtexfile); + virtual bool save(QIODevice *iodevice, const File* bibtexfile, QStringList *errorLog = NULL) = 0; virtual bool save(QIODevice *iodevice, const Element* element, QStringList *errorLog = NULL) = 0; - /** - * When exporting data, show a dialog where the user may select options on the - * export process such as selecting encoding. Re-implementing this function is - * optional and should only be done if user interaction is necessary at export - * actions. - * Whatever settings are made in the export dialog have to be stored in the - * File object via its property map. - * A calling application should call this function before calling save() or similar - * functions. However, an application may opt to not call this function, e.g. in case - * of a Save operation opposed to a SaveAs option which would call this function. - * The implementing class must be aware of this behaviour. - * The implementer may choose to show or not show a dialog, depending on e.g. if - * additional information is necessary or not. - */ - /** - * @see File::setProperty() - * @see File::property() - */ - virtual void showExportDialog(QWidget *parent, File *bibtexfile) const { - Q_UNUSED(parent); - Q_UNUSED(bibtexfile); - } - signals: void progress(int current, int total); diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterpdf.cpp kbibtex-0.4/src/libkbibtexio/fileexporterpdf.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporterpdf.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterpdf.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include @@ -31,11 +33,19 @@ #include "fileexporterpdf.h" FileExporterPDF::FileExporterPDF(bool embedFiles) - : FileExporterToolchain(), m_latexLanguage("english"), m_latexBibStyle("plain"), m_embedFiles(embedFiles) + : FileExporterToolchain(), m_embedFiles(embedFiles) { m_laTeXFilename = tempDir.name() + QLatin1String("/bibtex-to-pdf.tex"); m_bibTeXFilename = tempDir.name() + QLatin1String("/bibtex-to-pdf.bib"); m_outputFilename = tempDir.name() + QLatin1String("/bibtex-to-pdf.pdf"); + + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("kbibtexrc")); + KConfigGroup configGroup(config, QLatin1String("FileExporterPDFPS")); + m_babelLanguage = configGroup.readEntry(keyBabelLanguage, defaultBabelLanguage); + m_bibliographyStyle = configGroup.readEntry(keyBibliographyStyle, defaultBibliographyStyle); + + KConfigGroup configGroupGeneral(config, QLatin1String("General")); + m_paperSize = configGroupGeneral.readEntry(keyPaperSize, defaultPaperSize); } FileExporterPDF::~FileExporterPDF() @@ -54,7 +64,8 @@ QFile output(m_bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter* bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX* bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("latex")); result = bibtexExporter->save(&output, bibtexfile, errorLog); output.close(); delete bibtexExporter; @@ -75,7 +86,8 @@ QFile output(m_bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter * bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX * bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("latex")); result = bibtexExporter->save(&output, element, errorLog); output.close(); delete bibtexExporter; @@ -87,16 +99,6 @@ return result; } -void FileExporterPDF::setLaTeXLanguage(const QString& language) -{ - m_latexLanguage = language; -} - -void FileExporterPDF::setLaTeXBibliographyStyle(const QString& bibStyle) -{ - m_latexBibStyle = bibStyle; -} - void FileExporterPDF::setDocumentSearchPaths(const QStringList& searchPaths) { m_searchPaths = searchPaths; @@ -106,12 +108,7 @@ { QStringList cmdLines = QStringList() << QLatin1String("pdflatex -halt-on-error bibtex-to-pdf.tex") << QLatin1String("bibtex bibtex-to-pdf") << QLatin1String("pdflatex -halt-on-error bibtex-to-pdf.tex") << QLatin1String("pdflatex -halt-on-error bibtex-to-pdf.tex"); - if (writeLatexFile(m_laTeXFilename) && runProcesses(cmdLines, errorLog) && writeFileToIODevice(m_outputFilename, iodevice)) - return true; - else { - kWarning() << "Generating PDF failed"; - return false; - } + return writeLatexFile(m_laTeXFilename) && runProcesses(cmdLines, errorLog) && writeFileToIODevice(m_outputFilename, iodevice, errorLog); } bool FileExporterPDF::writeLatexFile(const QString &filename) @@ -121,39 +118,44 @@ m_embedFiles &= kpsewhich("embedfile.sty"); QTextStream ts(&latexFile); ts.setCodec("UTF-8"); - ts << "\\documentclass{article}\n"; - ts << "\\usepackage[T1]{fontenc}\n"; - ts << "\\usepackage[utf8]{inputenc}\n"; - ts << "\\usepackage[" << m_latexLanguage << "]{babel}\n"; + ts << "\\documentclass{article}" << endl; + ts << "\\usepackage[T1]{fontenc}" << endl; + ts << "\\usepackage[utf8]{inputenc}" << endl; + if (kpsewhich("babel.sty")) + ts << "\\usepackage[" << m_babelLanguage << "]{babel}" << endl; if (kpsewhich("hyperref.sty")) - ts << "\\usepackage[pdfproducer={KBibTeX: http://home.gna.org/kbibtex/},pdftex]{hyperref}\n"; + ts << "\\usepackage[pdfproducer={KBibTeX: http://home.gna.org/kbibtex/},pdftex]{hyperref}" << endl; else if (kpsewhich("url.sty")) - ts << "\\usepackage{url}\n"; - if (m_latexBibStyle.startsWith("apacite") && kpsewhich("apacite.sty")) - ts << "\\usepackage[bibnewpage]{apacite}\n"; + ts << "\\usepackage{url}" << endl; + if (m_bibliographyStyle.startsWith("apacite") && kpsewhich("apacite.sty")) + ts << "\\usepackage[bibnewpage]{apacite}" << endl; + if (m_bibliographyStyle == QLatin1String("dcu") && kpsewhich("harvard.sty") && kpsewhich("html.sty")) + ts << "\\usepackage{html}" << endl << "\\usepackage[dcucite]{harvard}" << endl << "\\renewcommand{\\harvardurl}{URL: \\url}" << endl; if (kpsewhich("embedfile.sty")) - ts << "\\usepackage{embedfile}\n"; - ts << "\\bibliographystyle{" << m_latexBibStyle << "}\n"; - ts << "\\begin{document}\n"; - - if (kpsewhich("embedfile.sty")) { - ts << "\\embedfile[desc={" << i18n("BibTeX file") << "}]{bibtex-to-pdf.bib}\n"; - if (m_embedFiles) - for (QStringList::ConstIterator it = m_embeddedFileList.begin(); it != m_embeddedFileList.end(); ++it) { - QStringList param = (*it).split("|"); - QFile file(param[1]); - if (file.exists()) - ts << "\\embedfile[desc={" << param[0] << "}]{" << param[1] << "}\n"; - } + ts << "\\usepackage{embedfile}" << endl; + if (kpsewhich("geometry.sty")) + ts << "\\usepackage[paper=" << m_paperSize << (m_paperSize.length() <= 2 ? "paper" : "") << "]{geometry}" << endl; + ts << "\\bibliographystyle{" << m_bibliographyStyle << "}" << endl; + ts << "\\begin{document}" << endl; + + if (m_embedFiles) { + ts << "\\embedfile[desc={" << i18n("BibTeX file") << "}]{bibtex-to-pdf.bib}" << endl; + + for (QStringList::ConstIterator it = m_embeddedFileList.begin(); it != m_embeddedFileList.end(); ++it) { + QStringList param = (*it).split("|"); + QFile file(param[1]); + if (file.exists()) + ts << "\\embedfile[desc={" << param[0] << "}]{" << param[1] << "}" << endl; + } } - ts << "\\nocite{*}\n"; - ts << "\\bibliography{bibtex-to-pdf}\n"; - ts << "\\end{document}\n"; + ts << "\\nocite{*}" << endl; + ts << "\\bibliography{bibtex-to-pdf}" << endl; + ts << "\\end{document}" << endl; latexFile.close(); - return TRUE; + return true; } else - return FALSE; + return false; } void FileExporterPDF::fillEmbeddedFileList(const File* bibtexfile) diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterpdf.h kbibtex-0.4/src/libkbibtexio/fileexporterpdf.h --- kbibtex-0.3/src/libkbibtexio/fileexporterpdf.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterpdf.h 2011-11-20 20:36:55.000000000 +0000 @@ -36,16 +36,15 @@ bool save(QIODevice* iodevice, const File* bibtexfile, QStringList *errorLog = NULL); bool save(QIODevice* iodevice, const Element* element, QStringList *errorLog = NULL); - void setLaTeXLanguage(const QString& language); - void setLaTeXBibliographyStyle(const QString& bibStyle); void setDocumentSearchPaths(const QStringList& searchPaths); private: QString m_laTeXFilename; QString m_bibTeXFilename; QString m_outputFilename; - QString m_latexLanguage; - QString m_latexBibStyle; + QString m_babelLanguage; + QString m_paperSize; + QString m_bibliographyStyle; bool m_embedFiles; QStringList m_embeddedFileList; QStringList m_searchPaths; diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterps.cpp kbibtex-0.4/src/libkbibtexio/fileexporterps.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporterps.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterps.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -21,16 +21,27 @@ #include #include +#include +#include + #include #include #include "fileexporterps.h" FileExporterPS::FileExporterPS() - : FileExporterToolchain(), m_latexLanguage("english"), m_latexBibStyle("plain") + : FileExporterToolchain() { m_laTeXFilename = tempDir.name() + QLatin1String("/bibtex-to-ps.tex"); m_bibTeXFilename = tempDir.name() + QLatin1String("/bibtex-to-ps.bib"); m_outputFilename = tempDir.name() + QLatin1String("/bibtex-to-ps.ps"); + + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("kbibtexrc")); + KConfigGroup configGroup(config, QLatin1String("FileExporterPDFPS")); + m_babelLanguage = configGroup.readEntry(keyBabelLanguage, defaultBabelLanguage); + m_bibliographyStyle = configGroup.readEntry(keyBibliographyStyle, defaultBibliographyStyle); + + KConfigGroup configGroupGeneral(config, QLatin1String("General")); + m_paperSize = configGroupGeneral.readEntry(keyPaperSize, defaultPaperSize); } FileExporterPS::~FileExporterPS() @@ -44,7 +55,8 @@ QFile output(m_bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter * bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX * bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("latex")); result = bibtexExporter->save(&output, bibtexfile, errorLog); output.close(); delete bibtexExporter; @@ -62,7 +74,8 @@ QFile output(m_bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter * bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX * bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("latex")); result = bibtexExporter->save(&output, element, errorLog); output.close(); delete bibtexExporter; @@ -74,24 +87,11 @@ return result; } -void FileExporterPS::setLaTeXLanguage(const QString& language) -{ - m_latexLanguage = language; -} - -void FileExporterPS::setLaTeXBibliographyStyle(const QString& bibStyle) -{ - m_latexBibStyle = bibStyle; -} - bool FileExporterPS::generatePS(QIODevice* iodevice, QStringList *errorLog) { - QStringList cmdLines = QStringList() << QLatin1String("latex -halt-on-error bibtex-to-ps.tex") << QLatin1String("bibtex bibtex-to-ps") << QLatin1String("latex -halt-on-error bibtex-to-ps.tex") << QLatin1String("latex -halt-on-error bibtex-to-ps.tex") << QLatin1String("dvips -o bibtex-to-ps.ps bibtex-to-ps.dvi"); + QStringList cmdLines = QStringList() << QLatin1String("latex -halt-on-error bibtex-to-ps.tex") << QLatin1String("bibtex bibtex-to-ps") << QLatin1String("latex -halt-on-error bibtex-to-ps.tex") << QLatin1String("latex -halt-on-error bibtex-to-ps.tex") << QLatin1String("dvips -R2 -o bibtex-to-ps.ps bibtex-to-ps.dvi"); - if (writeLatexFile(m_laTeXFilename) && runProcesses(cmdLines, errorLog) && writeFileToIODevice(m_outputFilename, iodevice)) - return TRUE; - else - return FALSE; + return writeLatexFile(m_laTeXFilename) && runProcesses(cmdLines, errorLog) && beautifyPostscriptFile(m_outputFilename, "Exported Bibliography") && writeFileToIODevice(m_outputFilename, iodevice, errorLog); } bool FileExporterPS::writeLatexFile(const QString &filename) @@ -100,24 +100,56 @@ if (latexFile.open(QIODevice::WriteOnly)) { QTextStream ts(&latexFile); ts.setCodec("UTF-8"); - ts << "\\documentclass{article}\n"; - if (kpsewhich("t2aenc.dfu") && kpsewhich("t1enc.dfu")) - ts << "\\usepackage[T1,T2A]{fontenc}\n"; - ts << "\\usepackage[utf8]{inputenc}\n"; + ts << "\\documentclass{article}" << endl; + ts << "\\usepackage[T1]{fontenc}" << endl; + ts << "\\usepackage[utf8]{inputenc}" << endl; if (kpsewhich("babel.sty")) - ts << "\\usepackage[" << m_latexLanguage << "]{babel}\n"; + ts << "\\usepackage[" << m_babelLanguage << "]{babel}" << endl; if (kpsewhich("url.sty")) - ts << "\\usepackage{url}\n"; - if (m_latexBibStyle.startsWith("apacite") && kpsewhich("apacite.sty")) - ts << "\\usepackage[bibnewpage]{apacite}\n"; - ts << "\\bibliographystyle{" << m_latexBibStyle << "}\n"; - ts << "\\begin{document}\n"; - ts << "\\nocite{*}\n"; - ts << "\\bibliography{bibtex-to-ps}\n"; - ts << "\\end{document}\n"; + ts << "\\usepackage{url}" << endl; + if (m_bibliographyStyle.startsWith("apacite") && kpsewhich("apacite.sty")) + ts << "\\usepackage[bibnewpage]{apacite}" << endl; + if (m_bibliographyStyle == QLatin1String("dcu") && kpsewhich("harvard.sty") && kpsewhich("html.sty")) + ts << "\\usepackage{html}" << endl << "\\usepackage[dcucite]{harvard}" << endl << "\\renewcommand{\\harvardurl}{URL: \\url}" << endl; + if (kpsewhich("geometry.sty")) + ts << "\\usepackage[paper=" << m_paperSize << (m_paperSize.length() <= 2 ? "paper" : "") << "]{geometry}" << endl; + ts << "\\bibliographystyle{" << m_bibliographyStyle << "}" << endl; + ts << "\\begin{document}" << endl; + ts << "\\nocite{*}" << endl; + ts << "\\bibliography{bibtex-to-ps}" << endl; + ts << "\\end{document}" << endl; latexFile.close(); - return TRUE; + return true; + } else + return false; +} + +bool FileExporterPS::beautifyPostscriptFile(const QString &filename, const QString &title) +{ + QFile postscriptFile(filename); + if (postscriptFile.open(QFile::ReadOnly)) { + QTextStream ts(&postscriptFile); + QStringList lines; + QString line; + int i = 0; + while (!(line = ts.readLine()).isNull()) { + if (i < 32 && line.startsWith("%%Title:")) + line = "%%Title: " + title; + else if (i < 32 && line.startsWith("%%Creator:")) + line += "; exported from within KBibTeX: http://home.gna.org/kbibtex/"; + lines += line; + ++i; + } + postscriptFile.close(); + + if (postscriptFile.open(QFile::WriteOnly)) { + QTextStream ts(&postscriptFile); + foreach(QString line, lines) ts << line << endl; + postscriptFile.close(); + } else + return false; } else - return FALSE; + return false; + return true; } diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterps.h kbibtex-0.4/src/libkbibtexio/fileexporterps.h --- kbibtex-0.3/src/libkbibtexio/fileexporterps.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterps.h 2011-11-20 20:36:55.000000000 +0000 @@ -36,18 +36,17 @@ bool save(QIODevice* iodevice, const File* bibtexfile, QStringList *errorLog = NULL); bool save(QIODevice* iodevice, const Element* element, QStringList *errorLog = NULL); - void setLaTeXLanguage(const QString& language); - void setLaTeXBibliographyStyle(const QString& bibStyle); - private: QString m_laTeXFilename; QString m_bibTeXFilename; QString m_outputFilename; - QString m_latexLanguage; - QString m_latexBibStyle; + QString m_babelLanguage; + QString m_paperSize; + QString m_bibliographyStyle; bool generatePS(QIODevice* iodevice, QStringList *errorLog); bool writeLatexFile(const QString &filename); + bool beautifyPostscriptFile(const QString &filename, const QString &title); }; diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterrtf.cpp kbibtex-0.4/src/libkbibtexio/fileexporterrtf.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporterrtf.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterrtf.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -21,16 +21,28 @@ #include #include +#include +#include + #include #include #include "fileexporterrtf.h" -FileExporterRTF::FileExporterRTF(const QString& latexBibStyle, const QString& latexLanguage) - : FileExporterToolchain(), m_latexLanguage(latexLanguage), m_latexBibStyle(latexBibStyle) +FileExporterRTF::FileExporterRTF() + : FileExporterToolchain() { m_laTeXFilename = tempDir.name() + QLatin1String("/bibtex-to-rtf.tex"); m_bibTeXFilename = tempDir.name() + QLatin1String("/bibtex-to-rtf.bib"); m_outputFilename = tempDir.name() + QLatin1String("/bibtex-to-rtf.rtf"); + + KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("kbibtexrc")); + KConfigGroup configGroup(config, QLatin1String("FileExporterPDFPS")); + m_babelLanguage = configGroup.readEntry(keyBabelLanguage, defaultBabelLanguage); + m_bibliographyStyle = configGroup.readEntry(keyBibliographyStyle, defaultBibliographyStyle); + + KConfigGroup configGroupGeneral(config, QLatin1String("General")); + m_paperSize = configGroupGeneral.readEntry(keyPaperSize, defaultPaperSize); + } FileExporterRTF::~FileExporterRTF() @@ -44,7 +56,8 @@ QFile output(m_bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter * bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX * bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("latex")); result = bibtexExporter->save(&output, bibtexfile, errorLog); output.close(); delete bibtexExporter; @@ -62,7 +75,8 @@ QFile output(m_bibTeXFilename); if (output.open(QIODevice::WriteOnly)) { - FileExporter * bibtexExporter = new FileExporterBibTeX(); + FileExporterBibTeX * bibtexExporter = new FileExporterBibTeX(); + bibtexExporter->setEncoding(QLatin1String("latex")); result = bibtexExporter->save(&output, element, errorLog); output.close(); delete bibtexExporter; @@ -71,18 +85,14 @@ if (result) result = generateRTF(iodevice, errorLog); - // m_mutex.unlock(); // FIXME: required? return result; } bool FileExporterRTF::generateRTF(QIODevice* iodevice, QStringList *errorLog) { - QStringList cmdLines = QStringList() << QLatin1String("latex -halt-on-error bibtex-to-rtf.tex") << QLatin1String("bibtex bibtex-to-rtf") << QLatin1String("latex -halt-on-error bibtex-to-rtf.tex") << QLatin1String("latex2rtf bibtex-to-rtf.tex"); + QStringList cmdLines = QStringList() << QLatin1String("latex -halt-on-error bibtex-to-rtf.tex") << QLatin1String("bibtex bibtex-to-rtf") << QLatin1String("latex -halt-on-error bibtex-to-rtf.tex") << QString(QLatin1String("latex2rtf -i %1 bibtex-to-rtf.tex")).arg(m_babelLanguage); - if (writeLatexFile(m_laTeXFilename) && runProcesses(cmdLines, errorLog) && writeFileToIODevice(m_outputFilename, iodevice)) - return TRUE; - else - return FALSE; + return writeLatexFile(m_laTeXFilename) && runProcesses(cmdLines, errorLog) && writeFileToIODevice(m_outputFilename, iodevice, errorLog); } bool FileExporterRTF::writeLatexFile(const QString &filename) @@ -91,23 +101,27 @@ if (latexFile.open(QIODevice::WriteOnly)) { QTextStream ts(&latexFile); ts.setCodec("UTF-8"); - ts << "\\documentclass{article}\n"; - if (kpsewhich("t2aenc.dfu") && kpsewhich("t1enc.dfu")) - ts << "\\usepackage[T1,T2A]{fontenc}\n"; - ts << "\\usepackage[utf8]{inputenc}\n"; + ts << "\\documentclass{article}" << endl; + ts << "\\usepackage[T1]{fontenc}" << endl; + ts << "\\usepackage[utf8]{inputenc}" << endl; if (kpsewhich("babel.sty")) - ts << "\\usepackage[" << m_latexLanguage << "]{babel}\n"; + ts << "\\usepackage[" << m_babelLanguage << "]{babel}" << endl; if (kpsewhich("url.sty")) - ts << "\\usepackage{url}\n"; - if (m_latexBibStyle.startsWith("apacite") && kpsewhich("apacite.sty")) - ts << "\\usepackage[bibnewpage]{apacite}\n"; - ts << "\\bibliographystyle{" << m_latexBibStyle << "}\n"; - ts << "\\begin{document}\n"; - ts << "\\nocite{*}\n"; - ts << "\\bibliography{bibtex-to-rtf}\n"; - ts << "\\end{document}\n"; + ts << "\\usepackage{url}" << endl; + if (m_bibliographyStyle.startsWith("apacite") && kpsewhich("apacite.sty")) + ts << "\\usepackage[bibnewpage]{apacite}" << endl; + if (m_bibliographyStyle == QLatin1String("dcu") && kpsewhich("harvard.sty") && kpsewhich("html.sty")) + ts << "\\usepackage{html}" << endl << "\\usepackage[dcucite]{harvard}" << endl << "\\renewcommand{\\harvardurl}{URL: \\url}" << endl; + if (kpsewhich("geometry.sty")) + ts << "\\usepackage[paper=" << m_paperSize << (m_paperSize.length() <= 2 ? "paper" : "") << "]{geometry}" << endl; + ts << "\\bibliographystyle{" << m_bibliographyStyle << "}" << endl; + ts << "\\begin{document}" << endl; + ts << "\\nocite{*}" << endl; + ts << "\\bibliography{bibtex-to-rtf}" << endl; + ts << "\\end{document}" << endl; latexFile.close(); - return TRUE; - } else - return FALSE; + return true; + } + + return false; } diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterrtf.h kbibtex-0.4/src/libkbibtexio/fileexporterrtf.h --- kbibtex-0.3/src/libkbibtexio/fileexporterrtf.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterrtf.h 2011-11-20 20:36:55.000000000 +0000 @@ -30,7 +30,7 @@ class KBIBTEXIO_EXPORT FileExporterRTF : public FileExporterToolchain { public: - FileExporterRTF(const QString& latexBibStyle = "plain", const QString& latexLanguage = "english"); + FileExporterRTF(); ~FileExporterRTF(); bool save(QIODevice* iodevice, const File* bibtexfile, QStringList *errorLog = NULL); @@ -40,8 +40,9 @@ QString m_laTeXFilename; QString m_bibTeXFilename; QString m_outputFilename; - QString m_latexLanguage; - QString m_latexBibStyle; + QString m_babelLanguage; + QString m_bibliographyStyle; + QString m_paperSize; bool generateRTF(QIODevice* iodevice, QStringList *errorLog); bool writeLatexFile(const QString &filename); diff -Nru kbibtex-0.3/src/libkbibtexio/fileexportertoolchain.cpp kbibtex-0.4/src/libkbibtexio/fileexportertoolchain.cpp --- kbibtex-0.3/src/libkbibtexio/fileexportertoolchain.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexportertoolchain.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -25,10 +25,17 @@ #include #include +#include + #include "fileexportertoolchain.h" static const QRegExp chompRegExp = QRegExp("[\\n\\r]+$"); +const QString FileExporterToolchain::keyBabelLanguage = QLatin1String("babelLanguage"); +const QString FileExporterToolchain::defaultBabelLanguage = QLatin1String("english"); +const QString FileExporterToolchain::keyBibliographyStyle = QLatin1String("bibliographyStyle"); +const QString FileExporterToolchain::defaultBibliographyStyle = QLatin1String("plain"); + FileExporterToolchain::FileExporterToolchain() : FileExporter(), m_errorLog(NULL) { @@ -37,7 +44,7 @@ bool FileExporterToolchain::runProcesses(const QStringList &progs, QStringList *errorLog) { - bool result = TRUE; + bool result = true; int i = 0; emit progress(0, progs.size()); @@ -63,30 +70,35 @@ processEnvironment.insert("openout_any", "r"); m_process->setProcessEnvironment(processEnvironment); m_process->setWorkingDirectory(tempDir.name()); + connect(m_process, SIGNAL(readyRead()), this, SLOT(slotReadProcessOutput())); + if (errorLog != NULL) + errorLog->append(i18n("Running process '%1' using working directory '%2'", (cmd + " " + args.join(" ")), m_process->workingDirectory())); m_process->start(cmd, args); m_errorLog = errorLog; if (m_process->waitForStarted(3000)) { if (m_process->waitForFinished(30000)) - result = m_process->exitStatus() == QProcess::NormalExit; + result = m_process->exitStatus() == QProcess::NormalExit && m_process->exitCode() == 0; else result = false; } else result = false; if (!result) - errorLog->append(QString("Process '%1' failed.").arg(args.join(" "))); + errorLog->append(i18n("Process '%1' failed", (cmd + " " + args.join(" ")))); + + if (errorLog != NULL) + errorLog->append(i18n("Stopped process '%1' with exit code %2", (cmd + " " + args.join(" ")), m_process->exitCode())); - disconnect(m_process, SIGNAL(readyRead()), this, SLOT(slotReadProcessOutput())); delete(m_process); m_process = NULL; return result; } -bool FileExporterToolchain::writeFileToIODevice(const QString &filename, QIODevice *device) +bool FileExporterToolchain::writeFileToIODevice(const QString &filename, QIODevice *device, QStringList *errorLog) { QFile file(filename); if (file.open(QIODevice::ReadOnly)) { @@ -101,9 +113,13 @@ file.close(); delete[] buffer; + if (errorLog != NULL) + errorLog->append(i18n("Writing to file '%1'' succeeded", filename)); return result; } + if (errorLog != NULL) + errorLog->append(i18n("Writing to file '%1'' failed", filename)); return false; } diff -Nru kbibtex-0.3/src/libkbibtexio/fileexportertoolchain.h kbibtex-0.4/src/libkbibtexio/fileexportertoolchain.h --- kbibtex-0.3/src/libkbibtexio/fileexportertoolchain.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexportertoolchain.h 2011-11-20 20:36:55.000000000 +0000 @@ -36,6 +36,12 @@ { Q_OBJECT public: + static const QString keyBabelLanguage; + static const QString defaultBabelLanguage; + + static const QString keyBibliographyStyle; + static const QString defaultBibliographyStyle; + FileExporterToolchain(); static bool kpsewhich(const QString& filename); @@ -49,7 +55,7 @@ bool runProcesses(const QStringList &progs, QStringList *errorLog = NULL); bool runProcess(const QString &cmd, const QStringList &args, QStringList *errorLog = NULL); - bool writeFileToIODevice(const QString &filename, QIODevice *device); + bool writeFileToIODevice(const QString &filename, QIODevice *device, QStringList *errorLog = NULL); private: QProcess *m_process; diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterxml.cpp kbibtex-0.4/src/libkbibtexio/fileexporterxml.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporterxml.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterxml.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -220,8 +220,6 @@ Person *p = dynamic_cast(item); if (p != NULL) { result.append(""); - if (!p->prefix().isEmpty()) - result.append("" + cleanXML(EncoderXML::currentEncoderXML() ->encode(p->lastName())) + ""); if (!p->firstName().isEmpty()) result.append("" + cleanXML(EncoderXML::currentEncoderXML() ->encode(p->firstName())) + ""); if (!p->lastName().isEmpty()) diff -Nru kbibtex-0.3/src/libkbibtexio/fileexporterxslt.cpp kbibtex-0.4/src/libkbibtexio/fileexporterxslt.cpp --- kbibtex-0.3/src/libkbibtexio/fileexporterxslt.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileexporterxslt.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -17,9 +17,11 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ + #include #include #include +#include #include #include @@ -38,7 +40,7 @@ FileExporterXSLT::FileExporterXSLT(const QString& xsltFilename) : FileExporter() { - if (xsltFilename.isNull()) + if (xsltFilename.isEmpty() || !QFile(xsltFilename).exists()) setXSLTFilename(KStandardDirs::locate("appdata", "standard.xsl")); else setXSLTFilename(xsltFilename); diff -Nru kbibtex-0.3/src/libkbibtexio/file.h kbibtex-0.4/src/libkbibtexio/file.h --- kbibtex-0.3/src/libkbibtexio/file.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/file.h 2011-11-20 20:36:55.000000000 +0000 @@ -43,26 +43,56 @@ class KBIBTEXIO_EXPORT File : public QList { public: + /// enum and flags to differ between entries, macros etc + /// used for @see #allKeys() and @see #containsKey() + enum ElementType { + etEntry = 0x1, etMacro = 0x2, etAll = 0x3 + }; + Q_DECLARE_FLAGS(ElementTypes, ElementType) + /// used for property map - const static QString Url, Encoding; + const static QString Url; + const static QString Encoding; + const static QString StringDelimiter; + const static QString QuoteComment; + const static QString KeywordCasing; + const static QString ProtectCasing; + const static QString NameFormatting; File(); + File(const File &other); virtual ~File(); /** - * Check if a given key,for example a key for a macro or an id for an entry + * Check if a given key (e.g. a key for a macro or an id for an entry) * is contained in the file object. * @see #allKeys() const - * @return @c the object addressed by the key @c NULL if no such file has been found + * @return @c the object addressed by the key @c, NULL if no such file has been found */ - const Element *containsKey(const QString &key) const; + const Element *containsKey(const QString &key, ElementTypes elementTypes = etAll) const; /** * Retrieves a list of all keys for example from macros or entries. * @see #const containsKey(const QString &) const * @return list of keys */ - QStringList allKeys() const; + QStringList allKeys(ElementTypes elementTypes = etAll) const; + + /** + * Retrieves a set of all unique values (as text) for a specified + * field from all entries + * @param fieldName field name to scan, e.g. "volume" + * @return list of unique values + */ + QSet uniqueEntryValuesSet(const QString &fieldName) const; + + /** + * Retrieves a list of all unique values (as text) for a specified + * field from all entries + * @param fieldName field name to scan, e.g. "volume" + * @return list of unique values + */ + QStringList uniqueEntryValuesList(const QString &fieldName) const; void setProperty(const QString &key, const QVariant &value); QVariant property(const QString &key) const; diff -Nru kbibtex-0.3/src/libkbibtexio/fileimporterbibtex.cpp kbibtex-0.4/src/libkbibtexio/fileimporterbibtex.cpp --- kbibtex-0.3/src/libkbibtexio/fileimporterbibtex.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileimporterbibtex.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include + #include #include #include @@ -42,10 +44,10 @@ #include "fileimporterbibtex.h" const QString extraAlphaNumChars = QString("?'`-_:.+/$\\\"&"); -const QRegExp htmlRegExp = QRegExp("]*>", Qt::CaseInsensitive); +const QRegExp htmlRegExp = QRegExp("]*>", Qt::CaseInsensitive); -FileImporterBibTeX::FileImporterBibTeX(const QString& encoding, bool ignoreComments, KBibTeX::Casing keywordCasing) - : FileImporter(), m_cancelFlag(false), m_lineNo(1), m_textStream(NULL), m_currentChar(' '), m_ignoreComments(ignoreComments), m_encoding(encoding.toLower()), m_newEncoding(QString::null), m_keywordCasing(keywordCasing) +FileImporterBibTeX::FileImporterBibTeX(bool ignoreComments, KBibTeX::Casing keywordCasing) + : FileImporter(), m_cancelFlag(false), m_lineNo(1), m_textStream(NULL), m_currentChar(' '), m_ignoreComments(ignoreComments), m_keywordCasing(keywordCasing) { // nothing } @@ -58,21 +60,19 @@ { m_cancelFlag = false; + File *result = new File(); + m_textStreamLastPos = 0; m_textStream = new QTextStream(iodevice); - m_newEncoding = m_encoding; - m_textStream->setCodec(m_encoding == "latex" ? "UTF-8" : m_encoding.toAscii()); + m_textStream->setCodec("us-ascii"); ///< unless we learn something else, assume 7-bit US-ASCII QString rawText = ""; while (!m_textStream->atEnd()) { QString line = m_textStream->readLine(); - bool skipline = evaluateParameterComments(m_textStream, line.toLower()); + bool skipline = evaluateParameterComments(m_textStream, line.toLower(), result); if (!skipline) rawText.append(line).append("\n"); } - File *result = new File(); - result->setProperty(m_newEncoding == "latex" ? "latex" : File::Encoding, m_textStream->codec()->name()); - delete m_textStream; /** Remove HTML code from the input source */ @@ -92,8 +92,7 @@ Element * element = nextElement(); if (element != NULL) { - Comment *comment = dynamic_cast(element); - if (!m_ignoreComments || comment == NULL) + if (!m_ignoreComments || typeid(*element) != typeid(Comment)) result->append(element); else delete element; @@ -114,7 +113,7 @@ bool FileImporterBibTeX::guessCanDecode(const QString & rawText) { - QString text = EncoderLaTeX::currentEncoderLaTeX() ->decode(rawText); + QString text = EncoderLaTeX::currentEncoderLaTeX()->decode(rawText); return text.indexOf(QRegExp("@\\w+\\{.+\\}")) >= 0; } @@ -207,7 +206,7 @@ Macro *macro = new Macro(key); do { bool isStringKey = false; - QString text = readString(isStringKey).replace(QRegExp("\\s+"), " "); + QString text = readString(isStringKey).simplified(); if (isStringKey) macro->value().append(new MacroKey(text)); else @@ -233,7 +232,7 @@ Preamble *preamble = new Preamble(); do { bool isStringKey = FALSE; - QString text = readString(isStringKey).replace(QRegExp("\\s+"), " "); + QString text = readString(isStringKey).simplified(); if (isStringKey) preamble->value().append(new MacroKey(text)); else @@ -293,10 +292,6 @@ return NULL; } - /// some sources such as ACM's Digital Library use "issue" instead of "number" -> fix that - if (keyName.toLower() == QLatin1String("issue")) - keyName = QLatin1String("number"); // FIXME use constants here or even better code? - Value value; /** check for duplicate fields */ @@ -463,7 +458,7 @@ return result; } -QString FileImporterBibTeX::readBracketString(const QChar openingBracket) +QString FileImporterBibTeX::readBracketString(const QChar openingBracket) ///< do not use reference on QChar here! { QString result; QChar closingBracket = '}'; @@ -497,16 +492,15 @@ do { bool isStringKey = false; - QString text = readString(isStringKey).trimmed(); + QString text = readString(isStringKey).simplified(); /// for all entries except for abstracts ... if (iKey != Entry::ftAbstract) { /// ... remove redundant spaces including newlines - text = text.replace(QRegExp("\\s+"), " "); + text = text.simplified(); } /// abstracts will keep their formatting (regarding line breaks) /// as requested by Thomas Jensch via mail (20 October 2010) - // FIXME: comparisons should be made case-insensitive if (iKey == Entry::ftAuthor || iKey == Entry::ftEditor) { if (isStringKey) value.append(new MacroKey(text)); @@ -536,7 +530,7 @@ value.append(new MacroKey(text)); else value.append(new PlainText(text)); - } else if (iKey.startsWith(Entry::ftUrl) || iKey.startsWith(Entry::ftLocalFile) || iKey.compare(QLatin1String("ee"), Qt::CaseInsensitive) == 0 || iKey.compare(QLatin1String("biburl"), Qt::CaseInsensitive) == 0) { + } else if ((iKey.startsWith(Entry::ftUrl) && !iKey.startsWith(Entry::ftUrlDate)) || iKey.startsWith(Entry::ftLocalFile) || iKey.compare(QLatin1String("ee"), Qt::CaseInsensitive) == 0 || iKey.compare(QLatin1String("biburl"), Qt::CaseInsensitive) == 0) { if (isStringKey) value.append(new MacroKey(text)); else { @@ -664,20 +658,20 @@ Person *FileImporterBibTeX::splitName(const QString& text) { QStringList segments; - bool containsComma = splitName(text, segments); + CommaContainment commaContainment = splitName(text, segments); QString firstName = ""; QString lastName = ""; if (segments.isEmpty()) return NULL; - if (!containsComma) { + if (commaContainment == ccNoComma) { /** PubMed uses a special writing style for names, where the last name is followed by single capital letter, * each being the first letter of each first name * So, check how many single capital letters are at the end of the given segment list */ int singleCapitalLettersCounter = 0; int p = segments.count() - 1; - while (segments[p].length() == 1 && segments[p].compare(segments[p].toUpper()) == 0) { + while (p >= 0 && segments[p].length() == 1 && segments[p][0].isUpper()) { --p; ++singleCapitalLettersCounter; } @@ -732,10 +726,10 @@ } /** Splits a name into single words. If the name's text was reversed (Last, First), the result will be true and the comma will be added to segments. Otherwise the functions result will be false. This function respects protecting {...}. */ -bool FileImporterBibTeX::splitName(const QString& text, QStringList& segments) +FileImporterBibTeX::CommaContainment FileImporterBibTeX::splitName(const QString& text, QStringList& segments) { int bracketCounter = 0; - bool result = FALSE; + CommaContainment result = ccNoComma; QString buffer = ""; for (int pos = 0; pos < text.length(); ++pos) { @@ -755,7 +749,7 @@ buffer = ""; } segments.append(","); - result = TRUE; + result = ccContainsComma; } else buffer.append(text[pos]); } @@ -766,14 +760,18 @@ return result; } -bool FileImporterBibTeX::evaluateParameterComments(QTextStream *textStream, const QString &line) +bool FileImporterBibTeX::evaluateParameterComments(QTextStream *textStream, const QString &line, File *file) { /** check if this file requests a special encoding */ if (line.startsWith("@comment{x-kbibtex-encoding=") && line.endsWith("}")) { - m_newEncoding = line.mid(28, line.length() - 29).toLower(); - kDebug() << "x-kbibtex-encoding=" << m_newEncoding; - textStream->setCodec(m_newEncoding == "latex" ? "UTF-8" : m_newEncoding.toAscii()); - kDebug() << "m_textStream->codec()=" << m_textStream->codec()->name(); + QString encoding = line.mid(28, line.length() - 29).toLower(); + textStream->setCodec(encoding == "latex" ? "UTF-8" : encoding.toAscii()); + encoding = textStream->codec()->name(); + file->setProperty(File::Encoding, encoding); + return true; + } else if (line.startsWith("@comment{x-kbibtex-personnameformatting=") && line.endsWith("}")) { + QString personNameFormatting = line.mid(40, line.length() - 41); + file->setProperty(File::NameFormatting, personNameFormatting); return true; } diff -Nru kbibtex-0.3/src/libkbibtexio/fileimporterbibtex.h kbibtex-0.4/src/libkbibtexio/fileimporterbibtex.h --- kbibtex-0.3/src/libkbibtexio/fileimporterbibtex.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileimporterbibtex.h 2011-11-20 20:36:55.000000000 +0000 @@ -54,7 +54,7 @@ * Useful if you for example read from an HTML file, * as all HTML content you be treated as comments otherwise. */ - FileImporterBibTeX(const QString& encoding = "latex", bool ignoreComments = true, KBibTeX::Casing keywordCasing = KBibTeX::cLowerCase); + FileImporterBibTeX(bool ignoreComments = true, KBibTeX::Casing keywordCasing = KBibTeX::cLowerCase); ~FileImporterBibTeX(); /** @@ -94,6 +94,7 @@ enum Token { tAt = 1, tBracketOpen = 2, tBracketClose = 3, tAlphaNumText = 4, tComma = 5, tAssign = 6, tDoublecross = 7, tEOF = 0xffff, tUnknown = -1 }; + enum CommaContainment { ccNoComma = 0, ccContainsComma = 1 }; bool m_cancelFlag; unsigned int m_lineNo; @@ -101,7 +102,6 @@ int m_textStreamLastPos; QChar m_currentChar; bool m_ignoreComments; - QString m_encoding, m_newEncoding; KBibTeX::Casing m_keywordCasing; Comment *readCommentElement(); @@ -112,18 +112,18 @@ Element *nextElement(); Token nextToken(); QString readString(bool &isStringKey); - QString readSimpleString(QChar until = '\0'); + QString readSimpleString(QChar until = QChar('\0')); QString readQuotedString(); QString readLine(); - QString readBracketString(const QChar openingBracket); + QString readBracketString(const QChar openingBracket); ///< do not use reference on QChar here! Token readValue(Value& value, const QString& fieldType); void unescapeLaTeXChars(QString &text); static void splitPersonList(const QString& name, QStringList &resultList); - static bool splitName(const QString& name, QStringList& segments); + static CommaContainment splitName(const QString& name, QStringList& segments); - bool evaluateParameterComments(QTextStream *textStream, const QString &line); + bool evaluateParameterComments(QTextStream *textStream, const QString &line, File *file); QString tokenidToString(Token token); }; diff -Nru kbibtex-0.3/src/libkbibtexio/fileimporterris.cpp kbibtex-0.4/src/libkbibtexio/fileimporterris.cpp --- kbibtex-0.3/src/libkbibtexio/fileimporterris.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/fileimporterris.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -72,9 +72,9 @@ } key = line.left(2); - value = line.mid(6).trimmed(); + value = line.mid(6).simplified(); } else { - line = line.trimmed(); + line = line.simplified(); if (line.length() > 1) { /// multi-line field are joined to one long line value += ' ' + line; diff -Nru kbibtex-0.3/src/libkbibtexio/iconvlatex.cpp kbibtex-0.4/src/libkbibtexio/iconvlatex.cpp --- kbibtex-0.3/src/libkbibtexio/iconvlatex.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/iconvlatex.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -57,7 +57,13 @@ QByteArray IConvLaTeX::encode(const QString &input) { QByteArray inputByteArray = input.toUtf8(); +#ifdef Q_WS_WIN + /// iconv on Windows likes to have it as const char * + const char *inputBuffer = inputByteArray.data(); +#else + /// iconv on Linux likes to have it as char * char *inputBuffer = inputByteArray.data(); +#endif QByteArray outputByteArray(maxBufferSize, '\0'); char *outputBuffer = outputByteArray.data(); size_t inputBufferBytesLeft = inputByteArray.size(); diff -Nru kbibtex-0.3/src/libkbibtexio/preamble.cpp kbibtex-0.4/src/libkbibtexio/preamble.cpp --- kbibtex-0.3/src/libkbibtexio/preamble.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/preamble.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -41,7 +41,14 @@ Preamble::Preamble(const Preamble& other) : Element(), d(new Preamble::PreamblePrivate) { - d->value = other.d->value; + operator=(other); +} + +Preamble& Preamble::operator= (const Preamble & other) +{ + if (this != &other) + d->value = other.d->value; + return *this; } Value& Preamble::value() diff -Nru kbibtex-0.3/src/libkbibtexio/preamble.h kbibtex-0.4/src/libkbibtexio/preamble.h --- kbibtex-0.3/src/libkbibtexio/preamble.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/preamble.h 2011-11-20 20:36:55.000000000 +0000 @@ -36,6 +36,12 @@ Preamble(const Value& value = Value()); Preamble(const Preamble& other); + /** + * Assignment operator, working similar to a copy constructor, + * but overwrites the current object's values. + */ + Preamble& operator= (const Preamble& other); + Value& value(); const Value& value() const; void setValue(const Value& value); diff -Nru kbibtex-0.3/src/libkbibtexio/value.cpp kbibtex-0.4/src/libkbibtexio/value.cpp --- kbibtex-0.3/src/libkbibtexio/value.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/value.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -21,10 +21,14 @@ #include #include +#include +#include #include #include "value.h" +const QRegExp ValueItem::ignoredInSorting = QRegExp("[{}\\\\]+"); + Keyword::Keyword(const Keyword& other) : m_text(other.m_text) { @@ -55,7 +59,8 @@ bool Keyword::containsPattern(const QString &pattern, Qt::CaseSensitivity caseSensitive) const { - return m_text.contains(pattern, caseSensitive); + const QString text = QString(m_text).replace(ignoredInSorting, ""); + return text.contains(pattern, caseSensitive); } bool Keyword::operator==(const ValueItem &other) const @@ -68,26 +73,18 @@ } -Person::Person(const QString& firstName, const QString& lastName, const QString& prefix, const QString& suffix) - : m_firstName(firstName), m_lastName(lastName), m_prefix(prefix), m_suffix(suffix) +Person::Person(const QString& firstName, const QString& lastName, const QString& suffix) + : m_firstName(firstName), m_lastName(lastName), m_suffix(suffix) { // nothing } Person::Person(const Person& other) - : m_firstName(other.firstName()), m_lastName(other.lastName()), m_prefix(other.prefix()), m_suffix(other.suffix()) + : m_firstName(other.firstName()), m_lastName(other.lastName()), m_suffix(other.suffix()) { // nothing } -void Person::setName(const QString& firstName, const QString& lastName, const QString& prefix, const QString& suffix) -{ - m_firstName = firstName; - m_lastName = lastName; - m_prefix = prefix; - m_suffix = suffix; -} - QString Person::firstName() const { return m_firstName; @@ -98,11 +95,6 @@ return m_lastName; } -QString Person::prefix() const -{ - return m_prefix; -} - QString Person::suffix() const { return m_suffix; @@ -114,15 +106,17 @@ m_firstName = after; if (m_lastName == before) m_lastName = after; - if (m_prefix == before) - m_prefix = after; if (m_suffix == before) m_suffix = after; } bool Person::containsPattern(const QString &pattern, Qt::CaseSensitivity caseSensitive) const { - return m_firstName.contains(pattern, caseSensitive) || m_lastName.contains(pattern, caseSensitive) || m_prefix.contains(pattern, caseSensitive) || m_suffix.contains(pattern, caseSensitive) || QString("%1 %2|%2, %1").arg(m_firstName).arg(m_lastName).contains(pattern, caseSensitive); + const QString firstName = QString(m_firstName).replace(ignoredInSorting, ""); + const QString lastName = QString(m_lastName).replace(ignoredInSorting, ""); + const QString suffix = QString(m_suffix).replace(ignoredInSorting, ""); + + return firstName.contains(pattern, caseSensitive) || lastName.contains(pattern, caseSensitive) || suffix.contains(pattern, caseSensitive) || QString("%1 %2|%2, %1").arg(firstName).arg(lastName).contains(pattern, caseSensitive); } bool Person::operator==(const ValueItem &other) const @@ -134,6 +128,41 @@ return false; } +QString Person::transcribePersonName(const Person *person, const QString &formatting) +{ + return transcribePersonName(formatting, person->firstName(), person->lastName(), person->suffix()); +} + +QString Person::transcribePersonName(const QString &formatting, const QString& firstName, const QString& lastName, const QString& suffix) +{ + QString result = formatting; + int p1 = -1, p2 = -1, p3 = -1; + while ((p1 = result.indexOf('<')) >= 0 && (p2 = result.indexOf('>', p1 + 1)) >= 0 && (p3 = result.indexOf('%', p1)) >= 0 && p3 < p2) { + QString insert; + switch (result[p3+1].toAscii()) { + case 'f': + insert = firstName; + break; + case 'l': + insert = lastName; + break; + case 's': + insert = suffix; + break; + } + + if (!insert.isEmpty()) + insert = result.mid(p1 + 1, p3 - p1 - 1) + insert + result.mid(p3 + 2, p2 - p3 - 2); + + result = result.left(p1) + insert + result.mid(p2 + 1); + } + return result; +} + +const QString Person::keyPersonNameFormatting = QLatin1String("personNameFormatting"); +const QString Person::defaultPersonNameFormatting = QLatin1String("<%l><, %f>"); // "<%f ><%l>" + + const QRegExp MacroKey::validMacroKey = QRegExp("^[a-z][-.:/+_a-z0-9]*$|^[0-9]+$", Qt::CaseInsensitive); MacroKey::MacroKey(const MacroKey& other) @@ -173,7 +202,8 @@ bool MacroKey::containsPattern(const QString &pattern, Qt::CaseSensitivity caseSensitive) const { - return m_text.contains(pattern, caseSensitive); + const QString text = QString(m_text).replace(ignoredInSorting, ""); + return text.contains(pattern, caseSensitive); } bool MacroKey::operator==(const ValueItem &other) const @@ -216,7 +246,8 @@ bool PlainText::containsPattern(const QString &pattern, Qt::CaseSensitivity caseSensitive) const { - return m_text.contains(pattern, caseSensitive); + const QString text = QString(m_text).replace(ignoredInSorting, ""); + return text.contains(pattern, caseSensitive); } bool PlainText::operator==(const ValueItem &other) const @@ -259,7 +290,8 @@ bool VerbatimText::containsPattern(const QString &pattern, Qt::CaseSensitivity caseSensitive) const { - return m_text.contains(pattern, caseSensitive); + const QString text = QString(m_text).replace(ignoredInSorting, ""); + return text.contains(pattern, caseSensitive); } bool VerbatimText::operator==(const ValueItem &other) const @@ -281,7 +313,24 @@ Value::Value(const Value& other) : QList() { - copyFrom(other); + clear(); + mergeFrom(other); +} + +Value::~Value() +{ + // FIXME: at some point elements have to be deleted + // maybe use QSharedPointer? + //while (!isEmpty()) { + // ValueItem *item = first(); + // removeFirst(); + // delete item; + //} +} + +void Value::merge(const Value& other) +{ + mergeFrom(other); } void Value::replace(const QString &before, const QString &after) @@ -309,13 +358,13 @@ Value& Value::operator=(const Value & rhs) { - copyFrom(rhs); + clear(); + mergeFrom(rhs); return *this; } -void Value::copyFrom(const Value& other) +void Value::mergeFrom(const Value& other) { - clear(); for (QList::ConstIterator it = other.begin(); it != other.end(); ++it) { PlainText *plainText = dynamic_cast(*it); if (plainText != NULL) @@ -346,6 +395,7 @@ } QRegExp PlainTextValue::removeCurlyBrackets = QRegExp("(^|[^\\\\])[{}]"); +QString PlainTextValue::personNameFormatting = QString::null; QString PlainTextValue::text(const Value& value, const File* file, bool debug) { @@ -393,7 +443,12 @@ } else { const Person *person = dynamic_cast(&valueItem); if (person != NULL) { - result = person->firstName() + " " + person->lastName(); + if (personNameFormatting.isNull()) { + KSharedConfigPtr config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))); + KConfigGroup configGroup(config, "General"); + personNameFormatting = configGroup.readEntry(Person::keyPersonNameFormatting, Person::defaultPersonNameFormatting); + } + result = Person::transcribePersonName(person, personNameFormatting); vit = VITPerson; if (debug) result = "[:" + result + ":Person]"; } else { @@ -413,9 +468,9 @@ } } - /// remove curly brackets - int i = -1; - while ((i = result.indexOf(removeCurlyBrackets, i + 1)) >= 0) { + /// remove curly brackets, except for those with a preceeding backslash + int i = 0; + while ((i = result.indexOf(removeCurlyBrackets, i)) >= 0) { result = result.replace(removeCurlyBrackets.cap(0), removeCurlyBrackets.cap(1)); } /// remove hyphenation commands diff -Nru kbibtex-0.3/src/libkbibtexio/value.h kbibtex-0.4/src/libkbibtexio/value.h --- kbibtex-0.3/src/libkbibtexio/value.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/libkbibtexio/value.h 2011-11-20 20:36:55.000000000 +0000 @@ -55,6 +55,11 @@ * @return TRUE if both instances are equal */ virtual bool operator==(const ValueItem &other) const = 0; + +protected: + /// contains text fragments to be removed before performing a "contains pattern" operation + /// includes among other "{" and "}" + static const QRegExp ignoredInSorting; }; class KBIBTEXIO_EXPORT Keyword: public ValueItem @@ -79,33 +84,35 @@ class KBIBTEXIO_EXPORT Person: public ValueItem { public: + static const QString keyPersonNameFormatting; + static const QString defaultPersonNameFormatting; + /** * Create a representation for a person's name. In bibliographies, a person is either an author or an editor. * The four parameters cover all common parts of a name. Only first and last name are mandatory (each person should have those). - * Example: A name like "Dr. Wernher von Braun" would be split as follows: "Wernher" is assigned to @p firstName, "von Braun" to @p lastName, and "Dr." to @p prefix. @param firstName First name of a person. Example: "Peter" @param lastName Last name of a person. Example: "Smith" - @param prefix Prefix in front of a name. Example: "Dr." @param suffix Suffix after a name. Example: "jr." */ - Person(const QString& firstName, const QString& lastName, const QString& prefix = QString::null, const QString& suffix = QString::null); + Person(const QString& firstName, const QString& lastName, const QString& suffix = QString::null); Person(const Person& other); - void setName(const QString& firstName, const QString& lastName, const QString& prefix = QString::null, const QString& suffix = QString::null); QString firstName() const; QString lastName() const; - QString prefix() const; QString suffix() const; void replace(const QString &before, const QString &after); bool containsPattern(const QString &pattern, Qt::CaseSensitivity caseSensitive = Qt::CaseInsensitive) const; bool operator==(const ValueItem &other) const; + static QString transcribePersonName(const QString &formatting, const QString& firstName, const QString& lastName, const QString& suffix = QString::null); + static QString transcribePersonName(const Person *person, const QString &formatting); + private: QString m_firstName; QString m_lastName; - QString m_prefix; QString m_suffix; + }; class KBIBTEXIO_EXPORT MacroKey: public ValueItem @@ -171,6 +178,9 @@ public: Value(); Value(const Value& other); + ~Value(); + + void merge(const Value& other); void replace(const QString &before, const QString &after); @@ -187,7 +197,7 @@ Value& operator=(const Value& rhs); private: - void copyFrom(const Value& other); + void mergeFrom(const Value& other); }; class KBIBTEXIO_EXPORT PlainTextValue @@ -199,6 +209,7 @@ private: enum ValueItemType { VITOther = 0, VITPerson, VITKeyword} lastItem; static QRegExp removeCurlyBrackets; + static QString personNameFormatting; static QString text(const ValueItem& valueItem, ValueItemType &vit, const File* file, bool debug); }; diff -Nru kbibtex-0.3/src/parts/CMakeLists.txt kbibtex-0.4/src/parts/CMakeLists.txt --- kbibtex-0.3/src/parts/CMakeLists.txt 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/parts/CMakeLists.txt 2011-11-20 20:36:55.000000000 +0000 @@ -5,6 +5,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/../gui/config ${CMAKE_CURRENT_SOURCE_DIR}/../gui/widgets ${CMAKE_CURRENT_SOURCE_DIR}/../gui/bibtex + ${CMAKE_CURRENT_SOURCE_DIR}/../processing/ ) # debug area for KBibTeX's IO library @@ -20,9 +21,10 @@ kde4_add_plugin( kbibtexpart ${kbibtexpart_SRCS}) target_link_libraries( kbibtexpart + ${KDE4_KPARTS_LIBS} kbibtexio kbibtexgui - ${KDE4_KPARTS_LIBS} + kbibtexproc ) install( TARGETS kbibtexpart DESTINATION ${PLUGIN_INSTALL_DIR}) diff -Nru kbibtex-0.3/src/parts/part.cpp kbibtex-0.4/src/parts/part.cpp --- kbibtex-0.3/src/parts/part.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/parts/part.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -18,16 +18,19 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include + #include #include +#include #include #include #include #include #include +#include #include -#include #include #include #include @@ -47,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +61,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -85,26 +93,32 @@ QMenu *viewDocumentMenu; QSignalMapper *signalMapperViewDocument; bool isSaveAsOperation; + LyX *lyx; + ColorLabelContextMenu *colorLabelContextMenu; KBibTeXPartPrivate(KBibTeXPart *parent) - : p(parent), sortFilterProxyModel(NULL), signalMapperNewElement(new QSignalMapper(parent)), viewDocumentMenu(new QMenu(i18n("View Document"), parent->widget())), signalMapperViewDocument(new QSignalMapper(parent)), isSaveAsOperation(false) { + : p(parent), model(NULL), sortFilterProxyModel(NULL), signalMapperNewElement(new QSignalMapper(parent)), viewDocumentMenu(new QMenu(i18n("View Document"), parent->widget())), signalMapperViewDocument(new QSignalMapper(parent)), isSaveAsOperation(false) { connect(signalMapperViewDocument, SIGNAL(mapped(QObject*)), p, SLOT(elementViewDocumentMenu(QObject*))); } + ~KBibTeXPartPrivate() { + delete model; + delete signalMapperNewElement; + delete viewDocumentMenu; + delete signalMapperViewDocument; + } + FileImporter *fileImporterFactory(const KUrl& url) { QString ending = url.path().toLower(); int p = ending.lastIndexOf("."); ending = ending.mid(p + 1); if (ending == "pdf") { - kDebug() << "Selecting FileImporterPDF" << endl; return new FileImporterPDF(); } else if (ending == "ris") { - kDebug() << "Selecting FileImporterRIS" << endl; return new FileImporterRIS(); } else { - kDebug() << "Selecting FileImporterBibTeX" << endl; - return new FileImporterBibTeX("latex", false); + return new FileImporterBibTeX(false); } } @@ -114,25 +128,20 @@ ending = ending.mid(p + 1); if (ending == "html") { - kDebug() << "Selecting FileExporterXSLT" << endl; return new FileExporterXSLT(); } else if (ending == "xml") { - kDebug() << "Selecting FileExporterXML" << endl; return new FileExporterXML(); } else if (ending == "ris") { - kDebug() << "Selecting FileExporterRIS" << endl; return new FileExporterRIS(); } else if (ending == "pdf") { - kDebug() << "Selecting FileExporterPDF" << endl; return new FileExporterPDF(); + } else if (ending == "ps") { + return new FileExporterPS(); } else if (ending == "rtf") { - kDebug() << "Selecting FileExporterRTF" << endl; return new FileExporterRTF(); } else if (ending == "html" || ending == "html") { - kDebug() << "Selecting FileExporterBibTeX2HTML" << endl; return new FileExporterBibTeX2HTML(); } else { - kDebug() << "Selecting FileExporterBibTeX" << endl; return new FileExporterBibTeX(); } } @@ -158,7 +167,6 @@ sortFilterProxyModel->setSourceModel(model); editor->setModel(sortFilterProxyModel); connect(filterBar, SIGNAL(filterChanged(SortFilterBibTeXFileModel::FilterQuery)), sortFilterProxyModel, SLOT(updateFilter(SortFilterBibTeXFileModel::FilterQuery))); - //connect(editor->valueListWidget(), SIGNAL(filterChanged(SortFilterBibTeXFileModel::FilterQuery)), filterBar, SLOT(setFilter(SortFilterBibTeXFileModel::FilterQuery))); //FIXME } void makeBackup(const KUrl &url) const { @@ -197,16 +205,17 @@ } KUrl getSaveFilename(bool mustBeImportable = true) { - QString startDir = QString();// QLatin1String(":save"); // FIXME: Does not work yet + QString startDir = p->url().isValid() ? p->url().path() : QLatin1String("kfiledialog:///opensave"); QString supportedMimeTypes = QLatin1String("text/x-bibtex application/xml application/x-research-info-systems"); if (!mustBeImportable && FileExporterToolchain::kpsewhich(QLatin1String("embedfile.sty"))) supportedMimeTypes += QLatin1String(" application/pdf"); + if (!mustBeImportable && FileExporterToolchain::which(QLatin1String("dvips"))) + supportedMimeTypes += QLatin1String(" application/postscript"); if (!mustBeImportable) supportedMimeTypes += QLatin1String(" text/html"); if (!mustBeImportable && FileExporterToolchain::which(QLatin1String("latex2rtf"))) supportedMimeTypes += QLatin1String(" application/rtf"); - return KFileDialog::getSaveUrl(startDir, supportedMimeTypes, p->widget()); } @@ -226,14 +235,20 @@ SortFilterBibTeXFileModel *model = dynamic_cast(editor->model()); Q_ASSERT(model != NULL); FileExporter *exporter = fileExporterFactory(url); - FileExporterBibTeX *exporterBibTeX = dynamic_cast(exporter); if (isSaveAsOperation) { /// only show export dialog at SaveAs or SaveCopyAs operations - exporter->showExportDialog(p->widget(), model->bibTeXSourceModel()->bibTeXFile()); + + if (typeid(*exporter) == typeid(FileExporterBibTeX)) { + KDialog dlg(p->widget()); + File *file = model->bibTeXSourceModel()->bibTeXFile(); + SettingsFileExporterBibTeXWidget settingsWidget(file, &dlg); + dlg.setMainWidget(&settingsWidget); + dlg.setButtons(KDialog::Ok); + dlg.exec(); + settingsWidget.saveProperties(file); + } } - if (exporterBibTeX != NULL) - exporterBibTeX->setEncoding(model->bibTeXSourceModel()->bibTeXFile()->property(File::Encoding).toString()); qApp->setOverrideCursor(Qt::WaitCursor); @@ -258,14 +273,6 @@ qApp->restoreOverrideCursor(); if (!result) KMessageBox::error(p->widget(), i18n("Saving the bibliography to file \"%1\" failed.", url.pathOrUrl()), i18n("Saving bibliography failed")); - else { - File *file = model->bibTeXSourceModel()->bibTeXFile(); - /// store new URL in BibTeX File object - file->setProperty(File::Url, url); - /// store encoding in BibTeX File object - if (exporterBibTeX != NULL) - file->setProperty(File::Encoding, exporterBibTeX->encoding()); - } delete exporter; return result; @@ -279,22 +286,22 @@ if (!info.exists()) return true; - return KMessageBox::Cancel != KMessageBox::warningContinueCancel(parent, i18n("A file named \"%1\" already exists. Are you sure you want to overwrite it?", info.fileName()), - i18n("Overwrite File?"), KStandardGuiItem::overwrite(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous); + return KMessageBox::Cancel != KMessageBox::warningContinueCancel(parent, i18n("A file named \"%1\" already exists. Are you sure you want to overwrite it?", info.fileName()), i18n("Overwrite File?"), KStandardGuiItem::overwrite(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous); } int updateViewDocumentMenu() { viewDocumentMenu->clear(); int result = 0; - Entry *entry = dynamic_cast(editor->currentElement()); + const Entry *entry = dynamic_cast(editor->currentElement()); if (entry != NULL) { QList urlList = FileInfo::entryUrls(entry, editor->bibTeXModel()->bibTeXFile()->property(File::Url).value()); if (!urlList.isEmpty()) { for (QList::ConstIterator it = urlList.constBegin(); it != urlList.constEnd(); ++it) { // FIXME: the signal mapper will fill up with mappings, as they are never removed - KAction *action = new KAction((*it).pathOrUrl(), p); // TODO beautify (icon, url) + KAction *action = new KAction(KIcon(KMimeType::iconNameForUrl(*it)), (*it).pathOrUrl(), p); action->setData((*it).pathOrUrl()); + action->setToolTip((*it).prettyUrl()); connect(action, SIGNAL(triggered()), signalMapperViewDocument, SLOT(map())); signalMapperViewDocument->setMapping(action, action); viewDocumentMenu->addAction(action); @@ -314,7 +321,7 @@ setObjectName("KBibTeXPart::KBibTeXPart"); // TODO Setup view - d->editor = new BibTeXEditor(parentWidget); + d->editor = new BibTeXEditor(QLatin1String("Main"), parentWidget); d->editor->setReadOnly(!isReadWrite()); setWidget(d->editor); @@ -335,7 +342,7 @@ KBibTeXPart::~KBibTeXPart() { - // nothing + delete d; } void KBibTeXPart::setModified(bool modified) @@ -355,6 +362,7 @@ connect(saveCopyAsAction, SIGNAL(triggered()), this, SLOT(documentSaveCopyAs())); d->filterBar = new FilterBar(0); + d->editor->setFilterBar(d->filterBar); KAction *filterWidgetAction = new KAction(i18n("Filter"), this); actionCollection()->addAction("toolbar_filter_widget", filterWidgetAction); filterWidgetAction->setShortcut(Qt::CTRL + Qt::Key_F); @@ -406,26 +414,36 @@ actionCollection()->addAction(QLatin1String("edit_copy_references"), d->editCopyReferencesAction); d->editPasteAction = actionCollection()->addAction(KStandardAction::Paste, clipboard, SLOT(paste())); + /// build context menu for central BibTeX file view d->editor->setContextMenuPolicy(Qt::ActionsContextMenu); - d->editor->insertAction(NULL, d->elementEditAction); - d->editor->insertAction(NULL, d->elementViewDocumentAction); + d->editor->addAction(d->elementEditAction); + d->editor->addAction(d->elementViewDocumentAction); QAction *separator = new QAction(this); separator->setSeparator(true); - d->editor->insertAction(NULL, separator); - d->editor->insertAction(NULL, d->editCutAction); - d->editor->insertAction(NULL, d->editCopyAction); - d->editor->insertAction(NULL, d->editCopyReferencesAction); - d->editor->insertAction(NULL, d->editPasteAction); - d->editor->insertAction(NULL, d->editDeleteAction); + d->editor->addAction(separator); + d->editor->addAction(d->editCutAction); + d->editor->addAction(d->editCopyAction); + d->editor->addAction(d->editCopyReferencesAction); + d->editor->addAction(d->editPasteAction); + d->editor->addAction(d->editDeleteAction); + separator = new QAction(this); + separator->setSeparator(true); + d->editor->addAction(separator); // TODO connect(d->editor, SIGNAL(selectedElementsChanged()), this, SLOT(updateActions())); - updateActions(); - - fitActionSettings(); setXMLFile(RCFileName); + + new FindDuplicatesUI(this, d->editor); + d->lyx = new LyX(this, d->editor); + connect(d->editor, SIGNAL(selectedElementsChanged()), d->lyx, SLOT(updateActions())); + + d->colorLabelContextMenu = new ColorLabelContextMenu(d->editor); + + updateActions(); + fitActionSettings(); } bool KBibTeXPart::saveFile() @@ -484,7 +502,7 @@ if (!d->viewDocumentMenu->actions().isEmpty()) { // TODO: Choose "best" URL instead of first (local file over remote, PDF over website, ...) QAction *action = d->viewDocumentMenu->actions().first(); - QDesktopServices::openUrl(KUrl(action->text())); // TODO KDE way? + QDesktopServices::openUrl(KUrl(action->data().toString())); // TODO KDE way? } } @@ -501,12 +519,9 @@ bool KBibTeXPart::openFile() { - kDebug() << "Opening file: " << url(); - setObjectName("KBibTeXPart::KBibTeXPart for " + url().pathOrUrl()); FileImporter *importer = d->fileImporterFactory(url()); - importer->showImportDialog(widget()); qApp->setOverrideCursor(Qt::WaitCursor); @@ -518,13 +533,12 @@ delete importer; if (bibtexFile == NULL) { - kWarning() << "Opening file failed: " << url(); + kWarning() << "Opening file failed:" << url().pathOrUrl(); qApp->restoreOverrideCursor(); return false; - } else - kDebug() << "File contains " << bibtexFile->count() << " entries"; + } - bibtexFile->setProperty(File::Url, url()); + bibtexFile->setProperty(File::Url, QUrl(url())); d->model->setBibTeXFile(bibtexFile); d->editor->setModel(d->model); @@ -600,12 +614,27 @@ d->elementEditAction->setEnabled(!emptySelection); d->editCopyAction->setEnabled(!emptySelection); d->editCopyReferencesAction->setEnabled(!emptySelection); - d->editCutAction->setEnabled(!emptySelection); - d->editDeleteAction->setEnabled(!emptySelection); + d->editCutAction->setEnabled(!emptySelection && isReadWrite()); + d->editDeleteAction->setEnabled(!emptySelection && isReadWrite()); int numDocumentsToView = d->updateViewDocumentMenu(); /// enable menu item only if there is at least one document to view d->elementViewDocumentAction->setEnabled(!emptySelection && numDocumentsToView > 0); /// activate sub-menu only if there are at least two documents to view d->elementViewDocumentAction->setMenu(numDocumentsToView > 1 ? d->viewDocumentMenu : NULL); + d->elementViewDocumentAction->setToolTip(numDocumentsToView == 1 ? d->viewDocumentMenu->actions().first()->text() : QLatin1String("")); + + /// update list of references which can be sent to LyX + QStringList references; + if (d->editor->selectionModel() != NULL) { + QModelIndexList mil = d->editor->selectionModel()->selectedRows(); + for (QModelIndexList::ConstIterator it = mil.constBegin(); it != mil.constEnd(); ++it) { + Entry *entry = dynamic_cast(d->editor->bibTeXModel()->element(d->editor->sortFilterProxyModel()->mapToSource(*it).row())); + if (entry != NULL) + references << entry->id(); + } + } + d->lyx->setReferences(references); + + d->colorLabelContextMenu->setEnabled(isReadWrite()); } diff -Nru kbibtex-0.3/src/parts/partfactory.cpp kbibtex-0.4/src/parts/partfactory.cpp --- kbibtex-0.3/src/parts/partfactory.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/parts/partfactory.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -24,13 +24,14 @@ #include "part.h" #include "partfactory.h" +#include "version.h" static const char PartId[] = "kbibtexpart"; static const char PartName[] = I18N_NOOP("KBibTeXPart"); static const char PartDescription[] = I18N_NOOP("BibTeX Editor Component"); -static const char PartVersion[] = "0.3.50"; -static const char PartCopyright[] = "2004-2010 Thomas Fischer"; -static const char PartContactEMail[] = "fischer@unix-ag.uni-kl.de"; // FIXME Use some GNA mailinglist address +static const char PartCopyright[] = "Copyright 2004-2011 Thomas Fischer"; +static const char *programHomepage = I18N_NOOP("http://home.gna.org/kbibtex/"); +static const char *bugTrackerHomepage = "https://gna.org/bugs/?group=kbibtex"; static KComponentData *_componentData = 0; static KAboutData* _aboutData = 0; @@ -66,8 +67,12 @@ const KComponentData &KBibTeXPartFactory::componentData() { if (!_componentData) { - _aboutData = new KAboutData(PartId, 0, ki18n(PartName), PartVersion, ki18n(PartDescription), KAboutData::License_GPL_V2, ki18n(PartCopyright), KLocalizedString(), 0, PartContactEMail); - _aboutData->addAuthor(ki18n("Thomas Fischer"), ki18n("Author"), "fischer@unix-ag.uni-kl.de", "http://www.t-fischer.net/"); + _aboutData = new KAboutData(PartId, 0, ki18n(PartName), "0.4", + ki18n(PartDescription), KAboutData::License_GPL_V2, + ki18n(PartCopyright), KLocalizedString(), + programHomepage, bugTrackerHomepage); + _aboutData->addAuthor(ki18n("Thomas Fischer"), ki18n("Maintainer"), "fischer@unix-ag.uni-kl.de", "http://www.t-fischer.net/"); + _aboutData->setCustomAuthorText(ki18n("Please use https://gna.org/bugs/?group=kbibtex to report bugs.\n"), ki18n("Please use https://gna.org/bugs/?group=kbibtex to report bugs.\n")); _componentData = new KComponentData(_aboutData); } return *_componentData; diff -Nru kbibtex-0.3/src/preferences.h kbibtex-0.4/src/preferences.h --- kbibtex-0.3/src/preferences.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/preferences.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,38 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PREFERENCES_H +#define KBIBTEX_PREFERENCES_H + +#include +#include + +#include + +namespace Preferences +{ +static const QString groupColor = QLatin1String("Color Labels"); +static const QString keyColorCodes = QLatin1String("colorCodes"); +static const QStringList defaultColorCodes = QStringList() << QLatin1String("#cc3300") << QLatin1String("#0033ff") << QLatin1String("#009966"); +static const QString keyColorLabels = QLatin1String("colorLabels"); +static const QStringList defaultcolorLabels = QStringList() << I18N_NOOP("Important") << I18N_NOOP("Unread") << I18N_NOOP("Read"); +} + +#endif // KBIBTEX_PREFERENCES_H diff -Nru kbibtex-0.3/src/processing/CMakeLists.txt kbibtex-0.4/src/processing/CMakeLists.txt --- kbibtex-0.3/src/processing/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/processing/CMakeLists.txt 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,28 @@ +# Processing library + +set( processing_LIB_SRCS + findduplicates.cpp + lyx.cpp +) + +add_definitions( -DMAKE_PROCESSING_LIB ) + +# debug area for KBibTeX's processing library +add_definitions(-DKDE_DEFAULT_DEBUG_AREA=101016) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../libkbibtexio +) + +kde4_add_library( kbibtexproc SHARED ${processing_LIB_SRCS} ) + +target_link_libraries( kbibtexproc + ${QT_QTCORE_LIBRARY} + ${KDE4_KDECORE_LIBS} + ${KDE4_KDEUI_LIBS} + kbibtexio +) + +install( TARGETS kbibtexproc RUNTIME DESTINATION bin LIBRARY DESTINATION ${LIB_INSTALL_DIR} ) + +install( FILES lyx.rc DESTINATION ${DATA_INSTALL_DIR}/kbibtex ) diff -Nru kbibtex-0.3/src/processing/findduplicates.cpp kbibtex-0.4/src/processing/findduplicates.cpp --- kbibtex-0.3/src/processing/findduplicates.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/processing/findduplicates.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,514 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include + +#include +#include +#include +#include + +#include "file.h" +#include "entry.h" +#include "findduplicates.h" + +#define getText(entry, fieldname) PlainTextValue::text((entry)->value((fieldname))) + +EntryClique::EntryClique() +{ + // nothing +} + +int EntryClique::entryCount() const +{ + return checkedEntries.count(); +} + +QList EntryClique::entryList() const +{ + return checkedEntries.keys(); +} + +bool EntryClique::isEntryChecked(Entry *entry) const +{ + return checkedEntries[entry]; +} + +void EntryClique::setEntryChecked(Entry *entry, bool isChecked) +{ + checkedEntries[entry] = isChecked; + recalculateValueMap(); +} + +int EntryClique::fieldCount() const +{ + return valueMap.count(); +} + +QList EntryClique::fieldList() const +{ + return valueMap.keys(); +} + +QList EntryClique::values(const QString &field) const +{ + return valueMap[field]; +} + +Value EntryClique::chosenValue(const QString &field) const +{ + Q_ASSERT(chosenValueMap[field].count() == 1); + return chosenValueMap[field].first(); +} + +QList EntryClique::chosenValues(const QString &field) const +{ + return chosenValueMap[field]; +} + +void EntryClique::setChosenValue(const QString &field, Value &value, ValueOperation valueOperation) +{ + switch (valueOperation) { + case SetValue: { + chosenValueMap[field].clear(); + chosenValueMap[field] << value; + break; + } + case AddValue: { + QString text = PlainTextValue::text(value); + foreach(Value value, chosenValueMap[field]) + if (PlainTextValue::text(value) == text) + return; + chosenValueMap[field] << value; + break; + } + case RemoveValue: { + QString text = PlainTextValue::text(value); + for (QList::Iterator it = chosenValueMap[field].begin(); it != chosenValueMap[field].end(); ++it) + if (PlainTextValue::text(*it) == text) { + chosenValueMap[field].erase(it); + return; + } + break; + } + } +} + +void EntryClique::addEntry(Entry* entry) +{ + checkedEntries.insert(entry, false); /// remember to call recalculateValueMap later +} + +void EntryClique::recalculateValueMap() +{ + valueMap.clear(); + chosenValueMap.clear(); + + /// go through each and every entry ... + const QList el = entryList(); + foreach(Entry *entry, el) + if (isEntryChecked(entry)) { + + /// cover entry type + Value v; + v.append(new VerbatimText(entry->type())); + insertKeyValueToValueMap(QLatin1String("^type"), v, entry->type()); + + /// cover entry id + v.clear(); + v.append(new VerbatimText(entry->id())); + insertKeyValueToValueMap(QLatin1String("^id"), v, entry->id()); + + /// go through each and every field of this entry + for (Entry::ConstIterator fieldIt = entry->constBegin(); fieldIt != entry->constEnd(); ++fieldIt) { + /// store both field name and value for later reference + const QString fieldName = fieldIt.key().toLower(); + const Value fieldValue = fieldIt.value(); + + if (fieldName == Entry::ftKeywords || fieldName == Entry::ftUrl) { + foreach(ValueItem* vi, fieldValue) { + const QString text = PlainTextValue::text(*vi); + Value v; + v << vi; + insertKeyValueToValueMap(fieldName, v, text); + } + } else { + const QString fieldValueText = PlainTextValue::text(fieldValue); + insertKeyValueToValueMap(fieldName, fieldValue, fieldValueText); + } + } + } + + QList fl = fieldList(); + foreach(QString fieldName, fl) + if (valueMap[fieldName].count() < 2) { + valueMap.remove(fieldName); + chosenValueMap.remove(fieldName); + } +} + +void EntryClique::insertKeyValueToValueMap(const QString &fieldName, const Value &fieldValue, const QString &fieldValueText) +{ + if (fieldValueText.isEmpty()) return; + + if (valueMap.contains(fieldName)) { + /// in the list of alternatives, search of a value identical + /// to the current (as of fieldIt) value (to avoid duplicates) + + bool alreadyContained = false; + QList alternatives = valueMap[fieldName]; + foreach(Value v, alternatives) + if (PlainTextValue::text(v) == fieldValueText) { + alreadyContained = true; + break; + } + + if (!alreadyContained) { + alternatives << fieldValue; + valueMap[fieldName] = alternatives; + } + } else { + QList alternatives = valueMap[fieldName]; + alternatives << fieldValue; + valueMap.insert(fieldName, alternatives); + QList chosen; + chosen << fieldValue; + chosenValueMap.insert(fieldName, chosen); + } +} + +class FindDuplicates::FindDuplicatesPrivate +{ +private: + FindDuplicates *p; + const int maxDistance; + int **d; + static const int dsize; + +public: + bool gotCanceled; + int sensitivity; + QWidget *widget; + + FindDuplicatesPrivate(FindDuplicates *parent, int sens, QWidget *w) + : p(parent), maxDistance(10000), gotCanceled(false), sensitivity(sens), widget(w) { + d = new int*[dsize]; + for (int i = 0; i < dsize; ++i) + d[i] = new int[dsize]; + } + + ~FindDuplicatesPrivate() { + for (int i = 0; i < dsize; ++i) delete[] d[i]; + delete [] d; + } + + /** + * Determine the Levenshtein distance between two words. + * See also http://en.wikipedia.org/wiki/Levenshtein_distance + * @param s first word, all chars already in lower case + * @param t second word, all chars already in lower case + * @return distance between both words + */ + double levenshteinDistanceWord(const QString &s, const QString &t) { + int m = qMin(s.length(), dsize - 1), n = qMin(t.length(), dsize - 1); + if (m < 1 && n < 1) return 0.0; + if (m < 1 || n < 1) return 1.0; + + for (int i = 0; i <= m; ++i) + d[i][0] = i; + + for (int i = 0; i <= n; ++i) d[0][i] = i; + + for (int i = 1; i <= m;++i) + for (int j = 1; j <= n;++j) { + d[i][j] = d[i-1][j] + 1; + int c = d[i][j-1] + 1; + if (c < d[i][j]) d[i][j] = c; + c = d[i-1][j-1] + (s[i-1] == t[j-1] ? 0 : 1); + if (c < d[i][j]) d[i][j] = c; + } + + double result = d[m][n]; + + result = result / (double)qMax(m, n); + result *= result; + return result; + } + + /** + * Determine the Levenshtein distance between two sentences (list of words). + * See also http://en.wikipedia.org/wiki/Levenshtein_distance + * @param s first sentence + * @param t second sentence + * @return distance between both sentences + */ + double levenshteinDistance(const QStringList &s, const QStringList &t) { + int m = s.size(), n = t.size(); + if (m < 1 && n < 1) return 0.0; + if (m < 1 || n < 1) return 1.0; + + double **d = new double*[m+1]; + for (int i = 0; i <= m; ++i) { + d[i] = new double[n+1]; d[i][0] = i; + } + for (int i = 0; i <= n; ++i) d[0][i] = i; + + for (int i = 1; i <= m;++i) + for (int j = 1; j <= n;++j) { + d[i][j] = d[i-1][j] + 1; + double c = d[i][j-1] + 1; + if (c < d[i][j]) d[i][j] = c; + c = d[i-1][j-1] + levenshteinDistanceWord(s[i-1], t[j-1]); + if (c < d[i][j]) d[i][j] = c; + } + + double result = d[m][n]; + for (int i = 0; i <= m; ++i) delete[] d[i]; + delete [] d; + + result = result / (double)qMax(m, n); + + return result; + } + + + /** + * Determine the Levenshtein distance between two sentences, + * where each sentence is in a string (not split into single words). + * See also http://en.wikipedia.org/wiki/Levenshtein_distance + * @param s first sentence + * @param t second sentence + * @return distance between both sentences + */ + double levenshteinDistance(const QString &s, const QString &t) { + const QRegExp nonWordRegExp("[^a-z']+", Qt::CaseInsensitive); + if (s.isEmpty() || t.isEmpty()) return 1.0; + return levenshteinDistance(s.toLower().split(nonWordRegExp, QString::SkipEmptyParts), t.toLower().split(nonWordRegExp, QString::SkipEmptyParts)); + } + + /** + * Distance between two BibTeX entries, scaled by maxDistance. + */ + int entryDistance(Entry *entryA, Entry *entryB) { + double titleValue = levenshteinDistance(getText(entryA, Entry::ftTitle), getText(entryB, Entry::ftTitle)); + double authorValue = levenshteinDistance(getText(entryA, Entry::ftAuthor), getText(entryB, Entry::ftAuthor)); + bool ok1 = false, ok2 = false; + double yearValue = QString(getText(entryA, Entry::ftYear)).toInt(&ok1) - QString(getText(entryB, Entry::ftYear)).toInt(&ok2); + yearValue = ok1 && ok2 ? qMin(1.0, yearValue * yearValue / 100.0) : 100.0; + int distance = (unsigned int)(maxDistance * (titleValue * 0.6 + authorValue * 0.3 + yearValue * 0.1)); + + return distance; + } + +}; + +const int FindDuplicates::FindDuplicatesPrivate::dsize = 32; + + +FindDuplicates::FindDuplicates(QWidget *parent, int sensitivity) + : d(new FindDuplicatesPrivate(this, sensitivity, parent)) +{ + // nothing +} + +FindDuplicates::~FindDuplicates() +{ + delete d; +} + +bool FindDuplicates::findDuplicateEntries(File *file, QList &entryCliqueList) +{ + KApplication::setOverrideCursor(Qt::WaitCursor); + KProgressDialog *progressDlg = new KProgressDialog(d->widget, i18n("Finding Duplicates")); + progressDlg->setModal(true); + progressDlg->setLabelText(i18n("Searching ...")); + progressDlg->setMinimumWidth(d->widget->fontMetrics().averageCharWidth()*48); + progressDlg->setAllowCancel(true); + connect(progressDlg, SIGNAL(cancelClicked()), this, SLOT(gotCanceled())); + + entryCliqueList.clear(); + d->gotCanceled = false; + + /// assemble list of entries only (ignoring comments, macros, ...) + QList listOfEntries; + for (File::ConstIterator it = file->constBegin(); it != file->constEnd(); ++it) { + Entry *e = dynamic_cast(*it); + if (e != NULL && !e->isEmpty()) + listOfEntries << e; + } + + if (listOfEntries.isEmpty()) { + /// no entries to compare found + entryCliqueList.clear(); + progressDlg->deleteLater(); + KApplication::restoreOverrideCursor(); + return d->gotCanceled; + } + + int curProgress = 0, maxProgress = listOfEntries.count() * (listOfEntries.count() - 1) / 2; + int progressDelta = 1; + + progressDlg->progressBar()->setMaximum(maxProgress); + progressDlg->show(); + progressDlg->setLabelText(i18n("Searching ...")); + + /// go through all entries ... + for (QList::ConstIterator eit = listOfEntries.constBegin(); eit != listOfEntries.constEnd(); ++eit) { + KApplication::instance()->processEvents(); + if (d->gotCanceled) { + entryCliqueList.clear(); + break; + } + + progressDlg->progressBar()->setValue(curProgress); + /// ... and find a "clique" of entries where it will match, i.e. distance is below sensitivity + + /// assume current entry will match in no clique + bool foundClique = false; + + /// go through all existing cliques + for (QList::Iterator cit = entryCliqueList.begin(); cit != entryCliqueList.end(); ++cit) { + /// check distance between current entry and clique's first entry + if (d->entryDistance(*eit, (*cit)->entryList().first()) < d->sensitivity) { + /// if distance is below sensitivity, add current entry to clique + foundClique = true; + (*cit)->addEntry(*eit); + break; + } + + KApplication::instance()->processEvents(); + if (d->gotCanceled) { + entryCliqueList.clear(); + break; + } + } + + if (!d->gotCanceled && !foundClique) { + /// no clique matched to current entry, so create and add new clique + /// consisting only of the current entry + EntryClique *newClique = new EntryClique(); + newClique->addEntry(*eit); + entryCliqueList << newClique; + } + + curProgress += progressDelta; + ++progressDelta; + progressDlg->progressBar()->setValue(curProgress); + } + + progressDlg->progressBar()->setValue(progressDlg->progressBar()->maximum()); + progressDlg->close(); + + /// remove cliques with only one element (nothing to merge here) from the list of cliques + for (QList::Iterator cit = entryCliqueList.begin(); cit != entryCliqueList.end();) + if ((*cit)->entryCount() < 2) { + EntryClique *ec = *cit; + cit = entryCliqueList.erase(cit); + delete ec; + } else { + /// entries have been inserted as checked, + /// therefore recalculate alternatives + (*cit)->recalculateValueMap(); + + ++cit; + } + + progressDlg->deleteLater(); + KApplication::restoreOverrideCursor(); + return d->gotCanceled; +} + +void FindDuplicates::gotCanceled() +{ + d->gotCanceled = true; +} + + +class MergeDuplicates::MergeDuplicatesPrivate +{ +private: + MergeDuplicates *p; + +public: + QWidget *widget; + + MergeDuplicatesPrivate(MergeDuplicates *parent, QWidget *w) + : p(parent), widget(w) { + // nothing + } +}; + +MergeDuplicates::MergeDuplicates(QWidget *parent) + : d(new MergeDuplicatesPrivate(this, parent)) +{ + // TODO +} + +bool MergeDuplicates::mergeDuplicateEntries(const QList &entryCliques, File *file) +{ + bool didMerge = false; + + foreach(EntryClique *entryClique, entryCliques) { + Entry *mergedEntry = new Entry(QString::null, QString::null); + foreach(QString field, entryClique->fieldList()) { + if (field == QLatin1String("^id")) + mergedEntry->setId(PlainTextValue::text(entryClique->chosenValue(field))); + else if (field == QLatin1String("^type")) + mergedEntry->setType(PlainTextValue::text(entryClique->chosenValue(field))); + else { + Value combined; + foreach(Value v, entryClique->chosenValues(field)) { + combined.merge(v); + } + if (!combined.isEmpty()) + mergedEntry->insert(field, combined); + } + } + + bool actuallyMerged = false; + foreach(Entry *entry, entryClique->entryList()) { + /// if merging entries with identical ids, the merged entry will not yet have an id (is null) + if (mergedEntry->id().isEmpty()) + mergedEntry->setId(entry->id()); + /// if merging entries with identical types, the merged entry will not yet have an type (is null) + if (mergedEntry->type().isEmpty()) + mergedEntry->setType(entry->type()); + + /// add all other fields not covered by user selection + /// those fields did only occur in one entry (no conflict) + /// may add a lot of bloat to merged entry + if (entryClique->isEntryChecked(entry)) { + actuallyMerged = true; + for (Entry::ConstIterator it = entry->constBegin(); it != entry->constEnd(); ++it) + if (!mergedEntry->contains(it.key())) + mergedEntry->insert(it.key(), it.value()); + file->removeOne(entry); + } + } + + if (actuallyMerged) + file->append(mergedEntry); + else + delete mergedEntry; + didMerge |= actuallyMerged; + } + + return didMerge; +} diff -Nru kbibtex-0.3/src/processing/findduplicates.h kbibtex-0.4/src/processing/findduplicates.h --- kbibtex-0.3/src/processing/findduplicates.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/processing/findduplicates.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,109 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PROC_FINDDUPLICATES_H +#define KBIBTEX_PROC_FINDDUPLICATES_H + +#include "kbibtexproc_export.h" + +#include +#include + +#include + +class Entry; +class File; + +class KBIBTEXPROC_EXPORT FindDuplicates; + +/** + * @author Thomas Fischer + */ +class KBIBTEXPROC_EXPORT EntryClique +{ + friend class FindDuplicates; +public: + EntryClique(); + + enum ValueOperation { SetValue, AddValue, RemoveValue }; + + int entryCount() const; + QList entryList() const; + bool isEntryChecked(Entry *entry) const; + void setEntryChecked(Entry *entry, bool isChecked); + + int fieldCount() const; + QList fieldList() const; + QList values(const QString &field) const; + Value chosenValue(const QString &field) const; + QList chosenValues(const QString &field) const; + void setChosenValue(const QString &field, Value &value, ValueOperation valueOperation = SetValue); + + QString dump() const; + +protected: + void addEntry(Entry* entry); + +private: + QMap checkedEntries; + QMap > valueMap; + QMap > chosenValueMap; + + void recalculateValueMap(); + void insertKeyValueToValueMap(const QString &fieldName, const Value &fieldValue, const QString &fieldValueText); +}; + +/** + * @author Thomas Fischer + */ +class KBIBTEXPROC_EXPORT FindDuplicates : public QObject +{ + Q_OBJECT + +public: + FindDuplicates(QWidget *parent, int sensitivity = 4000); + ~FindDuplicates(); + + bool findDuplicateEntries(File *file, QList &entryCliqueList); + +private slots: + void gotCanceled(); + +private: + class FindDuplicatesPrivate; + FindDuplicatesPrivate *d; +}; + +/** + * @author Thomas Fischer + */ +class KBIBTEXPROC_EXPORT MergeDuplicates +{ +public: + MergeDuplicates(QWidget *parent); + + bool mergeDuplicateEntries(const QList &entryCliques, File *file); + +private: + class MergeDuplicatesPrivate; + MergeDuplicatesPrivate *d; +}; + +#endif // KBIBTEX_PROC_FINDDUPLICATES_H diff -Nru kbibtex-0.3/src/processing/kbibtexproc_export.h kbibtex-0.4/src/processing/kbibtexproc_export.h --- kbibtex-0.3/src/processing/kbibtexproc_export.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/processing/kbibtexproc_export.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,16 @@ +#ifndef KBIBTEXPROC_EXPORT_H +#define KBIBTEXPROC_EXPORT_H + +#include + +#ifndef KBIBTEXPROC_EXPORT +# if defined(MAKE_KBIBTEXPROC_LIB) +/* We are building this library */ +# define KBIBTEXPROC_EXPORT KDE_EXPORT +# else // MAKE_KBIBTEXPROC_LIB +/* We are using this library */ +# define KBIBTEXPROC_EXPORT KDE_IMPORT +# endif // MAKE_KBIBTEXPROC_LIB +#endif // KBIBTEXPROC_EXPORT + +#endif // KBIBTEXPROC_EXPORT_H diff -Nru kbibtex-0.3/src/processing/lyx.cpp kbibtex-0.4/src/processing/lyx.cpp --- kbibtex-0.3/src/processing/lyx.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/processing/lyx.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,140 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lyx.h" + +class LyX::LyXPrivate +{ +private: + LyX *p; + +public: + QTreeView *widget; + KAction *action; + QStringList references; + KSharedConfigPtr config; + const QString configGroupNameLyX; + + LyXPrivate(LyX *parent, QTreeView *widget) + : p(parent), action(NULL), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupNameLyX(QLatin1String("LyX")) { + this->widget = widget; + } + + QString findLyXServerPipeName() { + QString result = QString::null; + + QFile lyxConfigFile(QDir::homePath() + QDir::separator() + ".lyx" + QDir::separator() + "preferences"); // FIXME does this work on Windows/Mac systems? + if (lyxConfigFile.open(QFile::ReadOnly)) { + QRegExp serverPipeRegExp("^\\\\serverpipe\\s+\"([^\"]+)\""); + QString line = QString::null; + while (!(line = lyxConfigFile.readLine()).isNull()) { + if (line.isEmpty() || line[0] == '#' || line.length() < 3) continue; + if (serverPipeRegExp.indexIn(line) >= 0) { + result = serverPipeRegExp.cap(1) + ".in"; + break; + } + } + lyxConfigFile.close(); + } + + return result; + } +}; + +const QString LyX::keyLyXServerPipeName = QLatin1String("LyXServerPipeName"); +const QString LyX::defaultLyXServerPipeName = QLatin1String(""); + +LyX::LyX(KParts::ReadOnlyPart *part, QTreeView *widget) + : QObject(part), d(new LyX::LyXPrivate(this, widget)) +{ + d->action = new KAction(KIcon("application-x-lyx"), i18n("Send Reference to LyX"), this); + part->actionCollection()->addAction("sendtolyx", d->action); + d->action->setEnabled(false); + connect(d->action, SIGNAL(triggered()), this, SLOT(sendReferenceToLyX())); +#if KDE_VERSION_MINOR >= 4 + part->replaceXMLFile(KStandardDirs::locate("appdata", "lyx.rc"), KStandardDirs::locateLocal("appdata", "lyx.rc"), true); +#endif + widget->addAction(d->action); +} + +void LyX::setReferences(const QStringList &references) +{ + d->references = references; +} + +void LyX::updateActions() +{ + d->action->setEnabled(d->widget != NULL && !d->widget->selectionModel()->selection().isEmpty()); +} + +void LyX::sendReferenceToLyX() +{ + const QString defaultHintOnLyXProblems = i18n("\n\nCheck that LyX is running and configured to receive references (see \"LyX server pipe\" in LyX's settings)."); + const QString msgBoxTitle = i18n("Send Reference to LyX"); + + if (d->references.isEmpty()) { + KMessageBox::error(d->widget, i18n("No references to send to LyX."), msgBoxTitle); + return; + } + + KConfigGroup configGroup(d->config, d->configGroupNameLyX); + QString pipeName = configGroup.readEntry(LyX::keyLyXServerPipeName, LyX::defaultLyXServerPipeName); + if (pipeName.isEmpty()) { + KMessageBox::error(d->widget, i18n("No \"LyX server pipe\" has been configured in KBibTeX's settings."), msgBoxTitle); + return; + } + + QFileInfo fi(pipeName); + if (!fi.exists()) { + KMessageBox::error(d->widget, i18n("LyX server pipe \"%1\" does not exist.", pipeName) + defaultHintOnLyXProblems, msgBoxTitle); + return; + } + + QFile pipe(pipeName); + if (!pipe.open(QFile::WriteOnly)) { + KMessageBox::error(d->widget, i18n("Could not open LyX server pipe \"%1\".", pipeName) + defaultHintOnLyXProblems, msgBoxTitle); + return; + } + + QTextStream ts(&pipe); + QString msg = QString("LYXCMD:kbibtex:citation-insert:%1").arg(d->references.join(",")); + + ts << msg << endl; + ts.flush(); + + pipe.close(); +} diff -Nru kbibtex-0.3/src/processing/lyx.h kbibtex-0.4/src/processing/lyx.h --- kbibtex-0.3/src/processing/lyx.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/processing/lyx.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,60 @@ +/*************************************************************************** +* Copyright (C) 2004-2011 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PROC_LYX_H +#define KBIBTEX_PROC_LYX_H + +#include "kbibtexproc_export.h" + +#include + +namespace KParts +{ +class ReadOnlyPart; +} + +class QTreeView; + +/** + * @author Thomas Fischer + */ +class KBIBTEXPROC_EXPORT LyX: public QObject +{ + Q_OBJECT +public: + static const QString keyLyXServerPipeName; + static const QString defaultLyXServerPipeName; + + LyX(KParts::ReadOnlyPart *part, QTreeView *widget); + + void setReferences(const QStringList &references); + +public slots: + void updateActions(); + +private slots: + void sendReferenceToLyX(); + +private: + class LyXPrivate; + LyXPrivate *d; +}; + +#endif // KBIBTEX_PROC_LYX_H diff -Nru kbibtex-0.3/src/processing/lyx.rc kbibtex-0.4/src/processing/lyx.rc --- kbibtex-0.3/src/processing/lyx.rc 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/processing/lyx.rc 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff -Nru kbibtex-0.3/src/program/CMakeLists.txt kbibtex-0.4/src/program/CMakeLists.txt --- kbibtex-0.3/src/program/CMakeLists.txt 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/CMakeLists.txt 2011-11-20 20:36:55.000000000 +0000 @@ -2,6 +2,7 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/docklets ${CMAKE_CURRENT_SOURCE_DIR}/../gui ${CMAKE_CURRENT_SOURCE_DIR}/../gui/config ${CMAKE_CURRENT_SOURCE_DIR}/../gui/bibtex @@ -20,12 +21,12 @@ mainwindow.cpp documentlist.cpp mdiwidget.cpp - referencepreview.cpp - urlpreview.cpp - valuelist.cpp - searchform.cpp - searchresults.cpp - elementform.cpp + docklets/referencepreview.cpp + docklets/urlpreview.cpp + docklets/valuelist.cpp + docklets/searchform.cpp + docklets/searchresults.cpp + docklets/elementform.cpp openfileinfo.cpp ) diff -Nru kbibtex-0.3/src/program/docklets/elementform.cpp kbibtex-0.4/src/program/docklets/elementform.cpp --- kbibtex-0.3/src/program/docklets/elementform.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/elementform.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,174 @@ +/*************************************************************************** +* Copyright (C) 2004-2009 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include "elementform.h" + +class ElementForm::ElementFormPrivate +{ +private: + ElementForm *p; + QGridLayout *layout; + Entry emptyElement; + Element *element; + const File *file; + +public: + ElementEditor *elementEditor; + MDIWidget *mdiWidget; + KPushButton *buttonApply, *buttonReset; + QWidget *widgetUnmodifiedChanges; + + ElementFormPrivate(ElementForm *parent) + : p(parent), element(NULL), file(NULL), elementEditor(NULL) { + layout = new QGridLayout(p); + layout->setColumnStretch(0, 1); + layout->setColumnStretch(1, 0); + layout->setColumnStretch(2, 0); + + /// Create a special widget that shows a small icon and a text + /// stating that there are unsaved changes. It will be shown + /// simultaneously when the Apply and Reset buttons are enabled. + widgetUnmodifiedChanges = new QWidget(p); + layout->addWidget(widgetUnmodifiedChanges, 1, 0, 1, 1); + QBoxLayout *layoutUnmodifiedChanges = new QHBoxLayout(widgetUnmodifiedChanges); + layoutUnmodifiedChanges->addStretch(100); + QLabel *label = new QLabel(widgetUnmodifiedChanges); + label->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); + label->setPixmap(KIconLoader::global()->loadIcon("dialog-information", KIconLoader::Dialog, KIconLoader::SizeSmall)); + layoutUnmodifiedChanges->addWidget(label); + label = new QLabel(i18n("There are unmodified changes. Please press either 'Apply' or 'Reset'."), widgetUnmodifiedChanges); + label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layoutUnmodifiedChanges->addWidget(label); + + buttonApply = new KPushButton(KIcon("dialog-ok-apply"), i18n("Apply"), p); + layout->addWidget(buttonApply, 1, 1, 1, 1); + + buttonReset = new KPushButton(KIcon("edit-undo"), i18n("Reset"), p); + layout->addWidget(buttonReset, 1, 2, 1, 1); + + loadElement(NULL, NULL); + + connect(buttonApply, SIGNAL(clicked()), p, SIGNAL(elementModified())); + } + + void refreshElement() { + loadElement(element, file); + } + + void loadElement(Element *element, const File *file) { + /// store both element and file for later refresh + this->element = element; + this->file = file; + + /// skip whole process of loading an element if not visible + if (isVisible()) + p->setEnabled(true); + else { + p->setEnabled(false); + return; + } + + /// recreate and reset element editor + int tabIndex = 0; + if (elementEditor != NULL) { + tabIndex = elementEditor->currentTab(); + delete elementEditor; + } + elementEditor = element == NULL ? new ElementEditor(&emptyElement, file, p) : new ElementEditor(element, file, p); + layout->addWidget(elementEditor, 0, 0, 1, 3); + elementEditor->setEnabled(element != NULL); + elementEditor->setCurrentTab(tabIndex); + elementEditor->layout()->setMargin(0); + connect(elementEditor, SIGNAL(modified(bool)), p, SLOT(modified())); + + /// make apply and reset buttons aware of new element editor + buttonApply->setEnabled(false); + buttonReset->setEnabled(false); + widgetUnmodifiedChanges->setVisible(false); + connect(buttonApply, SIGNAL(clicked()), p, SLOT(apply())); + connect(buttonReset, SIGNAL(clicked()), p, SLOT(reset())); + } + + bool isVisible() { + /// get dock where this widget is inside + /// static cast is save as constructor requires parent to be QDockWidget + QDockWidget *pp = static_cast(p->parent()); + return pp != NULL && !pp->isHidden(); + } + + void apply() { + elementEditor->apply(); + buttonApply->setEnabled(false); + buttonReset->setEnabled(false); + widgetUnmodifiedChanges->setVisible(false); + } + + void reset() { + elementEditor->reset(); + buttonApply->setEnabled(false); + buttonReset->setEnabled(false); + widgetUnmodifiedChanges->setVisible(false); + } +}; + +ElementForm::ElementForm(MDIWidget *mdiWidget, QDockWidget *parent) + : QWidget(parent), d(new ElementFormPrivate(this)) +{ + connect(parent, SIGNAL(visibilityChanged(bool)), this, SLOT(visibilityChanged(bool))); + d->mdiWidget = mdiWidget; +} + +void ElementForm::setElement(Element* element, const File *file) +{ + d->loadElement(element, file); +} + +void ElementForm::modified() +{ + d->buttonApply->setEnabled(true); + d->buttonReset->setEnabled(true); + d->widgetUnmodifiedChanges->setVisible(true); +} + +void ElementForm::apply() +{ + d->apply(); +} + +void ElementForm::reset() +{ + d->reset(); +} + +void ElementForm::visibilityChanged(bool) +{ + d->refreshElement(); +} diff -Nru kbibtex-0.3/src/program/docklets/elementform.h kbibtex-0.4/src/program/docklets/elementform.h --- kbibtex-0.3/src/program/docklets/elementform.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/elementform.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,57 @@ +/*************************************************************************** +* Copyright (C) 2004-2009 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PROGRAM_ELEMENTFORM_H +#define KBIBTEX_PROGRAM_ELEMENTFORM_H + + +#include + +class QDockWidget; + +class MDIWidget; +class Element; +class File; + +class ElementForm : public QWidget +{ + Q_OBJECT + +public: + ElementForm(MDIWidget *mdiWidget, QDockWidget *parent); + +public slots: + void setElement(Element*, const File *); + +signals: + void elementModified(); + +private: + class ElementFormPrivate; + ElementFormPrivate *d; + +private slots: + void modified(); + void apply(); + void reset(); + void visibilityChanged(bool); +}; + +#endif // KBIBTEX_PROGRAM_ELEMENTFORM_H diff -Nru kbibtex-0.3/src/program/docklets/referencepreview.cpp kbibtex-0.4/src/program/docklets/referencepreview.cpp --- kbibtex-0.3/src/program/docklets/referencepreview.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/referencepreview.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,365 @@ +/*************************************************************************** + * Copyright (C) 2004-2011 by Thomas Fischer * + * and contributors * + * * + * Contributions to this file were made by * + * - Jurgen Spitzmuller * + * * + * 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) any later version. * + * * + * 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#ifdef HAVE_QTWEBKIT +#include +#else // HAVE_QTWEBKIT +#include +#endif // HAVE_QTWEBKIT +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "referencepreview.h" + +class ReferencePreview::ReferencePreviewPrivate +{ +private: + ReferencePreview *p; + +public: + KSharedConfigPtr config; + const QString configGroupName; + const QString configKeyName; + + KPushButton *buttonOpen, *buttonSaveAsHTML; + QString htmlText; + QUrl baseUrl; +#ifdef HAVE_QTWEBKIT + QWebView *webView; +#else // HAVE_QTWEBKIT + QLabel *messageLabel; +#endif // HAVE_QTWEBKIT + KComboBox *comboBox; + const Element* element; + const File *file; + BibTeXEditor *editor; + const QString notAvailableMessage; + + ReferencePreviewPrivate(ReferencePreview *parent) + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupName(QLatin1String("Reference Preview Docklet")), + configKeyName(QLatin1String("Style")), element(NULL), editor(NULL), notAvailableMessage("

" + i18n("No preview available") + "

" + i18n("Reason:") + " %1

") { + QGridLayout *gridLayout = new QGridLayout(p); + gridLayout->setMargin(0); + gridLayout->setColumnStretch(0, 1); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + + comboBox = new KComboBox(p); + gridLayout->addWidget(comboBox, 0, 0, 1, 3); + + QFrame *frame = new QFrame(p); + gridLayout->addWidget(frame, 1, 0, 1, 3); + frame->setFrameShadow(QFrame::Sunken); + frame->setFrameShape(QFrame::StyledPanel); + + QVBoxLayout *layout = new QVBoxLayout(frame); + layout->setMargin(0); +#ifdef HAVE_QTWEBKIT + webView = new QWebView(frame); + layout->addWidget(webView); + webView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); + connect(webView, SIGNAL(linkClicked(const QUrl &)), p, SLOT(linkClicked(const QUrl &))); +#else // HAVE_QTWEBKIT + messageLabel = new QLabel(i18n("No preview available due to missing QtWebKit support on your system."), frame); + messageLabel->setWordWrap(true); + messageLabel->setAlignment(Qt::AlignCenter); + layout->addWidget(messageLabel); +#endif // HAVE_QTWEBKIT + + buttonOpen = new KPushButton(KIcon("document-open"), i18n("Open"), p); + buttonOpen->setToolTip(i18n("Open reference in web browser.")); + gridLayout->addWidget(buttonOpen, 2, 1, 1, 1); + + buttonSaveAsHTML = new KPushButton(KIcon("document-save"), i18n("Save as HTML"), p); + buttonSaveAsHTML->setToolTip(i18n("Save reference as HTML fragment.")); + gridLayout->addWidget(buttonSaveAsHTML, 2, 2, 1, 1); + } + + bool saveHTML(const KUrl& url) const { + KTemporaryFile file; + file.setAutoRemove(true); + + bool result = saveHTML(file); + + if (result) { + KIO::NetAccess::del(url, p); /// ignore error if file does not exist + result = KIO::NetAccess::file_copy(KUrl(file.fileName()), url, p); + } + + return result; + } + + bool saveHTML(KTemporaryFile &file) const { + if (file.open()) { + QTextStream ts(&file); + ts << QString(htmlText).replace(QRegExp("]+href=\"kbibtex:[^>]+>([^<]+)"), "\\1"); + file.close(); + return true; + } + + return false; + } + + void loadState() { + comboBox->clear(); + + /// source view should always be included + comboBox->addItem(i18n("Source"), QStringList(QStringList() << "source" << "source")); + + KConfigGroup configGroup(config, configGroupName); + QStringList previewStyles = configGroup.readEntry("PreviewStyles", QStringList()); + foreach(const QString &entry, previewStyles) { + QStringList style = entry.split("|"); + QString styleLabel = style.at(0); + style.removeFirst(); + comboBox->addItem(styleLabel, style); + } + int styleIndex = comboBox->findData(configGroup.readEntry(configKeyName, QStringList())); + if (styleIndex < 0) styleIndex = 0; + comboBox->setCurrentIndex(styleIndex); + } + + void saveState() { + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(configKeyName, comboBox->itemData(comboBox->currentIndex()).toStringList()); + config->sync(); + } +}; + +ReferencePreview::ReferencePreview(QWidget *parent) + : QWidget(parent), d(new ReferencePreviewPrivate(this)) +{ + setEnabled(false); + + d->loadState(); + + connect(d->buttonOpen, SIGNAL(clicked()), this, SLOT(openAsHTML())); + connect(d->buttonSaveAsHTML, SIGNAL(clicked()), this, SLOT(saveAsHTML())); + connect(d->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(renderHTML())); +} + +void ReferencePreview::setHtml(const QString & html, const QUrl & baseUrl) +{ + d->htmlText = QString(html).replace("", ""); + d->baseUrl = baseUrl; +#ifdef HAVE_QTWEBKIT + d->webView->setHtml(html, baseUrl); +#endif // HAVE_QTWEBKIT + d->buttonOpen->setEnabled(true); + d->buttonSaveAsHTML->setEnabled(true); +} + +void ReferencePreview::setEnabled(bool enabled) +{ + if (enabled) + setHtml(d->htmlText, d->baseUrl); + else { +#ifdef HAVE_QTWEBKIT + d->webView->setHtml(d->notAvailableMessage.arg(i18n("Preview disabled")), d->baseUrl); +#endif // HAVE_QTWEBKIT + d->buttonOpen->setEnabled(false); + d->buttonSaveAsHTML->setEnabled(false); + } +#ifdef HAVE_QTWEBKIT + d->webView->setEnabled(enabled); +#endif // HAVE_QTWEBKIT + d->comboBox->setEnabled(enabled); +} + +void ReferencePreview::setElement(Element* element, const File *file) +{ + d->element = element; + d->file = file; + renderHTML(); +} + +void ReferencePreview::renderHTML() +{ + enum { ignore, /// do not include crossref'ed entry's values (one entry) + add, /// feed both the current entry as well as the crossref'ed entry into the exporter (two entries) + merge /// merge the crossref'ed entry's values into the current entry (one entry) + } crossRefHandling = ignore; + + if (d->element == NULL) { +#ifdef HAVE_QTWEBKIT + d->webView->setHtml(d->notAvailableMessage.arg(i18n("No element selected")), d->baseUrl); +#endif // HAVE_QTWEBKIT + d->buttonOpen->setEnabled(false); + d->buttonSaveAsHTML->setEnabled(false); + return; + } + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + QStringList errorLog; + FileExporter *exporter = NULL; + + QStringList data = d->comboBox->itemData(d->comboBox->currentIndex()).toStringList(); + QString type = data.at(1); + QString style = data.at(0); + + if (type == "source") { + FileExporterBibTeX *exporterBibTeX = new FileExporterBibTeX(); + exporterBibTeX->setEncoding(QLatin1String("utf-8")); + exporter = exporterBibTeX; + } else if (type == "bibtex2html") { + crossRefHandling = merge; + FileExporterBibTeX2HTML *exporterHTML = new FileExporterBibTeX2HTML(); + exporterHTML->setLaTeXBibliographyStyle(style); + exporter = exporterHTML; + } else if (type == "xml") { + crossRefHandling = merge; + FileExporterXSLT *exporterXSLT = new FileExporterXSLT(); + QString filename = style + ".xsl"; + exporterXSLT->setXSLTFilename(KStandardDirs::locate("appdata", filename)); + exporter = exporterXSLT; + } + + QBuffer buffer(this); + buffer.open(QBuffer::WriteOnly); + + const Entry *entry = dynamic_cast(d->element); + if (crossRefHandling == add && entry != NULL) { + QString crossRef = PlainTextValue::text(entry->value(QLatin1String("crossref")), d->file); + const Entry *crossRefEntry = dynamic_cast((d->file != NULL) ? d->file->containsKey(crossRef) : NULL); + if (crossRefEntry != NULL) { + File file; + file.append(new Entry(*entry)); + file.append(new Entry(*crossRefEntry)); + exporter->save(&buffer, &file, &errorLog); + } else + exporter->save(&buffer, d->element, &errorLog); + } else if (crossRefHandling == merge && entry != NULL) { + Entry *merged = Entry::resolveCrossref(*entry, d->file); + exporter->save(&buffer, merged, &errorLog); + delete merged; + } else + exporter->save(&buffer, d->element, &errorLog); + buffer.close(); + delete exporter; + + buffer.open(QBuffer::ReadOnly); + QTextStream ts(&buffer); + QString text = ts.readAll(); + buffer.close(); + + if (text.isEmpty()) { + /// something went wrong, no output ... + text = d->notAvailableMessage.arg(i18n("No HTML output generated")); + kDebug() << errorLog.join("\n"); + } + + if (d->comboBox->currentIndex() == 0) { + /// source + text.prepend("
"); //FIXME: Font size seems to be too small
+        text.append("
"); + } else if (d->comboBox->currentIndex() < 9) { + /// bibtex2html + + /// remove "generated by" line from HTML code if BibTeX2HTML was used + text.replace(QRegExp("

.*

"), ""); + text.replace(QRegExp("<[/]?(font)[^>]*>"), ""); + QRegExp reTable("^.*"); + reTable.setMinimal(true); + text.replace(reTable, ""); + text.replace(QRegExp(".*$"), ""); + QRegExp reAnchor("\\[ \\]"); + reAnchor.setMinimal(true); + text.replace(reAnchor, ""); + + text.prepend(""); + text.append(""); + } else { + /// XML/XSLT + text.prepend(""); + text.append(""); + } + + /// beautify text + text.replace("``", "“"); + text.replace("''", "”"); + + setHtml(text, d->baseUrl); + + d->saveState(); + + QApplication::restoreOverrideCursor(); +} + +void ReferencePreview::openAsHTML() +{ + KTemporaryFile file; + file.setSuffix(".html"); + file.setAutoRemove(false); /// let file stay alive for browser + d->saveHTML(file); + QDesktopServices::openUrl(KUrl(file.fileName())); +} + +void ReferencePreview::saveAsHTML() +{ + KUrl url = KFileDialog::getSaveUrl(KUrl(), "text/html", this, i18n("Save as HTML")); + if (url.isValid()) + d->saveHTML(url); +} + +void ReferencePreview::linkClicked(const QUrl& url) +{ + QString text = url.toString(); + if (text.startsWith("kbibtex:filter:")) { + text = text.mid(15); + if (d->editor != NULL) { + int p = text.indexOf("="); + SortFilterBibTeXFileModel::FilterQuery fq; + fq.terms << text.mid(p + 1); + fq.combination = SortFilterBibTeXFileModel::EveryTerm; + fq.field = text.left(p); + d->editor->setFilterBarFilter(fq); + } + } +} + +void ReferencePreview::setEditor(BibTeXEditor *editor) +{ + d->editor = editor; +} diff -Nru kbibtex-0.3/src/program/docklets/referencepreview.h kbibtex-0.4/src/program/docklets/referencepreview.h --- kbibtex-0.3/src/program/docklets/referencepreview.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/referencepreview.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,57 @@ +/*************************************************************************** +* Copyright (C) 2004-2009 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PROGRAM_REFERENCEPREVIEW_H +#define KBIBTEX_PROGRAM_REFERENCEPREVIEW_H + +#include +#include + +class Element; +class File; +class BibTeXEditor; + +class ReferencePreview : public QWidget +{ + Q_OBJECT +public: + ReferencePreview(QWidget *parent); + + void setHtml(const QString & html, const QUrl & baseUrl = QUrl()); + void setEnabled(bool); + + void setEditor(BibTeXEditor *editor); + +public slots: + void setElement(Element*, const File *); + +private: + class ReferencePreviewPrivate; + ReferencePreviewPrivate *d; + +private slots: + void renderHTML(); + void openAsHTML(); + void saveAsHTML(); + void linkClicked(const QUrl&); +}; + + +#endif // KBIBTEX_PROGRAM_REFERENCEPREVIEW_H diff -Nru kbibtex-0.3/src/program/docklets/searchform.cpp kbibtex-0.4/src/program/docklets/searchform.cpp --- kbibtex-0.3/src/program/docklets/searchform.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/searchform.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,458 @@ +/*************************************************************************** +* Copyright (C) 2004-2010 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "searchform.h" + +const int HomepageRole = Qt::UserRole + 5; +const int WidgetRole = Qt::UserRole + 6; +const int NameRole = Qt::UserRole + 7; + +class SearchForm::SearchFormPrivate +{ +private: + SearchForm *p; + QStackedWidget *queryTermsStack; + QWidget *listContainer; + QListWidget *enginesList; + QLabel *whichEnginesLabel; + KAction *actionOpenHomepage; + +public: + KSharedConfigPtr config; + const QString configGroupName; + + MDIWidget *m; + SearchResults *sr; + QMap itemToWebSearch; + QSet runningSearches; + KPushButton *searchButton; + KPushButton *useEntryButton; + WebSearchQueryFormGeneral *generalQueryTermsForm; + QTabWidget *tabWidget; + Entry *currentEntry; + QProgressBar *progressBar; + QMap progressMap; + + SearchFormPrivate(MDIWidget *mdiWidget, SearchResults *searchResults, SearchForm *parent) + : p(parent), whichEnginesLabel(NULL), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), + configGroupName(QLatin1String("Search Engines Docklet")), m(mdiWidget), sr(searchResults), currentEntry(NULL) { + // nothing + } + + WebSearchQueryFormAbstract *currentQueryForm() { + return static_cast(queryTermsStack->currentWidget()); + } + + QWidget* createQueryTermsStack(QWidget *parent) { + QWidget *container = new QWidget(parent); + QVBoxLayout *vLayout = new QVBoxLayout(container); + + whichEnginesLabel = new QLabel(container); + whichEnginesLabel->setWordWrap(true); + vLayout->addWidget(whichEnginesLabel); + vLayout->setStretch(0, 10); + connect(whichEnginesLabel, SIGNAL(linkActivated(QString)), p, SLOT(switchToEngines())); + + vLayout->addSpacing(8); + + queryTermsStack = new QStackedWidget(parent); + vLayout->addWidget(queryTermsStack); + queryTermsStack->addWidget(createGeneralQueryTermsForm(queryTermsStack)); + connect(queryTermsStack, SIGNAL(currentChanged(int)), p, SLOT(currentStackWidgetChanged(int))); + + vLayout->addSpacing(8); + + QHBoxLayout *hLayout = new QHBoxLayout(); + vLayout->addLayout(hLayout); + useEntryButton = new KPushButton(i18n("Use Entry"), parent); + hLayout->addWidget(useEntryButton); + useEntryButton->setEnabled(false); + connect(useEntryButton, SIGNAL(clicked()), p, SLOT(copyFromEntry())); + hLayout->addStretch(10); + + vLayout->addStretch(100); + + return container; + } + + QWidget* createGeneralQueryTermsForm(QWidget *parent) { + generalQueryTermsForm = new WebSearchQueryFormGeneral(parent); + return generalQueryTermsForm; + } + + QWidget* createEnginesGUI(QWidget *parent) { + listContainer = new QWidget(parent); + QGridLayout *layout = new QGridLayout(listContainer); + layout->setRowStretch(0, 1); + layout->setRowStretch(1, 0); + + enginesList = new QListWidget(listContainer); + layout->addWidget(enginesList, 0, 0, 1, 1); + connect(enginesList, SIGNAL(itemChanged(QListWidgetItem*)), p, SLOT(itemCheckChanged(QListWidgetItem*))); + connect(enginesList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), p, SLOT(enginesListCurrentChanged(QListWidgetItem*, QListWidgetItem*))); + enginesList->setSelectionMode(QAbstractItemView::NoSelection); + + actionOpenHomepage = new KAction(KIcon("internet-web-browser"), i18n("Go to Homepage"), p); + connect(actionOpenHomepage, SIGNAL(triggered()), p, SLOT(openHomepage())); + enginesList->addAction(actionOpenHomepage); + enginesList->setContextMenuPolicy(Qt::ActionsContextMenu); + + return listContainer; + } + + void createGUI() { + QGridLayout *layout = new QGridLayout(p); + layout->setMargin(0); + layout->setRowStretch(0, 1); + layout->setRowStretch(1, 0); + layout->setColumnStretch(0, 1); + layout->setColumnStretch(1, 0); + + tabWidget = new QTabWidget(p); + layout->addWidget(tabWidget, 0, 0, 1, 2); + connect(tabWidget, SIGNAL(currentChanged(int)), p, SLOT(tabSwitched(int))); + + QWidget *widget = createQueryTermsStack(tabWidget); + tabWidget->addTab(widget, KIcon("edit-rename"), i18n("Query Terms")); + + QWidget *listContainer = createEnginesGUI(tabWidget); + tabWidget->addTab(listContainer, KIcon("applications-engineering"), i18n("Engines")); + + progressBar = new QProgressBar(p); + layout->addWidget(progressBar, 1, 0, 1, 1); + progressBar->setMaximum(1000); + progressBar->hide(); + + searchButton = new KPushButton(KIcon("edit-find"), i18n("Search"), p); + layout->addWidget(searchButton, 1, 1, 1, 1); + connect(generalQueryTermsForm, SIGNAL(returnPressed()), searchButton, SIGNAL(clicked())); + + loadEngines(); + updateGUI(); + } + + void loadEngines() { + enginesList->clear(); + + addEngine(new WebSearchBibsonomy(p)); + addEngine(new WebSearchGoogleScholar(p)); + addEngine(new WebSearchAcmPortal(p)); + addEngine(new WebSearchArXiv(p)); + addEngine(new WebSearchPubMed(p)); + addEngine(new WebSearchIEEEXplore(p)); + addEngine(new WebSearchScienceDirect(p)); + addEngine(new WebSearchSpringerLink(p)); + addEngine(new WebSearchJStor(p)); + + p->itemCheckChanged(NULL); + updateGUI(); + } + + void addEngine(WebSearchAbstract *engine) { + KConfigGroup configGroup(config, configGroupName); + + QListWidgetItem *item = new QListWidgetItem(engine->label(), enginesList); + item->setCheckState(configGroup.readEntry(engine->name(), false) ? Qt::Checked : Qt::Unchecked); + item->setIcon(engine->icon()); + item->setData(HomepageRole, engine->homepage()); + item->setData(NameRole, engine->name()); + + WebSearchQueryFormAbstract *widget = engine->customWidget(queryTermsStack); + item->setData(WidgetRole, QVariant::fromValue(widget)); + if (widget != NULL) + queryTermsStack->addWidget(widget); + + itemToWebSearch.insert(item, engine); + connect(engine, SIGNAL(foundEntry(Entry*)), p, SLOT(foundEntry(Entry*))); + connect(engine, SIGNAL(stoppedSearch(int)), p, SLOT(stoppedSearch(int))); + connect(engine, SIGNAL(progress(int, int)), p, SLOT(updateProgress(int, int))); + } + + void switchToSearch() { + for (QMap::ConstIterator it = itemToWebSearch.constBegin(); it != itemToWebSearch.constEnd(); ++it) + disconnect(searchButton, SIGNAL(clicked()), it.value(), SLOT(cancel())); + + connect(searchButton, SIGNAL(clicked()), p, SLOT(startSearch())); + searchButton->setText(i18n("Search")); + searchButton->setIcon(KIcon("media-playback-start")); + tabWidget->setEnabled(true); + tabWidget->unsetCursor(); + } + + void switchToCancel() { + disconnect(searchButton, SIGNAL(clicked()), p, SLOT(startSearch())); + + for (QMap::ConstIterator it = itemToWebSearch.constBegin(); it != itemToWebSearch.constEnd(); ++it) + connect(searchButton, SIGNAL(clicked()), it.value(), SLOT(cancel())); + searchButton->setText(i18n("Cancel")); + searchButton->setIcon(KIcon("media-playback-stop")); + tabWidget->setEnabled(false); + tabWidget->setCursor(Qt::WaitCursor); + } + + void switchToEngines() { + tabWidget->setCurrentWidget(listContainer); + } + + void updateGUI() { + if (whichEnginesLabel == NULL) return; + + QStringList checkedEngines; + QListWidgetItem *cursor = NULL; + for (QMap::ConstIterator it = itemToWebSearch.constBegin(); it != itemToWebSearch.constEnd(); ++it) + if (it.key()->checkState() == Qt::Checked) { + checkedEngines << it.key()->text(); + cursor = it.key(); + } + + switch (checkedEngines.size()) { + case 0: whichEnginesLabel->setText(i18n("No search engine selected. Change"));break; + case 1: whichEnginesLabel->setText(i18n("Search engine %1 is selected. Change", checkedEngines.first()));break; + case 2: whichEnginesLabel->setText(i18n("Search engines %1 and %2 are selected. Change", checkedEngines.first(), checkedEngines.at(1)));break; + case 3: whichEnginesLabel->setText(i18n("Search engines %1, %2, and %3 are selected. Change", checkedEngines.first(), checkedEngines.at(1), checkedEngines.at(2)));break; + default: whichEnginesLabel->setText(i18n("Search engines %1, %2, and more are selected. Change", checkedEngines.first(), checkedEngines.at(1)));break; + } + + WebSearchQueryFormAbstract *currentQueryWidget = NULL; + if (checkedEngines.size() == 1) + currentQueryWidget = cursor->data(WidgetRole).value(); + if (currentQueryWidget == NULL) + currentQueryWidget = generalQueryTermsForm; + queryTermsStack->setCurrentWidget(currentQueryWidget); + } + + void openHomepage() { + QListWidgetItem *item = enginesList->currentItem(); + if (item != NULL) { + KUrl url = item->data(HomepageRole).value(); + QDesktopServices::openUrl(url); // TODO KDE way? + } + } + + void enginesListCurrentChanged(QListWidgetItem *current) { + actionOpenHomepage->setEnabled(current != NULL); + } + + void currentStackWidgetChanged(int index) { + for (int i = queryTermsStack->count() - 1; i >= 0; --i) { + WebSearchQueryFormAbstract *wsqfa = static_cast(queryTermsStack->widget(i)); + if (i == index) + connect(wsqfa, SIGNAL(returnPressed()), searchButton, SLOT(click())); + else + disconnect(wsqfa, SIGNAL(returnPressed()), searchButton, SLOT(click())); + } + } +}; + +SearchForm::SearchForm(MDIWidget *mdiWidget, SearchResults *searchResults, QWidget *parent) + : QWidget(parent), d(new SearchFormPrivate(mdiWidget, searchResults, this)) +{ + d->createGUI(); + d->switchToSearch(); +} + +void SearchForm::updatedConfiguration() +{ + d->loadEngines(); +} + +void SearchForm::setElement(Element *element, const File *) +{ + d->currentEntry = dynamic_cast(element); + d->useEntryButton->setEnabled(d->currentEntry != NULL); +} + +void SearchForm::switchToEngines() +{ + d->switchToEngines(); +} + +void SearchForm::startSearch() +{ + WebSearchQueryFormAbstract *currentForm = d->currentQueryForm(); + if (!currentForm->readyToStart()) { + KMessageBox::sorry(this, i18n("Could not start searching the Internet:\nThe search terms are not complete or invalid."), i18n("Searching the Internet")); + return; + } + + d->runningSearches.clear(); + d->sr->clear(); + d->progressBar->setValue(0); + d->progressMap.clear(); + d->progressBar->show(); + + if (currentForm == d->generalQueryTermsForm) { + /// start search using the general-purpose form's values + + QMap queryTerms = d->generalQueryTermsForm->getQueryTerms(); + int numResults = d->generalQueryTermsForm->getNumResults(); + for (QMap::ConstIterator it = d->itemToWebSearch.constBegin(); it != d->itemToWebSearch.constEnd(); ++it) + if (it.key()->checkState() == Qt::Checked) { + it.value()->startSearch(queryTerms, numResults); + d->runningSearches.insert(it.value()); + } + if (d->runningSearches.isEmpty()) { + /// if no search engine has been checked (selected), something went wrong + return; + } + } else { + /// use the single selected search engine's specific form + + for (QMap::ConstIterator it = d->itemToWebSearch.constBegin(); it != d->itemToWebSearch.constEnd(); ++it) + if (it.key()->checkState() == Qt::Checked) { + it.value()->startSearch(); + d->runningSearches.insert(it.value()); + } + if (d->runningSearches.isEmpty()) { + /// if no search engine has been checked (selected), something went wrong + return; + } + } + + d->switchToCancel(); +} + +void SearchForm::foundEntry(Entry*entry) +{ + d->sr->insertElement(entry); +} + +void SearchForm::stoppedSearch(int resultCode) +{ + WebSearchAbstract *engine = static_cast(sender()); + if (d->runningSearches.remove(engine)) { + kDebug() << "Search from engine" << engine->label() << "stopped with code" << resultCode << (resultCode == 0 ? "(OK)" : "(Error)"); + if (d->runningSearches.isEmpty()) { + /// last search engine stopped + d->switchToSearch(); + emit doneSearching(); + + QTimer::singleShot(1000, d->progressBar, SLOT(hide())); + } else { + QStringList remainingEngines; + foreach(WebSearchAbstract *running, d->runningSearches) { + remainingEngines.append(running->label()); + } + if (!remainingEngines.isEmpty()) + kDebug() << "Remaining running engines:" << remainingEngines.join(", "); + } + } +} + +void SearchForm::tabSwitched(int newTab) +{ + Q_UNUSED(newTab); + d->updateGUI(); +} + +void SearchForm::itemCheckChanged(QListWidgetItem *item) +{ + int numCheckedEngines = 0; + for (QMap::ConstIterator it = d->itemToWebSearch.constBegin(); it != d->itemToWebSearch.constEnd(); ++it) + if (it.key()->checkState() == Qt::Checked) + ++numCheckedEngines; + + d->searchButton->setEnabled(numCheckedEngines > 0); + + if (item != NULL) { + KConfigGroup configGroup(d->config, d->configGroupName); + QString name = item->data(NameRole).toString(); + configGroup.writeEntry(name, item->checkState() == Qt::Checked); + d->config->sync(); + } +} + +void SearchForm::openHomepage() +{ + d->openHomepage(); +} + +void SearchForm::enginesListCurrentChanged(QListWidgetItem *current, QListWidgetItem *) +{ + d->enginesListCurrentChanged(current); +} + +void SearchForm::currentStackWidgetChanged(int index) +{ + d->currentStackWidgetChanged(index); +} + +void SearchForm::copyFromEntry() +{ + Q_ASSERT(d->currentEntry != NULL); + + d->currentQueryForm()->copyFromEntry(*(d->currentEntry)); +} + +void SearchForm::updateProgress(int cur, int total) +{ + WebSearchAbstract *ws = static_cast(sender()); + d->progressMap[ws] = total > 0 ? cur * 1000 / total : 0; + + int progress = 0, count = 0; + for (QMap::ConstIterator it = d->progressMap.constBegin(); it != d->progressMap.constEnd(); ++it, ++count) + progress += it.value(); + + d->progressBar->setValue(count >= 1 ? progress / count : 0); +} diff -Nru kbibtex-0.3/src/program/docklets/searchform.h kbibtex-0.4/src/program/docklets/searchform.h --- kbibtex-0.3/src/program/docklets/searchform.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/searchform.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,67 @@ +/*************************************************************************** +* Copyright (C) 2004-2009 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PROGRAM_SEARCHFORM_H +#define KBIBTEX_PROGRAM_SEARCHFORM_H + +#include + +class QListWidgetItem; + +class Element; +class File; +class Entry; +class MDIWidget; +class SearchResults; + +class SearchForm : public QWidget +{ + Q_OBJECT + +public: + SearchForm(MDIWidget *mdiWidget, SearchResults *searchResults, QWidget *parent); + +signals: + void doneSearching(); + +public slots: + void updatedConfiguration(); + void setElement(Element*, const File *); + +private: + class SearchFormPrivate; + SearchFormPrivate *d; + +private slots: + void switchToEngines(); + void startSearch(); + void foundEntry(Entry *entry); + void stoppedSearch(int resultCode); + void tabSwitched(int newTab); + void itemCheckChanged(QListWidgetItem*); + void openHomepage(); + void enginesListCurrentChanged(QListWidgetItem*, QListWidgetItem*); + void currentStackWidgetChanged(int); + void copyFromEntry(); + void updateProgress(int, int); +}; + + +#endif // KBIBTEX_PROGRAM_SEARCHFORM_H diff -Nru kbibtex-0.3/src/program/docklets/searchresults.cpp kbibtex-0.4/src/program/docklets/searchresults.cpp --- kbibtex-0.3/src/program/docklets/searchresults.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/searchresults.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,150 @@ +/*************************************************************************** +* Copyright (C) 2004-2010 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "searchresults.h" + +class SearchResults::SearchResultsPrivate +{ +private: + SearchResults *p; + Clipboard *clipboard; + +public: + MDIWidget *m; + BibTeXEditor *currentFile; + KPushButton *buttonImport; + BibTeXEditor *editor; + KAction *actionViewCurrent, *actionImportSelected, *actionCopySelected; + + SearchResultsPrivate(MDIWidget *mdiWidget, SearchResults *parent) + : p(parent), m(mdiWidget), currentFile(NULL) { + QGridLayout *layout = new QGridLayout(parent); + layout->setMargin(0); + layout->setColumnStretch(0, 1); + layout->setColumnStretch(1, 0); + + editor = new BibTeXEditor(QLatin1String("SearchResults"), parent); + editor->setReadOnly(true); + editor->setFrameShadow(QFrame::Sunken); + editor->setFrameShape(QFrame::StyledPanel); + editor->setContextMenuPolicy(Qt::ActionsContextMenu); + layout->addWidget(editor, 0, 0, 1, 2); + + clipboard = new Clipboard(editor); + + buttonImport = new KPushButton(KIcon("svn-update"), i18n("Import"), parent); + layout->addWidget(buttonImport, 1, 1, 1, 1); + buttonImport->setEnabled(false); + + SortFilterBibTeXFileModel *model = new SortFilterBibTeXFileModel(parent); + model->setSourceModel(new BibTeXFileModel(parent)); + editor->setModel(model); + + actionViewCurrent = new KAction(KIcon("document-preview"), i18n("View Element"), parent); + editor->addAction(actionViewCurrent); + actionViewCurrent->setEnabled(false); + connect(actionViewCurrent, SIGNAL(triggered()), editor, SLOT(viewCurrentElement())); + + actionImportSelected = new KAction(KIcon("svn-update"), i18n("Import"), parent); + editor->addAction(actionImportSelected); + actionImportSelected->setEnabled(false); + connect(actionImportSelected, SIGNAL(triggered()), parent, SLOT(importSelected())); + + actionCopySelected = new KAction(KIcon("edit-copy"), i18n("Copy"), parent); + editor->addAction(actionCopySelected); + actionCopySelected->setEnabled(false); + connect(actionCopySelected, SIGNAL(triggered()), clipboard, SLOT(copy())); + + connect(editor, SIGNAL(doubleClicked(QModelIndex)), editor, SLOT(viewCurrentElement())); + connect(editor, SIGNAL(selectedElementsChanged()), parent, SLOT(updateGUI())); + connect(buttonImport, SIGNAL(clicked()), parent, SLOT(importSelected())); + } + + void clear() { + File *file = editor->bibTeXModel()->bibTeXFile(); + delete file; + editor->bibTeXModel()->setBibTeXFile(new File()); + } + + bool insertElement(Element *element) { + BibTeXFileModel *model = editor->bibTeXModel(); + return model->insertRow(element, model->rowCount()); + } + +}; + +SearchResults::SearchResults(MDIWidget *mdiWidget, QWidget *parent) + : QWidget(parent), d(new SearchResultsPrivate(mdiWidget, this)) +{ + // nothing +} + +void SearchResults::clear() +{ + d->clear(); +} + +bool SearchResults::insertElement(Element *element) +{ + return d->insertElement(element); +} + +void SearchResults::documentSwitched(BibTeXEditor *oldEditor, BibTeXEditor *newEditor) +{ + Q_UNUSED(oldEditor); + d->currentFile = newEditor; + updateGUI(); +} + +void SearchResults::updateGUI() +{ + d->buttonImport->setEnabled(d->currentFile != NULL && !d->editor->selectedElements().isEmpty()); + d->actionImportSelected->setEnabled(d->buttonImport->isEnabled()); + d->actionCopySelected->setEnabled(!d->editor->selectedElements().isEmpty()); + d->actionViewCurrent->setEnabled(d->editor->currentElement() != NULL); +} + +void SearchResults::importSelected() +{ + Q_ASSERT(d->currentFile != NULL); + + BibTeXFileModel *targetModel = d->currentFile->bibTeXModel(); + BibTeXFileModel *sourceModel = d->editor->bibTeXModel(); + QList selList = d->editor->selectionModel()->selectedRows(); + for (QList::ConstIterator it = selList.constBegin(); it != selList.constEnd(); ++it) { + int row = d->editor->sortFilterProxyModel()->mapToSource(*it).row(); + Element *element = sourceModel->element(row); + targetModel->insertRow(element, targetModel->rowCount()); + } + + if (!selList.isEmpty()) + d->currentFile->externalModification(); +} diff -Nru kbibtex-0.3/src/program/docklets/searchresults.h kbibtex-0.4/src/program/docklets/searchresults.h --- kbibtex-0.3/src/program/docklets/searchresults.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/searchresults.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,53 @@ +/*************************************************************************** +* Copyright (C) 2004-2010 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PROGRAM_SEARCHRESULTS_H +#define KBIBTEX_PROGRAM_SEARCHRESULTS_H + +#include + +class MDIWidget; + +class Element; +class BibTeXEditor; + +class SearchResults : public QWidget +{ + Q_OBJECT + +public: + SearchResults(MDIWidget *mdiWidget, QWidget *parent); + + void clear(); + bool insertElement(Element *element); + +public slots: + void documentSwitched(BibTeXEditor*, BibTeXEditor*); + +private: + class SearchResultsPrivate; + SearchResultsPrivate *d; + +private slots: + void updateGUI(); + void importSelected(); +}; + +#endif // KBIBTEX_PROGRAM_SEARCHRESULTS_H diff -Nru kbibtex-0.3/src/program/docklets/urlpreview.cpp kbibtex-0.4/src/program/docklets/urlpreview.cpp --- kbibtex-0.3/src/program/docklets/urlpreview.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/urlpreview.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,339 @@ +/*************************************************************************** +* Copyright (C) 2004-2010 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#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 +#include +#include +#include "urlpreview.h" + +class UrlPreview::UrlPreviewPrivate +{ +private: + UrlPreview *p; + + KSharedConfigPtr config; + const QString configGroupName; + const QString onlyLocalFilesCheckConfig; + + KComboBox *urlComboBox; + KPushButton *externalViewerButton; + QCheckBox *onlyLocalFilesCheckBox; + QStackedWidget *stackedWidget; + QLabel *message; + QMap cbxEntryToUrl; + QMutex addingUrlMutex; + + QString arXivPDFUrlStart; + bool anyLocal; + +public: + struct UrlInfo { + KUrl url; + QString mimeType; + KIcon icon; + }; + + QList runningJobs; + const Entry* entry; + KUrl baseUrl; + + UrlPreviewPrivate(UrlPreview *parent) + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), + configGroupName(QLatin1String("URL Preview")), onlyLocalFilesCheckConfig(QLatin1String("OnlyLocalFiles")), + arXivPDFUrlStart("http://arxiv.org/pdf/"), entry(NULL) { + setupGUI(); + } + + /** + * Create user interface for this widget. + * It consists of some controlling widget on the top, + * but the most space is consumed by KPart widgets + * inside a QStackedWidget to show the external content + * (PDF file, web page, ...). + */ + void setupGUI() { + QVBoxLayout *layout = new QVBoxLayout(p); + layout->setMargin(0); + + /// some widgets on the top to control the view + + QHBoxLayout *innerLayout = new QHBoxLayout(); + layout->addLayout(innerLayout, 0); + urlComboBox = new KComboBox(false, p); + innerLayout->addWidget(urlComboBox, 1); + + externalViewerButton = new KPushButton(KIcon("document-open"), i18n("Open..."), p); + innerLayout->addWidget(externalViewerButton, 0); + + onlyLocalFilesCheckBox = new QCheckBox(i18n("Only local files"), p); + layout->addWidget(onlyLocalFilesCheckBox, 0); + + /// main part of the widget: A stacked widget to hold + /// one KPart widget per URL in above combo box + + stackedWidget = new QStackedWidget(p); + layout->addWidget(stackedWidget, 1); + stackedWidget->hide(); + + /// default widget if no preview is available + message = new QLabel(i18n("No preview available"), p); + message->setAlignment(Qt::AlignCenter); + layout->addWidget(message, 1); + + loadState(); + + connect(externalViewerButton, SIGNAL(clicked()), p, SLOT(openExternally())); + connect(urlComboBox, SIGNAL(activated(int)), stackedWidget, SLOT(setCurrentIndex(int))); + connect(onlyLocalFilesCheckBox, SIGNAL(toggled(bool)), p, SLOT(onlyLocalFilesChanged())); + } + + bool addUrl(const struct UrlInfo &urlInfo) { + bool isLocal = urlInfo.url.isLocalFile(); + anyLocal |= isLocal; + + if (onlyLocalFilesCheckBox->isChecked() && !isLocal) return true; ///< ignore URL if only local files are allowed + + if (isLocal) { + /// create a drop-down list entry if file is a local file + /// (based on patch by Luis Silva) + QString fn = urlInfo.url.fileName(); + QString full = urlInfo.url.pathOrUrl(); + QString dir = urlInfo.url.directory(); + QString text = fn.isEmpty() ? full : (dir.isEmpty() ? fn : QString("%1 [%2]").arg(fn).arg(dir)); + urlComboBox->addItem(urlInfo.icon, text); + } else { + /// create a drop-down list entry if file is a remote file + urlComboBox->addItem(urlInfo.icon, urlInfo.url.prettyUrl()); + } + cbxEntryToUrl.insert(urlComboBox->count() - 1, urlInfo.url); + + KParts::ReadOnlyPart* part = NULL; + KService::Ptr serivcePtr = KMimeTypeTrader::self()->preferredService(urlInfo.mimeType, "KParts/ReadOnlyPart"); + if (!serivcePtr.isNull()) + part = serivcePtr->createInstance((QWidget*)p, (QObject*)p); + if (part != NULL) { + stackedWidget->addWidget(part->widget()); + part->openUrl(urlInfo.url); + } else { + QLabel *label = new QLabel(i18n("Cannot create preview for\n%1\n\nNo part available.", urlInfo.url.pathOrUrl()), stackedWidget); + label->setAlignment(Qt::AlignCenter); + stackedWidget->addWidget(label); + } + + urlComboBox->setEnabled(true); + externalViewerButton->setEnabled(true); + if (isLocal || ///< local files always preferred over URLs + /// prefer arXiv summary URLs over other URLs + (!anyLocal && urlInfo.url.host().contains("arxiv.org/abs"))) { + urlComboBox->setCurrentIndex(urlComboBox->count() - 1); + stackedWidget->setCurrentIndex(urlComboBox->count() - 1); + } + message->hide(); + stackedWidget->show(); + + return true; + } + + void update() { + QCursor prevCursor = p->cursor(); + p->setCursor(Qt::WaitCursor); + + /// cancel/kill all running jobs + for (QList::ConstIterator it = runningJobs.constBegin(); it != runningJobs.constEnd(); ++it) + (*it)->kill(); + runningJobs.clear(); + /// remove all shown widgets/parts + urlComboBox->clear(); + while (stackedWidget->count() > 0) + stackedWidget->removeWidget(stackedWidget->currentWidget()); + urlComboBox->setEnabled(false); + externalViewerButton->setEnabled(false); + + /// clear flag that memorizes if any local file was referenced + anyLocal = false; + + /// do not load external reference if widget is hidden + if (isVisible()) { + QList urlList = FileInfo::entryUrls(entry, baseUrl); + for (QList::ConstIterator it = urlList.constBegin(); it != urlList.constEnd(); ++it) { + bool isLocal = (*it).isLocalFile(); + if (onlyLocalFilesCheckBox->isChecked() && !isLocal) continue; + + KIO::StatJob *job = KIO::stat(*it, KIO::StatJob::SourceSide, 3, KIO::HideProgressInfo); + runningJobs << job; + job->ui()->setWindow(p); + connect(job, SIGNAL(result(KJob*)), p, SLOT(statFinished(KJob*))); + } + } + + message->setText(i18n("No preview available")); + message->show(); + stackedWidget->hide(); + p->setEnabled(isVisible()); + + p->setCursor(prevCursor); + } + + void openExternally() { + KUrl url(cbxEntryToUrl[urlComboBox->currentIndex()]); + QDesktopServices::openUrl(url); // TODO KDE way? + } + + UrlInfo urlMetaInfo(const KUrl &url) { + UrlInfo result; + result.url = url; + + if (!url.isLocalFile() && url.fileName().isEmpty()) { + /// URLs not pointing to a specific file should be opened with a web browser component + kDebug() << "Not pointing to file, falling back to text/html for url " << url.pathOrUrl(); + result.icon = KIcon("text-html"); + result.mimeType = QLatin1String("text/html"); + return result; + } + + int accuracy = 0; + KMimeType::Ptr mimeTypePtr = KMimeType::findByUrl(url, 0, url.isLocalFile(), true, &accuracy); + if (accuracy < 50) { + kDebug() << "discarding mime type " << mimeTypePtr->name() << ", trying filename "; + mimeTypePtr = KMimeType::findByPath(url.fileName(), 0, true, &accuracy); + } + result.mimeType = mimeTypePtr->name(); + result.icon = KIcon(mimeTypePtr->iconName()); + + if (result.mimeType == QLatin1String("application/octet-stream")) { + /// application/octet-stream is a fall-back if KDE did not know better + kDebug() << "Got mime type \"application/octet-stream\", falling back to text/html"; + result.icon = KIcon("text-html"); + result.mimeType = QLatin1String("text/html"); + } else if (result.mimeType == QLatin1String("inode/directory") && (result.url.protocol() == QLatin1String("http") || result.url.protocol() == QLatin1String("https"))) { + /// directory via http means normal webpage (not browsable directory) + kDebug() << "Got mime type \"inode/directory\" via http, falling back to text/html"; + result.icon = KIcon("text-html"); + result.mimeType = QLatin1String("text/html"); + } + + if (url.pathOrUrl().startsWith(arXivPDFUrlStart)) { + kDebug() << "URL looks like a PDF url from arXiv"; + result.icon = KIcon("application-pdf"); + result.mimeType = QLatin1String("application/pdf"); + } + + kDebug() << "For url " << result.url.pathOrUrl() << " selected mime type " << result.mimeType; + return result; + } + + bool isVisible() { + /// get dock where this widget is inside + /// static cast is save as constructor requires parent to be QDockWidget + QDockWidget *pp = static_cast(p->parent()); + return pp != NULL && !pp->isHidden(); + } + + void loadState() { + KConfigGroup configGroup(config, configGroupName); + onlyLocalFilesCheckBox->setChecked(configGroup.readEntry(onlyLocalFilesCheckConfig, true)); + } + + void saveState() { + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(onlyLocalFilesCheckConfig, onlyLocalFilesCheckBox->isChecked()); + config->sync(); + } +}; + +UrlPreview::UrlPreview(QDockWidget *parent) + : QWidget(parent), d(new UrlPreviewPrivate(this)) +{ + connect(parent, SIGNAL(visibilityChanged(bool)), this, SLOT(visibilityChanged(bool))); +} + +void UrlPreview::setElement(Element* element, const File *) +{ + d->entry = dynamic_cast(element); + d->update(); +} + +void UrlPreview::openExternally() +{ + d->openExternally(); +} + +void UrlPreview::setBibTeXUrl(const KUrl&url) +{ + d->baseUrl = url; +} + +void UrlPreview::onlyLocalFilesChanged() +{ + d->saveState(); + d->update(); +} + +void UrlPreview::visibilityChanged(bool) +{ + d->update(); +} + +void UrlPreview::statFinished(KJob *kjob) +{ + KIO::StatJob *job = static_cast(kjob); + d->runningJobs.removeOne(job); + if (!job->error()) { +#if KDE_VERSION_MINOR >= 4 + const KUrl url = job->mostLocalUrl(); +#else // KDE_VERSION_MINOR + const KUrl url = job->url(); +#endif // KDE_VERSION_MINOR + kDebug() << "stat succeeded for " << url.pathOrUrl(); + UrlPreviewPrivate::UrlInfo urlInfo = d->urlMetaInfo(url); + d->addUrl(urlInfo); + } else + kDebug() << job->errorString(); +} diff -Nru kbibtex-0.3/src/program/docklets/urlpreview.h kbibtex-0.4/src/program/docklets/urlpreview.h --- kbibtex-0.3/src/program/docklets/urlpreview.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/urlpreview.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,62 @@ +/*************************************************************************** +* Copyright (C) 2004-2010 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PROGRAM_URLPREVIEW_H +#define KBIBTEX_PROGRAM_URLPREVIEW_H + +#include + +#include + +class QDockWidget; + +class KJob; +namespace KIO +{ +class Job; +} + +class Element; +class File; + +class UrlPreview : public QWidget +{ + Q_OBJECT +public: + UrlPreview(QDockWidget *parent); + +public slots: + void setElement(Element*, const File *); + void setBibTeXUrl(const KUrl&); + +private: + class UrlPreviewPrivate; + UrlPreviewPrivate *d; + + QString mimeType(const KUrl &url); + +private slots: + void openExternally(); + void onlyLocalFilesChanged(); + void visibilityChanged(bool); + void statFinished(KJob*); +}; + +#endif // KBIBTEX_PROGRAM_URLPREVIEW_H diff -Nru kbibtex-0.3/src/program/docklets/valuelist.cpp kbibtex-0.4/src/program/docklets/valuelist.cpp --- kbibtex-0.3/src/program/docklets/valuelist.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/valuelist.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,282 @@ +/*************************************************************************** +* Copyright (C) 2004-2010 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "valuelist.h" + +class ValueList::ValueListPrivate +{ +private: + ValueList *p; + ValueListDelegate *delegate; + +public: + KSharedConfigPtr config; + const QString configGroupName; + const QString configKeyFieldName, configKeyShowCountColumn, configKeySortByCountAction, configKeyHeaderState; + + BibTeXEditor *editor; + QTreeView *treeviewFieldValues; + ValueListModel *model; + QSortFilterProxyModel *sortingModel; + KComboBox *comboboxFieldNames; + const int countWidth; + KToggleAction *showCountColumnAction; + KToggleAction *sortByCountAction; + + ValueListPrivate(ValueList *parent) + : p(parent), config(KSharedConfig::openConfig(QLatin1String("kbibtexrc"))), configGroupName(QLatin1String("Value List Docklet")), + configKeyFieldName(QLatin1String("FieldName")), configKeyShowCountColumn(QLatin1String("ShowCountColumn")), + configKeySortByCountAction(QLatin1String("SortByCountAction")), configKeyHeaderState(QLatin1String("HeaderState")), + model(NULL), sortingModel(NULL), countWidth(8 + parent->fontMetrics().width(i18n("Count"))) { + setupGUI(); + initialize(); + } + + void setupGUI() { + QGridLayout *layout = new QGridLayout(p); + layout->setMargin(0); + + comboboxFieldNames = new KComboBox(true, p); + layout->addWidget(comboboxFieldNames, 0, 0, 1, 1); + + treeviewFieldValues = new QTreeView(p); + layout->addWidget(treeviewFieldValues, 1, 0, 1, 1); + treeviewFieldValues->setEditTriggers(QAbstractItemView::EditKeyPressed); + treeviewFieldValues->setSortingEnabled(true); + delegate = new ValueListDelegate(treeviewFieldValues); + treeviewFieldValues->setItemDelegate(delegate); + treeviewFieldValues->setRootIsDecorated(false); + treeviewFieldValues->setSelectionMode(QTreeView::ExtendedSelection); + treeviewFieldValues->setAlternatingRowColors(true); + + treeviewFieldValues->setContextMenuPolicy(Qt::ActionsContextMenu); + /// create context menu item to start renaming + KAction *action = new KAction(KIcon("edit-rename"), i18n("Replace all occurrences"), p); + connect(action, SIGNAL(triggered()), p, SLOT(startItemRenaming())); + treeviewFieldValues->addAction(action); + /// create context menu item to search for multiple selections + action = new KAction(KIcon("edit-find"), i18n("Search for selected values"), p); + connect(action, SIGNAL(triggered()), p, SLOT(searchSelection())); + treeviewFieldValues->addAction(action); + + p->setEnabled(false); + + connect(comboboxFieldNames, SIGNAL(activated(int)), p, SLOT(update())); + connect(treeviewFieldValues, SIGNAL(activated(const QModelIndex &)), p, SLOT(listItemActivated(const QModelIndex &))); + connect(delegate, SIGNAL(closeEditor(QWidget*)), treeviewFieldValues, SLOT(reset())); + + /// add context menu to header + treeviewFieldValues->header()->setContextMenuPolicy(Qt::ActionsContextMenu); + showCountColumnAction = new KToggleAction(i18n("Show Count Column"), treeviewFieldValues); + connect(showCountColumnAction, SIGNAL(triggered()), p, SLOT(showCountColumnToggled())); + treeviewFieldValues->header()->addAction(showCountColumnAction); + + sortByCountAction = new KToggleAction(i18n("Sort by Count"), treeviewFieldValues); + connect(sortByCountAction, SIGNAL(triggered()), p, SLOT(sortByCountToggled())); + treeviewFieldValues->header()->addAction(sortByCountAction); + } + + void setComboboxFieldNamesCurrentItem(const QString &text) { + int index = comboboxFieldNames->findText(text, Qt::MatchExactly); + if (index < 0) index = comboboxFieldNames->findText(text, Qt::MatchStartsWith); + if (index < 0) index = comboboxFieldNames->findText(text, Qt::MatchContains); + if (index >= 0) comboboxFieldNames->setCurrentIndex(index); + } + + void initialize() { + const BibTeXFields *bibtexFields = BibTeXFields::self(); + + comboboxFieldNames->clear(); + foreach(const FieldDescription &fd, *bibtexFields) { + if (!fd.upperCamelCaseAlt.isEmpty()) continue; /// keep only "single" fields and not combined ones like "Author or Editor" + if (fd.upperCamelCase.startsWith('^')) continue; /// skip "type" and "id" + comboboxFieldNames->addItem(fd.label, fd.upperCamelCase); + } + + KConfigGroup configGroup(config, configGroupName); + QString fieldName = configGroup.readEntry(configKeyFieldName, QString(Entry::ftAuthor)); + setComboboxFieldNamesCurrentItem(fieldName); + showCountColumnAction->setChecked(configGroup.readEntry(configKeyShowCountColumn, true)); + sortByCountAction->setChecked(configGroup.readEntry(configKeySortByCountAction, false)); + sortByCountAction->setEnabled(showCountColumnAction->isChecked()); + QByteArray headerState = configGroup.readEntry(configKeyHeaderState, QByteArray()); + treeviewFieldValues->header()->restoreState(headerState); + + connect(treeviewFieldValues->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), p, SLOT(columnsChanged())); + } + + void update() { + QVariant var = comboboxFieldNames->itemData(comboboxFieldNames->currentIndex()); + QString text = var.toString(); + if (text.isEmpty()) text = comboboxFieldNames->currentText(); + + delegate->setFieldName(text); + model = editor == NULL ? NULL : editor->valueListModel(text); + QAbstractItemModel *usedModel = model; + if (usedModel != NULL) { + model->setShowCountColumn(showCountColumnAction->isChecked()); + model->setSortBy(sortByCountAction->isChecked() ? ValueListModel::SortByCount : ValueListModel::SortByText); + + if (sortingModel != NULL) delete sortingModel; + sortingModel = new QSortFilterProxyModel(p); + sortingModel->setSourceModel(model); + if (treeviewFieldValues->header()->isSortIndicatorShown()) + sortingModel->sort(treeviewFieldValues->header()->sortIndicatorSection(), treeviewFieldValues->header()->sortIndicatorOrder()); + else + sortingModel->sort(1, Qt::DescendingOrder); + sortingModel->setSortRole(SortRole); + usedModel = sortingModel; + } + treeviewFieldValues->setModel(usedModel); + treeviewFieldValues->header()->setResizeMode(QHeaderView::Fixed); + + KConfigGroup configGroup(config, configGroupName); + configGroup.writeEntry(configKeyFieldName, text); + config->sync(); + } +}; + +ValueList::ValueList(QWidget *parent) + : QWidget(parent), d(new ValueListPrivate(this)) +{ + QTimer::singleShot(500, this, SLOT(delayedResize())); +} + +void ValueList::setEditor(BibTeXEditor *editor) +{ + d->editor = editor; + update(); + resizeEvent(NULL); +} + +void ValueList::update() +{ + d->update(); + setEnabled(d->editor != NULL); +} + +void ValueList::resizeEvent(QResizeEvent *) +{ + int widgetWidth = d->treeviewFieldValues->size().width() - d->treeviewFieldValues->verticalScrollBar()->size().width() - 8; + d->treeviewFieldValues->setColumnWidth(0, widgetWidth - d->countWidth); + d->treeviewFieldValues->setColumnWidth(1, d->countWidth); +} + +void ValueList::listItemActivated(const QModelIndex &index) +{ + QString itemText = d->sortingModel->mapToSource(index).data(SearchTextRole).toString(); + QVariant fieldVar = d->comboboxFieldNames->itemData(d->comboboxFieldNames->currentIndex()); + QString fieldText = fieldVar.toString(); + if (fieldText.isEmpty()) fieldText = d->comboboxFieldNames->currentText(); + + SortFilterBibTeXFileModel::FilterQuery fq; + fq.terms << itemText; + fq.combination = SortFilterBibTeXFileModel::EveryTerm; + fq.field = fieldText; + + d->editor->setFilterBarFilter(fq); +} + +void ValueList::searchSelection() +{ + QVariant fieldVar = d->comboboxFieldNames->itemData(d->comboboxFieldNames->currentIndex()); + QString fieldText = fieldVar.toString(); + if (fieldText.isEmpty()) fieldText = d->comboboxFieldNames->currentText(); + + SortFilterBibTeXFileModel::FilterQuery fq; + fq.combination = SortFilterBibTeXFileModel::EveryTerm; + fq.field = fieldText; + foreach(const QModelIndex &index, d->treeviewFieldValues->selectionModel()->selectedIndexes()) { + if (index.column() == 0) { + QString itemText = index.data(SearchTextRole).toString(); + fq.terms << itemText; + } + } + + if (!fq.terms.isEmpty()) + d->editor->setFilterBarFilter(fq); +} + +void ValueList::startItemRenaming() +{ + int row = d->treeviewFieldValues->selectionModel()->selectedIndexes().first().row(); + QModelIndex index = d->treeviewFieldValues->model()->index(row, 0); + d->treeviewFieldValues->edit(index); +} + +void ValueList::showCountColumnToggled() +{ + if (d->model != NULL) + d->model->setShowCountColumn(d->showCountColumnAction->isChecked()); + if (d->showCountColumnAction->isChecked()) + resizeEvent(NULL); + + d->sortByCountAction->setEnabled(!d->showCountColumnAction->isChecked()); + + KConfigGroup configGroup(d->config, d->configGroupName); + configGroup.writeEntry(d->configKeyShowCountColumn, d->showCountColumnAction->isChecked()); + d->config->sync(); +} + +void ValueList::sortByCountToggled() +{ + if (d->model != NULL) + d->model->setSortBy(d->sortByCountAction->isChecked() ? ValueListModel::SortByCount : ValueListModel::SortByText); + + KConfigGroup configGroup(d->config, d->configGroupName); + configGroup.writeEntry(d->configKeySortByCountAction, d->sortByCountAction->isChecked()); + d->config->sync(); +} + +void ValueList::delayedResize() +{ + resizeEvent(NULL); +} + +void ValueList::columnsChanged() +{ + QByteArray headerState = d->treeviewFieldValues->header()->saveState(); + KConfigGroup configGroup(d->config, d->configGroupName); + configGroup.writeEntry(d->configKeyHeaderState, headerState); + d->config->sync(); + + resizeEvent(NULL); +} diff -Nru kbibtex-0.3/src/program/docklets/valuelist.h kbibtex-0.4/src/program/docklets/valuelist.h --- kbibtex-0.3/src/program/docklets/valuelist.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/program/docklets/valuelist.h 2011-11-20 20:36:55.000000000 +0000 @@ -0,0 +1,61 @@ +/*************************************************************************** +* Copyright (C) 2004-2010 by Thomas Fischer * +* fischer@unix-ag.uni-kl.de * +* * +* 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) any later version. * +* * +* 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, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef KBIBTEX_PROGRAM_VALUELIST_H +#define KBIBTEX_PROGRAM_VALUELIST_H + +#include + +#include + +class Element; +class File; +class BibTeXEditor; + +class ValueList : public QWidget +{ + Q_OBJECT + +public: + ValueList(QWidget *parent); + + void setEditor(BibTeXEditor *editor); + +public slots: + void update(); + +protected slots: + void resizeEvent(QResizeEvent *e); + +private slots: + void listItemActivated(const QModelIndex &); + void searchSelection(); + void startItemRenaming(); + void showCountColumnToggled(); + void sortByCountToggled(); + void delayedResize(); + void columnsChanged(); + +private: + class ValueListPrivate; + ValueListPrivate *d; +}; + +#endif // KBIBTEX_PROGRAM_VALUELIST_H diff -Nru kbibtex-0.3/src/program/documentlist.cpp kbibtex-0.4/src/program/documentlist.cpp --- kbibtex-0.3/src/program/documentlist.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/documentlist.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -49,14 +49,17 @@ DirOperatorWidget(QWidget *parent) : QWidget(parent) { QGridLayout *layout = new QGridLayout(this); + layout->setMargin(0); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 0); layout->setColumnStretch(2, 1); KPushButton *buttonUp = new KPushButton(KIcon("go-up"), "", this); + buttonUp->setToolTip(i18n("One level up")); layout->addWidget(buttonUp, 0, 0, 1, 1); KPushButton *buttonHome = new KPushButton(KIcon("user-home"), "", this); + buttonHome->setToolTip(i18n("Go to Home folder")); layout->addWidget(buttonHome, 0, 1, 1, 1); dirOperator = new KDirOperator(KUrl("file:" + QDir::homePath()), this); @@ -366,7 +369,7 @@ p->addTab(listFavorites, KIcon("favorites"), i18n("Favorites")); dirOperator = new DirOperatorWidget(p); - p->addTab(dirOperator, i18n("Filesystem Browser")); + p->addTab(dirOperator, KIcon("system-file-manager"), i18n("Filesystem Browser")); connect(dirOperator->dirOperator, SIGNAL(fileSelected(KFileItem)), p, SLOT(fileSelected(KFileItem))); /** set minimum width of widget depending on tab's text width */ diff -Nru kbibtex-0.3/src/program/elementform.cpp kbibtex-0.4/src/program/elementform.cpp --- kbibtex-0.3/src/program/elementform.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/elementform.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2009 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#include -#include - -#include -#include - -#include -#include -#include -#include "elementform.h" - -class ElementForm::ElementFormPrivate -{ -private: - ElementForm *p; - QGridLayout *layout; - Entry emptyElement; - Element *element; - const File *file; - -public: - ElementEditor *elementEditor; - MDIWidget *mdiWidget; - KPushButton *buttonApply, *buttonReset; - - ElementFormPrivate(ElementForm *parent) - : p(parent), element(NULL), file(NULL), elementEditor(NULL) { - layout = new QGridLayout(p); - layout->setColumnStretch(0, 1); - layout->setColumnStretch(1, 0); - layout->setColumnStretch(2, 0); - - buttonApply = new KPushButton(KIcon("apply"), i18n("Apply"), p); - layout->addWidget(buttonApply, 1, 1, 1, 1); - - buttonReset = new KPushButton(KIcon("reset"), i18n("Reset"), p); - layout->addWidget(buttonReset, 1, 2, 1, 1); - - loadElement(NULL, NULL); - - connect(buttonApply, SIGNAL(clicked()), p, SIGNAL(elementModified())); - connect(buttonApply, SIGNAL(clicked()), p, SLOT(modificationCleared())); - connect(buttonReset, SIGNAL(clicked()), p, SLOT(modificationCleared())); - } - - void refreshElement() { - loadElement(element, file); - } - - void loadElement(Element *element, const File *file) { - /// store both element and file for later refresh - this->element = element; - this->file = file; - - /// skip whole process of loading an element if not visible - if (isVisible()) - p->setEnabled(true); - else { - p->setEnabled(false); - return; - } - - /// recreate and reset element editor - if (elementEditor != NULL) - delete elementEditor; - elementEditor = element == NULL ? new ElementEditor(&emptyElement, file, p) : new ElementEditor(element, file, p); - layout->addWidget(elementEditor, 0, 0, 1, 3); - elementEditor->setEnabled(element != NULL); - elementEditor->layout()->setMargin(0); - connect(elementEditor, SIGNAL(modified(bool)), p, SLOT(modified())); - - /// make apply and reset buttons aware of new element editor - buttonApply->setEnabled(false); - buttonReset->setEnabled(false); - connect(buttonApply, SIGNAL(clicked()), elementEditor, SLOT(apply())); - connect(buttonReset, SIGNAL(clicked()), elementEditor, SLOT(reset())); - } - - bool isVisible() { - /// get dock where this widget is inside - /// static cast is save as constructor requires parent to be QDockWidget - QDockWidget *pp = static_cast(p->parent()); - return pp != NULL && !pp->isHidden(); - } -}; - -ElementForm::ElementForm(MDIWidget *mdiWidget, QDockWidget *parent) - : QWidget(parent), d(new ElementFormPrivate(this)) -{ - connect(parent, SIGNAL(visibilityChanged(bool)), this, SLOT(visibilityChanged(bool))); - d->mdiWidget = mdiWidget; -} - -void ElementForm::setElement(Element* element, const File *file) -{ - d->loadElement(element, file); -} - -void ElementForm::modified() -{ - d->buttonApply->setEnabled(true); - d->buttonReset->setEnabled(true); -} - -void ElementForm::modificationCleared() -{ - d->buttonApply->setEnabled(false); - d->buttonReset->setEnabled(false); -} - -void ElementForm::visibilityChanged(bool) -{ - d->refreshElement(); -} diff -Nru kbibtex-0.3/src/program/elementform.h kbibtex-0.4/src/program/elementform.h --- kbibtex-0.3/src/program/elementform.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/elementform.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2009 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#ifndef KBIBTEX_PROGRAM_ELEMENTFORM_H -#define KBIBTEX_PROGRAM_ELEMENTFORM_H - - -#include - -class QDockWidget; - -class MDIWidget; -class Element; -class File; - -class ElementForm : public QWidget -{ - Q_OBJECT - -public: - ElementForm(MDIWidget *mdiWidget, QDockWidget *parent); - -public slots: - void setElement(Element*, const File *); - -signals: - void elementModified(); - -private: - class ElementFormPrivate; - ElementFormPrivate *d; - -private slots: - void modified(); - void modificationCleared(); - void visibilityChanged(bool); -}; - -#endif // KBIBTEX_PROGRAM_ELEMENTFORM_H diff -Nru kbibtex-0.3/src/program/kbibtex.desktop kbibtex-0.4/src/program/kbibtex.desktop --- kbibtex-0.3/src/program/kbibtex.desktop 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/kbibtex.desktop 2011-11-20 20:36:55.000000000 +0000 @@ -1,3 +1,4 @@ +#!/usr/bin/env xdg-open [Desktop Entry] Encoding=UTF-8 Name=KBibTeX diff -Nru kbibtex-0.3/src/program/mainwindow.cpp kbibtex-0.4/src/program/mainwindow.cpp --- kbibtex-0.3/src/program/mainwindow.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/mainwindow.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -36,6 +36,7 @@ #include #include +#include "preferences/kbibtexpreferencesdialog.h" #include "mainwindow.h" #include "valuelist.h" #include "documentlist.h" @@ -134,7 +135,7 @@ d->dockReferencePreview->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); showPanelsMenu->addAction(d->dockReferencePreview->toggleViewAction()); - d->dockUrlPreview = new QDockWidget(i18n("Preview"), this); + d->dockUrlPreview = new QDockWidget(i18n("Document Preview"), this); d->dockUrlPreview->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::RightDockWidgetArea, d->dockUrlPreview); d->urlPreview = new UrlPreview(d->dockUrlPreview); @@ -186,6 +187,7 @@ d->actionClose = actionCollection()->addAction(KStandardAction::Close, this, SLOT(closeDocument())); d->actionClose->setEnabled(false); actionCollection()->addAction(KStandardAction::Quit, kapp, SLOT(quit())); + actionCollection()->addAction(KStandardAction::Preferences, this, SLOT(showPreferences())); documentListsChanged(OpenFileInfo::RecentlyUsed); /// force initialization of menu of recently used files @@ -253,7 +255,9 @@ void KBibTeXMainWindow::openDocumentDialog() { - QString startDir = QString();// QLatin1String(":open"); // FIXME: Does not work yet + OpenFileInfo *currFile = d->openFileInfoManager->currentFile(); + KUrl currFileUrl = currFile == NULL ? KUrl() : currFile->url(); + QString startDir = currFileUrl.isValid() ? KUrl(currFileUrl.url()).path() : QLatin1String("kfiledialog:///opensave"); OpenFileInfo *ofi = d->openFileInfoManager->currentFile(); if (ofi != NULL) { KUrl url = ofi->url(); @@ -279,6 +283,12 @@ d->openFileInfoManager->close(d->openFileInfoManager->currentFile()); } +void KBibTeXMainWindow::showPreferences() +{ + KBibTeXPreferencesDialog dlg(this); + dlg.exec(); +} + void KBibTeXMainWindow::documentSwitched(BibTeXEditor *oldEditor, BibTeXEditor *newEditor) { OpenFileInfo *openFileInfo = OpenFileInfoManager::getOpenFileInfoManager()->currentFile(); @@ -294,6 +304,7 @@ disconnect(oldEditor, SIGNAL(currentElementChanged(Element*, const File *)), d->referencePreview, SLOT(setElement(Element*, const File *))); disconnect(oldEditor, SIGNAL(currentElementChanged(Element*, const File *)), d->elementForm, SLOT(setElement(Element*, const File *))); disconnect(oldEditor, SIGNAL(currentElementChanged(Element*, const File *)), d->urlPreview, SLOT(setElement(Element*, const File *))); + disconnect(oldEditor, SIGNAL(currentElementChanged(Element*, const File *)), d->searchForm, SLOT(setElement(Element*, const File *))); disconnect(oldEditor, SIGNAL(modified()), d->valueList, SLOT(update())); disconnect(d->elementForm, SIGNAL(elementModified()), newEditor, SLOT(externalModification())); } @@ -301,6 +312,7 @@ connect(newEditor, SIGNAL(currentElementChanged(Element*, const File *)), d->referencePreview, SLOT(setElement(Element*, const File *))); connect(newEditor, SIGNAL(currentElementChanged(Element*, const File *)), d->elementForm, SLOT(setElement(Element*, const File *))); connect(newEditor, SIGNAL(currentElementChanged(Element*, const File *)), d->urlPreview, SLOT(setElement(Element*, const File *))); + connect(newEditor, SIGNAL(currentElementChanged(Element*, const File *)), d->searchForm, SLOT(setElement(Element*, const File *))); connect(newEditor, SIGNAL(modified()), d->valueList, SLOT(update())); connect(d->elementForm, SIGNAL(elementModified()), newEditor, SLOT(externalModification())); } @@ -310,6 +322,7 @@ d->elementForm->setElement(NULL, NULL); d->urlPreview->setElement(NULL, NULL); d->valueList->setEditor(newEditor); + d->referencePreview->setEditor(newEditor); } void KBibTeXMainWindow::showSearchResults() diff -Nru kbibtex-0.3/src/program/mainwindow.h kbibtex-0.4/src/program/mainwindow.h --- kbibtex-0.3/src/program/mainwindow.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/mainwindow.h 2011-11-20 20:36:55.000000000 +0000 @@ -58,6 +58,7 @@ void newDocument(); void openDocumentDialog(); void closeDocument(); + void showPreferences(); void documentSwitched(BibTeXEditor*, BibTeXEditor*); private slots: diff -Nru kbibtex-0.3/src/program/mdiwidget.cpp kbibtex-0.4/src/program/mdiwidget.cpp --- kbibtex-0.3/src/program/mdiwidget.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/mdiwidget.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -154,6 +154,9 @@ kDebug() << "Url changed from " << oldUrl.pathOrUrl() << " to " << newUrl.pathOrUrl() << endl; OpenFileInfoManager::getOpenFileInfoManager()->changeUrl(ofi, newUrl); + /// completely opened or saved files should be marked as "recently used" + ofi->addFlags(OpenFileInfo::RecentlyUsed); + emit setCaption(QString("%1 [%2]").arg(ofi->shortCaption()).arg(ofi->fullCaption())); } } diff -Nru kbibtex-0.3/src/program/openfileinfo.cpp kbibtex-0.4/src/program/openfileinfo.cpp --- kbibtex-0.3/src/program/openfileinfo.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/openfileinfo.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -108,7 +108,6 @@ return NULL; } - kDebug() << "using service " << newServicePtr->name() << "(name) " << newServicePtr->library() << "(library)"; part = newServicePtr->createInstance(newWidgetParent, (QObject*)newWidgetParent); if (part == NULL) { /// creating a read-write part failed, so maybe it is read-only (like Okular's PDF viewer)? @@ -302,7 +301,6 @@ KService::Ptr OpenFileInfo::defaultService() { const QString mt = mimeType(); - kDebug() << "Looking for service for mimetype " << mt; KService::Ptr result = KMimeTypeTrader::self()->preferredService(mt, QLatin1String("KParts/ReadWritePart")); if (result.isNull()) result = KMimeTypeTrader::self()->preferredService(mt, QLatin1String("KParts/ReadOnlyPart")); @@ -358,7 +356,7 @@ } void readConfig(OpenFileInfo::StatusFlag statusFlag, const QString& configGroupName, int maxNumFiles) { - KSharedConfig::Ptr config = KGlobal::config(); + KSharedConfigPtr config = KSharedConfig::openConfig("kbibtexrc"); KConfigGroup cg(config, configGroupName); for (int i = 0; i < maxNumFiles; ++i) { @@ -375,7 +373,7 @@ } void writeConfig(OpenFileInfo::StatusFlag statusFlag, const QString& configGroupName, int maxNumFiles) { - KSharedConfig::Ptr config = KGlobal::config(); + KSharedConfigPtr config = KSharedConfig::openConfig("kbibtexrc"); KConfigGroup cg(config, configGroupName); QList list = p->filteredItems(statusFlag); diff -Nru kbibtex-0.3/src/program/program.cpp kbibtex-0.4/src/program/program.cpp --- kbibtex-0.3/src/program/program.cpp 2011-06-02 09:38:02.000000000 +0000 +++ kbibtex-0.4/src/program/program.cpp 2011-11-20 20:36:55.000000000 +0000 @@ -21,12 +21,13 @@ #include #include #include +#include #include "program.h" #include "mainwindow.h" #include "version.h" -const char *programVersion = "0.3"; +const char *programVersion = "0.4"; const char *description = I18N_NOOP("A BibTeX editor for KDE"); const char *programHomepage = I18N_NOOP("http://home.gna.org/kbibtex/"); const char *bugTrackerHomepage = "https://gna.org/bugs/?group=kbibtex"; @@ -34,7 +35,7 @@ int main(int argc, char *argv[]) { - KAboutData aboutData("kbibtex", 0, ki18n("KBibTeX"), programVersion, + KAboutData aboutData("kbibtex", 0, ki18n("KBibTeX"), "0.4", ki18n(description), KAboutData::License_GPL_V2, ki18n("Copyright 2004-2011 Thomas Fischer"), KLocalizedString(), programHomepage, bugTrackerHomepage); @@ -52,11 +53,15 @@ KGlobal::locale()->insertCatalog("libkbibtexgui"); KGlobal::locale()->insertCatalog("libkbibtexws"); - // started by session management? + KService::Ptr service = KService::serviceByDesktopPath("kbibtexpart.desktop"); + if (service.isNull()) + KMessageBox::error(NULL, i18n("KBibTeX seems to be not installed completely. KBibTeX could not locate its own KPart.\n\nOnly limited functionality will be available."), i18n("Incomplete KBibTeX Installation")); + + /// started by session management? if (programCore.isSessionRestored()) { RESTORE(KBibTeXMainWindow()); } else { - // no session.. just start up normally + /// no session.. just start up normally KBibTeXMainWindow *mainWindow = new KBibTeXMainWindow(); KCmdLineArgs *arguments = KCmdLineArgs::parsedArgs(); diff -Nru kbibtex-0.3/src/program/referencepreview.cpp kbibtex-0.4/src/program/referencepreview.cpp --- kbibtex-0.3/src/program/referencepreview.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/referencepreview.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,318 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2009 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "referencepreview.h" - -const QString notAvailableMessage = "

" + i18n("No preview available") + "

" + i18n("Reason:") + " %1

"; //FIXME: Font size seems to be too small -class ReferencePreview::ReferencePreviewPrivate -{ -private: - ReferencePreview *p; - -public: - KPushButton *buttonOpen, *buttonSaveAsHTML; - QString htmlText; - QUrl baseUrl; - QWebView *webView; - KComboBox *comboBox; - const Element* element; - const File *file; - - ReferencePreviewPrivate(ReferencePreview *parent) - : p(parent), element(NULL) { - QGridLayout *gridLayout = new QGridLayout(p); - gridLayout->setMargin(0); - gridLayout->setColumnStretch(0, 1); - gridLayout->setColumnStretch(1, 0); - gridLayout->setColumnStretch(2, 0); - - comboBox = new KComboBox(p); - gridLayout->addWidget(comboBox, 0, 0, 1, 3); - - QFrame *frame = new QFrame(p); - gridLayout->addWidget(frame, 1, 0, 1, 3); - frame->setFrameShadow(QFrame::Sunken); - frame->setFrameShape(QFrame::StyledPanel); - - QVBoxLayout *layout = new QVBoxLayout(frame); - layout->setMargin(0); - webView = new QWebView(frame); - layout->addWidget(webView); - - buttonOpen = new KPushButton(KIcon("document-open"), i18n("Open"), p); - buttonOpen->setToolTip(i18n("Open reference in web browser.")); - gridLayout->addWidget(buttonOpen, 2, 1, 1, 1); - connect(buttonOpen, SIGNAL(clicked()), p, SLOT(openAsHTML())); - - buttonSaveAsHTML = new KPushButton(KIcon("document-save"), i18n("Save as HTML"), p); - buttonSaveAsHTML->setToolTip(i18n("Save reference as HTML fragment.")); - gridLayout->addWidget(buttonSaveAsHTML, 2, 2, 1, 1); - connect(buttonSaveAsHTML, SIGNAL(clicked()), p, SLOT(saveAsHTML())); - - comboBox->addItem(i18n("Source")); - comboBox->addItem(i18n("abbrv (bibtex2html)")); - comboBox->addItem(i18n("acm (bibtex2html)")); - comboBox->addItem(i18n("alpha (bibtex2html)")); - comboBox->addItem(i18n("apalike (bibtex2html)")); - comboBox->addItem(i18n("ieeetr (bibtex2html)")); - comboBox->addItem(i18n("plain (bibtex2html)")); - comboBox->addItem(i18n("siam (bibtex2html)")); - comboBox->addItem(i18n("unsrt (bibtex2html)")); - comboBox->addItem(i18n("standard (XML/XSLT)")); - comboBox->addItem(i18n("fancy (XML/XSLT)")); - connect(comboBox, SIGNAL(currentIndexChanged(int)), p, SLOT(renderHTML())); - } - - bool saveHTML(const KUrl& url) { - KTemporaryFile file; - file.setAutoRemove(true); - - bool result = saveHTML(file); - - if (result) { - KIO::NetAccess::del(url, p); /// ignore error if file does not exist - result = KIO::NetAccess::file_copy(KUrl(file.fileName()), url, p); - } - - return result; - } - - bool saveHTML(KTemporaryFile &file) { - if (file.open()) { - QTextStream ts(&file); - ts << htmlText; - file.close(); - return true; - } - - return false; - } - -}; - -ReferencePreview::ReferencePreview(QWidget *parent) - : QWidget(parent), d(new ReferencePreviewPrivate(this)) -{ - setEnabled(false); -} - -void ReferencePreview::setHtml(const QString & html, const QUrl & baseUrl) -{ - d->htmlText = html; - d->baseUrl = baseUrl; - d->webView->setHtml(html, baseUrl); - d->buttonOpen->setEnabled(true); - d->buttonSaveAsHTML->setEnabled(true); -} - -void ReferencePreview::setEnabled(bool enabled) -{ - if (enabled) - setHtml(d->htmlText, d->baseUrl); - else { - d->webView->setHtml(notAvailableMessage.arg(i18n("Preview disabled")), d->baseUrl); - d->buttonOpen->setEnabled(false); - d->buttonSaveAsHTML->setEnabled(false); - } - d->webView->setEnabled(enabled); - d->comboBox->setEnabled(enabled); -} - -void ReferencePreview::setElement(Element* element, const File *file) -{ - d->element = element; - d->file = file; - renderHTML(); -} - -void ReferencePreview::renderHTML() -{ - enum { ignore, /// do not include crossref'ed entry's values (one entry) - add, /// feed both the current entry as well as the crossref'ed entry into the exporter (two entries) - merge /// merge the crossref'ed entry's values into the current entry (one entry) - } crossRefHandling = ignore; - - if (d->element == NULL) { - d->webView->setHtml(notAvailableMessage.arg(i18n("No element selected")), d->baseUrl); - d->buttonOpen->setEnabled(false); - d->buttonSaveAsHTML->setEnabled(false); - return; - } - - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - - QStringList errorLog; - FileExporter *exporter = NULL; - - if (d->comboBox->currentIndex() == 0) - exporter = new FileExporterBibTeX(); - else if (d->comboBox->currentIndex() < 9) { - crossRefHandling = merge; - FileExporterBibTeX2HTML *exporterHTML = new FileExporterBibTeX2HTML(); - switch (d->comboBox->currentIndex()) { - case 1: /// BibTeX2HTML (abbrv) - exporterHTML->setLaTeXBibliographyStyle(QLatin1String("abbrv")); - break; - case 2: /// BibTeX2HTML (acm) - exporterHTML->setLaTeXBibliographyStyle(QLatin1String("acm")); - break; - case 3: /// BibTeX2HTML (alpha) - exporterHTML->setLaTeXBibliographyStyle(QLatin1String("alpha")); - break; - case 4: /// BibTeX2HTML (apalike) - exporterHTML->setLaTeXBibliographyStyle(QLatin1String("apalike")); - break; - case 5: /// BibTeX2HTML (ieeetr) - exporterHTML->setLaTeXBibliographyStyle(QLatin1String("ieeetr")); - break; - case 6: /// BibTeX2HTML (plain) - exporterHTML->setLaTeXBibliographyStyle(QLatin1String("plain")); - break; - case 7: /// BibTeX2HTML (siam) - exporterHTML->setLaTeXBibliographyStyle(QLatin1String("siam")); - break; - case 8: /// BibTeX2HTML (unsrt) - exporterHTML->setLaTeXBibliographyStyle(QLatin1String("unsrt")); - break; - } - exporter = exporterHTML; - } else { - crossRefHandling = merge; - FileExporterXSLT *exporterXSLT = new FileExporterXSLT(); - switch (d->comboBox->currentIndex()) { - case 9: /// XML/XSLT (standard) - exporterXSLT->setXSLTFilename(KStandardDirs::locate("appdata", "standard.xsl")); - break; - case 10: /// XML/XSLT (fancy) - exporterXSLT->setXSLTFilename(KStandardDirs::locate("appdata", "fancy.xsl")); - break; - } - exporter = exporterXSLT; - } - - QBuffer buffer(this); - buffer.open(QBuffer::WriteOnly); - - const Entry *entry = dynamic_cast(d->element); - if (crossRefHandling == add && entry != NULL) { - QString crossRef = PlainTextValue::text(entry->value(QLatin1String("crossref")), d->file); - const Entry *crossRefEntry = dynamic_cast((d->file != NULL) ? d->file->containsKey(crossRef) : NULL); - if (crossRefEntry != NULL) { - File file; - file.append(new Entry(*entry)); - file.append(new Entry(*crossRefEntry)); - exporter->save(&buffer, &file, &errorLog); - } else - exporter->save(&buffer, d->element, &errorLog); - } else if (crossRefHandling == merge && entry != NULL) { - Entry *merged = Entry::resolveCrossref(*entry, d->file); - exporter->save(&buffer, merged, &errorLog); - delete merged; - } else - exporter->save(&buffer, d->element, &errorLog); - buffer.close(); - delete exporter; - - buffer.open(QBuffer::ReadOnly); - QTextStream ts(&buffer); - QString text = ts.readAll(); - buffer.close(); - - if (text.isEmpty()) { - /// something went wrong, no output ... - text = notAvailableMessage.arg(i18n("No HTML output generated")); - kDebug() << errorLog.join("\n"); - } - - if (d->comboBox->currentIndex() == 0) { - /// source - text.prepend("
"); //FIXME: Font size seems to be too small
-        text.append("
"); - } else if (d->comboBox->currentIndex() < 9) { - /// bibtex2html - - /// remove "generated by" line from HTML code if BibTeX2HTML was used - text.replace(QRegExp("

.*

"), ""); - text.replace(QRegExp("<[/]?(font)[^>]*>"), ""); - QRegExp reTable("^.*"); - reTable.setMinimal(true); - text.replace(reTable, ""); - text.replace(QRegExp(".*$"), ""); - QRegExp reAnchor("\\[ \\]"); - reAnchor.setMinimal(true); - text.replace(reAnchor, ""); - - /// beautify text - text.replace("``", "“"); - text.replace("''", "”"); - - text.prepend(""); - text.append(""); - } else { - /// XML/XSLT - text.replace("", ""); - text.prepend(""); - text.append(""); - } - - setHtml(text, d->baseUrl); - - QApplication::restoreOverrideCursor(); -} - -void ReferencePreview::openAsHTML() -{ - KTemporaryFile file; - file.setSuffix(".html"); - file.setAutoRemove(false); /// let file stay alive for browser - d->saveHTML(file); - QDesktopServices::openUrl(KUrl(file.fileName())); -} - -void ReferencePreview::saveAsHTML() -{ - KUrl url = KFileDialog::getSaveUrl(KUrl(), "text/html", this, i18n("Save as HTML")); - if (url.isValid()) - d->saveHTML(url); -} diff -Nru kbibtex-0.3/src/program/referencepreview.h kbibtex-0.4/src/program/referencepreview.h --- kbibtex-0.3/src/program/referencepreview.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/referencepreview.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2009 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#ifndef KBIBTEX_PROGRAM_REFERENCEPREVIEW_H -#define KBIBTEX_PROGRAM_REFERENCEPREVIEW_H - -#include -#include - -class Element; -class File; - -class ReferencePreview : public QWidget -{ - Q_OBJECT -public: - ReferencePreview(QWidget *parent); - - void setHtml(const QString & html, const QUrl & baseUrl = QUrl()); - void setEnabled(bool); - -public slots: - void setElement(Element*, const File *); - -private: - class ReferencePreviewPrivate; - ReferencePreviewPrivate *d; - -private slots: - void renderHTML(); - void openAsHTML(); - void saveAsHTML(); -}; - - -#endif // KBIBTEX_PROGRAM_REFERENCEPREVIEW_H diff -Nru kbibtex-0.3/src/program/searchform.cpp kbibtex-0.4/src/program/searchform.cpp --- kbibtex-0.3/src/program/searchform.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/searchform.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,376 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2010 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#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 -#include -#include -#include "searchform.h" - -const int HomepageRole = Qt::UserRole + 5; -const int WidgetRole = Qt::UserRole + 6; - -class SearchForm::SearchFormPrivate -{ -private: - SearchForm *p; - QStackedWidget *queryTermsStack; - QWidget *listContainer; - QListWidget *enginesList; - QLabel *whichEnginesLabel; - KAction *actionOpenHomepage; - -public: - MDIWidget *m; - SearchResults *sr; - QMap itemToWebSearch; - int runningSearches; - KPushButton *searchButton; - WebSearchQueryFormGeneral *generalQueryTermsForm; - QTabWidget *tabWidget; - - SearchFormPrivate(MDIWidget *mdiWidget, SearchResults *searchResults, SearchForm *parent) - : p(parent), whichEnginesLabel(NULL), m(mdiWidget), sr(searchResults), runningSearches(0) { - // nothing - } - - virtual ~SearchFormPrivate() { - // nothing - } - - WebSearchQueryFormAbstract *currentQueryForm() { - return static_cast(queryTermsStack->currentWidget()); - } - - QWidget* createQueryTermsStack(QWidget *parent) { - QWidget *container = new QWidget(parent); - QVBoxLayout *vLayout = new QVBoxLayout(container); - - whichEnginesLabel = new QLabel(container); - whichEnginesLabel->setWordWrap(true); - vLayout->addWidget(whichEnginesLabel); - vLayout->setStretch(0, 10); - connect(whichEnginesLabel, SIGNAL(linkActivated(QString)), p, SLOT(switchToEngines())); - - vLayout->addSpacing(8); - - queryTermsStack = new QStackedWidget(parent); - vLayout->addWidget(queryTermsStack); - queryTermsStack->addWidget(createGeneralQueryTermsForm(queryTermsStack)); - connect(queryTermsStack, SIGNAL(currentChanged(int)), p, SLOT(currentStackWidgetChanged(int))); - - vLayout->addStretch(100); - - return container; - } - - QWidget* createGeneralQueryTermsForm(QWidget *parent) { - generalQueryTermsForm = new WebSearchQueryFormGeneral(parent); - return generalQueryTermsForm; - } - - QWidget* createEnginesGUI(QWidget *parent) { - listContainer = new QWidget(parent); - QGridLayout *layout = new QGridLayout(listContainer); - layout->setRowStretch(0, 1); - layout->setRowStretch(1, 0); - - enginesList = new QListWidget(listContainer); - layout->addWidget(enginesList, 0, 0, 1, 1); - connect(enginesList, SIGNAL(itemChanged(QListWidgetItem*)), p, SLOT(itemCheckChanged())); - connect(enginesList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), p, SLOT(enginesListCurrentChanged(QListWidgetItem*, QListWidgetItem*))); - enginesList->setSelectionMode(QAbstractItemView::NoSelection); - - actionOpenHomepage = new KAction(KIcon("internet-web-browser"), i18n("Go to Homepage"), p); - connect(actionOpenHomepage, SIGNAL(triggered()), p, SLOT(openHomepage())); - enginesList->addAction(actionOpenHomepage); - enginesList->setContextMenuPolicy(Qt::ActionsContextMenu); - - return listContainer; - } - - void createGUI() { - QGridLayout *layout = new QGridLayout(p); - layout->setMargin(0); - layout->setRowStretch(0, 1); - layout->setRowStretch(1, 0); - layout->setColumnStretch(0, 1); - layout->setColumnStretch(1, 0); - - tabWidget = new QTabWidget(p); - layout->addWidget(tabWidget, 0, 0, 1, 2); - connect(tabWidget, SIGNAL(currentChanged(int)), p, SLOT(tabSwitched(int))); - - QWidget *widget = createQueryTermsStack(tabWidget); - tabWidget->addTab(widget, KIcon("edit-rename"), i18n("Query Terms")); - - QWidget *listContainer = createEnginesGUI(tabWidget); - tabWidget->addTab(listContainer, KIcon("applications-engineering"), i18n("Engines")); - - searchButton = new KPushButton(KIcon("edit-find"), i18n("Search"), p); - layout->addWidget(searchButton, 1, 1, 1, 1); - connect(generalQueryTermsForm, SIGNAL(returnPressed()), searchButton, SIGNAL(clicked())); - - loadEngines(); - updateGUI(); - } - - void loadEngines() { - enginesList->clear(); - - addEngine(new WebSearchBibsonomy(p)); - addEngine(new WebSearchGoogleScholar(p)); - addEngine(new WebSearchArXiv(p)); - - p->itemCheckChanged(); - updateGUI(); - } - - void addEngine(WebSearchAbstract *engine) { - QListWidgetItem *item = new QListWidgetItem(engine->label(), enginesList); - item->setCheckState(Qt::Checked); - item->setIcon(engine->icon()); - item->setData(HomepageRole, engine->homepage()); - - WebSearchQueryFormAbstract *widget = engine->customWidget(queryTermsStack); - item->setData(WidgetRole, QVariant::fromValue(widget)); - if (widget != NULL) - queryTermsStack->addWidget(widget); - - itemToWebSearch.insert(item, engine); - connect(engine, SIGNAL(foundEntry(Entry*)), p, SLOT(foundEntry(Entry*))); - connect(engine, SIGNAL(stoppedSearch(int)), p, SLOT(stoppedSearch(int))); - } - - void switchToSearch() { - for (QMap::ConstIterator it = itemToWebSearch.constBegin(); it != itemToWebSearch.constEnd(); ++it) - disconnect(searchButton, SIGNAL(clicked()), it.value(), SLOT(cancel())); - - connect(searchButton, SIGNAL(clicked()), p, SLOT(startSearch())); - searchButton->setText(i18n("Search")); - searchButton->setIcon(KIcon("media-playback-start")); - tabWidget->setEnabled(true); - sr->setEnabled(true); - tabWidget->unsetCursor(); - sr->unsetCursor(); - } - - void switchToCancel() { - disconnect(searchButton, SIGNAL(clicked()), p, SLOT(startSearch())); - - for (QMap::ConstIterator it = itemToWebSearch.constBegin(); it != itemToWebSearch.constEnd(); ++it) - connect(searchButton, SIGNAL(clicked()), it.value(), SLOT(cancel())); - searchButton->setText(i18n("Cancel")); - searchButton->setIcon(KIcon("media-playback-stop")); - tabWidget->setEnabled(false); - sr->setEnabled(false); - tabWidget->setCursor(Qt::WaitCursor); - sr->setCursor(Qt::WaitCursor); - } - - void switchToEngines() { - tabWidget->setCurrentWidget(listContainer); - } - - void updateGUI() { - if (whichEnginesLabel == NULL) return; - - QStringList checkedEngines; - QListWidgetItem *cursor = NULL; - for (QMap::ConstIterator it = itemToWebSearch.constBegin(); it != itemToWebSearch.constEnd(); ++it) - if (it.key()->checkState() == Qt::Checked) { - checkedEngines << it.key()->text(); - cursor = it.key(); - } - - switch (checkedEngines.size()) { - case 0: whichEnginesLabel->setText(i18n("No search engine selected. Change"));break; - case 1: whichEnginesLabel->setText(i18n("Search engine %1 is selected. Change", checkedEngines.first()));break; - case 2: whichEnginesLabel->setText(i18n("Search engines %1 and %2 are selected. Change", checkedEngines.first(), checkedEngines.at(1)));break; - case 3: whichEnginesLabel->setText(i18n("Search engines %1, %2, and %3 are selected. Change", checkedEngines.first(), checkedEngines.at(1), checkedEngines.at(2)));break; - default: whichEnginesLabel->setText(i18n("Search engines %1, %2, and more are selected. Change", checkedEngines.first(), checkedEngines.at(1)));break; - } - - WebSearchQueryFormAbstract *currentQueryWidget = NULL; - if (checkedEngines.size() == 1) - currentQueryWidget = cursor->data(WidgetRole).value(); - if (currentQueryWidget == NULL) - currentQueryWidget = generalQueryTermsForm; - queryTermsStack->setCurrentWidget(currentQueryWidget); - } - - void openHomepage() { - QListWidgetItem *item = enginesList->currentItem(); - if (item != NULL) { - KUrl url = item->data(HomepageRole).value(); - QDesktopServices::openUrl(url); // TODO KDE way? - } - } - - void enginesListCurrentChanged(QListWidgetItem *current) { - actionOpenHomepage->setEnabled(current != NULL); - } - - void currentStackWidgetChanged(int index) { - for (int i = queryTermsStack->count() - 1; i >= 0; --i) { - WebSearchQueryFormAbstract *wsqfa = static_cast(queryTermsStack->widget(i)); - if (i == index) - connect(wsqfa, SIGNAL(returnPressed()), searchButton, SLOT(click())); - else - disconnect(wsqfa, SIGNAL(returnPressed()), searchButton, SLOT(click())); - } - } -}; - -SearchForm::SearchForm(MDIWidget *mdiWidget, SearchResults *searchResults, QWidget *parent) - : QWidget(parent), d(new SearchFormPrivate(mdiWidget, searchResults, this)) -{ - d->createGUI(); - d->switchToSearch(); -} - -void SearchForm::updatedConfiguration() -{ - d->loadEngines(); -} - -void SearchForm::switchToEngines() -{ - d->switchToEngines(); -} - -void SearchForm::startSearch() -{ - WebSearchQueryFormAbstract *currentForm = d->currentQueryForm(); - if (!currentForm->readyToStart()) { - KMessageBox::sorry(this, i18n("Could not start searching the Internet:\nThe search terms are not complete or invalid."), i18n("Searching the Internet")); - return; - } - - d->runningSearches = 0; - d->sr->clear(); - - if (currentForm == d->generalQueryTermsForm) { - /// start search using the general-purpose form's values - - QMap queryTerms = d->generalQueryTermsForm->getQueryTerms(); - int numResults = d->generalQueryTermsForm->getNumResults(); - for (QMap::ConstIterator it = d->itemToWebSearch.constBegin(); it != d->itemToWebSearch.constEnd(); ++it) - if (it.key()->checkState() == Qt::Checked) { - it.value()->startSearch(queryTerms, numResults); - ++d->runningSearches; - } - if (d->runningSearches <= 0) { - /// if no search engine has been checked (selected), something went wrong - return; - } - } else { - /// use the single selected search engine's specific form - - for (QMap::ConstIterator it = d->itemToWebSearch.constBegin(); it != d->itemToWebSearch.constEnd(); ++it) - if (it.key()->checkState() == Qt::Checked) { - it.value()->startSearch(); - ++d->runningSearches; - } - if (d->runningSearches <= 0) { - /// if no search engine has been checked (selected), something went wrong - return; - } - } - - d->switchToCancel(); -} - -void SearchForm::foundEntry(Entry*entry) -{ - d->sr->insertElement(entry); -} - -void SearchForm::stoppedSearch(int resultCode) -{ - WebSearchAbstract *engine = static_cast(sender()); - kDebug() << " search from engine " << engine->label() << " stopped with code " << resultCode << " (" << (resultCode == 0 ? "OK)" : "Error)"); - - --d->runningSearches; - if (d->runningSearches <= 0) { - /// last search engine stopped - d->switchToSearch(); - emit doneSearching(); - } -} - -void SearchForm::tabSwitched(int newTab) -{ - Q_UNUSED(newTab); - d->updateGUI(); -} - -void SearchForm::itemCheckChanged() -{ - int numCheckedEngines = 0; - for (QMap::ConstIterator it = d->itemToWebSearch.constBegin(); it != d->itemToWebSearch.constEnd(); ++it) - if (it.key()->checkState() == Qt::Checked) - ++numCheckedEngines; - - d->searchButton->setEnabled(numCheckedEngines > 0); -} - -void SearchForm::openHomepage() -{ - d->openHomepage(); -} - -void SearchForm::enginesListCurrentChanged(QListWidgetItem *current, QListWidgetItem *) -{ - d->enginesListCurrentChanged(current); -} - -void SearchForm::currentStackWidgetChanged(int index) -{ - d->currentStackWidgetChanged(index); -} diff -Nru kbibtex-0.3/src/program/searchform.h kbibtex-0.4/src/program/searchform.h --- kbibtex-0.3/src/program/searchform.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/searchform.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2009 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#ifndef KBIBTEX_PROGRAM_SEARCHFORM_H -#define KBIBTEX_PROGRAM_SEARCHFORM_H - -#include - -class QListWidgetItem; - -class Entry; -class MDIWidget; -class SearchResults; - -class SearchForm : public QWidget -{ - Q_OBJECT - -public: - SearchForm(MDIWidget *mdiWidget, SearchResults *searchResults, QWidget *parent); - -signals: - void doneSearching(); - -public slots: - void updatedConfiguration(); - -private: - class SearchFormPrivate; - SearchFormPrivate *d; - -private slots: - void switchToEngines(); - void startSearch(); - void foundEntry(Entry *entry); - void stoppedSearch(int resultCode); - void tabSwitched(int newTab); - void itemCheckChanged(); - void openHomepage(); - void enginesListCurrentChanged(QListWidgetItem*, QListWidgetItem*); - void currentStackWidgetChanged(int); -}; - - -#endif // KBIBTEX_PROGRAM_SEARCHFORM_H diff -Nru kbibtex-0.3/src/program/searchresults.cpp kbibtex-0.4/src/program/searchresults.cpp --- kbibtex-0.3/src/program/searchresults.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/searchresults.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,146 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2010 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include "searchresults.h" - -class SearchResults::SearchResultsPrivate -{ -private: - SearchResults *p; - Clipboard *clipboard; - -public: - MDIWidget *m; - BibTeXEditor *currentFile; - KPushButton *buttonImport; - BibTeXEditor *editor; - KAction *actionViewCurrent, *actionImportSelected, *actionCopySelected; - - SearchResultsPrivate(MDIWidget *mdiWidget, SearchResults *parent) - : p(parent), m(mdiWidget), currentFile(NULL) { - QGridLayout *layout = new QGridLayout(parent); - layout->setColumnStretch(0, 1); - layout->setColumnStretch(1, 0); - - editor = new BibTeXEditor(parent); - editor->setReadOnly(true); - editor->setFrameShadow(QFrame::Sunken); - editor->setFrameShape(QFrame::StyledPanel); - editor->setContextMenuPolicy(Qt::ActionsContextMenu); - layout->addWidget(editor, 0, 0, 1, 2); - - clipboard = new Clipboard(editor); - - buttonImport = new KPushButton(KIcon("svn-update"), i18n("Import"), parent); - layout->addWidget(buttonImport, 1, 1, 1, 1); - buttonImport->setEnabled(false); - - SortFilterBibTeXFileModel *model = new SortFilterBibTeXFileModel(parent); - model->setSourceModel(new BibTeXFileModel(parent)); - editor->setModel(model); - - actionViewCurrent = new KAction(KIcon("document-preview"), i18n("View Element"), parent); - editor->addAction(actionViewCurrent); - actionViewCurrent->setEnabled(false); - connect(actionViewCurrent, SIGNAL(triggered()), editor, SLOT(viewCurrentElement())); - - actionImportSelected = new KAction(KIcon("svn-update"), i18n("Import"), parent); - editor->addAction(actionImportSelected); - actionImportSelected->setEnabled(false); - connect(actionImportSelected, SIGNAL(triggered()), parent, SLOT(importSelected())); - - actionCopySelected = new KAction(KIcon("edit-copy"), i18n("Copy"), parent); - editor->addAction(actionCopySelected); - actionCopySelected->setEnabled(false); - connect(actionCopySelected, SIGNAL(triggered()), clipboard, SLOT(copy())); - - connect(editor, SIGNAL(doubleClicked(QModelIndex)), editor, SLOT(viewCurrentElement())); - connect(editor, SIGNAL(selectedElementsChanged()), parent, SLOT(updateGUI())); - connect(buttonImport, SIGNAL(clicked()), parent, SLOT(importSelected())); - } - - void clear() { - File *file = editor->bibTeXModel()->bibTeXFile(); - delete file; - editor->bibTeXModel()->setBibTeXFile(new File()); - } - - bool insertElement(Element *element) { - BibTeXFileModel *model = editor->bibTeXModel(); - return model->insertRow(element, model->rowCount()); - } - -}; - -SearchResults::SearchResults(MDIWidget *mdiWidget, QWidget *parent) - : QWidget(parent), d(new SearchResultsPrivate(mdiWidget, this)) -{ - // nothing -} - -void SearchResults::clear() -{ - d->clear(); -} - -bool SearchResults::insertElement(Element *element) -{ - return d->insertElement(element); -} - -void SearchResults::documentSwitched(BibTeXEditor *oldEditor, BibTeXEditor *newEditor) -{ - Q_UNUSED(oldEditor); - d->currentFile = newEditor; - updateGUI(); -} - -void SearchResults::updateGUI() -{ - d->buttonImport->setEnabled(d->currentFile != NULL && !d->editor->selectedElements().isEmpty()); - d->actionImportSelected->setEnabled(d->buttonImport->isEnabled()); - d->actionCopySelected->setEnabled(!d->editor->selectedElements().isEmpty()); - d->actionViewCurrent->setEnabled(d->editor->currentElement() != NULL); -} - -void SearchResults::importSelected() -{ - Q_ASSERT(d->currentFile != NULL); - - BibTeXFileModel *targetModel = d->currentFile->bibTeXModel(); - BibTeXFileModel *sourceModel = d->editor->bibTeXModel(); - QList selList = d->editor->selectionModel()->selectedRows(); - for (QList::ConstIterator it = selList.constBegin(); it != selList.constEnd(); ++it) { - int row = d->editor->sortFilterProxyModel()->mapToSource(*it).row(); - Element *element = sourceModel->element(row); - targetModel->insertRow(element, targetModel->rowCount()); - } -} diff -Nru kbibtex-0.3/src/program/searchresults.h kbibtex-0.4/src/program/searchresults.h --- kbibtex-0.3/src/program/searchresults.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/searchresults.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2010 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#ifndef KBIBTEX_PROGRAM_SEARCHRESULTS_H -#define KBIBTEX_PROGRAM_SEARCHRESULTS_H - -#include - -class MDIWidget; - -class Element; -class BibTeXEditor; - -class SearchResults : public QWidget -{ - Q_OBJECT - -public: - SearchResults(MDIWidget *mdiWidget, QWidget *parent); - - void clear(); - bool insertElement(Element *element); - -public slots: - void documentSwitched(BibTeXEditor*, BibTeXEditor*); - -private: - class SearchResultsPrivate; - SearchResultsPrivate *d; - -private slots: - void updateGUI(); - void importSelected(); -}; - -#endif // KBIBTEX_PROGRAM_SEARCHRESULTS_H diff -Nru kbibtex-0.3/src/program/urlpreview.cpp kbibtex-0.4/src/program/urlpreview.cpp --- kbibtex-0.3/src/program/urlpreview.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/urlpreview.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,314 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2010 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#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 -#include -#include -#include "urlpreview.h" - -class UrlPreview::UrlPreviewPrivate -{ -private: - UrlPreview *p; - KComboBox *urlComboBox; - KPushButton *externalViewerButton; - QCheckBox *onlyLocalFilesCheckBox; - QStackedWidget *stackedWidget; - QLabel *message; - QMap cbxEntryToUrl; - QMutex addingUrlMutex; - QString arXivPDFUrlStart; - bool anyLocal; - -public: - struct UrlInfo { - KUrl url; - QString mimeType; - KIcon icon; - }; - - QList runningJobs; - const Entry* entry; - KUrl baseUrl; - - UrlPreviewPrivate(UrlPreview *parent) - : p(parent), arXivPDFUrlStart("http://arxiv.org/pdf/"), entry(NULL) { - setupGUI(); - } - - /** - * Create user interface for this widget. - * It consists of some controlling widget on the top, - * but the most space is consumed by KPart widgets - * inside a QStackedWidget to show the external content - * (PDF file, web page, ...). - */ - void setupGUI() { - QVBoxLayout *layout = new QVBoxLayout(p); - layout->setMargin(0); - - /// some widgets on the top to control the view - - QHBoxLayout *innerLayout = new QHBoxLayout(); - layout->addLayout(innerLayout, 0); - urlComboBox = new KComboBox(false, p); - innerLayout->addWidget(urlComboBox, 1); - - externalViewerButton = new KPushButton(KIcon("document-open"), i18n("Open..."), p); - innerLayout->addWidget(externalViewerButton, 0); - - onlyLocalFilesCheckBox = new QCheckBox(i18n("Only local files"), p); - layout->addWidget(onlyLocalFilesCheckBox, 0); - - /// main part of the widget: A stacked widget to hold - /// one KPart widget per URL in above combo box - - stackedWidget = new QStackedWidget(p); - layout->addWidget(stackedWidget, 1); - stackedWidget->hide(); - - /// default widget if no preview is available - message = new QLabel(i18n("No preview available"), p); - message->setAlignment(Qt::AlignCenter); - layout->addWidget(message, 1); - - connect(externalViewerButton, SIGNAL(clicked()), p, SLOT(openExternally())); - connect(urlComboBox, SIGNAL(activated(int)), stackedWidget, SLOT(setCurrentIndex(int))); - connect(onlyLocalFilesCheckBox, SIGNAL(toggled(bool)), p, SLOT(onlyLocalFilesChanged())); - } - - bool addUrl(const struct UrlInfo &urlInfo) { - bool isLocal = urlInfo.url.isLocalFile(); - anyLocal |= isLocal; - - if (onlyLocalFilesCheckBox->isChecked() && !isLocal) return true; ///< ignore URL if only local files are allowed - - addingUrlMutex.lock(); - if (isLocal) { - /// create a drop-down list entry if file is a local file - /// (based on patch by Luis Silva) - QString fn = urlInfo.url.fileName(); - QString full = urlInfo.url.pathOrUrl(); - QString dir = urlInfo.url.directory(); - QString text = fn.isEmpty() ? full : (dir.isEmpty() ? fn : QString("%1 [%2]").arg(fn).arg(dir)); - urlComboBox->addItem(urlInfo.icon, text); - } else { - /// create a drop-down list entry if file is a remote file - urlComboBox->addItem(urlInfo.icon, urlInfo.url.prettyUrl()); - } - cbxEntryToUrl.insert(urlComboBox->count() - 1, urlInfo.url); - addingUrlMutex.unlock(); - - KParts::ReadOnlyPart* part = NULL; - KService::Ptr serivcePtr = KMimeTypeTrader::self()->preferredService(urlInfo.mimeType, "KParts/ReadOnlyPart"); - if (!serivcePtr.isNull()) - part = serivcePtr->createInstance((QWidget*)p, (QObject*)p); - if (part != NULL) { - stackedWidget->addWidget(part->widget()); - part->openUrl(urlInfo.url); - } else { - QLabel *label = new QLabel(i18n("Cannot create preview for\n%1\n\nNo part available.", urlInfo.url.pathOrUrl()), stackedWidget); - message->setAlignment(Qt::AlignCenter); - stackedWidget->addWidget(label); - } - - urlComboBox->setEnabled(true); - externalViewerButton->setEnabled(true); - if (isLocal || ///< local files always preferred over URLs - /// prefer arXiv summary URLs over other URLs - (!anyLocal && urlInfo.url.host().contains("arxiv.org/abs"))) { - urlComboBox->setCurrentIndex(urlComboBox->count() - 1); - stackedWidget->setCurrentIndex(urlComboBox->count() - 1); - } - message->hide(); - stackedWidget->show(); - - return true; - } - - void update() { - QCursor prevCursor = p->cursor(); - p->setCursor(Qt::WaitCursor); - - /// cancel/kill all running jobs - for (QList::ConstIterator it = runningJobs.constBegin(); it != runningJobs.constEnd(); ++it) - (*it)->kill(); - runningJobs.clear(); - /// remove all shown widgets/parts - urlComboBox->clear(); - while (stackedWidget->count() > 0) - stackedWidget->removeWidget(stackedWidget->currentWidget()); - urlComboBox->setEnabled(false); - externalViewerButton->setEnabled(false); - - /// clear flag that memorizes if any local file was referenced - anyLocal = false; - - /// do not load external reference if widget is hidden - if (isVisible()) { - QList urlList = FileInfo::entryUrls(entry, baseUrl); - for (QList::ConstIterator it = urlList.constBegin(); it != urlList.constEnd(); ++it) { - bool isLocal = (*it).isLocalFile(); - if (onlyLocalFilesCheckBox->isChecked() && !isLocal) continue; - - KIO::StatJob *job = KIO::stat(*it, KIO::StatJob::SourceSide, 3, KIO::HideProgressInfo); - runningJobs << job; - job->ui()->setWindow(p); - connect(job, SIGNAL(result(KJob*)), p, SLOT(statFinished(KJob*))); - } - } - - message->setText(i18n("No preview available")); - message->show(); - stackedWidget->hide(); - - p->setCursor(prevCursor); - } - - void openExternally() { - KUrl url(cbxEntryToUrl[urlComboBox->currentIndex()]); - QDesktopServices::openUrl(url); // TODO KDE way? - } - - UrlInfo urlMetaInfo(const KUrl &url) { - UrlInfo result; - result.url = url; - - if (!url.isLocalFile() && url.fileName().isEmpty()) { - /// URLs not pointing to a specific file should be opened with a web browser component - kDebug() << "Not pointing to file, falling back to text/html for url " << url.pathOrUrl(); - result.icon = KIcon("text-html"); - result.mimeType = QLatin1String("text/html"); - return result; - } - - int accuracy = 0; - KMimeType::Ptr mimeTypePtr = KMimeType::findByUrl(url, 0, url.isLocalFile(), true, &accuracy); - if (accuracy < 50) { - kDebug() << "discarding mime type " << mimeTypePtr->name() << ", trying filename "; - mimeTypePtr = KMimeType::findByPath(url.fileName(), 0, true, &accuracy); - } - result.mimeType = mimeTypePtr->name(); - result.icon = KIcon(mimeTypePtr->iconName()); - - if (result.mimeType == QLatin1String("application/octet-stream")) { - /// application/octet-stream is a fall-back if KDE did not know better - kDebug() << "Got mime type \"application/octet-stream\", falling back to text/html"; - result.icon = KIcon("text-html"); - result.mimeType = QLatin1String("text/html"); - } else if (result.mimeType == QLatin1String("inode/directory") && result.url.protocol() == QLatin1String("http")) { - /// directory via http means normal webpage (not browsable directory) - kDebug() << "Got mime type \"inode/directory\" via http, falling back to text/html"; - result.icon = KIcon("text-html"); - result.mimeType = QLatin1String("text/html"); - } - - if (url.pathOrUrl().startsWith(arXivPDFUrlStart)) { - kDebug() << "URL looks like a PDF url from arXiv"; - result.icon = KIcon("application-pdf"); - result.mimeType = QLatin1String("application/pdf"); - } - - kDebug() << "For url " << result.url.pathOrUrl() << " selected mime type " << result.mimeType; - return result; - } - - bool isVisible() { - /// get dock where this widget is inside - /// static cast is save as constructor requires parent to be QDockWidget - QDockWidget *pp = static_cast(p->parent()); - return pp != NULL && !pp->isHidden(); - } -}; - -UrlPreview::UrlPreview(QDockWidget *parent) - : QWidget(parent), d(new UrlPreviewPrivate(this)) -{ - connect(parent, SIGNAL(visibilityChanged(bool)), this, SLOT(visibilityChanged(bool))); -} - -void UrlPreview::setElement(Element* element, const File *) -{ - d->entry = dynamic_cast(element); - d->update(); -} - -void UrlPreview::openExternally() -{ - d->openExternally(); -} - -void UrlPreview::setBibTeXUrl(const KUrl&url) -{ - d->baseUrl = url; -} - -void UrlPreview::onlyLocalFilesChanged() -{ - d->update(); -} - -void UrlPreview::visibilityChanged(bool) -{ - d->update(); -} - -void UrlPreview::statFinished(KJob *kjob) -{ - KIO::StatJob *job = static_cast(kjob); - d->runningJobs.removeOne(job); - if (!job->error()) { - const KUrl url = job->mostLocalUrl(); - kDebug() << "stat succeeded for " << url.pathOrUrl(); - UrlPreviewPrivate::UrlInfo urlInfo = d->urlMetaInfo(url); - d->addUrl(urlInfo); - } else - kDebug() << job->errorString(); -} diff -Nru kbibtex-0.3/src/program/urlpreview.h kbibtex-0.4/src/program/urlpreview.h --- kbibtex-0.3/src/program/urlpreview.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/urlpreview.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2010 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#ifndef KBIBTEX_PROGRAM_URLPREVIEW_H -#define KBIBTEX_PROGRAM_URLPREVIEW_H - -#include - -#include - -class QDockWidget; - -class KJob; -namespace KIO -{ -class Job; -} - -class Element; -class File; - -class UrlPreview : public QWidget -{ - Q_OBJECT -public: - UrlPreview(QDockWidget *parent); - -public slots: - void setElement(Element*, const File *); - void setBibTeXUrl(const KUrl&); - -private: - class UrlPreviewPrivate; - UrlPreviewPrivate *d; - - QString mimeType(const KUrl &url); - -private slots: - void openExternally(); - void onlyLocalFilesChanged(); - void visibilityChanged(bool); - void statFinished(KJob*); -}; - -#endif // KBIBTEX_PROGRAM_URLPREVIEW_H diff -Nru kbibtex-0.3/src/program/valuelist.cpp kbibtex-0.4/src/program/valuelist.cpp --- kbibtex-0.3/src/program/valuelist.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/valuelist.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,145 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2010 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "valuelist.h" - -class ValueList::ValueListPrivate -{ -private: - ValueList *p; - -public: - BibTeXEditor *editor; - QTreeView *treeviewFieldValues; - QSortFilterProxyModel *sortingModel; - KComboBox *comboboxFieldNames; - const int countWidth; - - ValueListPrivate(ValueList *parent) - : p(parent), sortingModel(NULL), countWidth(parent->fontMetrics().width(QLatin1String("Count888"))) { - setupGUI(); - initialize(); - } - - void setupGUI() { - QGridLayout *layout = new QGridLayout(p); - - comboboxFieldNames = new KComboBox(true, p); - layout->addWidget(comboboxFieldNames, 0, 0, 1, 1); - - treeviewFieldValues = new QTreeView(p); - layout->addWidget(treeviewFieldValues, 1, 0, 1, 1); - treeviewFieldValues->setEditTriggers(QAbstractItemView::NoEditTriggers); - treeviewFieldValues->setSortingEnabled(true); - - p->setEnabled(false); - - connect(comboboxFieldNames, SIGNAL(activated(int)), p, SLOT(comboboxChanged())); - connect(treeviewFieldValues, SIGNAL(activated(const QModelIndex &)), p, SLOT(listItemActivated(const QModelIndex &))); - } - - void initialize() { - const BibTeXFields *bibtexFields = BibTeXFields::self(); - - comboboxFieldNames->clear(); - for (BibTeXFields::ConstIterator it = bibtexFields->constBegin(); it != bibtexFields->constEnd(); ++it) { - FieldDescription fd = *it; - if (!fd.upperCamelCaseAlt.isEmpty()) continue; /// keep only "single" fields and not combined ones like "Author or Editor" - if (fd.upperCamelCase.startsWith('^')) continue; /// skip "type" and "id" - comboboxFieldNames->addItem(fd.label, fd.upperCamelCase); - } - } - - void comboboxChanged() { - update(); - } - - void update() { - QVariant var = comboboxFieldNames->itemData(comboboxFieldNames->currentIndex()); - QString text = var.toString(); - if (text.isEmpty()) text = comboboxFieldNames->currentText(); - - QAbstractItemModel *model = editor == NULL ? NULL : editor->valueListModel(text); - if (model != NULL) { - if (sortingModel != NULL) delete sortingModel; - sortingModel = new QSortFilterProxyModel(p); - sortingModel->setSourceModel(model); - sortingModel->sort(1, Qt::DescendingOrder); - sortingModel->setSortRole(SortRole); - model = sortingModel; - } - treeviewFieldValues->setModel(model); - } -}; - -ValueList::ValueList(QWidget *parent) - : QWidget(parent), d(new ValueListPrivate(this)) -{ -} - -void ValueList::setEditor(BibTeXEditor *editor) -{ - d->editor = editor; - d->update(); - setEnabled(d->editor != NULL); - QTimer::singleShot(100, this, SLOT(resizeEvent())); -} - -void ValueList::resizeEvent(QResizeEvent */*event*/) -{ - int widgetWidth = d->treeviewFieldValues->size().width() - d->treeviewFieldValues->verticalScrollBar()->size().width(); - d->treeviewFieldValues->setColumnWidth(0, widgetWidth - d->countWidth * 4 / 3); - d->treeviewFieldValues->setColumnWidth(1, d->countWidth); -} - -void ValueList::comboboxChanged() -{ - d->comboboxChanged(); -} - -void ValueList::listItemActivated(const QModelIndex &index) -{ - QString itemText = d->sortingModel->mapToSource(index).data(Qt::DisplayRole).toString(); - QVariant fieldVar = d->comboboxFieldNames->itemData(d->comboboxFieldNames->currentIndex()); - QString fieldText = fieldVar.toString(); - if (fieldText.isEmpty()) fieldText = d->comboboxFieldNames->currentText(); - - SortFilterBibTeXFileModel::FilterQuery fq; - fq.terms << itemText; - fq.combination = SortFilterBibTeXFileModel::EveryTerm; - fq.field = fieldText; - - emit filterChanged(fq); -} diff -Nru kbibtex-0.3/src/program/valuelist.h kbibtex-0.4/src/program/valuelist.h --- kbibtex-0.3/src/program/valuelist.h 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/program/valuelist.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2004-2010 by Thomas Fischer * -* fischer@unix-ag.uni-kl.de * -* * -* 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) any later version. * -* * -* 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, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#ifndef KBIBTEX_PROGRAM_VALUELIST_H -#define KBIBTEX_PROGRAM_VALUELIST_H - -#include - -#include - -class Element; -class File; -class BibTeXEditor; - -class ValueList : public QWidget -{ - Q_OBJECT - -public: - ValueList(QWidget *parent); - - void setEditor(BibTeXEditor *editor); - -protected slots: - void resizeEvent(QResizeEvent *e = NULL); - -signals: - void filterChanged(SortFilterBibTeXFileModel::FilterQuery); - -private slots: - void comboboxChanged(); - void listItemActivated(const QModelIndex &); - -private: - class ValueListPrivate; - ValueListPrivate *d; -}; - -#endif // KBIBTEX_PROGRAM_VALUELIST_H diff -Nru kbibtex-0.3/src/websearch/CMakeLists.txt kbibtex-0.4/src/websearch/CMakeLists.txt --- kbibtex-0.3/src/websearch/CMakeLists.txt 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/websearch/CMakeLists.txt 2011-11-20 20:36:54.000000000 +0000 @@ -4,13 +4,19 @@ websearchabstract.cpp websearchbibsonomy.cpp websearcharxiv.cpp + websearchsciencedirect.cpp websearchgooglescholar.cpp + websearchieeexplore.cpp + websearchpubmed.cpp + websearchacmportal.cpp + websearchspringerlink.cpp + websearchjstor.cpp websearchgeneral.cpp ) add_definitions( -DMAKE_WEBSEARCH_LIB ) -# debug area for KBibTeX's IO library +# debug area for KBibTeX's web search library add_definitions(-DKDE_DEFAULT_DEBUG_AREA=101015) include_directories( @@ -19,12 +25,13 @@ kde4_add_library( kbibtexws SHARED ${websearch_LIB_SRCS} ) -target_link_libraries( kbibtexws +target_link_libraries( kbibtexws ${QT_QTCORE_LIBRARY} + ${QT_QTWEBKIT_LIBRARY} ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kbibtexio ) -install(TARGETS kbibtexws DESTINATION ${LIB_INSTALL_DIR}) +install(TARGETS kbibtexws RUNTIME DESTINATION bin LIBRARY DESTINATION ${LIB_INSTALL_DIR}) diff -Nru kbibtex-0.3/src/websearch/kbibtexws_export.h kbibtex-0.4/src/websearch/kbibtexws_export.h --- kbibtex-0.3/src/websearch/kbibtexws_export.h 1970-01-01 00:00:00.000000000 +0000 +++ kbibtex-0.4/src/websearch/kbibtexws_export.h 2011-11-20 20:36:54.000000000 +0000 @@ -0,0 +1,16 @@ +#ifndef KBIBTEXWS_EXPORT_H +#define KBIBTEXWS_EXPORT_H + +#include + +#ifndef KBIBTEXWS_EXPORT +# if defined(MAKE_WEBSEARCH_LIB) +/* We are building this library */ +# define KBIBTEXWS_EXPORT KDE_EXPORT +# else // MAKE_WEBSEARCH_LIB +/* We are using this library */ +# define KBIBTEXWS_EXPORT KDE_IMPORT +# endif // MAKE_WEBSEARCH_LIB +#endif // KBIBTEXWS_EXPORT + +#endif // KBIBTEXWS_EXPORT_H diff -Nru kbibtex-0.3/src/websearch/websearchabstract.cpp kbibtex-0.4/src/websearch/websearchabstract.cpp --- kbibtex-0.3/src/websearch/websearchabstract.cpp 2011-06-02 09:38:01.000000000 +0000 +++ kbibtex-0.4/src/websearch/websearchabstract.cpp 2011-11-20 20:36:54.000000000 +0000 @@ -18,11 +18,22 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include +#include + #include +#include +#include +#include #include #include +#include +#include +#include +#include +#include #include "websearchabstract.h" const QString WebSearchAbstract::queryKeyFreeText = QLatin1String("free"); @@ -30,10 +41,76 @@ const QString WebSearchAbstract::queryKeyAuthor = QLatin1String("author"); const QString WebSearchAbstract::queryKeyYear = QLatin1String("year"); +/// various browser strings to "disguise" origin +const QStringList WebSearchAbstract::m_userAgentList = QStringList() + << QLatin1String("Mozilla/5.0 (Linux; U; Android 2.3.3; en-us; HTC_DesireS_S510e Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1") + << QLatin1String("Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.3) Gecko/20100402 Prism/1.0b4") + << QLatin1String("Mozilla/5.0 (Windows; U; Win 9x 4.90; SG; rv:1.9.2.4) Gecko/20101104 Netscape/9.1.0285") + << QLatin1String("Mozilla/5.0 (compatible; Konqueror/4.5; FreeBSD) KHTML/4.5.4 (like Gecko)") + << QLatin1String("Mozilla/5.0 (compatible; Yahoo! Slurp China; http://misc.yahoo.com.cn/help.html)") + << QLatin1String("yacybot (x86 Windows XP 5.1; java 1.6.0_12; Europe/de) http://yacy.net/bot.html") + << QLatin1String("Nokia6230i/2.0 (03.25) Profile/MIDP-2.0 Configuration/CLDC-1.1") + << QLatin1String("Links (2.3-pre1; NetBSD 5.0 i386; 96x36)") + << QLatin1String("Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)") + << QLatin1String("Mozilla/4.0 (compatible; Dillo 2.2)") + << QLatin1String("Emacs-W3/4.0pre.46 URL/p4.0pre.46 (i686-pc-linux; X11)") + << QLatin1String("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.13) Gecko/20080208 Galeon/2.0.4 (2008.1) Firefox/2.0.0.13") + << QLatin1String("Lynx/2.8 (compatible; iCab 2.9.8; Macintosh; U; 68K)") + << QLatin1String("Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.14) Gecko/20080409 Camino/1.6 (like Firefox/2.0.0.14)") + << QLatin1String("Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16"); + const int WebSearchAbstract::resultNoError = 0; const int WebSearchAbstract::resultCancelled = 0; /// may get redefined in the future! const int WebSearchAbstract::resultUnspecifiedError = 1; +const char* WebSearchAbstract::httpUnsafeChars = "%:/=+$?&\0"; + + +HTTPEquivCookieJar::HTTPEquivCookieJar(QNetworkAccessManager *parent = NULL) + : QNetworkCookieJar(parent), m_nam(parent) +{ + // nothing +} + +void HTTPEquivCookieJar::checkForHttpEqiuv(const QString &htmlCode, const QUrl &url) +{ + static QRegExp cookieContent("^([^\"=; ]+)=([^\"=; ]+).*\\bpath=([^\"=; ]+)", Qt::CaseInsensitive); + int p1 = -1; + if ((p1 = htmlCode.toLower().indexOf("http-equiv=\"set-cookie\"", 0, Qt::CaseInsensitive)) >= 5 + && (p1 = htmlCode.lastIndexOf("= 0 + && (p1 = htmlCode.indexOf("content=\"", p1, Qt::CaseInsensitive)) >= 0 + && cookieContent.indexIn(htmlCode.mid(p1 + 9, 256)) >= 0) { + const QString key = cookieContent.cap(1); + const QString value = cookieContent.cap(2); + const QString path = cookieContent.cap(3); + QUrl cookieUrl = url; + //cookieUrl.setPath(path); + QList cookies = cookiesForUrl(cookieUrl); + cookies.append(QNetworkCookie(key.toAscii(), value.toAscii())); + setCookiesFromUrl(cookies, cookieUrl); + } +} + +QStringList WebSearchQueryFormAbstract::authorLastNames(const Entry &entry) +{ + QStringList result; + EncoderLaTeX *encoder = EncoderLaTeX::currentEncoderLaTeX(); + + const Value v = entry[Entry::ftAuthor]; + Person *p = NULL; + foreach(ValueItem *vi, v) + if ((p = dynamic_cast(vi)) != NULL) + result.append(encoder->convertToPlainAscii(p->lastName())); + + return result; +} + +WebSearchAbstract::WebSearchAbstract(QWidget *parent) + : QObject(parent), m_name(QString::null) +{ + m_parent = parent; +} + KIcon WebSearchAbstract::icon() const { QString fileName = favIconUrl(); @@ -48,6 +125,18 @@ return KIcon(fileName); } +QString WebSearchAbstract::name() +{ + if (m_name.isNull()) + m_name = label().replace(QRegExp("[^a-z0-9]", Qt::CaseInsensitive), QLatin1String("")); + return m_name; +} + +void WebSearchAbstract::cancel() +{ + m_hasBeenCanceled = true; +} + QStringList WebSearchAbstract::splitRespectingQuotationMarks(const QString &text) { int p1 = 0, p2, max = text.length(); @@ -61,8 +150,183 @@ while (p2 < max && text[p2] != '"') ++p2; } else while (p2 < max && text[p2] != ' ') ++p2; - result << text.mid(p1, p2 - p1 + 1); + result << text.mid(p1, p2 - p1 + 1).simplified(); p1 = p2 + 1; } return result; } + +bool WebSearchAbstract::handleErrors(QNetworkReply *reply) +{ + if (m_hasBeenCanceled) { + kDebug() << "Searching" << label() << "got cancelled"; + emit stoppedSearch(resultCancelled); + return false; + } else if (reply->error() != QNetworkReply::NoError) { + m_hasBeenCanceled = true; + kWarning() << "Search using" << label() << "failed (HTTP code" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray() << ")"; + KMessageBox::error(m_parent, reply->errorString().isEmpty() ? i18n("Searching \"%1\" failed for unknown reason.", label()) : i18n("Searching \"%1\" failed with error message:\n\n%2", label(), reply->errorString())); + emit stoppedSearch(resultUnspecifiedError); + return false; + } + return true; +} + +QString WebSearchAbstract::encodeURL(QString rawText) +{ + const char *cur = httpUnsafeChars; + while (*cur != '\0') { + rawText = rawText.replace(QChar(*cur), '%' + QString::number(*cur, 16)); + ++cur; + } + rawText = rawText.replace(" ", "+"); + return rawText; +} + +QString WebSearchAbstract::decodeURL(QString rawText) +{ + static QRegExp mimeRegExp("%([0-9A-Fa-f]{2})"); + while (mimeRegExp.indexIn(rawText) >= 0) { + bool ok = false; + QChar c(mimeRegExp.cap(1).toInt(&ok, 16)); + if (ok) + rawText = rawText.replace(mimeRegExp.cap(0), c); + } + rawText = rawText.replace("&", "&").replace("+", " "); + return rawText; +} + +QMap WebSearchAbstract::formParameters(const QString &htmlText, const QString &formTagBegin) +{ + /// how to recognize HTML tags + static const QString formTagEnd = QLatin1String(""); + static const QString inputTagBegin = QLatin1String(""); + static const QString optionTagBegin = QLatin1String("