mirror of
https://invent.kde.org/graphics/okular
synced 2024-08-27 19:50:27 +00:00
Merge branch 'dont-use-docdata-for-annots-and-forms' into Applications/17.12
This commit is contained in:
commit
3957683d76
427
autotests/data/file1-docdata.xml
Normal file
427
autotests/data/file1-docdata.xml
Normal file
|
@ -0,0 +1,427 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE documentInfo>
|
||||
<documentInfo url="/home/kdeunstable/okular/autotests/data/file1.pdf">
|
||||
<pageList>
|
||||
<page number="0">
|
||||
<annotationList>
|
||||
<annotation type="6">
|
||||
<base color="#00ff00" modifyDate="2017-09-11T19:40:57" flags="0" creationDate="2017-09-11T19:40:57" uniqueName="testannot" author="someone">
|
||||
<boundary l="0.195707" t="0.129927" b="0.232603" r="0.818182"/>
|
||||
<penStyle marks="3" width="2" style="1" ycr="0" xcr="0" spaces="0"/>
|
||||
</base>
|
||||
<ink>
|
||||
<path>
|
||||
<point x="0.445076" y="0.149878"/>
|
||||
<point x="0.445076" y="0.149392"/>
|
||||
<point x="0.444444" y="0.148905"/>
|
||||
<point x="0.442551" y="0.148418"/>
|
||||
<point x="0.440025" y="0.147932"/>
|
||||
<point x="0.435606" y="0.146959"/>
|
||||
<point x="0.433081" y="0.145985"/>
|
||||
<point x="0.429924" y="0.145012"/>
|
||||
<point x="0.424874" y="0.145012"/>
|
||||
<point x="0.418561" y="0.144039"/>
|
||||
<point x="0.412247" y="0.142579"/>
|
||||
<point x="0.407197" y="0.142579"/>
|
||||
<point x="0.398359" y="0.141606"/>
|
||||
<point x="0.392045" y="0.140146"/>
|
||||
<point x="0.38447" y="0.138686"/>
|
||||
<point x="0.379419" y="0.1382"/>
|
||||
<point x="0.372475" y="0.1382"/>
|
||||
<point x="0.368056" y="0.137226"/>
|
||||
<point x="0.361111" y="0.135766"/>
|
||||
<point x="0.352904" y="0.134307"/>
|
||||
<point x="0.34596" y="0.133333"/>
|
||||
<point x="0.339015" y="0.132847"/>
|
||||
<point x="0.332702" y="0.132847"/>
|
||||
<point x="0.327652" y="0.131873"/>
|
||||
<point x="0.322601" y="0.131873"/>
|
||||
<point x="0.318813" y="0.131873"/>
|
||||
<point x="0.314394" y="0.131873"/>
|
||||
<point x="0.310606" y="0.131873"/>
|
||||
<point x="0.306818" y="0.131873"/>
|
||||
<point x="0.304293" y="0.131873"/>
|
||||
<point x="0.301768" y="0.131873"/>
|
||||
<point x="0.299242" y="0.131873"/>
|
||||
<point x="0.295455" y="0.131873"/>
|
||||
<point x="0.292298" y="0.131873"/>
|
||||
<point x="0.289141" y="0.131873"/>
|
||||
<point x="0.285985" y="0.131387"/>
|
||||
<point x="0.282828" y="0.1309"/>
|
||||
<point x="0.278409" y="0.1309"/>
|
||||
<point x="0.273359" y="0.1309"/>
|
||||
<point x="0.269571" y="0.1309"/>
|
||||
<point x="0.265152" y="0.1309"/>
|
||||
<point x="0.262626" y="0.131387"/>
|
||||
<point x="0.258207" y="0.131387"/>
|
||||
<point x="0.253157" y="0.131873"/>
|
||||
<point x="0.25" y="0.13236"/>
|
||||
<point x="0.247475" y="0.133333"/>
|
||||
<point x="0.244318" y="0.13382"/>
|
||||
<point x="0.24053" y="0.13528"/>
|
||||
<point x="0.238636" y="0.13528"/>
|
||||
<point x="0.234217" y="0.136253"/>
|
||||
<point x="0.232955" y="0.137226"/>
|
||||
<point x="0.229167" y="0.1382"/>
|
||||
<point x="0.22601" y="0.140146"/>
|
||||
<point x="0.225379" y="0.142092"/>
|
||||
<point x="0.223485" y="0.143066"/>
|
||||
<point x="0.220328" y="0.144526"/>
|
||||
<point x="0.219066" y="0.146472"/>
|
||||
<point x="0.21654" y="0.148418"/>
|
||||
<point x="0.215909" y="0.149878"/>
|
||||
<point x="0.215278" y="0.151825"/>
|
||||
<point x="0.214646" y="0.152798"/>
|
||||
<point x="0.212753" y="0.154745"/>
|
||||
<point x="0.21149" y="0.157178"/>
|
||||
<point x="0.210227" y="0.159611"/>
|
||||
<point x="0.208333" y="0.161557"/>
|
||||
<point x="0.206439" y="0.16399"/>
|
||||
<point x="0.204545" y="0.165937"/>
|
||||
<point x="0.203914" y="0.167397"/>
|
||||
<point x="0.203283" y="0.169343"/>
|
||||
<point x="0.202652" y="0.171776"/>
|
||||
<point x="0.200758" y="0.173723"/>
|
||||
<point x="0.200126" y="0.176642"/>
|
||||
<point x="0.200126" y="0.179075"/>
|
||||
<point x="0.199495" y="0.181995"/>
|
||||
<point x="0.199495" y="0.185401"/>
|
||||
<point x="0.198864" y="0.188808"/>
|
||||
<point x="0.198864" y="0.191241"/>
|
||||
<point x="0.198864" y="0.193674"/>
|
||||
<point x="0.19697" y="0.196107"/>
|
||||
<point x="0.19697" y="0.19854"/>
|
||||
<point x="0.198864" y="0.2"/>
|
||||
<point x="0.198864" y="0.201946"/>
|
||||
<point x="0.200126" y="0.20438"/>
|
||||
<point x="0.200126" y="0.204866"/>
|
||||
<point x="0.200758" y="0.205353"/>
|
||||
<point x="0.20202" y="0.206813"/>
|
||||
<point x="0.203283" y="0.207786"/>
|
||||
<point x="0.204545" y="0.208759"/>
|
||||
<point x="0.205808" y="0.209246"/>
|
||||
<point x="0.207702" y="0.210706"/>
|
||||
<point x="0.210859" y="0.211679"/>
|
||||
<point x="0.213384" y="0.212165"/>
|
||||
<point x="0.21654" y="0.213139"/>
|
||||
<point x="0.22096" y="0.214112"/>
|
||||
<point x="0.22601" y="0.214599"/>
|
||||
<point x="0.232955" y="0.216058"/>
|
||||
<point x="0.24053" y="0.216058"/>
|
||||
<point x="0.248737" y="0.217518"/>
|
||||
<point x="0.258207" y="0.218491"/>
|
||||
<point x="0.265783" y="0.218978"/>
|
||||
<point x="0.27399" y="0.220925"/>
|
||||
<point x="0.280303" y="0.220925"/>
|
||||
<point x="0.287879" y="0.221411"/>
|
||||
<point x="0.29798" y="0.221411"/>
|
||||
<point x="0.305556" y="0.221411"/>
|
||||
<point x="0.314394" y="0.222384"/>
|
||||
<point x="0.322601" y="0.222384"/>
|
||||
<point x="0.330177" y="0.222384"/>
|
||||
<point x="0.339646" y="0.223358"/>
|
||||
<point x="0.34596" y="0.223358"/>
|
||||
<point x="0.353535" y="0.223358"/>
|
||||
<point x="0.361111" y="0.223844"/>
|
||||
<point x="0.368687" y="0.224818"/>
|
||||
<point x="0.376263" y="0.224818"/>
|
||||
<point x="0.388258" y="0.224818"/>
|
||||
<point x="0.397727" y="0.225791"/>
|
||||
<point x="0.405303" y="0.225791"/>
|
||||
<point x="0.414141" y="0.225791"/>
|
||||
<point x="0.42298" y="0.224818"/>
|
||||
<point x="0.431818" y="0.224818"/>
|
||||
<point x="0.435606" y="0.225791"/>
|
||||
<point x="0.443813" y="0.225791"/>
|
||||
<point x="0.452652" y="0.225791"/>
|
||||
<point x="0.463384" y="0.226764"/>
|
||||
<point x="0.474116" y="0.227251"/>
|
||||
<point x="0.48548" y="0.227251"/>
|
||||
<point x="0.493056" y="0.227251"/>
|
||||
<point x="0.501894" y="0.228224"/>
|
||||
<point x="0.510101" y="0.228224"/>
|
||||
<point x="0.518939" y="0.23017"/>
|
||||
<point x="0.530303" y="0.230657"/>
|
||||
<point x="0.541667" y="0.230657"/>
|
||||
<point x="0.549874" y="0.23163"/>
|
||||
<point x="0.558712" y="0.23163"/>
|
||||
<point x="0.567551" y="0.23163"/>
|
||||
<point x="0.57702" y="0.23163"/>
|
||||
<point x="0.584596" y="0.23163"/>
|
||||
<point x="0.596591" y="0.23163"/>
|
||||
<point x="0.609217" y="0.23163"/>
|
||||
<point x="0.617424" y="0.23163"/>
|
||||
<point x="0.629419" y="0.23163"/>
|
||||
<point x="0.63952" y="0.23163"/>
|
||||
<point x="0.646465" y="0.23163"/>
|
||||
<point x="0.653409" y="0.23163"/>
|
||||
<point x="0.659091" y="0.23163"/>
|
||||
<point x="0.666035" y="0.23163"/>
|
||||
<point x="0.672348" y="0.23163"/>
|
||||
<point x="0.679924" y="0.231144"/>
|
||||
<point x="0.684343" y="0.23017"/>
|
||||
<point x="0.690657" y="0.23017"/>
|
||||
<point x="0.695076" y="0.229197"/>
|
||||
<point x="0.700126" y="0.22871"/>
|
||||
<point x="0.703914" y="0.227737"/>
|
||||
<point x="0.707702" y="0.226277"/>
|
||||
<point x="0.710859" y="0.225304"/>
|
||||
<point x="0.714015" y="0.224331"/>
|
||||
<point x="0.717803" y="0.223358"/>
|
||||
<point x="0.720328" y="0.222871"/>
|
||||
<point x="0.72601" y="0.221411"/>
|
||||
<point x="0.731061" y="0.220925"/>
|
||||
<point x="0.73548" y="0.220438"/>
|
||||
<point x="0.74053" y="0.218978"/>
|
||||
<point x="0.745581" y="0.218491"/>
|
||||
<point x="0.747475" y="0.217032"/>
|
||||
<point x="0.751263" y="0.215572"/>
|
||||
<point x="0.754419" y="0.213625"/>
|
||||
<point x="0.756944" y="0.213139"/>
|
||||
<point x="0.75947" y="0.211192"/>
|
||||
<point x="0.763889" y="0.211192"/>
|
||||
<point x="0.768308" y="0.211192"/>
|
||||
<point x="0.770202" y="0.210219"/>
|
||||
<point x="0.774621" y="0.210219"/>
|
||||
<point x="0.777778" y="0.208759"/>
|
||||
<point x="0.78346" y="0.208273"/>
|
||||
<point x="0.787247" y="0.206813"/>
|
||||
<point x="0.78851" y="0.206813"/>
|
||||
<point x="0.790404" y="0.206326"/>
|
||||
<point x="0.791667" y="0.206326"/>
|
||||
<point x="0.794192" y="0.204866"/>
|
||||
<point x="0.795455" y="0.20438"/>
|
||||
<point x="0.798611" y="0.203893"/>
|
||||
<point x="0.801768" y="0.201946"/>
|
||||
<point x="0.802399" y="0.20146"/>
|
||||
<point x="0.805556" y="0.2"/>
|
||||
<point x="0.806818" y="0.19854"/>
|
||||
<point x="0.808081" y="0.198054"/>
|
||||
<point x="0.808712" y="0.196594"/>
|
||||
<point x="0.809975" y="0.196107"/>
|
||||
<point x="0.810606" y="0.194647"/>
|
||||
<point x="0.810606" y="0.193674"/>
|
||||
<point x="0.811237" y="0.192214"/>
|
||||
<point x="0.811869" y="0.190754"/>
|
||||
<point x="0.813763" y="0.188808"/>
|
||||
<point x="0.813763" y="0.187348"/>
|
||||
<point x="0.814394" y="0.185401"/>
|
||||
<point x="0.815025" y="0.183942"/>
|
||||
<point x="0.815657" y="0.181022"/>
|
||||
<point x="0.816288" y="0.179562"/>
|
||||
<point x="0.816288" y="0.177616"/>
|
||||
<point x="0.816288" y="0.176156"/>
|
||||
<point x="0.816919" y="0.173723"/>
|
||||
<point x="0.816919" y="0.171776"/>
|
||||
<point x="0.816919" y="0.170316"/>
|
||||
<point x="0.816919" y="0.167883"/>
|
||||
<point x="0.816919" y="0.165937"/>
|
||||
<point x="0.816288" y="0.16399"/>
|
||||
<point x="0.816288" y="0.162044"/>
|
||||
<point x="0.816288" y="0.160584"/>
|
||||
<point x="0.816288" y="0.159124"/>
|
||||
<point x="0.815657" y="0.158151"/>
|
||||
<point x="0.815657" y="0.156691"/>
|
||||
<point x="0.815025" y="0.155231"/>
|
||||
<point x="0.814394" y="0.154258"/>
|
||||
<point x="0.813763" y="0.152798"/>
|
||||
<point x="0.813763" y="0.151825"/>
|
||||
<point x="0.8125" y="0.150365"/>
|
||||
<point x="0.811869" y="0.149392"/>
|
||||
<point x="0.811869" y="0.148905"/>
|
||||
<point x="0.810606" y="0.147445"/>
|
||||
<point x="0.809975" y="0.146959"/>
|
||||
<point x="0.809343" y="0.146472"/>
|
||||
<point x="0.808712" y="0.145985"/>
|
||||
<point x="0.808081" y="0.145499"/>
|
||||
<point x="0.807449" y="0.145012"/>
|
||||
<point x="0.806818" y="0.145012"/>
|
||||
<point x="0.805556" y="0.144526"/>
|
||||
<point x="0.804293" y="0.144039"/>
|
||||
<point x="0.80303" y="0.143552"/>
|
||||
<point x="0.802399" y="0.143552"/>
|
||||
<point x="0.800505" y="0.143552"/>
|
||||
<point x="0.799874" y="0.143066"/>
|
||||
<point x="0.796717" y="0.142579"/>
|
||||
<point x="0.793561" y="0.142579"/>
|
||||
<point x="0.791667" y="0.142092"/>
|
||||
<point x="0.789773" y="0.142092"/>
|
||||
<point x="0.787247" y="0.141119"/>
|
||||
<point x="0.78346" y="0.140633"/>
|
||||
<point x="0.780934" y="0.140633"/>
|
||||
<point x="0.777778" y="0.140633"/>
|
||||
<point x="0.773359" y="0.140146"/>
|
||||
<point x="0.768939" y="0.140146"/>
|
||||
<point x="0.763889" y="0.139659"/>
|
||||
<point x="0.761364" y="0.139173"/>
|
||||
<point x="0.757576" y="0.139173"/>
|
||||
<point x="0.752525" y="0.139173"/>
|
||||
<point x="0.747475" y="0.1382"/>
|
||||
<point x="0.744318" y="0.1382"/>
|
||||
<point x="0.739899" y="0.137713"/>
|
||||
<point x="0.73548" y="0.137713"/>
|
||||
<point x="0.732323" y="0.137713"/>
|
||||
<point x="0.727904" y="0.137713"/>
|
||||
<point x="0.723485" y="0.137713"/>
|
||||
<point x="0.72096" y="0.137713"/>
|
||||
<point x="0.718434" y="0.137713"/>
|
||||
<point x="0.715909" y="0.137713"/>
|
||||
<point x="0.71149" y="0.137226"/>
|
||||
<point x="0.707702" y="0.137226"/>
|
||||
<point x="0.704545" y="0.137226"/>
|
||||
<point x="0.700126" y="0.137226"/>
|
||||
<point x="0.696338" y="0.137226"/>
|
||||
<point x="0.693182" y="0.137226"/>
|
||||
<point x="0.689394" y="0.137226"/>
|
||||
<point x="0.686869" y="0.137226"/>
|
||||
<point x="0.683712" y="0.137226"/>
|
||||
<point x="0.681818" y="0.137226"/>
|
||||
<point x="0.679293" y="0.137226"/>
|
||||
<point x="0.676136" y="0.137226"/>
|
||||
<point x="0.67298" y="0.137226"/>
|
||||
<point x="0.669823" y="0.137226"/>
|
||||
<point x="0.667298" y="0.137226"/>
|
||||
<point x="0.66351" y="0.137226"/>
|
||||
<point x="0.661616" y="0.137226"/>
|
||||
<point x="0.660354" y="0.137226"/>
|
||||
<point x="0.657828" y="0.137226"/>
|
||||
<point x="0.654672" y="0.13674"/>
|
||||
<point x="0.652778" y="0.13674"/>
|
||||
<point x="0.648359" y="0.13674"/>
|
||||
<point x="0.645202" y="0.13674"/>
|
||||
<point x="0.642045" y="0.13674"/>
|
||||
<point x="0.636995" y="0.137713"/>
|
||||
<point x="0.632576" y="0.137713"/>
|
||||
<point x="0.627525" y="0.1382"/>
|
||||
<point x="0.623106" y="0.1382"/>
|
||||
<point x="0.618687" y="0.1382"/>
|
||||
<point x="0.614899" y="0.137713"/>
|
||||
<point x="0.611742" y="0.137713"/>
|
||||
<point x="0.606692" y="0.137713"/>
|
||||
<point x="0.602273" y="0.137713"/>
|
||||
<point x="0.599747" y="0.137713"/>
|
||||
<point x="0.596591" y="0.137226"/>
|
||||
<point x="0.592172" y="0.137226"/>
|
||||
<point x="0.589646" y="0.136253"/>
|
||||
<point x="0.585227" y="0.135766"/>
|
||||
<point x="0.582071" y="0.135766"/>
|
||||
<point x="0.577652" y="0.135766"/>
|
||||
<point x="0.575758" y="0.13528"/>
|
||||
<point x="0.571338" y="0.134307"/>
|
||||
<point x="0.568813" y="0.13382"/>
|
||||
<point x="0.565025" y="0.13382"/>
|
||||
<point x="0.5625" y="0.133333"/>
|
||||
<point x="0.559975" y="0.133333"/>
|
||||
<point x="0.557449" y="0.13236"/>
|
||||
<point x="0.554924" y="0.13236"/>
|
||||
<point x="0.551768" y="0.131873"/>
|
||||
<point x="0.54798" y="0.131873"/>
|
||||
<point x="0.544823" y="0.131873"/>
|
||||
<point x="0.541667" y="0.131873"/>
|
||||
<point x="0.539773" y="0.131873"/>
|
||||
<point x="0.537879" y="0.131873"/>
|
||||
<point x="0.536616" y="0.131873"/>
|
||||
<point x="0.534722" y="0.131873"/>
|
||||
<point x="0.532197" y="0.131873"/>
|
||||
<point x="0.529672" y="0.131873"/>
|
||||
<point x="0.526515" y="0.131873"/>
|
||||
<point x="0.52399" y="0.131387"/>
|
||||
<point x="0.522096" y="0.131387"/>
|
||||
<point x="0.520202" y="0.131387"/>
|
||||
<point x="0.518308" y="0.131387"/>
|
||||
<point x="0.513889" y="0.131387"/>
|
||||
<point x="0.510732" y="0.131387"/>
|
||||
<point x="0.507576" y="0.131387"/>
|
||||
<point x="0.505051" y="0.131387"/>
|
||||
<point x="0.502525" y="0.131387"/>
|
||||
<point x="0.499369" y="0.131387"/>
|
||||
<point x="0.496843" y="0.131873"/>
|
||||
<point x="0.493687" y="0.131873"/>
|
||||
<point x="0.489268" y="0.131873"/>
|
||||
<point x="0.486111" y="0.131873"/>
|
||||
<point x="0.483586" y="0.131873"/>
|
||||
<point x="0.481692" y="0.131873"/>
|
||||
<point x="0.478535" y="0.131873"/>
|
||||
<point x="0.476641" y="0.131873"/>
|
||||
<point x="0.474116" y="0.131873"/>
|
||||
<point x="0.471591" y="0.132847"/>
|
||||
<point x="0.47096" y="0.132847"/>
|
||||
<point x="0.470328" y="0.132847"/>
|
||||
<point x="0.467803" y="0.133333"/>
|
||||
<point x="0.467172" y="0.133333"/>
|
||||
<point x="0.465909" y="0.133333"/>
|
||||
<point x="0.464646" y="0.133333"/>
|
||||
<point x="0.462121" y="0.13382"/>
|
||||
<point x="0.460859" y="0.134307"/>
|
||||
<point x="0.459596" y="0.134793"/>
|
||||
<point x="0.458965" y="0.134793"/>
|
||||
<point x="0.456439" y="0.13528"/>
|
||||
<point x="0.455808" y="0.135766"/>
|
||||
<point x="0.455177" y="0.135766"/>
|
||||
<point x="0.453914" y="0.135766"/>
|
||||
<point x="0.451389" y="0.135766"/>
|
||||
<point x="0.450126" y="0.136253"/>
|
||||
<point x="0.449495" y="0.136253"/>
|
||||
<point x="0.44697" y="0.137226"/>
|
||||
<point x="0.445707" y="0.137226"/>
|
||||
<point x="0.443182" y="0.137713"/>
|
||||
<point x="0.441919" y="0.137713"/>
|
||||
<point x="0.440657" y="0.138686"/>
|
||||
<point x="0.438131" y="0.139173"/>
|
||||
<point x="0.435606" y="0.139659"/>
|
||||
<point x="0.432449" y="0.140146"/>
|
||||
<point x="0.429924" y="0.140146"/>
|
||||
<point x="0.428662" y="0.140633"/>
|
||||
<point x="0.426136" y="0.140633"/>
|
||||
<point x="0.424242" y="0.141119"/>
|
||||
<point x="0.421086" y="0.142092"/>
|
||||
<point x="0.419192" y="0.142092"/>
|
||||
<point x="0.416667" y="0.142579"/>
|
||||
<point x="0.414141" y="0.142579"/>
|
||||
<point x="0.411616" y="0.142579"/>
|
||||
<point x="0.409091" y="0.142579"/>
|
||||
<point x="0.405934" y="0.142579"/>
|
||||
<point x="0.403409" y="0.142579"/>
|
||||
<point x="0.399621" y="0.143552"/>
|
||||
<point x="0.396465" y="0.143552"/>
|
||||
<point x="0.395202" y="0.144039"/>
|
||||
<point x="0.392677" y="0.144039"/>
|
||||
<point x="0.390152" y="0.144039"/>
|
||||
<point x="0.387626" y="0.144039"/>
|
||||
<point x="0.386364" y="0.144039"/>
|
||||
<point x="0.385101" y="0.144039"/>
|
||||
<point x="0.381944" y="0.144039"/>
|
||||
<point x="0.380682" y="0.144039"/>
|
||||
<point x="0.379419" y="0.144039"/>
|
||||
<point x="0.378788" y="0.144039"/>
|
||||
<point x="0.378157" y="0.144039"/>
|
||||
<point x="0.377525" y="0.144039"/>
|
||||
<point x="0.376263" y="0.144526"/>
|
||||
<point x="0.375631" y="0.144526"/>
|
||||
<point x="0.375" y="0.144526"/>
|
||||
<point x="0.374369" y="0.144526"/>
|
||||
<point x="0.373737" y="0.144526"/>
|
||||
<point x="0.373106" y="0.144526"/>
|
||||
<point x="0.372475" y="0.144526"/>
|
||||
<point x="0.371843" y="0.145012"/>
|
||||
<point x="0.370581" y="0.145012"/>
|
||||
<point x="0.370581" y="0.145499"/>
|
||||
<point x="0.369949" y="0.145985"/>
|
||||
<point x="0.369318" y="0.145985"/>
|
||||
</path>
|
||||
</ink>
|
||||
</annotation>
|
||||
</annotationList>
|
||||
</page>
|
||||
</pageList>
|
||||
<generalInfo>
|
||||
<history>
|
||||
<current viewport="0;C2:0.499684:0.23236:1"/>
|
||||
</history>
|
||||
<views>
|
||||
<view name="PageView">
|
||||
<zoom value="1.3145" mode="1"/>
|
||||
</view>
|
||||
</views>
|
||||
</generalInfo>
|
||||
</documentInfo>
|
BIN
autotests/data/potato.jpg
Normal file
BIN
autotests/data/potato.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 by Fabio D'Urso <fabiodurso@hotmail.it> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -11,9 +14,12 @@
|
|||
|
||||
#include <threadweaver/queue.h>
|
||||
|
||||
#include "../core/annotations.h"
|
||||
#include "../core/document.h"
|
||||
#include "../core/document_p.h"
|
||||
#include "../core/generator.h"
|
||||
#include "../core/observer.h"
|
||||
#include "../core/page.h"
|
||||
#include "../core/rotationjob_p.h"
|
||||
#include "../settings_core.h"
|
||||
|
||||
|
@ -24,6 +30,7 @@ class DocumentTest
|
|||
|
||||
private slots:
|
||||
void testCloseDuringRotationJob();
|
||||
void testDocdataMigration();
|
||||
};
|
||||
|
||||
// Test that we don't crash if the document is closed while a RotationJob
|
||||
|
@ -57,6 +64,69 @@ void DocumentTest::testCloseDuringRotationJob()
|
|||
ThreadWeaver::Queue::instance()->resume();
|
||||
ThreadWeaver::Queue::instance()->finish();
|
||||
qApp->processEvents();
|
||||
|
||||
delete dummyDocumentObserver;
|
||||
}
|
||||
|
||||
// Test that, if there's a XML file in docdata referring to a document, we
|
||||
// detect that it must be migrated, that it doesn't get wiped out if you close
|
||||
// the document without migrating and that it does get wiped out after migrating
|
||||
void DocumentTest::testDocdataMigration()
|
||||
{
|
||||
Okular::SettingsCore::instance( "documenttest" );
|
||||
|
||||
const QUrl testFileUrl = QUrl::fromLocalFile(KDESRCDIR "data/file1.pdf");
|
||||
const QString testFilePath = testFileUrl.toLocalFile();
|
||||
const qint64 testFileSize = QFileInfo(testFilePath).size();
|
||||
|
||||
// Copy XML file to the docdata/ directory
|
||||
const QString docDataPath = Okular::DocumentPrivate::docDataFileName(testFileUrl, testFileSize);
|
||||
QFile::remove(docDataPath);
|
||||
QVERIFY( QFile::copy(KDESRCDIR "data/file1-docdata.xml", docDataPath) );
|
||||
|
||||
// Open our document
|
||||
Okular::Document *m_document = new Okular::Document( 0 );
|
||||
QMimeDatabase db;
|
||||
const QMimeType mime = db.mimeTypeForFile( testFilePath );
|
||||
QCOMPARE( m_document->openDocument( testFilePath, testFileUrl, mime ), Okular::Document::OpenSuccess );
|
||||
|
||||
// Check that the annotation from file1-docdata.xml was loaded
|
||||
QCOMPARE( m_document->page( 0 )->annotations().size(), 1 );
|
||||
QCOMPARE( m_document->page( 0 )->annotations().first()->uniqueName(), QString("testannot") );
|
||||
|
||||
// Check that we detect that it must be migrated
|
||||
QCOMPARE( m_document->isDocdataMigrationNeeded(), true );
|
||||
m_document->closeDocument();
|
||||
|
||||
// Reopen the document and check that the annotation is still present
|
||||
// (because we have not migrated)
|
||||
QCOMPARE( m_document->openDocument( testFilePath, testFileUrl, mime ), Okular::Document::OpenSuccess );
|
||||
QCOMPARE( m_document->page( 0 )->annotations().size(), 1 );
|
||||
QCOMPARE( m_document->page( 0 )->annotations().first()->uniqueName(), QString("testannot") );
|
||||
QCOMPARE( m_document->isDocdataMigrationNeeded(), true );
|
||||
|
||||
// Do the migration
|
||||
QTemporaryFile migratedSaveFile( QString( "%1/okrXXXXXX.pdf" ).arg( QDir::tempPath() ) );
|
||||
QVERIFY( migratedSaveFile.open() );
|
||||
migratedSaveFile.close();
|
||||
QVERIFY( m_document->saveChanges( migratedSaveFile.fileName() ) );
|
||||
m_document->docdataMigrationDone();
|
||||
QCOMPARE( m_document->isDocdataMigrationNeeded(), false );
|
||||
m_document->closeDocument();
|
||||
|
||||
// Now the docdata file should have no annotations, let's check
|
||||
QCOMPARE( m_document->openDocument( testFilePath, testFileUrl, mime ), Okular::Document::OpenSuccess );
|
||||
QCOMPARE( m_document->page( 0 )->annotations().size(), 0 );
|
||||
QCOMPARE( m_document->isDocdataMigrationNeeded(), false );
|
||||
m_document->closeDocument();
|
||||
|
||||
// And the new file should have 1 annotation, let's check
|
||||
QCOMPARE( m_document->openDocument( migratedSaveFile.fileName(), migratedSaveFile.fileName(), mime ), Okular::Document::OpenSuccess );
|
||||
QCOMPARE( m_document->page( 0 )->annotations().size(), 1 );
|
||||
QCOMPARE( m_document->isDocdataMigrationNeeded(), false );
|
||||
m_document->closeDocument();
|
||||
|
||||
delete m_document;
|
||||
}
|
||||
|
||||
QTEST_MAIN( DocumentTest )
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -9,6 +12,8 @@
|
|||
|
||||
#include <QtTest>
|
||||
|
||||
#include "../core/annotations.h"
|
||||
#include "../core/form.h"
|
||||
#include "../core/page.h"
|
||||
#include "../part.h"
|
||||
#include "../ui/toc.h"
|
||||
|
@ -17,6 +22,8 @@
|
|||
#include <KConfigDialog>
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QScrollBar>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTreeView>
|
||||
|
@ -24,6 +31,40 @@
|
|||
#include <QDesktopServices>
|
||||
#include <QMenu>
|
||||
|
||||
class CloseDialogHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CloseDialogHelper(Okular::Part *p, QDialogButtonBox::StandardButton b) : m_part(p), m_button(b), m_clicked(false)
|
||||
{
|
||||
QTimer::singleShot(0, this, &CloseDialogHelper::closeDialog);
|
||||
}
|
||||
|
||||
~CloseDialogHelper()
|
||||
{
|
||||
QVERIFY(m_clicked);
|
||||
}
|
||||
|
||||
private slots:
|
||||
void closeDialog()
|
||||
{
|
||||
QDialog *dialog = m_part->widget()->findChild<QDialog*>();
|
||||
if (!dialog) {
|
||||
QTimer::singleShot(0, this, &CloseDialogHelper::closeDialog);
|
||||
return;
|
||||
}
|
||||
QDialogButtonBox *buttonBox = dialog->findChild<QDialogButtonBox*>();
|
||||
buttonBox->button(m_button)->click();
|
||||
m_clicked = true;
|
||||
}
|
||||
|
||||
private:
|
||||
Okular::Part *m_part;
|
||||
QDialogButtonBox::StandardButton m_button;
|
||||
bool m_clicked;
|
||||
};
|
||||
|
||||
namespace Okular
|
||||
{
|
||||
class PartTest
|
||||
|
@ -45,6 +86,12 @@ class PartTest
|
|||
void testGeneratorPreferences();
|
||||
void testSelectText();
|
||||
void testClickInternalLink();
|
||||
void testSaveAs();
|
||||
void testSaveAs_data();
|
||||
void testSaveAsUndoStackAnnotations();
|
||||
void testSaveAsUndoStackAnnotations_data();
|
||||
void testSaveAsUndoStackForms();
|
||||
void testSaveAsUndoStackForms_data();
|
||||
void testMouseMoveOverLinkWhileInSelectionMode();
|
||||
void testClickUrlLinkWhileInSelectionMode();
|
||||
void testeTextSelectionOverAndAcrossLinks_data();
|
||||
|
@ -725,6 +772,412 @@ void PartTest::simulateMouseSelection(double startX, double startY, double endX,
|
|||
events.simulate(target);
|
||||
}
|
||||
|
||||
void PartTest::testSaveAs()
|
||||
{
|
||||
QFETCH(QString, file);
|
||||
QFETCH(QString, extension);
|
||||
QFETCH(bool, nativelySupportsAnnotations);
|
||||
QFETCH(bool, canSwapBackingFile);
|
||||
|
||||
QScopedPointer<CloseDialogHelper> closeDialogHelper;
|
||||
|
||||
QString annotName;
|
||||
QTemporaryFile archiveSave( QString( "%1/okrXXXXXX.okular" ).arg( QDir::tempPath() ) );
|
||||
QTemporaryFile nativeDirectSave( QString( "%1/okrXXXXXX.%2" ).arg( QDir::tempPath() ).arg ( extension ) );
|
||||
QTemporaryFile nativeFromArchiveFile( QString( "%1/okrXXXXXX.%2" ).arg( QDir::tempPath() ).arg ( extension ) );
|
||||
QVERIFY( archiveSave.open() );
|
||||
archiveSave.close();
|
||||
QVERIFY( nativeDirectSave.open() );
|
||||
nativeDirectSave.close();
|
||||
QVERIFY( nativeFromArchiveFile.open() );
|
||||
nativeFromArchiveFile.close();
|
||||
|
||||
qDebug() << "Open file, add annotation and save both natively and to .okular";
|
||||
{
|
||||
Okular::Part part(nullptr, nullptr, QVariantList());
|
||||
part.openDocument( file );
|
||||
|
||||
QCOMPARE(part.m_document->canSwapBackingFile(), canSwapBackingFile);
|
||||
|
||||
Okular::Annotation *annot = new Okular::TextAnnotation();
|
||||
annot->setBoundingRectangle( Okular::NormalizedRect( 0.1, 0.1, 0.15, 0.15 ) );
|
||||
annot->setContents( "annot contents" );
|
||||
part.m_document->addPageAnnotation( 0, annot );
|
||||
annotName = annot->uniqueName();
|
||||
|
||||
if ( canSwapBackingFile )
|
||||
{
|
||||
if ( !nativelySupportsAnnotations )
|
||||
{
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( nativeDirectSave.fileName() ), Part::NoSaveAsFlags ) );
|
||||
// For backends that don't support annotations natively we mark the part as still modified
|
||||
// after a save because we keep the annotation around but it will get lost if the user closes the app
|
||||
// so we want to give her a last chance to save on close with the "you have changes dialog"
|
||||
QCOMPARE( part.isModified(), !nativelySupportsAnnotations );
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( archiveSave.fileName() ), Part::SaveAsOkularArchive ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to save to archive first otherwise we lose the annotation
|
||||
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::Yes )); // this is the "you're going to lose the undo/redo stack" dialog
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( archiveSave.fileName() ), Part::SaveAsOkularArchive ) );
|
||||
|
||||
if ( !nativelySupportsAnnotations )
|
||||
{
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( nativeDirectSave.fileName() ), Part::NoSaveAsFlags ) );
|
||||
}
|
||||
|
||||
part.closeUrl();
|
||||
}
|
||||
|
||||
qDebug() << "Open the .okular, check that the annotation is present and save to native";
|
||||
{
|
||||
Okular::Part part(nullptr, nullptr, QVariantList());
|
||||
part.openDocument( archiveSave.fileName() );
|
||||
|
||||
QCOMPARE( part.m_document->page( 0 )->annotations().size(), 1 );
|
||||
QCOMPARE( part.m_document->page( 0 )->annotations().first()->uniqueName(), annotName );
|
||||
|
||||
if ( !nativelySupportsAnnotations )
|
||||
{
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( nativeFromArchiveFile.fileName() ), Part::NoSaveAsFlags ) );
|
||||
|
||||
if ( canSwapBackingFile && !nativelySupportsAnnotations )
|
||||
{
|
||||
// For backends that don't support annotations natively we mark the part as still modified
|
||||
// after a save because we keep the annotation around but it will get lost if the user closes the app
|
||||
// so we want to give her a last chance to save on close with the "you have changes dialog"
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "do you want to save or discard" dialog
|
||||
}
|
||||
|
||||
part.closeUrl();
|
||||
}
|
||||
|
||||
qDebug() << "Open the native file saved directly, and check that the annot"
|
||||
<< "is there iff we expect it";
|
||||
{
|
||||
Okular::Part part(nullptr, nullptr, QVariantList());
|
||||
part.openDocument( nativeDirectSave.fileName() );
|
||||
|
||||
QCOMPARE( part.m_document->page( 0 )->annotations().size(), nativelySupportsAnnotations ? 1 : 0 );
|
||||
if ( nativelySupportsAnnotations )
|
||||
QCOMPARE( part.m_document->page( 0 )->annotations().first()->uniqueName(), annotName );
|
||||
|
||||
part.closeUrl();
|
||||
}
|
||||
|
||||
qDebug() << "Open the native file saved from the .okular, and check that the annot"
|
||||
<< "is there iff we expect it";
|
||||
{
|
||||
Okular::Part part(nullptr, nullptr, QVariantList());
|
||||
part.openDocument( nativeFromArchiveFile.fileName() );
|
||||
|
||||
QCOMPARE( part.m_document->page( 0 )->annotations().size(), nativelySupportsAnnotations ? 1 : 0 );
|
||||
if ( nativelySupportsAnnotations )
|
||||
QCOMPARE( part.m_document->page( 0 )->annotations().first()->uniqueName(), annotName );
|
||||
|
||||
part.closeUrl();
|
||||
}
|
||||
}
|
||||
|
||||
void PartTest::testSaveAs_data()
|
||||
{
|
||||
QTest::addColumn<QString>("file");
|
||||
QTest::addColumn<QString>("extension");
|
||||
QTest::addColumn<bool>("nativelySupportsAnnotations");
|
||||
QTest::addColumn<bool>("canSwapBackingFile");
|
||||
|
||||
QTest::newRow("pdf") << KDESRCDIR "data/file1.pdf" << "pdf" << true << true;
|
||||
QTest::newRow("epub") << KDESRCDIR "data/contents.epub" << "epub" << false << false;
|
||||
QTest::newRow("jpg") << KDESRCDIR "data/potato.jpg" << "jpg" << false << true;
|
||||
}
|
||||
|
||||
void PartTest::testSaveAsUndoStackAnnotations()
|
||||
{
|
||||
QFETCH(QString, file);
|
||||
QFETCH(QString, extension);
|
||||
QFETCH(bool, nativelySupportsAnnotations);
|
||||
QFETCH(bool, canSwapBackingFile);
|
||||
QFETCH(bool, saveToArchive);
|
||||
|
||||
const Part::SaveAsFlag saveFlags = saveToArchive ? Part::SaveAsOkularArchive : Part::NoSaveAsFlags;
|
||||
|
||||
QScopedPointer<CloseDialogHelper> closeDialogHelper;
|
||||
|
||||
QTemporaryFile saveFile( QString( "%1/okrXXXXXX.%2" ).arg( QDir::tempPath() ).arg ( extension ) );
|
||||
QVERIFY( saveFile.open() );
|
||||
saveFile.close();
|
||||
|
||||
Okular::Part part(nullptr, nullptr, QVariantList());
|
||||
part.openDocument( file );
|
||||
|
||||
QCOMPARE(part.m_document->canSwapBackingFile(), canSwapBackingFile);
|
||||
|
||||
Okular::Annotation *annot = new Okular::TextAnnotation();
|
||||
annot->setBoundingRectangle( Okular::NormalizedRect( 0.1, 0.1, 0.15, 0.15 ) );
|
||||
annot->setContents( "annot contents" );
|
||||
part.m_document->addPageAnnotation( 0, annot );
|
||||
QString annotName = annot->uniqueName();
|
||||
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
|
||||
if (!canSwapBackingFile) {
|
||||
// The undo/redo stack gets lost if you can not swap the backing file
|
||||
QVERIFY( !part.m_document->canUndo() );
|
||||
QVERIFY( !part.m_document->canRedo() );
|
||||
return;
|
||||
}
|
||||
|
||||
// Check we can still undo the annot add after save
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( !part.m_document->canUndo() );
|
||||
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->page( 0 )->annotations().isEmpty() );
|
||||
|
||||
// Check we can redo the annot add after save
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( !part.m_document->canRedo() );
|
||||
|
||||
if ( nativelySupportsAnnotations ) {
|
||||
// If the annots are provived by the backend we need to refetch the pointer after save
|
||||
annot = part.m_document->page( 0 )->annotation( annotName );
|
||||
QVERIFY( annot );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Remove the annotation, creates another undo command
|
||||
QVERIFY( part.m_document->canRemovePageAnnotation( annot ) );
|
||||
part.m_document->removePageAnnotation( 0, annot );
|
||||
QVERIFY( part.m_document->page( 0 )->annotations().isEmpty() );
|
||||
|
||||
// Check we can still undo the annot remove after save
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
QCOMPARE( part.m_document->page( 0 )->annotations().count(), 1 );
|
||||
|
||||
// Check we can still undo the annot add after save
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( !part.m_document->canUndo() );
|
||||
QVERIFY( part.m_document->page( 0 )->annotations().isEmpty() );
|
||||
|
||||
|
||||
// Redo the add annotation
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
|
||||
if ( nativelySupportsAnnotations ) {
|
||||
// If the annots are provived by the backend we need to refetch the pointer after save
|
||||
annot = part.m_document->page( 0 )->annotation( annotName );
|
||||
QVERIFY( annot );
|
||||
}
|
||||
|
||||
|
||||
// Add translate, adjust and modify commands
|
||||
part.m_document->translatePageAnnotation( 0, annot, Okular::NormalizedPoint( 0.1, 0.1 ) );
|
||||
part.m_document->adjustPageAnnotation( 0, annot, Okular::NormalizedPoint( 0.1, 0.1 ), Okular::NormalizedPoint( 0.1, 0.1 ) );
|
||||
part.m_document->prepareToModifyAnnotationProperties( annot );
|
||||
part.m_document->modifyPageAnnotationProperties( 0, annot );
|
||||
|
||||
// Now check we can still undo/redo/save at all the intermediate states and things still work
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( !part.m_document->canUndo() );
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
QVERIFY( part.m_document->page( 0 )->annotations().isEmpty() );
|
||||
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
|
||||
if ( !nativelySupportsAnnotations && !saveToArchive ) {
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog
|
||||
}
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( !part.m_document->canRedo() );
|
||||
|
||||
closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "do you want to save or discard" dialog
|
||||
part.closeUrl();
|
||||
}
|
||||
|
||||
void PartTest::testSaveAsUndoStackAnnotations_data()
|
||||
{
|
||||
QTest::addColumn<QString>("file");
|
||||
QTest::addColumn<QString>("extension");
|
||||
QTest::addColumn<bool>("nativelySupportsAnnotations");
|
||||
QTest::addColumn<bool>("canSwapBackingFile");
|
||||
QTest::addColumn<bool>("saveToArchive");
|
||||
|
||||
QTest::newRow("pdf") << KDESRCDIR "data/file1.pdf" << "pdf" << true << true << false;
|
||||
QTest::newRow("epub") << KDESRCDIR "data/contents.epub" << "epub" << false << false << false;
|
||||
QTest::newRow("jpg") << KDESRCDIR "data/potato.jpg" << "jpg" << false << true << false;
|
||||
QTest::newRow("pdfarchive") << KDESRCDIR "data/file1.pdf" << "okular" << true << true << true;
|
||||
QTest::newRow("jpgarchive") << KDESRCDIR "data/potato.jpg" << "okular" << false << true << true;
|
||||
}
|
||||
|
||||
void PartTest::testSaveAsUndoStackForms()
|
||||
{
|
||||
QFETCH(QString, file);
|
||||
QFETCH(QString, extension);
|
||||
QFETCH(bool, saveToArchive);
|
||||
|
||||
const Part::SaveAsFlag saveFlags = saveToArchive ? Part::SaveAsOkularArchive : Part::NoSaveAsFlags;
|
||||
|
||||
QTemporaryFile saveFile( QString( "%1/okrXXXXXX.%2" ).arg( QDir::tempPath(), extension ) );
|
||||
QVERIFY( saveFile.open() );
|
||||
saveFile.close();
|
||||
|
||||
Okular::Part part(nullptr, nullptr, QVariantList());
|
||||
part.openDocument( file );
|
||||
|
||||
for ( FormField *ff : part.m_document->page( 0 )->formFields() )
|
||||
{
|
||||
if ( ff->id() == 65537 )
|
||||
{
|
||||
QCOMPARE( ff->type(), FormField::FormText );
|
||||
FormFieldText *fft = static_cast<FormFieldText *>( ff );
|
||||
part.m_document->editFormText( 0, fft, "BlaBla", 6, 0, 0 );
|
||||
}
|
||||
else if ( ff->id() == 65538 )
|
||||
{
|
||||
QCOMPARE( ff->type(), FormField::FormButton );
|
||||
FormFieldButton *ffb = static_cast<FormFieldButton *>( ff );
|
||||
QCOMPARE( ffb->buttonType(), FormFieldButton::Radio );
|
||||
part.m_document->editFormButtons( 0, QList< FormFieldButton* >() << ffb, QList< bool >() << true );
|
||||
}
|
||||
else if ( ff->id() == 65542 )
|
||||
{
|
||||
QCOMPARE( ff->type(), FormField::FormChoice );
|
||||
FormFieldChoice *ffc = static_cast<FormFieldChoice *>( ff );
|
||||
QCOMPARE( ffc->choiceType(), FormFieldChoice::ListBox );
|
||||
part.m_document->editFormList( 0, ffc, QList< int >() << 1 );
|
||||
}
|
||||
else if ( ff->id() == 65543 )
|
||||
{
|
||||
QCOMPARE( ff->type(), FormField::FormChoice );
|
||||
FormFieldChoice *ffc = static_cast<FormFieldChoice *>( ff );
|
||||
QCOMPARE( ffc->choiceType(), FormFieldChoice::ComboBox );
|
||||
part.m_document->editFormCombo( 0, ffc, "combo2", 3, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
|
||||
QVERIFY( part.m_document->canUndo() );
|
||||
part.m_document->undo();
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
QVERIFY( !part.m_document->canUndo() );
|
||||
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
|
||||
QVERIFY( part.m_document->canRedo() );
|
||||
part.m_document->redo();
|
||||
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), saveFlags ) );
|
||||
}
|
||||
|
||||
void PartTest::testSaveAsUndoStackForms_data()
|
||||
{
|
||||
QTest::addColumn<QString>("file");
|
||||
QTest::addColumn<QString>("extension");
|
||||
QTest::addColumn<bool>("saveToArchive");
|
||||
|
||||
QTest::newRow("pdf") << KDESRCDIR "data/formSamples.pdf" << "pdf" << false;
|
||||
QTest::newRow("pdfarchive") << KDESRCDIR "data/formSamples.pdf" << "okular" << true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
|
|
@ -114,6 +114,7 @@ struct ArchiveData
|
|||
{
|
||||
}
|
||||
|
||||
QString originalFileName;
|
||||
QTemporaryFile document;
|
||||
QTemporaryFile metadataFile;
|
||||
};
|
||||
|
@ -526,22 +527,22 @@ qulonglong DocumentPrivate::getFreeMemory( qulonglong *freeSwap )
|
|||
#endif
|
||||
}
|
||||
|
||||
void DocumentPrivate::loadDocumentInfo()
|
||||
bool DocumentPrivate::loadDocumentInfo( LoadDocumentInfoFlags loadWhat )
|
||||
// note: load data and stores it internally (document or pages). observers
|
||||
// are still uninitialized at this point so don't access them
|
||||
{
|
||||
//qCDebug(OkularCoreDebug).nospace() << "Using '" << d->m_xmlFileName << "' as document info file.";
|
||||
if ( m_xmlFileName.isEmpty() )
|
||||
return;
|
||||
return false;
|
||||
|
||||
QFile infoFile( m_xmlFileName );
|
||||
loadDocumentInfo( infoFile );
|
||||
return loadDocumentInfo( infoFile, loadWhat );
|
||||
}
|
||||
|
||||
void DocumentPrivate::loadDocumentInfo( QFile &infoFile )
|
||||
bool DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags loadWhat )
|
||||
{
|
||||
if ( !infoFile.exists() || !infoFile.open( QIODevice::ReadOnly ) )
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Load DOM from XML file
|
||||
QDomDocument doc( QStringLiteral("documentInfo") );
|
||||
|
@ -549,13 +550,17 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile )
|
|||
{
|
||||
qCDebug(OkularCoreDebug) << "Can't load XML pair! Check for broken xml.";
|
||||
infoFile.close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
infoFile.close();
|
||||
|
||||
QDomElement root = doc.documentElement();
|
||||
|
||||
if ( root.tagName() != QLatin1String("documentInfo") )
|
||||
return;
|
||||
return false;
|
||||
|
||||
QUrl documentUrl( root.attribute( "url" ) );
|
||||
bool loadedAnything = false; // set if something gets actually loaded
|
||||
|
||||
// Parse the DOM tree
|
||||
QDomNode topLevelNode = root.firstChild();
|
||||
|
@ -564,7 +569,7 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile )
|
|||
QString catName = topLevelNode.toElement().tagName();
|
||||
|
||||
// Restore page attributes (bookmark, annotations, ...) from the DOM
|
||||
if ( catName == QLatin1String("pageList") )
|
||||
if ( catName == QLatin1String("pageList") && ( loadWhat & LoadPageInfo ) )
|
||||
{
|
||||
QDomNode pageNode = topLevelNode.firstChild();
|
||||
while ( pageNode.isElement() )
|
||||
|
@ -578,14 +583,17 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile )
|
|||
|
||||
// pass the domElement to the right page, to read config data from
|
||||
if ( ok && pageNumber >= 0 && pageNumber < (int)m_pagesVector.count() )
|
||||
m_pagesVector[ pageNumber ]->d->restoreLocalContents( pageElement );
|
||||
{
|
||||
if ( m_pagesVector[ pageNumber ]->d->restoreLocalContents( pageElement ) )
|
||||
loadedAnything = true;
|
||||
}
|
||||
}
|
||||
pageNode = pageNode.nextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
// Restore 'general info' from the DOM
|
||||
else if ( catName == QLatin1String("generalInfo") )
|
||||
else if ( catName == QLatin1String("generalInfo") && ( loadWhat & LoadGeneralInfo ) )
|
||||
{
|
||||
QDomNode infoNode = topLevelNode.firstChild();
|
||||
while ( infoNode.isElement() )
|
||||
|
@ -607,6 +615,7 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile )
|
|||
QString vpString = historyElement.attribute( QStringLiteral("viewport") );
|
||||
m_viewportIterator = m_viewportHistory.insert( m_viewportHistory.end(),
|
||||
DocumentViewport( vpString ) );
|
||||
loadedAnything = true;
|
||||
}
|
||||
historyNode = historyNode.nextSibling();
|
||||
}
|
||||
|
@ -622,6 +631,7 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile )
|
|||
if ( ok && newrotation != 0 )
|
||||
{
|
||||
setRotationInternal( newrotation, false );
|
||||
loadedAnything = true;
|
||||
}
|
||||
}
|
||||
else if ( infoElement.tagName() == QLatin1String("views") )
|
||||
|
@ -638,6 +648,7 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile )
|
|||
if ( view->name() == viewName )
|
||||
{
|
||||
loadViewsInfo( view, viewElement );
|
||||
loadedAnything = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -651,6 +662,8 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile )
|
|||
|
||||
topLevelNode = topLevelNode.nextSibling();
|
||||
} // </documentInfo>
|
||||
|
||||
return loadedAnything;
|
||||
}
|
||||
|
||||
void DocumentPrivate::loadViewsInfo( View *view, const QDomElement &e )
|
||||
|
@ -952,25 +965,6 @@ DocumentViewport DocumentPrivate::nextDocumentViewport() const
|
|||
return ret;
|
||||
}
|
||||
|
||||
void DocumentPrivate::warnLimitedAnnotSupport()
|
||||
{
|
||||
if ( !m_showWarningLimitedAnnotSupport )
|
||||
return;
|
||||
m_showWarningLimitedAnnotSupport = false; // Show the warning once
|
||||
|
||||
if ( m_annotationsNeedSaveAs )
|
||||
{
|
||||
// Shown if the user is editing annotations in a file whose metadata is
|
||||
// not stored locally (.okular archives belong to this category)
|
||||
KMessageBox::information( m_widget, i18n("Your annotation changes will not be saved automatically. Use File -> Save As...\nor your changes will be lost once the document is closed"), QString(), QStringLiteral("annotNeedSaveAs") );
|
||||
}
|
||||
else if ( !canAddAnnotationsNatively() )
|
||||
{
|
||||
// If the generator doesn't support native annotations
|
||||
KMessageBox::information( m_widget, i18n("Your annotations are saved internally by Okular.\nYou can export the annotated document using File -> Export As -> Document Archive"), QString(), QStringLiteral("annotExportAsArchive") );
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentPrivate::performAddPageAnnotation( int page, Annotation * annotation )
|
||||
{
|
||||
Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( m_generator );
|
||||
|
@ -1000,8 +994,6 @@ void DocumentPrivate::performAddPageAnnotation( int page, Annotation * annotatio
|
|||
// Redraw everything, including ExternallyDrawn annotations
|
||||
refreshPixmaps( page );
|
||||
}
|
||||
|
||||
warnLimitedAnnotSupport();
|
||||
}
|
||||
|
||||
void DocumentPrivate::performRemovePageAnnotation( int page, Annotation * annotation )
|
||||
|
@ -1038,8 +1030,6 @@ void DocumentPrivate::performRemovePageAnnotation( int page, Annotation * annota
|
|||
refreshPixmaps( page );
|
||||
}
|
||||
}
|
||||
|
||||
warnLimitedAnnotSupport();
|
||||
}
|
||||
|
||||
void DocumentPrivate::performModifyPageAnnotation( int page, Annotation * annotation, bool appearanceChanged )
|
||||
|
@ -1080,10 +1070,6 @@ void DocumentPrivate::performModifyPageAnnotation( int page, Annotation * annota
|
|||
qCDebug(OkularCoreDebug) << "Refreshing Pixmaps";
|
||||
refreshPixmaps( page );
|
||||
}
|
||||
|
||||
// If the user is moving or resizing the annotation, don't steal the focus
|
||||
if ( (annotation->flags() & (Annotation::BeingMoved | Annotation::BeingResized) ) == 0 )
|
||||
warnLimitedAnnotSupport();
|
||||
}
|
||||
|
||||
void DocumentPrivate::performSetAnnotationContents( const QString & newContents, Annotation *annot, int pageNumber )
|
||||
|
@ -1162,7 +1148,6 @@ void DocumentPrivate::saveDocumentInfo() const
|
|||
{
|
||||
qCWarning(OkularCoreDebug) << "Failed to open docdata file" << m_xmlFileName;
|
||||
return;
|
||||
|
||||
}
|
||||
// 1. Create DOM
|
||||
QDomDocument doc( QStringLiteral("documentInfo") );
|
||||
|
@ -1174,21 +1159,23 @@ void DocumentPrivate::saveDocumentInfo() const
|
|||
doc.appendChild( root );
|
||||
|
||||
// 2.1. Save page attributes (bookmark state, annotations, ... ) to DOM
|
||||
QDomElement pageList = doc.createElement( QStringLiteral("pageList") );
|
||||
root.appendChild( pageList );
|
||||
PageItems saveWhat = AllPageItems;
|
||||
if ( m_annotationsNeedSaveAs )
|
||||
// -> do this if there are not-yet-migrated annots or forms in docdata/
|
||||
if ( m_docdataMigrationNeeded )
|
||||
{
|
||||
/* In this case, if the user makes a modification, he's requested to
|
||||
* save to a new document. Therefore, if there are existing local
|
||||
* annotations, we save them back unmodified in the original
|
||||
* document's metadata, so that it appears that it was not changed */
|
||||
saveWhat |= OriginalAnnotationPageItems;
|
||||
QDomElement pageList = doc.createElement( "pageList" );
|
||||
root.appendChild( pageList );
|
||||
// OriginalAnnotationPageItems and OriginalFormFieldPageItems tell to
|
||||
// store the same unmodified annotation list and form contents that we
|
||||
// read when we opened the file and ignore any change made by the user.
|
||||
// Since we don't store annotations and forms in docdata/ any more, this is
|
||||
// necessary to preserve annotations/forms that previous Okular version
|
||||
// had stored there.
|
||||
const PageItems saveWhat = AllPageItems | OriginalAnnotationPageItems | OriginalFormFieldPageItems;
|
||||
// <page list><page number='x'>.... </page> save pages that hold data
|
||||
QVector< Page * >::const_iterator pIt = m_pagesVector.constBegin(), pEnd = m_pagesVector.constEnd();
|
||||
for ( ; pIt != pEnd; ++pIt )
|
||||
(*pIt)->d->saveLocalContents( pageList, doc, saveWhat );
|
||||
}
|
||||
// <page list><page number='x'>.... </page> save pages that hold data
|
||||
QVector< Page * >::const_iterator pIt = m_pagesVector.constBegin(), pEnd = m_pagesVector.constEnd();
|
||||
for ( ; pIt != pEnd; ++pIt )
|
||||
(*pIt)->d->saveLocalContents( pageList, doc, saveWhat );
|
||||
|
||||
// 2.2. Save document info (current viewport, history, ... ) to DOM
|
||||
QDomElement generalInfo = doc.createElement( QStringLiteral("generalInfo") );
|
||||
|
@ -2142,6 +2129,7 @@ Document::Document( QWidget *widget )
|
|||
connect( SettingsCore::self(), SIGNAL(configChanged()), this, SLOT(_o_configChanged()) );
|
||||
connect(d->m_undoStack, &QUndoStack::canUndoChanged, this, &Document::canUndoChanged);
|
||||
connect(d->m_undoStack, &QUndoStack::canRedoChanged, this, &Document::canRedoChanged);
|
||||
connect(d->m_undoStack, &QUndoStack::cleanChanged, this, &Document::undoHistoryCleanChanged);
|
||||
|
||||
qRegisterMetaType<Okular::FontInfo>();
|
||||
}
|
||||
|
@ -2290,7 +2278,6 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
QMimeDatabase db;
|
||||
QMimeType mime = _mime;
|
||||
QByteArray filedata;
|
||||
qint64 document_size = -1;
|
||||
bool isstdin = url.fileName() == QLatin1String( "-" );
|
||||
bool triedMimeFromFileContent = false;
|
||||
if ( !isstdin )
|
||||
|
@ -2298,21 +2285,11 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
if ( !mime.isValid() )
|
||||
return OpenError;
|
||||
|
||||
// docFile is always local so we can use QFileInfo on it
|
||||
QFileInfo fileReadTest( docFile );
|
||||
if ( fileReadTest.isFile() && !fileReadTest.isReadable() )
|
||||
{
|
||||
d->m_docFileName.clear();
|
||||
return OpenError;
|
||||
}
|
||||
// determine the related "xml document-info" filename
|
||||
d->m_url = url;
|
||||
d->m_docFileName = docFile;
|
||||
if ( url.isLocalFile() && !d->m_archiveData )
|
||||
{
|
||||
document_size = fileReadTest.size();
|
||||
d->m_xmlFileName = DocumentPrivate::docDataFileName(url, document_size);
|
||||
}
|
||||
|
||||
if ( !d->updateMetadataXmlNameAndDocSize() )
|
||||
return OpenError;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2322,7 +2299,7 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
mime = db.mimeTypeForData( filedata );
|
||||
if ( !mime.isValid() || mime.isDefault() )
|
||||
return OpenError;
|
||||
document_size = filedata.size();
|
||||
d->m_docSize = filedata.size();
|
||||
triedMimeFromFileContent = true;
|
||||
}
|
||||
|
||||
|
@ -2428,35 +2405,30 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
connect( d->m_pageController, SIGNAL(rotationFinished(int,Okular::Page*)),
|
||||
this, SLOT(rotationFinished(int,Okular::Page*)) );
|
||||
|
||||
bool containsExternalAnnotations = false;
|
||||
foreach ( Page * p, d->m_pagesVector )
|
||||
{
|
||||
p->d->m_doc = d;
|
||||
if ( !p->annotations().empty() )
|
||||
containsExternalAnnotations = true;
|
||||
}
|
||||
|
||||
// Be quiet while restoring local annotations
|
||||
d->m_showWarningLimitedAnnotSupport = false;
|
||||
d->m_annotationsNeedSaveAs = false;
|
||||
d->m_metadataLoadingCompleted = false;
|
||||
d->m_docdataMigrationNeeded = false;
|
||||
|
||||
// 2. load Additional Data (bookmarks, local annotations and metadata) about the document
|
||||
if ( d->m_archiveData )
|
||||
{
|
||||
d->loadDocumentInfo( d->m_archiveData->metadataFile );
|
||||
d->m_annotationsNeedSaveAs = true;
|
||||
d->loadDocumentInfo( d->m_archiveData->metadataFile, LoadPageInfo );
|
||||
d->loadDocumentInfo( LoadGeneralInfo );
|
||||
}
|
||||
else
|
||||
{
|
||||
d->loadDocumentInfo();
|
||||
d->m_annotationsNeedSaveAs = ( d->canAddAnnotationsNatively() && containsExternalAnnotations );
|
||||
if ( d->loadDocumentInfo( LoadPageInfo ) )
|
||||
d->m_docdataMigrationNeeded = true;
|
||||
d->loadDocumentInfo( LoadGeneralInfo );
|
||||
}
|
||||
|
||||
d->m_showWarningLimitedAnnotSupport = true;
|
||||
d->m_metadataLoadingCompleted = true;
|
||||
d->m_bookmarkManager->setUrl( d->m_url );
|
||||
|
||||
// 3. setup observers inernal lists and data
|
||||
foreachObserver( notifySetup( d->m_pagesVector, DocumentObserver::DocumentChanged ) );
|
||||
foreachObserver( notifySetup( d->m_pagesVector, DocumentObserver::DocumentChanged | DocumentObserver::UrlChanged ) );
|
||||
|
||||
// 4. set initial page (restoring the page saved in xml if loaded)
|
||||
DocumentViewport loadedViewport = (*d->m_viewportIterator);
|
||||
|
@ -2495,7 +2467,6 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
}
|
||||
|
||||
AudioPlayer::instance()->d->m_currentDocument = isstdin ? QUrl() : d->m_url;
|
||||
d->m_docSize = document_size;
|
||||
|
||||
const QStringList docScripts = d->m_generator->metaData( QStringLiteral("DocumentScripts"), QStringLiteral ( "JavaScript" ) ).toStringList();
|
||||
if ( !docScripts.isEmpty() )
|
||||
|
@ -2510,6 +2481,31 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
return OpenSuccess;
|
||||
}
|
||||
|
||||
bool DocumentPrivate::updateMetadataXmlNameAndDocSize()
|
||||
{
|
||||
// m_docFileName is always local so we can use QFileInfo on it
|
||||
QFileInfo fileReadTest( m_docFileName );
|
||||
if ( !fileReadTest.isFile() && !fileReadTest.isReadable() )
|
||||
return false;
|
||||
|
||||
m_docSize = fileReadTest.size();
|
||||
|
||||
// determine the related "xml document-info" filename
|
||||
if ( m_url.isLocalFile() )
|
||||
{
|
||||
const QString filePath = docDataFileName( m_url, m_docSize );
|
||||
qCDebug(OkularCoreDebug) << "Metadata file is now:" << filePath;
|
||||
m_xmlFileName = filePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
qCDebug(OkularCoreDebug) << "Metadata file: disabled";
|
||||
m_xmlFileName = QString();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
KXMLGUIClient* Document::guiClient()
|
||||
{
|
||||
|
@ -2618,7 +2614,7 @@ void Document::closeDocument()
|
|||
d->m_rotation = Rotation0;
|
||||
|
||||
// send an empty list to observers (to free their data)
|
||||
foreachObserver( notifySetup( QVector< Page * >(), DocumentObserver::DocumentChanged ) );
|
||||
foreachObserver( notifySetup( QVector< Page * >(), DocumentObserver::DocumentChanged | DocumentObserver::UrlChanged ) );
|
||||
|
||||
// delete pages and clear 'd->m_pagesVector' container
|
||||
QVector< Page * >::const_iterator pIt = d->m_pagesVector.constBegin();
|
||||
|
@ -2662,6 +2658,7 @@ void Document::closeDocument()
|
|||
AudioPlayer::instance()->d->m_currentDocument = QUrl();
|
||||
|
||||
d->m_undoStack->clear();
|
||||
d->m_docdataMigrationNeeded = false;
|
||||
}
|
||||
|
||||
void Document::addObserver( DocumentObserver * pObserver )
|
||||
|
@ -2672,7 +2669,7 @@ void Document::addObserver( DocumentObserver * pObserver )
|
|||
// if the observer is added while a document is already opened, tell it
|
||||
if ( !d->m_pagesVector.isEmpty() )
|
||||
{
|
||||
pObserver->notifySetup( d->m_pagesVector, DocumentObserver::DocumentChanged );
|
||||
pObserver->notifySetup( d->m_pagesVector, DocumentObserver::DocumentChanged | DocumentObserver::UrlChanged );
|
||||
pObserver->notifyViewportChanged( false /*disables smoothMove*/ );
|
||||
}
|
||||
}
|
||||
|
@ -2909,7 +2906,9 @@ QUrl Document::currentDocument() const
|
|||
|
||||
bool Document::isAllowed( Permission action ) const
|
||||
{
|
||||
if ( action == Okular::AllowNotes && !d->m_annotationEditingEnabled )
|
||||
if ( action == Okular::AllowNotes && ( d->m_docdataMigrationNeeded || !d->m_annotationEditingEnabled ) )
|
||||
return false;
|
||||
if ( action == Okular::AllowFillForms && d->m_docdataMigrationNeeded )
|
||||
return false;
|
||||
|
||||
#if !OKULAR_FORCE_DRM
|
||||
|
@ -3219,12 +3218,11 @@ void Document::requestTextPage( uint page )
|
|||
|
||||
void DocumentPrivate::notifyAnnotationChanges( int page )
|
||||
{
|
||||
int flags = DocumentObserver::Annotations;
|
||||
foreachObserverD( notifyPageChanged( page, DocumentObserver::Annotations ) );
|
||||
}
|
||||
|
||||
if ( m_annotationsNeedSaveAs )
|
||||
flags |= DocumentObserver::NeedSaveAs;
|
||||
|
||||
foreachObserverD( notifyPageChanged( page, flags ) );
|
||||
void DocumentPrivate::notifyFormChanges( int /*page*/ )
|
||||
{
|
||||
}
|
||||
|
||||
void Document::addPageAnnotation( int page, Annotation * annotation )
|
||||
|
@ -4317,6 +4315,9 @@ QStringList Document::supportedMimeTypes() const
|
|||
result.append(mimeType.name());
|
||||
}
|
||||
|
||||
// Add the Okular archive mimetype
|
||||
result << QStringLiteral("application/vnd.kde.okular-archive");
|
||||
|
||||
// Sorting by mimetype name doesn't make a ton of sense,
|
||||
// but ensures that the list is ordered the same way every time
|
||||
qSort(result);
|
||||
|
@ -4326,6 +4327,154 @@ QStringList Document::supportedMimeTypes() const
|
|||
return result;
|
||||
}
|
||||
|
||||
bool Document::canSwapBackingFile() const
|
||||
{
|
||||
if ( !d->m_generator )
|
||||
return false;
|
||||
Q_ASSERT( !d->m_generatorName.isEmpty() );
|
||||
|
||||
QHash< QString, GeneratorInfo >::iterator genIt = d->m_loadedGenerators.find( d->m_generatorName );
|
||||
Q_ASSERT( genIt != d->m_loadedGenerators.end() );
|
||||
|
||||
return genIt->generator->hasFeature( Generator::SwapBackingFile );
|
||||
}
|
||||
|
||||
bool Document::swapBackingFile( const QString &newFileName, const QUrl &url )
|
||||
{
|
||||
if ( !d->m_generator )
|
||||
return false;
|
||||
Q_ASSERT( !d->m_generatorName.isEmpty() );
|
||||
|
||||
QHash< QString, GeneratorInfo >::iterator genIt = d->m_loadedGenerators.find( d->m_generatorName );
|
||||
Q_ASSERT( genIt != d->m_loadedGenerators.end() );
|
||||
|
||||
if ( !genIt->generator->hasFeature( Generator::SwapBackingFile ) )
|
||||
return false;
|
||||
|
||||
// Save metadata about the file we're about to close
|
||||
d->saveDocumentInfo();
|
||||
|
||||
qCDebug(OkularCoreDebug) << "Swapping backing file to" << newFileName;
|
||||
QVector< Page * > newPagesVector;
|
||||
Generator::SwapBackingFileResult result = genIt->generator->swapBackingFile( newFileName, newPagesVector );
|
||||
if (result != Generator::SwapBackingFileError)
|
||||
{
|
||||
QLinkedList< ObjectRect* > rectsToDelete;
|
||||
QLinkedList< Annotation* > annotationsToDelete;
|
||||
QSet< PagePrivate* > pagePrivatesToDelete;
|
||||
|
||||
if (result == Generator::SwapBackingFileReloadInternalData)
|
||||
{
|
||||
// Here we need to replace everything that the old generator
|
||||
// had created with what the new one has without making it look like
|
||||
// we have actually closed and opened the file again
|
||||
|
||||
// Simple sanity check
|
||||
if (newPagesVector.count() != d->m_pagesVector.count())
|
||||
return false;
|
||||
|
||||
// Update the undo stack contents
|
||||
for (int i = 0; i < d->m_undoStack->count(); ++i)
|
||||
{
|
||||
// Trust me on the const_cast ^_^
|
||||
QUndoCommand *uc = const_cast<QUndoCommand *>( d->m_undoStack->command( i ) );
|
||||
if (OkularUndoCommand *ouc = dynamic_cast<OkularUndoCommand*>( uc ))
|
||||
{
|
||||
const bool success = ouc->refreshInternalPageReferences( newPagesVector );
|
||||
if ( !success )
|
||||
{
|
||||
qWarning() << "Document::swapBackingFile: refreshInternalPageReferences failed" << ouc;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Document::swapBackingFile: Unhandled undo command" << uc;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < d->m_pagesVector.count(); ++i)
|
||||
{
|
||||
// switch the PagePrivate* from newPage to oldPage
|
||||
// this way everyone still holding Page* doesn't get
|
||||
// disturbed by it
|
||||
Page *oldPage = d->m_pagesVector[i];
|
||||
Page *newPage = newPagesVector[i];
|
||||
newPage->d->adoptGeneratedContents(oldPage->d);
|
||||
|
||||
pagePrivatesToDelete << oldPage->d;
|
||||
oldPage->d = newPage->d;
|
||||
oldPage->d->m_page = oldPage;
|
||||
oldPage->d->m_doc = d;
|
||||
newPage->d = nullptr;
|
||||
|
||||
annotationsToDelete << oldPage->m_annotations;
|
||||
rectsToDelete << oldPage->m_rects;
|
||||
oldPage->m_annotations = newPage->m_annotations;
|
||||
oldPage->m_rects = newPage->m_rects;
|
||||
}
|
||||
qDeleteAll( newPagesVector );
|
||||
}
|
||||
|
||||
d->m_url = url;
|
||||
d->m_docFileName = newFileName;
|
||||
d->updateMetadataXmlNameAndDocSize();
|
||||
d->m_bookmarkManager->setUrl( d->m_url );
|
||||
|
||||
if ( d->m_synctex_scanner )
|
||||
{
|
||||
synctex_scanner_free( d->m_synctex_scanner );
|
||||
d->m_synctex_scanner = synctex_scanner_new_with_output_file( QFile::encodeName( newFileName ).constData(), nullptr, 1);
|
||||
if ( !d->m_synctex_scanner && QFile::exists(newFileName + QLatin1String( "sync" ) ) )
|
||||
{
|
||||
d->loadSyncFile(newFileName);
|
||||
}
|
||||
}
|
||||
|
||||
foreachObserver( notifySetup( d->m_pagesVector, DocumentObserver::UrlChanged ) );
|
||||
|
||||
qDeleteAll( annotationsToDelete );
|
||||
qDeleteAll( rectsToDelete );
|
||||
qDeleteAll( pagePrivatesToDelete );
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Document::swapBackingFileArchive( const QString &newFileName, const QUrl &url )
|
||||
{
|
||||
qCDebug(OkularCoreDebug) << "Swapping backing archive to" << newFileName;
|
||||
|
||||
ArchiveData *newArchive = DocumentPrivate::unpackDocumentArchive( newFileName );
|
||||
if ( !newArchive )
|
||||
return false;
|
||||
|
||||
const QString tempFileName = newArchive->document.fileName();
|
||||
|
||||
const bool success = swapBackingFile( tempFileName, url );
|
||||
|
||||
if ( success )
|
||||
{
|
||||
delete d->m_archiveData;
|
||||
d->m_archiveData = newArchive;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Document::setHistoryClean( bool clean )
|
||||
{
|
||||
if ( clean )
|
||||
d->m_undoStack->setClean();
|
||||
else
|
||||
d->m_undoStack->resetClean();
|
||||
}
|
||||
|
||||
bool Document::canSaveChanges() const
|
||||
{
|
||||
if ( !d->m_generator )
|
||||
|
@ -4423,31 +4572,31 @@ QByteArray Document::fontData(const FontInfo &font) const
|
|||
return result;
|
||||
}
|
||||
|
||||
Document::OpenResult Document::openDocumentArchive( const QString & docFile, const QUrl & url, const QString & password )
|
||||
ArchiveData *DocumentPrivate::unpackDocumentArchive( const QString &archivePath )
|
||||
{
|
||||
QMimeDatabase db;
|
||||
const QMimeType mime = db.mimeTypeForFile( docFile, QMimeDatabase::MatchExtension );
|
||||
const QMimeType mime = db.mimeTypeForFile( archivePath, QMimeDatabase::MatchExtension );
|
||||
if ( !mime.inherits( QStringLiteral("application/vnd.kde.okular-archive") ) )
|
||||
return OpenError;
|
||||
return nullptr;
|
||||
|
||||
KZip okularArchive( docFile );
|
||||
KZip okularArchive( archivePath );
|
||||
if ( !okularArchive.open( QIODevice::ReadOnly ) )
|
||||
return OpenError;
|
||||
return nullptr;
|
||||
|
||||
const KArchiveDirectory * mainDir = okularArchive.directory();
|
||||
const KArchiveEntry * mainEntry = mainDir->entry( QStringLiteral("content.xml") );
|
||||
if ( !mainEntry || !mainEntry->isFile() )
|
||||
return OpenError;
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr< QIODevice > mainEntryDevice( static_cast< const KZipFileEntry * >( mainEntry )->createDevice() );
|
||||
QDomDocument doc;
|
||||
if ( !doc.setContent( mainEntryDevice.get() ) )
|
||||
return OpenError;
|
||||
return nullptr;
|
||||
mainEntryDevice.reset();
|
||||
|
||||
QDomElement root = doc.documentElement();
|
||||
if ( root.tagName() != QLatin1String("OkularArchive") )
|
||||
return OpenError;
|
||||
if ( root.tagName() != QLatin1String("OkularArchive") )
|
||||
return nullptr;
|
||||
|
||||
QString documentFileName;
|
||||
QString metadataFileName;
|
||||
|
@ -4467,20 +4616,21 @@ Document::OpenResult Document::openDocumentArchive( const QString & docFile, con
|
|||
}
|
||||
}
|
||||
if ( documentFileName.isEmpty() )
|
||||
return OpenError;
|
||||
return nullptr;
|
||||
|
||||
const KArchiveEntry * docEntry = mainDir->entry( documentFileName );
|
||||
if ( !docEntry || !docEntry->isFile() )
|
||||
return OpenError;
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr< ArchiveData > archiveData( new ArchiveData() );
|
||||
const int dotPos = documentFileName.indexOf( QLatin1Char('.') );
|
||||
if ( dotPos != -1 )
|
||||
archiveData->document.setFileTemplate(QDir::tempPath() + QLatin1String("/okular_XXXXXX") + documentFileName.mid(dotPos));
|
||||
if ( !archiveData->document.open() )
|
||||
return OpenError;
|
||||
return nullptr;
|
||||
|
||||
archiveData->originalFileName = documentFileName;
|
||||
|
||||
QString tempFileName = archiveData->document.fileName();
|
||||
{
|
||||
std::unique_ptr< QIODevice > docEntryDevice( static_cast< const KZipFileEntry * >( docEntry )->createDevice() );
|
||||
copyQIODevice( docEntryDevice.get(), &archiveData->document );
|
||||
|
@ -4499,17 +4649,23 @@ Document::OpenResult Document::openDocumentArchive( const QString & docFile, con
|
|||
}
|
||||
}
|
||||
|
||||
return archiveData.release();
|
||||
}
|
||||
|
||||
Document::OpenResult Document::openDocumentArchive( const QString & docFile, const QUrl & url, const QString & password )
|
||||
{
|
||||
d->m_archiveData = DocumentPrivate::unpackDocumentArchive( docFile );
|
||||
if ( !d->m_archiveData )
|
||||
return OpenError;
|
||||
|
||||
const QString tempFileName = d->m_archiveData->document.fileName();
|
||||
QMimeDatabase db;
|
||||
const QMimeType docMime = db.mimeTypeForFile( tempFileName, QMimeDatabase::MatchContent );
|
||||
d->m_archiveData = archiveData.get();
|
||||
d->m_archivedFileName = documentFileName;
|
||||
const OpenResult ret = openDocument( tempFileName, url, docMime, password );
|
||||
|
||||
if ( ret == OpenSuccess )
|
||||
{
|
||||
archiveData.release();
|
||||
}
|
||||
else
|
||||
if ( ret != OpenSuccess )
|
||||
{
|
||||
delete d->m_archiveData;
|
||||
d->m_archiveData = nullptr;
|
||||
}
|
||||
|
||||
|
@ -4523,7 +4679,7 @@ bool Document::saveDocumentArchive( const QString &fileName )
|
|||
|
||||
/* If we opened an archive, use the name of original file (eg foo.pdf)
|
||||
* instead of the archive's one (eg foo.okular) */
|
||||
QString docFileName = d->m_archiveData ? d->m_archivedFileName : d->m_url.fileName();
|
||||
QString docFileName = d->m_archiveData ? d->m_archiveData->originalFileName : d->m_url.fileName();
|
||||
if ( docFileName == QLatin1String( "-" ) )
|
||||
return false;
|
||||
|
||||
|
@ -4564,7 +4720,8 @@ bool Document::saveDocumentArchive( const QString &fileName )
|
|||
// If the generator can save annotations natively, do it
|
||||
QTemporaryFile modifiedFile;
|
||||
bool annotationsSavedNatively = false;
|
||||
if ( d->canAddAnnotationsNatively() )
|
||||
bool formsSavedNatively = false;
|
||||
if ( d->canAddAnnotationsNatively() || canSaveChanges( SaveFormsCapability ) )
|
||||
{
|
||||
if ( !modifiedFile.open() )
|
||||
return false;
|
||||
|
@ -4575,7 +4732,8 @@ bool Document::saveDocumentArchive( const QString &fileName )
|
|||
if ( saveChanges( modifiedFile.fileName(), &errorText ) )
|
||||
{
|
||||
docPath = modifiedFile.fileName(); // Save this instead of the original file
|
||||
annotationsSavedNatively = true;
|
||||
annotationsSavedNatively = d->canAddAnnotationsNatively();
|
||||
formsSavedNatively = canSaveChanges( SaveFormsCapability );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4584,8 +4742,13 @@ bool Document::saveDocumentArchive( const QString &fileName )
|
|||
}
|
||||
}
|
||||
|
||||
PageItems saveWhat = None;
|
||||
if ( !annotationsSavedNatively )
|
||||
saveWhat |= AnnotationPageItems;
|
||||
if ( !formsSavedNatively )
|
||||
saveWhat |= FormFieldPageItems;
|
||||
|
||||
QTemporaryFile metadataFile;
|
||||
PageItems saveWhat = annotationsSavedNatively ? None : AnnotationPageItems;
|
||||
if ( !d->savePageDocumentInfo( &metadataFile, saveWhat ) )
|
||||
return false;
|
||||
|
||||
|
@ -4604,6 +4767,17 @@ bool Document::saveDocumentArchive( const QString &fileName )
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Document::extractArchivedFile( const QString &destFileName )
|
||||
{
|
||||
if ( !d->m_archiveData )
|
||||
return false;
|
||||
|
||||
// Remove existing file, if present (QFile::copy doesn't overwrite by itself)
|
||||
QFile::remove( destFileName );
|
||||
|
||||
return d->m_archiveData->document.copy( destFileName );
|
||||
}
|
||||
|
||||
QPrinter::Orientation Document::orientation() const
|
||||
{
|
||||
double width, height;
|
||||
|
@ -4641,6 +4815,20 @@ void Document::walletDataForFile( const QString &fileName, QString *walletName,
|
|||
}
|
||||
}
|
||||
|
||||
bool Document::isDocdataMigrationNeeded() const
|
||||
{
|
||||
return d->m_docdataMigrationNeeded;
|
||||
}
|
||||
|
||||
void Document::docdataMigrationDone()
|
||||
{
|
||||
if (d->m_docdataMigrationNeeded)
|
||||
{
|
||||
d->m_docdataMigrationNeeded = false;
|
||||
foreachObserver( notifySetup( d->m_pagesVector, 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
QAbstractItemModel * Document::layersModel() const
|
||||
{
|
||||
return d->m_generator ? d->m_generator->layersModel() : nullptr;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2004-2008 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -737,6 +740,48 @@ class OKULARCORE_EXPORT Document : public QObject
|
|||
*/
|
||||
KPluginMetaData generatorInfo() const;
|
||||
|
||||
/**
|
||||
* Returns whether the generator supports hot-swapping the current file
|
||||
* with another identical file
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
bool canSwapBackingFile() const;
|
||||
|
||||
/**
|
||||
* Reload the document from a new location, without any visible effect
|
||||
* to the user.
|
||||
*
|
||||
* The new file must be identical to the current one or, if the document
|
||||
* has been modified (eg the user edited forms and annotations), the new
|
||||
* document must have these changes too. For example, you can call
|
||||
* saveChanges first to write changes to a file and then swapBackingFile
|
||||
* to switch to the new location.
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
bool swapBackingFile( const QString &newFileName, const QUrl &url );
|
||||
|
||||
/**
|
||||
* Same as swapBackingFile, but newFileName must be a .okular file.
|
||||
*
|
||||
* The new file must be identical to the current one or, if the document
|
||||
* has been modified (eg the user edited forms and annotations), the new
|
||||
* document must have these changes too. For example, you can call
|
||||
* saveDocumentArchive first to write changes to a file and then
|
||||
* swapBackingFileArchive to switch to the new location.
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
bool swapBackingFileArchive( const QString &newFileName, const QUrl &url );
|
||||
|
||||
/**
|
||||
* Sets the history to be clean
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
void setHistoryClean( bool clean );
|
||||
|
||||
/**
|
||||
* Saving capabilities. Their availability varies according to the
|
||||
* underlying generator and/or the document type.
|
||||
|
@ -822,6 +867,15 @@ class OKULARCORE_EXPORT Document : public QObject
|
|||
*/
|
||||
bool saveDocumentArchive( const QString &fileName );
|
||||
|
||||
/**
|
||||
* Extract the document file from the current archive.
|
||||
*
|
||||
* @warning This function only works if the current file is a document archive
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
bool extractArchivedFile( const QString &destFileName );
|
||||
|
||||
/**
|
||||
* Asks the generator to dynamically generate a SourceReference for a given
|
||||
* page number and absolute X and Y position on this page.
|
||||
|
@ -857,6 +911,25 @@ class OKULARCORE_EXPORT Document : public QObject
|
|||
*/
|
||||
void walletDataForFile( const QString &fileName, QString *walletName, QString *walletFolder, QString *walletKey ) const;
|
||||
|
||||
/**
|
||||
* Since version 0.21, okular does not allow editing annotations and
|
||||
* form data if they are stored in the docdata directory (like older
|
||||
* okular versions did by default).
|
||||
* If this flag is set, then annotations and forms cannot be edited.
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
bool isDocdataMigrationNeeded() const;
|
||||
|
||||
/**
|
||||
* Delete annotations and form data from the docdata folder. Call it if
|
||||
* isDocdataMigrationNeeded() was true and you've just saved them to an
|
||||
* external file.
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
void docdataMigrationDone();
|
||||
|
||||
/**
|
||||
* Returns the model for rendering layers (NULL if the document has no layers)
|
||||
*
|
||||
|
@ -1066,6 +1139,12 @@ class OKULARCORE_EXPORT Document : public QObject
|
|||
*/
|
||||
void canRedoChanged( bool redoAvailable );
|
||||
|
||||
/**
|
||||
* This signal is emmitted whenever the undo history is clean (i.e. the same status the last time it was saved)
|
||||
* @since 1.3
|
||||
*/
|
||||
void undoHistoryCleanChanged( bool clean );
|
||||
|
||||
/**
|
||||
* This signal is emitted whenever an rendition action is triggered and the UI should process it.
|
||||
*
|
||||
|
|
|
@ -74,6 +74,15 @@ struct DoContinueDirectionMatchSearchStruct
|
|||
int searchID;
|
||||
};
|
||||
|
||||
enum LoadDocumentInfoFlag
|
||||
{
|
||||
LoadNone = 0,
|
||||
LoadPageInfo = 1, // Load annotations and forms
|
||||
LoadGeneralInfo = 2, // History, rotation, ...
|
||||
LoadAllInfo = 0xff
|
||||
};
|
||||
Q_DECLARE_FLAGS(LoadDocumentInfoFlags, LoadDocumentInfoFlag)
|
||||
|
||||
class DocumentPrivate
|
||||
{
|
||||
public:
|
||||
|
@ -99,12 +108,14 @@ class DocumentPrivate
|
|||
m_fontsCached( false ),
|
||||
m_annotationEditingEnabled ( true ),
|
||||
m_annotationBeingModified( false ),
|
||||
m_docdataMigrationNeeded( false ),
|
||||
m_synctex_scanner( nullptr )
|
||||
{
|
||||
calculateMaxTextPages();
|
||||
}
|
||||
|
||||
// private methods
|
||||
bool updateMetadataXmlNameAndDocSize();
|
||||
QString pagesSizeString() const;
|
||||
QString namePaperSize(double inchesWidth, double inchesHeight) const;
|
||||
QString localizedSize(const QSizeF &size) const;
|
||||
|
@ -115,8 +126,8 @@ class DocumentPrivate
|
|||
void calculateMaxTextPages();
|
||||
qulonglong getTotalMemory();
|
||||
qulonglong getFreeMemory( qulonglong *freeSwap = nullptr );
|
||||
void loadDocumentInfo();
|
||||
void loadDocumentInfo( QFile &infoFile );
|
||||
bool loadDocumentInfo( LoadDocumentInfoFlags loadWhat );
|
||||
bool loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags loadWhat );
|
||||
void loadViewsInfo( View *view, const QDomElement &e );
|
||||
void saveViewsInfo( View *view, QDomElement &e ) const;
|
||||
QUrl giveAbsoluteUrl( const QString & fileName ) const;
|
||||
|
@ -130,13 +141,14 @@ class DocumentPrivate
|
|||
ConfigInterface* generatorConfig( GeneratorInfo& info );
|
||||
SaveInterface* generatorSave( GeneratorInfo& info );
|
||||
Document::OpenResult openDocumentInternal( const KPluginMetaData& offer, bool isstdin, const QString& docFile, const QByteArray& filedata, const QString& password );
|
||||
static ArchiveData *unpackDocumentArchive( const QString &archivePath );
|
||||
bool savePageDocumentInfo( QTemporaryFile *infoFile, int what ) const;
|
||||
DocumentViewport nextDocumentViewport() const;
|
||||
void notifyAnnotationChanges( int page );
|
||||
void notifyFormChanges( int page );
|
||||
bool canAddAnnotationsNatively() const;
|
||||
bool canModifyExternalAnnotations() const;
|
||||
bool canRemoveExternalAnnotations() const;
|
||||
void warnLimitedAnnotSupport();
|
||||
OKULARCORE_EXPORT static QString docDataFileName(const QUrl &url, qint64 document_size);
|
||||
|
||||
// Methods that implement functionality needed by undo commands
|
||||
|
@ -273,13 +285,19 @@ class DocumentPrivate
|
|||
QSet< View * > m_views;
|
||||
|
||||
bool m_annotationEditingEnabled;
|
||||
bool m_annotationsNeedSaveAs;
|
||||
bool m_annotationBeingModified; // is an annotation currently being moved or resized?
|
||||
bool m_showWarningLimitedAnnotSupport;
|
||||
bool m_metadataLoadingCompleted;
|
||||
|
||||
QUndoStack *m_undoStack;
|
||||
QDomNode m_prevPropsOfAnnotBeingModified;
|
||||
|
||||
// Since 0.21, we no longer support saving annotations and form data in
|
||||
// the docdata/ directory and we ask the user to migrate them to an
|
||||
// external file as soon as possible, otherwise the document will be
|
||||
// shown in read-only mode. This flag is set if the docdata/ XML file
|
||||
// for the current document contains any annotation or form.
|
||||
bool m_docdataMigrationNeeded;
|
||||
|
||||
synctex_scanner_p m_synctex_scanner;
|
||||
|
||||
// generator selection
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 Jon Mease <jon.mease@gmail.com> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -15,6 +18,7 @@
|
|||
#include "form.h"
|
||||
#include "utils_p.h"
|
||||
#include "page.h"
|
||||
#include "page_p.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
|
@ -87,6 +91,21 @@ void AddAnnotationCommand::redo()
|
|||
m_done = true;
|
||||
}
|
||||
|
||||
bool AddAnnotationCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector )
|
||||
{
|
||||
if ( m_done )
|
||||
{
|
||||
// We don't always update m_annotation because even if the annotation has been added to the document
|
||||
// it can have been removed later so the annotation pointer is stored inside a following RemoveAnnotationCommand
|
||||
// and thus doesn't need updating because it didn't change
|
||||
// because of the document reload
|
||||
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
|
||||
if (a) m_annotation = a;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RemoveAnnotationCommand::RemoveAnnotationCommand(Okular::DocumentPrivate * doc, Okular::Annotation* annotation, int pageNumber)
|
||||
: m_docPriv( doc ),
|
||||
|
@ -112,12 +131,27 @@ void RemoveAnnotationCommand::undo()
|
|||
m_done = false;
|
||||
}
|
||||
|
||||
void RemoveAnnotationCommand::redo(){
|
||||
void RemoveAnnotationCommand::redo()
|
||||
{
|
||||
moveViewportIfBoundingRectNotFullyVisible( m_annotation->boundingRectangle(), m_docPriv, m_pageNumber );
|
||||
m_docPriv->performRemovePageAnnotation( m_pageNumber, m_annotation );
|
||||
m_done = true;
|
||||
}
|
||||
|
||||
bool RemoveAnnotationCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector )
|
||||
{
|
||||
if ( !m_done )
|
||||
{
|
||||
// We don't always update m_annotation because it can happen that the annotation remove has been undo
|
||||
// and that annotation addition has also been undone so the the annotation pointer is stored inside
|
||||
// a previous AddAnnotationCommand and thus doesn't need updating because it didn't change
|
||||
// because of the document reload
|
||||
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
|
||||
if (a) m_annotation = a;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ModifyAnnotationPropertiesCommand::ModifyAnnotationPropertiesCommand( DocumentPrivate* docPriv,
|
||||
Annotation* annotation,
|
||||
|
@ -147,6 +181,16 @@ void ModifyAnnotationPropertiesCommand::redo()
|
|||
m_docPriv->performModifyPageAnnotation( m_pageNumber, m_annotation, true );
|
||||
}
|
||||
|
||||
bool ModifyAnnotationPropertiesCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector )
|
||||
{
|
||||
// Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
|
||||
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
|
||||
if (a) m_annotation = a;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
TranslateAnnotationCommand::TranslateAnnotationCommand( DocumentPrivate* docPriv,
|
||||
Annotation* annotation,
|
||||
int pageNumber,
|
||||
|
@ -212,6 +256,16 @@ Okular::NormalizedRect TranslateAnnotationCommand::translateBoundingRectangle( c
|
|||
return boundingRect;
|
||||
}
|
||||
|
||||
bool TranslateAnnotationCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
|
||||
{
|
||||
// Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
|
||||
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
|
||||
if (a) m_annotation = a;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
AdjustAnnotationCommand::AdjustAnnotationCommand(Okular::DocumentPrivate * docPriv,
|
||||
Okular::Annotation * annotation,
|
||||
int pageNumber,
|
||||
|
@ -277,6 +331,16 @@ Okular::NormalizedRect AdjustAnnotationCommand::adjustBoundingRectangle(
|
|||
return Okular::NormalizedRect( left, top, right, bottom );
|
||||
}
|
||||
|
||||
bool AdjustAnnotationCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
|
||||
{
|
||||
// Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
|
||||
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
|
||||
if (a) m_annotation = a;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
EditTextCommand::EditTextCommand( const QString & newContents,
|
||||
int newCursorPos,
|
||||
const QString & prevContents,
|
||||
|
@ -363,6 +427,7 @@ QString EditTextCommand::newContentsRightOfCursor()
|
|||
return m_newContents.right(m_newContents.length() - m_newCursorPos);
|
||||
}
|
||||
|
||||
|
||||
EditAnnotationContentsCommand::EditAnnotationContentsCommand( DocumentPrivate* docPriv,
|
||||
Annotation* annotation,
|
||||
int pageNumber,
|
||||
|
@ -412,6 +477,15 @@ bool EditAnnotationContentsCommand::mergeWith(const QUndoCommand* uc)
|
|||
}
|
||||
}
|
||||
|
||||
bool EditAnnotationContentsCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
|
||||
{
|
||||
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
|
||||
if (a) m_annotation = a;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
EditFormTextCommand::EditFormTextCommand( Okular::DocumentPrivate* docPriv,
|
||||
Okular::FormFieldText* form,
|
||||
int pageNumber,
|
||||
|
@ -433,6 +507,7 @@ void EditFormTextCommand::undo()
|
|||
moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber );
|
||||
m_form->setText( m_prevContents );
|
||||
emit m_docPriv->m_parent->formTextChangedByUndoRedo( m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos );
|
||||
m_docPriv->notifyFormChanges( m_pageNumber );
|
||||
}
|
||||
|
||||
void EditFormTextCommand::redo()
|
||||
|
@ -440,6 +515,7 @@ void EditFormTextCommand::redo()
|
|||
moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber );
|
||||
m_form->setText( m_newContents );
|
||||
emit m_docPriv->m_parent->formTextChangedByUndoRedo( m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos );
|
||||
m_docPriv->notifyFormChanges( m_pageNumber );
|
||||
}
|
||||
|
||||
int EditFormTextCommand::id() const
|
||||
|
@ -461,6 +537,14 @@ bool EditFormTextCommand::mergeWith(const QUndoCommand* uc)
|
|||
}
|
||||
}
|
||||
|
||||
bool EditFormTextCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
|
||||
{
|
||||
m_form = dynamic_cast<FormFieldText *>(Okular::PagePrivate::findEquivalentForm( newPagesVector[m_pageNumber], m_form ));
|
||||
|
||||
return m_form;
|
||||
}
|
||||
|
||||
|
||||
EditFormListCommand::EditFormListCommand( Okular::DocumentPrivate* docPriv,
|
||||
FormFieldChoice* form,
|
||||
int pageNumber,
|
||||
|
@ -480,6 +564,7 @@ void EditFormListCommand::undo()
|
|||
moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber );
|
||||
m_form->setCurrentChoices( m_prevChoices );
|
||||
emit m_docPriv->m_parent->formListChangedByUndoRedo( m_pageNumber, m_form, m_prevChoices );
|
||||
m_docPriv->notifyFormChanges( m_pageNumber );
|
||||
}
|
||||
|
||||
void EditFormListCommand::redo()
|
||||
|
@ -487,8 +572,17 @@ void EditFormListCommand::redo()
|
|||
moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber );
|
||||
m_form->setCurrentChoices( m_newChoices );
|
||||
emit m_docPriv->m_parent->formListChangedByUndoRedo( m_pageNumber, m_form, m_newChoices );
|
||||
m_docPriv->notifyFormChanges( m_pageNumber );
|
||||
}
|
||||
|
||||
bool EditFormListCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
|
||||
{
|
||||
m_form = dynamic_cast<FormFieldChoice *>(Okular::PagePrivate::findEquivalentForm( newPagesVector[m_pageNumber], m_form ));
|
||||
|
||||
return m_form;
|
||||
}
|
||||
|
||||
|
||||
EditFormComboCommand::EditFormComboCommand( Okular::DocumentPrivate* docPriv,
|
||||
FormFieldChoice* form,
|
||||
int pageNumber,
|
||||
|
@ -533,6 +627,7 @@ void EditFormComboCommand::undo()
|
|||
}
|
||||
moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber );
|
||||
emit m_docPriv->m_parent->formComboChangedByUndoRedo( m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos );
|
||||
m_docPriv->notifyFormChanges( m_pageNumber );
|
||||
}
|
||||
|
||||
void EditFormComboCommand::redo()
|
||||
|
@ -547,6 +642,7 @@ void EditFormComboCommand::redo()
|
|||
}
|
||||
moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber );
|
||||
emit m_docPriv->m_parent->formComboChangedByUndoRedo( m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos );
|
||||
m_docPriv->notifyFormChanges( m_pageNumber );
|
||||
}
|
||||
|
||||
int EditFormComboCommand::id() const
|
||||
|
@ -573,6 +669,14 @@ bool EditFormComboCommand::mergeWith( const QUndoCommand *uc )
|
|||
}
|
||||
}
|
||||
|
||||
bool EditFormComboCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
|
||||
{
|
||||
m_form = dynamic_cast<FormFieldChoice *>(Okular::PagePrivate::findEquivalentForm( newPagesVector[m_pageNumber], m_form ));
|
||||
|
||||
return m_form;
|
||||
}
|
||||
|
||||
|
||||
EditFormButtonsCommand::EditFormButtonsCommand( Okular::DocumentPrivate* docPriv,
|
||||
int pageNumber,
|
||||
const QList< FormFieldButton* > & formButtons,
|
||||
|
@ -603,6 +707,7 @@ void EditFormButtonsCommand::undo()
|
|||
Okular::NormalizedRect boundingRect = buildBoundingRectangleForButtons( m_formButtons );
|
||||
moveViewportIfBoundingRectNotFullyVisible( boundingRect, m_docPriv, m_pageNumber );
|
||||
emit m_docPriv->m_parent->formButtonsChangedByUndoRedo( m_pageNumber, m_formButtons );
|
||||
m_docPriv->notifyFormChanges( m_pageNumber );
|
||||
}
|
||||
|
||||
void EditFormButtonsCommand::redo()
|
||||
|
@ -618,6 +723,22 @@ void EditFormButtonsCommand::redo()
|
|||
Okular::NormalizedRect boundingRect = buildBoundingRectangleForButtons( m_formButtons );
|
||||
moveViewportIfBoundingRectNotFullyVisible( boundingRect, m_docPriv, m_pageNumber );
|
||||
emit m_docPriv->m_parent->formButtonsChangedByUndoRedo( m_pageNumber, m_formButtons );
|
||||
m_docPriv->notifyFormChanges( m_pageNumber );
|
||||
}
|
||||
|
||||
bool EditFormButtonsCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector )
|
||||
{
|
||||
const QList< FormFieldButton* > oldFormButtons = m_formButtons;
|
||||
m_formButtons.clear();
|
||||
foreach( FormFieldButton* oldFormButton, oldFormButtons )
|
||||
{
|
||||
FormFieldButton *button = dynamic_cast<FormFieldButton *>(Okular::PagePrivate::findEquivalentForm( newPagesVector[m_pageNumber], oldFormButton ));
|
||||
if ( !button )
|
||||
return false;
|
||||
m_formButtons << button;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditFormButtonsCommand::clearFormButtonStates()
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2013 Jon Mease <jon.mease@gmail.com> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -23,8 +26,15 @@ class DocumentPrivate;
|
|||
class FormFieldText;
|
||||
class FormFieldButton;
|
||||
class FormFieldChoice;
|
||||
class Page;
|
||||
|
||||
class AddAnnotationCommand : public QUndoCommand
|
||||
class OkularUndoCommand : public QUndoCommand
|
||||
{
|
||||
public:
|
||||
virtual bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) = 0;
|
||||
};
|
||||
|
||||
class AddAnnotationCommand : public OkularUndoCommand
|
||||
{
|
||||
public:
|
||||
AddAnnotationCommand(Okular::DocumentPrivate * docPriv, Okular::Annotation* annotation, int pageNumber);
|
||||
|
@ -35,6 +45,8 @@ class AddAnnotationCommand : public QUndoCommand
|
|||
|
||||
void redo() override;
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate * m_docPriv;
|
||||
Okular::Annotation* m_annotation;
|
||||
|
@ -42,7 +54,7 @@ class AddAnnotationCommand : public QUndoCommand
|
|||
bool m_done;
|
||||
};
|
||||
|
||||
class RemoveAnnotationCommand : public QUndoCommand
|
||||
class RemoveAnnotationCommand : public OkularUndoCommand
|
||||
{
|
||||
public:
|
||||
RemoveAnnotationCommand(Okular::DocumentPrivate * doc, Okular::Annotation* annotation, int pageNumber);
|
||||
|
@ -50,6 +62,8 @@ class RemoveAnnotationCommand : public QUndoCommand
|
|||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate * m_docPriv;
|
||||
Okular::Annotation* m_annotation;
|
||||
|
@ -57,7 +71,7 @@ class RemoveAnnotationCommand : public QUndoCommand
|
|||
bool m_done;
|
||||
};
|
||||
|
||||
class ModifyAnnotationPropertiesCommand : public QUndoCommand
|
||||
class ModifyAnnotationPropertiesCommand : public OkularUndoCommand
|
||||
{
|
||||
public:
|
||||
ModifyAnnotationPropertiesCommand( Okular::DocumentPrivate* docPriv, Okular::Annotation* annotation,
|
||||
|
@ -68,6 +82,8 @@ class ModifyAnnotationPropertiesCommand : public QUndoCommand
|
|||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate * m_docPriv;
|
||||
Okular::Annotation* m_annotation;
|
||||
|
@ -76,7 +92,7 @@ class ModifyAnnotationPropertiesCommand : public QUndoCommand
|
|||
QDomNode m_newProperties;
|
||||
};
|
||||
|
||||
class TranslateAnnotationCommand : public QUndoCommand
|
||||
class TranslateAnnotationCommand : public OkularUndoCommand
|
||||
{
|
||||
public:
|
||||
TranslateAnnotationCommand(Okular::DocumentPrivate* docPriv,
|
||||
|
@ -92,6 +108,8 @@ class TranslateAnnotationCommand : public QUndoCommand
|
|||
Okular::NormalizedPoint minusDelta();
|
||||
Okular::NormalizedRect translateBoundingRectangle( const Okular::NormalizedPoint & delta );
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate * m_docPriv;
|
||||
Okular::Annotation* m_annotation;
|
||||
|
@ -100,7 +118,7 @@ class TranslateAnnotationCommand : public QUndoCommand
|
|||
bool m_completeDrag;
|
||||
};
|
||||
|
||||
class AdjustAnnotationCommand : public QUndoCommand
|
||||
class AdjustAnnotationCommand : public OkularUndoCommand
|
||||
{
|
||||
public:
|
||||
AdjustAnnotationCommand(Okular::DocumentPrivate * docPriv,
|
||||
|
@ -117,6 +135,8 @@ class AdjustAnnotationCommand : public QUndoCommand
|
|||
Okular::NormalizedRect adjustBoundingRectangle(
|
||||
const Okular::NormalizedPoint & delta1, const Okular::NormalizedPoint & delta2 );
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate * m_docPriv;
|
||||
Okular::Annotation* m_annotation;
|
||||
|
@ -126,7 +146,7 @@ class AdjustAnnotationCommand : public QUndoCommand
|
|||
bool m_completeDrag;
|
||||
};
|
||||
|
||||
class EditTextCommand : public QUndoCommand
|
||||
class EditTextCommand : public OkularUndoCommand
|
||||
{
|
||||
public:
|
||||
EditTextCommand( const QString & newContents,
|
||||
|
@ -182,6 +202,8 @@ class EditAnnotationContentsCommand : public EditTextCommand
|
|||
int id() const override;
|
||||
bool mergeWith(const QUndoCommand *uc) override;
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate * m_docPriv;
|
||||
Okular::Annotation* m_annotation;
|
||||
|
@ -203,13 +225,16 @@ class EditFormTextCommand : public EditTextCommand
|
|||
void redo() override;
|
||||
int id() const override;
|
||||
bool mergeWith( const QUndoCommand *uc ) override;
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate* m_docPriv;
|
||||
Okular::FormFieldText* m_form;
|
||||
int m_pageNumber;
|
||||
};
|
||||
|
||||
class EditFormListCommand : public QUndoCommand
|
||||
class EditFormListCommand : public OkularUndoCommand
|
||||
{
|
||||
public:
|
||||
EditFormListCommand( Okular::DocumentPrivate* docPriv,
|
||||
|
@ -222,6 +247,8 @@ class EditFormListCommand : public QUndoCommand
|
|||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate* m_docPriv;
|
||||
FormFieldChoice* m_form;
|
||||
|
@ -248,6 +275,8 @@ class EditFormComboCommand : public EditTextCommand
|
|||
int id() const override;
|
||||
bool mergeWith( const QUndoCommand *uc ) override;
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
Okular::DocumentPrivate* m_docPriv;
|
||||
FormFieldChoice* m_form;
|
||||
|
@ -256,7 +285,7 @@ class EditFormComboCommand : public EditTextCommand
|
|||
int m_prevIndex;
|
||||
};
|
||||
|
||||
class EditFormButtonsCommand : public QUndoCommand
|
||||
class EditFormButtonsCommand : public OkularUndoCommand
|
||||
{
|
||||
public:
|
||||
EditFormButtonsCommand( Okular::DocumentPrivate* docPriv,
|
||||
|
@ -268,6 +297,8 @@ class EditFormButtonsCommand : public QUndoCommand
|
|||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
|
||||
|
||||
private:
|
||||
void clearFormButtonStates();
|
||||
|
||||
|
|
|
@ -203,6 +203,11 @@ Document::OpenResult Generator::loadDocumentFromDataWithPassword( const QByteArr
|
|||
return loadDocumentFromData( fileData, pagesVector ) ? Document::OpenSuccess : Document::OpenError;
|
||||
}
|
||||
|
||||
Generator::SwapBackingFileResult Generator::swapBackingFile( QString const &/*newFileName */, QVector<Okular::Page*> & /*newPagesVector*/ )
|
||||
{
|
||||
return SwapBackingFileError;
|
||||
}
|
||||
|
||||
bool Generator::closeDocument()
|
||||
{
|
||||
Q_D( Generator );
|
||||
|
|
|
@ -211,7 +211,8 @@ class OKULARCORE_EXPORT Generator : public QObject
|
|||
PrintNative, ///< Whether the Generator supports native cross-platform printing (QPainter-based).
|
||||
PrintPostscript, ///< Whether the Generator supports postscript-based file printing.
|
||||
PrintToFile, ///< Whether the Generator supports export to PDF & PS through the Print Dialog
|
||||
TiledRendering ///< Whether the Generator can render tiles @since 0.16 (KDE 4.10)
|
||||
TiledRendering, ///< Whether the Generator can render tiles @since 0.16 (KDE 4.10)
|
||||
SwapBackingFile ///< Whether the Generator can hot-swap the file it's reading from @since 1.3
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -272,6 +273,27 @@ class OKULARCORE_EXPORT Generator : public QObject
|
|||
*/
|
||||
virtual Document::OpenResult loadDocumentFromDataWithPassword( const QByteArray & fileData, QVector< Page * > & pagesVector, const QString &password );
|
||||
|
||||
/**
|
||||
* Describes the result of an swap file operation.
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
enum SwapBackingFileResult
|
||||
{
|
||||
SwapBackingFileError, //< The document could not be swapped
|
||||
SwapBackingFileNoOp, //< The document was swapped and nothing needs to be done
|
||||
SwapBackingFileReloadInternalData //< The document was swapped and internal data (forms, annotations, etc) needs to be reloaded
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the path of the file we are reading from. The new path must
|
||||
* point to a copy of the same document.
|
||||
*
|
||||
* @note the Generator has to have the feature @ref SwapBackingFile enabled
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
virtual SwapBackingFileResult swapBackingFile( const QString & newFileName, QVector<Okular::Page*> & newPagesVector );
|
||||
|
||||
/**
|
||||
* This method is called when the document is closed and not used
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2005 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2005 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -45,7 +48,7 @@ class OKULARCORE_EXPORT DocumentObserver
|
|||
TextSelection = 8, ///< Text selection has been changed
|
||||
Annotations = 16, ///< Annotations have been changed
|
||||
BoundingBox = 32, ///< Bounding boxes have been changed
|
||||
NeedSaveAs = 64 ///< Set along with Annotations when Save As is needed or annotation changes will be lost @since 0.15 (KDE 4.9)
|
||||
NeedSaveAs = 64 ///< Set when "Save" is needed or annotation/form changes will be lost @since 0.15 (KDE 4.9) @deprecated
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -53,7 +56,8 @@ class OKULARCORE_EXPORT DocumentObserver
|
|||
*/
|
||||
enum SetupFlags {
|
||||
DocumentChanged = 1, ///< The document is a new document.
|
||||
NewLayoutForPages = 2 ///< All the pages have
|
||||
NewLayoutForPages = 2, ///< All the pages have
|
||||
UrlChanged = 4 ///< The URL has changed @since 1.3
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
107
core/page.cpp
107
core/page.cpp
|
@ -139,14 +139,17 @@ Page::Page( uint page, double w, double h, Rotation o )
|
|||
|
||||
Page::~Page()
|
||||
{
|
||||
deletePixmaps();
|
||||
deleteRects();
|
||||
d->deleteHighlights();
|
||||
deleteAnnotations();
|
||||
d->deleteTextSelections();
|
||||
deleteSourceReferences();
|
||||
if (d)
|
||||
{
|
||||
deletePixmaps();
|
||||
deleteRects();
|
||||
d->deleteHighlights();
|
||||
deleteAnnotations();
|
||||
d->deleteTextSelections();
|
||||
deleteSourceReferences();
|
||||
|
||||
delete d;
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
int Page::number() const
|
||||
|
@ -496,6 +499,16 @@ QLinkedList< Annotation* > Page::annotations() const
|
|||
return m_annotations;
|
||||
}
|
||||
|
||||
Annotation * Page::annotation( const QString & uniqueName ) const
|
||||
{
|
||||
foreach(Annotation *a, m_annotations)
|
||||
{
|
||||
if ( a->uniqueName() == uniqueName )
|
||||
return a;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Action * Page::pageAction( PageAction action ) const
|
||||
{
|
||||
switch ( action )
|
||||
|
@ -801,8 +814,10 @@ void Page::deleteAnnotations()
|
|||
m_annotations.clear();
|
||||
}
|
||||
|
||||
void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
|
||||
bool PagePrivate::restoreLocalContents( const QDomNode & pageNode )
|
||||
{
|
||||
bool loadedAnything = false; // set if something actually gets loaded
|
||||
|
||||
// iterate over all chilren (annotationList, ...)
|
||||
QDomNode childNode = pageNode.firstChild();
|
||||
while ( childNode.isElement() )
|
||||
|
@ -837,6 +852,7 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
|
|||
{
|
||||
m_doc->performAddPageAnnotation(m_number, annotation);
|
||||
qCDebug(OkularCoreDebug) << "restored annot:" << annotation->uniqueName();
|
||||
loadedAnything = true;
|
||||
}
|
||||
else
|
||||
qCWarning(OkularCoreDebug).nospace() << "page (" << m_number << "): can't restore an annotation from XML.";
|
||||
|
@ -848,6 +864,10 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
|
|||
// parse formList child element
|
||||
else if ( childElement.tagName() == QLatin1String("forms") )
|
||||
{
|
||||
// Clone forms as root node in restoredFormFieldList
|
||||
const QDomNode clonedNode = restoredFormFieldList.importNode( childElement, true );
|
||||
restoredFormFieldList.appendChild( clonedNode );
|
||||
|
||||
if ( formfields.isEmpty() )
|
||||
continue;
|
||||
|
||||
|
@ -880,9 +900,12 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
|
|||
|
||||
QString value = formElement.attribute( QStringLiteral("value") );
|
||||
(*wantedIt)->d_ptr->setValue( value );
|
||||
loadedAnything = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loadedAnything;
|
||||
}
|
||||
|
||||
void PagePrivate::saveLocalContents( QDomNode & parentNode, QDomDocument & document, PageItems what ) const
|
||||
|
@ -943,7 +966,17 @@ void PagePrivate::saveLocalContents( QDomNode & parentNode, QDomDocument & docum
|
|||
}
|
||||
|
||||
// add forms info if has got any
|
||||
if ( ( what & FormFieldPageItems ) && !formfields.isEmpty() )
|
||||
if ( ( what & FormFieldPageItems ) && ( what & OriginalFormFieldPageItems ) )
|
||||
{
|
||||
const QDomElement savedDocRoot = restoredFormFieldList.documentElement();
|
||||
if ( !savedDocRoot.isNull() )
|
||||
{
|
||||
// Import and append node in target document
|
||||
const QDomNode importedNode = document.importNode( savedDocRoot, true );
|
||||
pageElement.appendChild( importedNode );
|
||||
}
|
||||
}
|
||||
else if ( ( what & FormFieldPageItems ) && !formfields.isEmpty() )
|
||||
{
|
||||
// create the formList
|
||||
QDomElement formListElement = document.createElement( QStringLiteral("forms") );
|
||||
|
@ -1032,3 +1065,59 @@ void PagePrivate::setTilesManager( const DocumentObserver *observer, TilesManage
|
|||
|
||||
m_tilesManagers.insert(observer, tm);
|
||||
}
|
||||
|
||||
void PagePrivate::adoptGeneratedContents( PagePrivate *oldPage )
|
||||
{
|
||||
rotateAt( oldPage->m_rotation );
|
||||
|
||||
m_pixmaps = oldPage->m_pixmaps;
|
||||
oldPage->m_pixmaps.clear();
|
||||
|
||||
m_tilesManagers = oldPage->m_tilesManagers;
|
||||
oldPage->m_tilesManagers.clear();
|
||||
|
||||
m_boundingBox = oldPage->m_boundingBox;
|
||||
m_isBoundingBoxKnown = oldPage->m_isBoundingBoxKnown;
|
||||
m_text = oldPage->m_text;
|
||||
oldPage->m_text = nullptr;
|
||||
|
||||
m_textSelections = oldPage->m_textSelections;
|
||||
oldPage->m_textSelections = nullptr;
|
||||
|
||||
restoredLocalAnnotationList = oldPage->restoredLocalAnnotationList;
|
||||
restoredFormFieldList = oldPage->restoredFormFieldList;
|
||||
}
|
||||
|
||||
FormField *PagePrivate::findEquivalentForm( const Page *p, FormField *oldField )
|
||||
{
|
||||
// given how id is not very good of id (at least for pdf) we do a few passes
|
||||
// same rect, type and id
|
||||
foreach(FormField *f, p->d->formfields)
|
||||
{
|
||||
if (f->rect() == oldField->rect() && f->type() == oldField->type() && f->id() == oldField->id())
|
||||
return f;
|
||||
}
|
||||
// same rect and type
|
||||
foreach(FormField *f, p->d->formfields)
|
||||
{
|
||||
if (f->rect() == oldField->rect() && f->type() == oldField->type())
|
||||
return f;
|
||||
}
|
||||
// fuzzy rect, same type and id
|
||||
foreach(FormField *f, p->d->formfields)
|
||||
{
|
||||
if (f->type() == oldField->type() && f->id() == oldField->id() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) && qFuzzyCompare(f->rect().right, oldField->rect().right) && qFuzzyCompare(f->rect().bottom, oldField->rect().bottom))
|
||||
{
|
||||
return f;
|
||||
}
|
||||
}
|
||||
// fuzzy rect and same type
|
||||
foreach(FormField *f, p->d->formfields)
|
||||
{
|
||||
if (f->type() == oldField->type() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) && qFuzzyCompare(f->rect().right, oldField->rect().right) && qFuzzyCompare(f->rect().bottom, oldField->rect().bottom))
|
||||
{
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
11
core/page.h
11
core/page.h
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -249,6 +252,12 @@ class OKULARCORE_EXPORT Page
|
|||
*/
|
||||
QLinkedList< Annotation* > annotations() const;
|
||||
|
||||
/**
|
||||
* Returns the annotation with the given unique name.
|
||||
* @since 1.3
|
||||
*/
|
||||
Annotation * annotation( const QString & uniqueName ) const;
|
||||
|
||||
/**
|
||||
* Returns the @ref Action object which is associated with the given page @p action
|
||||
* or 0 if no page action is set.
|
||||
|
@ -386,7 +395,7 @@ class OKULARCORE_EXPORT Page
|
|||
QList<Tile> tilesAt( const DocumentObserver *observer, const NormalizedRect &rect ) const;
|
||||
|
||||
private:
|
||||
PagePrivate* const d;
|
||||
PagePrivate* d;
|
||||
/// @cond PRIVATE
|
||||
friend class PagePrivate;
|
||||
friend class Document;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -48,7 +51,11 @@ enum PageItem
|
|||
|
||||
/* If set along with AnnotationPageItems, tells saveLocalContents to save
|
||||
* the original annotations (if any) instead of the modified ones */
|
||||
OriginalAnnotationPageItems = 0x100
|
||||
OriginalAnnotationPageItems = 0x100,
|
||||
|
||||
/* If set along with FormFieldPageItems, tells saveLocalContents to save
|
||||
* the original form contents (if any) instead of the modified one */
|
||||
OriginalFormFieldPageItems = 0x200
|
||||
};
|
||||
Q_DECLARE_FLAGS(PageItems, PageItem)
|
||||
|
||||
|
@ -66,7 +73,7 @@ class PagePrivate
|
|||
/**
|
||||
* Loads the local contents (e.g. annotations) of the page.
|
||||
*/
|
||||
void restoreLocalContents( const QDomNode & pageNode );
|
||||
bool restoreLocalContents( const QDomNode & pageNode );
|
||||
|
||||
/**
|
||||
* Saves the local contents (e.g. annotations) of the page.
|
||||
|
@ -116,6 +123,17 @@ class PagePrivate
|
|||
*/
|
||||
void setTilesManager( const DocumentObserver *observer, TilesManager *tm );
|
||||
|
||||
/**
|
||||
* Moves contents that are generated from oldPage to this. And clears them from page
|
||||
* so it can be deleted fine.
|
||||
*/
|
||||
void adoptGeneratedContents( PagePrivate *oldPage );
|
||||
|
||||
/*
|
||||
* Tries to find an equivalent form field to oldField by looking into the rect, type and name
|
||||
*/
|
||||
OKULARCORE_EXPORT static FormField *findEquivalentForm( const Page *p, FormField *oldField );
|
||||
|
||||
class PixmapObject
|
||||
{
|
||||
public:
|
||||
|
@ -144,6 +162,7 @@ class PagePrivate
|
|||
|
||||
bool m_isBoundingBoxKnown : 1;
|
||||
QDomDocument restoredLocalAnnotationList; // <annotationList>...</annotationList>
|
||||
QDomDocument restoredFormFieldList; // <forms>...</forms>
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -438,9 +438,6 @@ Context menu actions like Rename Bookmarks etc.)
|
|||
<title>Annotations</title>
|
||||
<para>
|
||||
&okular; allows you to review and annotate your documents.
|
||||
Annotations created in &okular; are automatically saved in the internal local data folder
|
||||
for each user.
|
||||
&okular; does not implicitly change any document it opens.
|
||||
</para>
|
||||
<screenshot>
|
||||
<screeninfo>&okular;'s Annotations</screeninfo>
|
||||
|
@ -460,22 +457,11 @@ Context menu actions like Rename Bookmarks etc.)
|
|||
<para>Using the context menu either in the <guilabel>Reviews</guilabel> view of the navigation panel or in the main window you can open a <guilabel>Pop up Note</guilabel> for any kind of annotation and add or edit comments.</para>
|
||||
<para>Annotations are not only limited to &PDF; files, they can be used for any format &okular; supports.</para>
|
||||
<para>
|
||||
Since &kde; 4.2, &okular; has the "document archiving" feature. This is an &okular;-specific format for carrying the document plus various metadata related to it (currently only annotations). You can save a "document archive" from the open document by choosing <menuchoice><guimenu>File</guimenu><guisubmenu>Export As</guisubmenu><guimenuitem>Document Archive</guimenuitem></menuchoice>. To open an &okular; document archive, just open it with &okular; as it would be ⪚ a &PDF; document.
|
||||
&okular; has the "document archiving" feature. This is an &okular;-specific format for carrying the document plus various metadata related to it (currently only annotations). You can save a "document archive" from the open document by choosing <menuchoice><guimenu>File</guimenu><guisubmenu>Save As</guisubmenu></menuchoice> and selecting <guilabel>Okular document archive</guilabel> in the <guilabel>Filter</guilabel> selector. To open an &okular; document archive, just open it with &okular; as it would be ⪚ a &PDF; document.
|
||||
</para>
|
||||
<para>
|
||||
Since &okular; 0.15 you can <emphasis>also</emphasis> save annotations directly into &PDF; files. This feature is only available if &okular; has been built with version 0.20 or later of <ulink url="http://poppler.freedesktop.org/">Poppler rendering library</ulink>. You can use <menuchoice><guimenu>File</guimenu> <guimenuitem>Save As...</guimenuitem></menuchoice> to save the copy of &PDF; file with annotations.
|
||||
You can <emphasis>also</emphasis> save annotations directly into &PDF; files. You can use <menuchoice><guimenu>File</guimenu> <guimenuitem>Save</guimenuitem></menuchoice> to save it over the current file or <menuchoice><guimenu>File</guimenu> <guimenuitem>Save As...</guimenuitem></menuchoice> to save it to a new file.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
It is not possible to save annotations into &PDF; file if original file was encrypted and &okular; uses Poppler libraries of version which is lower than 0.22.
|
||||
</para>
|
||||
</note>
|
||||
<note>
|
||||
<para>
|
||||
If you open a &PDF; with existing annotations, your annotation changes are not automatically saved in the internal local data folder, and you need to save the modified document (using <menuchoice><guimenu>File</guimenu><guimenuitem>Save As...</guimenuitem></menuchoice>) before closing it. Should you forget to do this &okular; will show confirmation window that allows you to save the document.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
Due to DRM limitations (typically with &PDF; documents), adding, editing some properties
|
||||
|
@ -488,7 +474,7 @@ Context menu actions like Rename Bookmarks etc.)
|
|||
</para>
|
||||
</note>
|
||||
<para>
|
||||
Since &okular; 0.17 you can configure the default properties and appearance of each annotating tool. Please refer to the <link linkend="configannotations">corresponding section in this documentation</link>.
|
||||
You can configure the default properties and appearance of each annotating tool. Please refer to the <link linkend="configannotations">corresponding section in this documentation</link>.
|
||||
</para>
|
||||
<sect2 id="annotations-add">
|
||||
<title>Adding annotations</title>
|
||||
|
@ -954,7 +940,7 @@ Context menu actions like Rename Bookmarks etc.)
|
|||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<action>Open</action> a supported file or &okular; archive. If there is already an opened file it will be closed. For more information, see the section about <link linkend="opening">Opening Files</link>.
|
||||
<action>Open</action> a supported file or &okular; document archive. If there is already an opened file it will be closed. For more information, see the section about <link linkend="opening">Opening Files</link>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -1010,31 +996,31 @@ Context menu actions like Rename Bookmarks etc.)
|
|||
<keycombo action="simul">&Ctrl;<keycap>S</keycap></keycombo>
|
||||
</shortcut>
|
||||
<guimenu>File</guimenu>
|
||||
<guimenuitem>Save As...</guimenuitem>
|
||||
<guimenuitem>Save</guimenuitem>
|
||||
</menuchoice>
|
||||
</term>
|
||||
<listitem>
|
||||
<para><action>Saves</action> the document under a new name including all the changes (annotations, form contents, &etc;), provided the document backend supports saving changes. With the &PDF; backend it is possible to save the document with the changed values of the form fields.<!--Only useful for pdf? what happens with other formats/backends?--> It can be possible (provided that the data were not secured using DRM) to save annotations with &PDF; files.</para>
|
||||
<note>
|
||||
<para>
|
||||
Note that, due to the way this is implemented, even if there are no changes to the file, the new file need not to be an exact bit-for-bit copy of the original file (⪚ can have a different SHA-1 hash, &etc;).
|
||||
</para>
|
||||
</note>
|
||||
<para><action>Saves</action> the document including all the changes (annotations, form contents, &etc;), provided the document backend supports saving those changes. If the backend does not support saving the changes, the user will be given the option to either discard or to save them together with the document in an &okular; document archive.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<menuchoice>
|
||||
<shortcut>
|
||||
<keycombo action="simul">&Ctrl;&Shift;<keycap>S</keycap></keycombo>
|
||||
</shortcut>
|
||||
<guimenu>File</guimenu>
|
||||
<guimenuitem>Save Copy As...</guimenuitem>
|
||||
<guimenuitem>Save As...</guimenuitem>
|
||||
</menuchoice>
|
||||
</term>
|
||||
<listitem>
|
||||
<para><action>Saves</action> a copy of the original document under a new name (completely bypassing the document backend). The saved document will be a bit-for-bit copy of the original.</para>
|
||||
<para><action>Saves</action> the document under a new name including all the changes (annotations, form contents, &etc;), provided the document backend supports saving changes. If the backend does not support saving the changes, the user will be given the option to either discard or to save them together with the document in an &okular; document archive.</para>
|
||||
<note>
|
||||
<para>
|
||||
Note that, due to the way this is implemented, even if there are no changes to the file, the new file need not to be an exact bit-for-bit copy of the original file (⪚ can have a different SHA-1 hash, &etc;).
|
||||
</para>
|
||||
</note>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
* Copyright (C) 2005 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2006-2007 by Pino Toscano <pino@kde.org> *
|
||||
* Copyright (C) 2006-2007 by Tobias Koenig <tokoe@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -39,6 +42,7 @@ KIMGIOGenerator::KIMGIOGenerator( QObject *parent, const QVariantList &args )
|
|||
setFeature( TiledRendering );
|
||||
setFeature( PrintNative );
|
||||
setFeature( PrintToFile );
|
||||
setFeature( SwapBackingFile );
|
||||
}
|
||||
|
||||
KIMGIOGenerator::~KIMGIOGenerator()
|
||||
|
@ -94,6 +98,13 @@ bool KIMGIOGenerator::loadDocumentInternal(const QByteArray & fileData, const QS
|
|||
return true;
|
||||
}
|
||||
|
||||
KIMGIOGenerator::SwapBackingFileResult KIMGIOGenerator::swapBackingFile( QString const &/*newFileName*/, QVector<Okular::Page*> & /*newPagesVector*/ )
|
||||
{
|
||||
// NOP: We don't actually need to do anything because all data has already
|
||||
// been loaded in RAM
|
||||
return SwapBackingFileNoOp;
|
||||
}
|
||||
|
||||
bool KIMGIOGenerator::doCloseDocument()
|
||||
{
|
||||
m_img = QImage();
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2005 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -27,6 +30,7 @@ class KIMGIOGenerator : public Okular::Generator
|
|||
// [INHERITED] load a document and fill up the pagesVector
|
||||
bool loadDocument( const QString & fileName, QVector<Okular::Page*> & pagesVector ) override;
|
||||
bool loadDocumentFromData( const QByteArray & fileData, QVector<Okular::Page*> & pagesVector ) override;
|
||||
SwapBackingFileResult swapBackingFile( QString const &newFileName, QVector<Okular::Page*> & newPagesVector ) override;
|
||||
|
||||
// [INHERITED] print document using already configured kprinter
|
||||
bool print( QPrinter& printer ) override;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Pino Toscano <pino@kde.org> *
|
||||
* Copyright (C) 2012 by Guillermo A. Amaral B. <gamaral@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -62,8 +65,8 @@ static int maskExportedFlags(int flags)
|
|||
}
|
||||
|
||||
//BEGIN PopplerAnnotationProxy implementation
|
||||
PopplerAnnotationProxy::PopplerAnnotationProxy( Poppler::Document *doc, QMutex *userMutex )
|
||||
: ppl_doc ( doc ), mutex ( userMutex )
|
||||
PopplerAnnotationProxy::PopplerAnnotationProxy( Poppler::Document *doc, QMutex *userMutex, QHash<Okular::Annotation*, Poppler::Annotation*> *annotsOnOpenHash )
|
||||
: ppl_doc ( doc ), mutex ( userMutex ), annotationsOnOpenHash( annotsOnOpenHash )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -254,6 +257,7 @@ void PopplerAnnotationProxy::notifyRemoval( Okular::Annotation *okl_ann, int pag
|
|||
QMutexLocker ml(mutex);
|
||||
|
||||
Poppler::Page *ppl_page = ppl_doc->page( page );
|
||||
annotationsOnOpenHash->remove( okl_ann );
|
||||
ppl_page->removeAnnotation( ppl_ann ); // Also destroys ppl_ann
|
||||
delete ppl_page;
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2012 by Fabio D'Urso <fabiodurso@hotmail.it> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -23,7 +26,7 @@ extern Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annot
|
|||
class PopplerAnnotationProxy : public Okular::AnnotationProxy
|
||||
{
|
||||
public:
|
||||
PopplerAnnotationProxy( Poppler::Document *doc, QMutex *userMutex );
|
||||
PopplerAnnotationProxy( Poppler::Document *doc, QMutex *userMutex, QHash<Okular::Annotation*, Poppler::Annotation*> *annotsOnOpenHash );
|
||||
~PopplerAnnotationProxy();
|
||||
|
||||
bool supports( Capability capability ) const override;
|
||||
|
@ -33,6 +36,7 @@ class PopplerAnnotationProxy : public Okular::AnnotationProxy
|
|||
private:
|
||||
Poppler::Document *ppl_doc;
|
||||
QMutex *mutex;
|
||||
QHash<Okular::Annotation*, Poppler::Annotation*> *annotationsOnOpenHash;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -516,6 +516,7 @@ PDFGenerator::PDFGenerator( QObject *parent, const QVariantList &args )
|
|||
setFeature( PrintToFile );
|
||||
setFeature( ReadRawData );
|
||||
setFeature( TiledRendering );
|
||||
setFeature( SwapBackingFile );
|
||||
|
||||
// You only need to do it once not for each of the documents but it is cheap enough
|
||||
// so doing it all the time won't hurt either
|
||||
|
@ -582,7 +583,7 @@ Okular::Document::OpenResult PDFGenerator::init(QVector<Okular::Page*> & pagesVe
|
|||
pagesVector.resize(pageCount);
|
||||
rectsGenerated.fill(false, pageCount);
|
||||
|
||||
annotationsHash.clear();
|
||||
annotationsOnOpenHash.clear();
|
||||
|
||||
loadPages(pagesVector, 0, false);
|
||||
|
||||
|
@ -590,12 +591,22 @@ Okular::Document::OpenResult PDFGenerator::init(QVector<Okular::Page*> & pagesVe
|
|||
reparseConfig();
|
||||
|
||||
// create annotation proxy
|
||||
annotProxy = new PopplerAnnotationProxy( pdfdoc, userMutex() );
|
||||
annotProxy = new PopplerAnnotationProxy( pdfdoc, userMutex(), &annotationsOnOpenHash );
|
||||
|
||||
// the file has been loaded correctly
|
||||
return Okular::Document::OpenSuccess;
|
||||
}
|
||||
|
||||
PDFGenerator::SwapBackingFileResult PDFGenerator::swapBackingFile( QString const &newFileName, QVector<Okular::Page*> & newPagesVector )
|
||||
{
|
||||
doCloseDocument();
|
||||
auto openResult = loadDocumentWithPassword(newFileName, newPagesVector, QString());
|
||||
if (openResult != Okular::Document::OpenSuccess)
|
||||
return SwapBackingFileError;
|
||||
|
||||
return SwapBackingFileReloadInternalData;
|
||||
}
|
||||
|
||||
bool PDFGenerator::doCloseDocument()
|
||||
{
|
||||
// remove internal objects
|
||||
|
@ -1059,8 +1070,8 @@ void PDFGenerator::resolveMediaLinkReference( Okular::Action *action )
|
|||
if ( (action->actionType() != Okular::Action::Movie) && (action->actionType() != Okular::Action::Rendition) )
|
||||
return;
|
||||
|
||||
resolveMediaLinks<Poppler::LinkMovie, Okular::MovieAction, Poppler::MovieAnnotation, Okular::MovieAnnotation>( action, Okular::Annotation::AMovie, annotationsHash );
|
||||
resolveMediaLinks<Poppler::LinkRendition, Okular::RenditionAction, Poppler::ScreenAnnotation, Okular::ScreenAnnotation>( action, Okular::Annotation::AScreen, annotationsHash );
|
||||
resolveMediaLinks<Poppler::LinkMovie, Okular::MovieAction, Poppler::MovieAnnotation, Okular::MovieAnnotation>( action, Okular::Annotation::AMovie, annotationsOnOpenHash );
|
||||
resolveMediaLinks<Poppler::LinkRendition, Okular::RenditionAction, Poppler::ScreenAnnotation, Okular::ScreenAnnotation>( action, Okular::Annotation::AScreen, annotationsOnOpenHash );
|
||||
}
|
||||
|
||||
void PDFGenerator::resolveMediaLinkReferences( Okular::Page *page )
|
||||
|
@ -1617,7 +1628,7 @@ void PDFGenerator::addAnnotations( Poppler::Page * popplerPage, Okular::Page * p
|
|||
}
|
||||
|
||||
if ( !doDelete )
|
||||
annotationsHash.insert( newann, a );
|
||||
annotationsOnOpenHash.insert( newann, a );
|
||||
}
|
||||
if ( doDelete )
|
||||
delete a;
|
||||
|
@ -1770,6 +1781,18 @@ bool PDFGenerator::save( const QString &fileName, SaveOptions options, QString *
|
|||
pdfConv->setPDFOptions( pdfConv->pdfOptions() | Poppler::PDFConverter::WithChanges );
|
||||
|
||||
QMutexLocker locker( userMutex() );
|
||||
|
||||
QHashIterator<Okular::Annotation*, Poppler::Annotation*> it( annotationsOnOpenHash );
|
||||
while ( it.hasNext() )
|
||||
{
|
||||
it.next();
|
||||
|
||||
if ( it.value()->uniqueName().isEmpty() )
|
||||
{
|
||||
it.value()->setUniqueName( it.key()->uniqueName() );
|
||||
}
|
||||
}
|
||||
|
||||
bool success = pdfConv->convert();
|
||||
if (!success)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2004-2008 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -99,6 +102,7 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p
|
|||
Okular::AnnotationProxy* annotationProxy() const override;
|
||||
|
||||
protected:
|
||||
SwapBackingFileResult swapBackingFile( QString const &newFileName, QVector<Okular::Page*> & newPagesVector ) override;
|
||||
bool doCloseDocument() override;
|
||||
Okular::TextPage* textPage( Okular::Page *page ) override;
|
||||
|
||||
|
@ -136,7 +140,9 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p
|
|||
mutable QList<Okular::EmbeddedFile*> docEmbeddedFiles;
|
||||
int nextFontPage;
|
||||
PopplerAnnotationProxy *annotProxy;
|
||||
QHash<Okular::Annotation*, Poppler::Annotation*> annotationsHash;
|
||||
// the hash below only contains annotations that were present on the file at open time
|
||||
// this is enough for what we use it for
|
||||
QHash<Okular::Annotation*, Poppler::Annotation*> annotationsOnOpenHash;
|
||||
|
||||
QBitArray rectsGenerated;
|
||||
|
||||
|
|
616
part.cpp
616
part.cpp
|
@ -14,6 +14,9 @@
|
|||
* Copyright (C) 2004 by Waldo Bastian <bastian@kde.org> *
|
||||
* Copyright (C) 2004-2008 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2004 by Antti Markus <antti.markus@starman.ee> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -300,7 +303,7 @@ Part::Part(QWidget *parentWidget,
|
|||
QObject *parent,
|
||||
const QVariantList &args)
|
||||
: KParts::ReadWritePart(parent),
|
||||
m_tempfile( nullptr ), m_isReloading( false ), m_fileWasRemoved( false ), m_showMenuBarAction( nullptr ), m_showFullScreenAction( nullptr ), m_actionsSearched( false ),
|
||||
m_tempfile( nullptr ), m_documentOpenWithPassword( false ), m_swapInsteadOfOpening( false ), m_isReloading( false ), m_fileWasRemoved( false ), m_showMenuBarAction( nullptr ), m_showFullScreenAction( nullptr ), m_actionsSearched( false ),
|
||||
m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentWidget, parent, args)), m_generatorGuiClient(nullptr), m_keeper( nullptr )
|
||||
{
|
||||
// make sure that the component name is okular otherwise the XMLGUI .rc files are not found
|
||||
|
@ -389,6 +392,13 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
|
|||
connect( m_document, &Document::openUrl, this, &Part::openUrlFromDocument );
|
||||
connect( m_document->bookmarkManager(), &BookmarkManager::openUrl, this, &Part::openUrlFromBookmarks );
|
||||
connect( m_document, &Document::close, this, &Part::close );
|
||||
connect( m_document, &Document::undoHistoryCleanChanged, this,
|
||||
[this](bool clean)
|
||||
{
|
||||
setModified( !clean );
|
||||
setWindowTitleFromDocument();
|
||||
}
|
||||
);
|
||||
|
||||
if ( parent && parent->metaObject()->indexOfSlot( QMetaObject::normalizedSignature( "slotQuit()" ).constData() ) != -1 )
|
||||
connect( m_document, SIGNAL(quit()), parent, SLOT(slotQuit()) );
|
||||
|
@ -465,6 +475,12 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
|
|||
rightLayout->setSpacing( 0 );
|
||||
// KToolBar * rtb = new KToolBar( rightContainer, "mainToolBarSS" );
|
||||
// rightLayout->addWidget( rtb );
|
||||
m_migrationMessage = new KMessageWidget( rightContainer );
|
||||
m_migrationMessage->setVisible( false );
|
||||
m_migrationMessage->setWordWrap( true );
|
||||
m_migrationMessage->setMessageType( KMessageWidget::Warning );
|
||||
m_migrationMessage->setText( i18n( "This document contains annotations or form data that were saved internally by a previous Okular version. Internal storage is <b>no longer supported</b>.<br/>Please save to a file in order to move them if you want to continue to edit the document." ) );
|
||||
rightLayout->addWidget( m_migrationMessage );
|
||||
m_topMessage = new KMessageWidget( rightContainer );
|
||||
m_topMessage->setVisible( false );
|
||||
m_topMessage->setWordWrap( true );
|
||||
|
@ -554,9 +570,10 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
|
|||
m_watcher = new KDirWatch( this );
|
||||
connect( m_watcher, &KDirWatch::dirty, this, &Part::slotFileDirty );
|
||||
connect( m_watcher, &KDirWatch::created, this, &Part::slotFileDirty );
|
||||
connect( m_watcher, &KDirWatch::deleted, this, &Part::slotFileDirty );
|
||||
m_dirtyHandler = new QTimer( this );
|
||||
m_dirtyHandler->setSingleShot( true );
|
||||
connect( m_dirtyHandler, &QTimer::timeout,this, &Part::slotDoFileDirty );
|
||||
connect( m_dirtyHandler, &QTimer::timeout, this, [this] { slotAttemptReload(); } );
|
||||
|
||||
slotNewConfig();
|
||||
|
||||
|
@ -698,7 +715,7 @@ void Part::setupViewerActions()
|
|||
m_findPrev = KStandardAction::findPrev( this, SLOT(slotFindPrev()), ac );
|
||||
m_findPrev->setEnabled( false );
|
||||
|
||||
m_saveCopyAs = nullptr;
|
||||
m_save = nullptr;
|
||||
m_saveAs = nullptr;
|
||||
|
||||
QAction * prefs = KStandardAction::preferences( this, SLOT(slotPreferences()), ac);
|
||||
|
@ -807,15 +824,12 @@ void Part::setupActions()
|
|||
|
||||
m_selectAll = KStandardAction::selectAll( m_pageView, SLOT(selectAll()), ac );
|
||||
|
||||
m_saveCopyAs = KStandardAction::saveAs( this, SLOT(slotSaveCopyAs()), ac );
|
||||
m_saveCopyAs->setText( i18n( "Save &Copy As..." ) );
|
||||
ac->addAction( QStringLiteral("file_save_copy"), m_saveCopyAs );
|
||||
ac->setDefaultShortcuts(m_saveCopyAs, KStandardShortcut::shortcut(KStandardShortcut::SaveAs));
|
||||
m_saveCopyAs->setEnabled( false );
|
||||
m_save = KStandardAction::save( this, [this] { saveFile(); }, ac );
|
||||
m_save->setEnabled( false );
|
||||
|
||||
m_saveAs = KStandardAction::saveAs( this, SLOT(slotSaveFileAs()), ac );
|
||||
ac->setDefaultShortcuts(m_saveAs, KStandardShortcut::shortcut(KStandardShortcut::Save));
|
||||
m_saveAs->setEnabled( false );
|
||||
m_migrationMessage->addAction( m_saveAs );
|
||||
|
||||
m_showLeftPanel = ac->add<KToggleAction>(QStringLiteral("show_leftpanel"));
|
||||
m_showLeftPanel->setText(i18n( "Show &Navigation Panel"));
|
||||
|
@ -847,11 +861,6 @@ void Part::setupActions()
|
|||
m_exportAsMenu->addAction( m_exportAsText );
|
||||
m_exportAs->setEnabled( false );
|
||||
m_exportAsText->setEnabled( false );
|
||||
m_exportAsDocArchive = actionForExportFormat( Okular::ExportFormat(
|
||||
i18nc( "A document format, Okular-specific", "Document Archive" ),
|
||||
db.mimeTypeForName( QStringLiteral("application/vnd.kde.okular-archive") ) ), m_exportAsMenu );
|
||||
m_exportAsMenu->addAction( m_exportAsDocArchive );
|
||||
m_exportAsDocArchive->setEnabled( false );
|
||||
|
||||
#if PURPOSE_FOUND
|
||||
m_share = ac->addAction( QStringLiteral("file_share") );
|
||||
|
@ -1120,7 +1129,7 @@ void Part::loadCancelled(const QString &reason)
|
|||
emit setWindowCaption( QString() );
|
||||
resetStartArguments();
|
||||
|
||||
// when m_viewportDirty.pageNumber != -1 we come from slotDoFileDirty
|
||||
// when m_viewportDirty.pageNumber != -1 we come from slotAttemptReload
|
||||
// so we don't want to show an ugly messagebox just because the document is
|
||||
// taking more than usual to be recreated
|
||||
if (m_viewportDirty.pageNumber == -1)
|
||||
|
@ -1178,6 +1187,11 @@ KConfigDialog * Part::slotGeneratorPreferences( )
|
|||
|
||||
void Part::notifySetup( const QVector< Okular::Page * > & /*pages*/, int setupFlags )
|
||||
{
|
||||
// Hide the migration message if the user has just migrated. Otherwise,
|
||||
// if m_migrationMessage is already hidden, this does nothing.
|
||||
if ( !m_document->isDocdataMigrationNeeded() )
|
||||
m_migrationMessage->animatedHide();
|
||||
|
||||
if ( !( setupFlags & Okular::DocumentObserver::DocumentChanged ) )
|
||||
return;
|
||||
|
||||
|
@ -1194,9 +1208,6 @@ void Part::notifyViewportChanged( bool /*smoothMove*/ )
|
|||
|
||||
void Part::notifyPageChanged( int page, int flags )
|
||||
{
|
||||
if ( flags & Okular::DocumentObserver::NeedSaveAs )
|
||||
setModified();
|
||||
|
||||
if ( !(flags & Okular::DocumentObserver::Bookmark ) )
|
||||
return;
|
||||
|
||||
|
@ -1281,12 +1292,39 @@ bool Part::slotImportPSFile()
|
|||
return false;
|
||||
}
|
||||
|
||||
static void addFileToWatcher( KDirWatch *watcher, const QString &filePath)
|
||||
void Part::setFileToWatch( const QString &filePath )
|
||||
{
|
||||
if ( !watcher->contains( filePath ) ) watcher->addFile(filePath);
|
||||
if ( !m_watchedFilePath.isEmpty() )
|
||||
unsetFileToWatch();
|
||||
|
||||
const QFileInfo fi(filePath);
|
||||
if ( !watcher->contains( fi.absolutePath() ) ) watcher->addDir(fi.absolutePath());
|
||||
if ( fi.isSymLink() ) watcher->addFile( fi.readLink() );
|
||||
|
||||
m_watchedFilePath = filePath;
|
||||
m_watcher->addFile( m_watchedFilePath );
|
||||
|
||||
if ( fi.isSymLink() )
|
||||
{
|
||||
m_watchedFileSymlinkTarget = fi.readLink();
|
||||
m_watcher->addFile( m_watchedFileSymlinkTarget );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_watchedFileSymlinkTarget.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Part::unsetFileToWatch()
|
||||
{
|
||||
if ( m_watchedFilePath.isEmpty() )
|
||||
return;
|
||||
|
||||
m_watcher->removeFile( m_watchedFilePath );
|
||||
|
||||
if ( !m_watchedFileSymlinkTarget.isEmpty() )
|
||||
m_watcher->removeFile( m_watchedFileSymlinkTarget );
|
||||
|
||||
m_watchedFilePath.clear();
|
||||
m_watchedFileSymlinkTarget.clear();
|
||||
}
|
||||
|
||||
Document::OpenResult Part::doOpenFile( const QMimeType &mimeA, const QString &fileNameToOpenA, bool *isCompressedFile )
|
||||
|
@ -1308,6 +1346,29 @@ Document::OpenResult Part::doOpenFile( const QMimeType &mimeA, const QString &fi
|
|||
*isCompressedFile = false;
|
||||
}
|
||||
|
||||
if ( m_swapInsteadOfOpening )
|
||||
{
|
||||
m_swapInsteadOfOpening = false;
|
||||
|
||||
if ( !uncompressOk )
|
||||
return Document::OpenError;
|
||||
|
||||
if ( mime.inherits( QStringLiteral("application/vnd.kde.okular-archive") ) )
|
||||
{
|
||||
isDocumentArchive = true;
|
||||
if (!m_document->swapBackingFileArchive( fileNameToOpen, url() ))
|
||||
return Document::OpenError;
|
||||
}
|
||||
else
|
||||
{
|
||||
isDocumentArchive = false;
|
||||
if (!m_document->swapBackingFile( fileNameToOpen, url() ))
|
||||
return Document::OpenError;
|
||||
}
|
||||
|
||||
return Document::OpenSuccess;
|
||||
}
|
||||
|
||||
isDocumentArchive = false;
|
||||
if ( uncompressOk )
|
||||
{
|
||||
|
@ -1320,6 +1381,7 @@ Document::OpenResult Part::doOpenFile( const QMimeType &mimeA, const QString &fi
|
|||
{
|
||||
openResult = m_document->openDocument( fileNameToOpen, url(), mime );
|
||||
}
|
||||
m_documentOpenWithPassword = false;
|
||||
|
||||
// if the file didn't open correctly it might be encrypted, so ask for a pass
|
||||
QString walletName, walletFolder, walletKey;
|
||||
|
@ -1384,10 +1446,15 @@ Document::OpenResult Part::doOpenFile( const QMimeType &mimeA, const QString &fi
|
|||
openResult = m_document->openDocument( fileNameToOpen, url(), mime, password );
|
||||
}
|
||||
|
||||
// 3. if the password is correct and the user chose to remember it, store it to the wallet
|
||||
if ( openResult == Document::OpenSuccess && wallet && /*safety check*/ wallet->isOpen() && keep )
|
||||
if ( openResult == Document::OpenSuccess )
|
||||
{
|
||||
wallet->writePassword( walletKey, password );
|
||||
m_documentOpenWithPassword = true;
|
||||
|
||||
// 3. if the password is correct and the user chose to remember it, store it to the wallet
|
||||
if (wallet && /*safety check*/ wallet->isOpen() && keep )
|
||||
{
|
||||
wallet->writePassword( walletKey, password );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1452,14 +1519,15 @@ bool Part::openFile()
|
|||
m_find->setEnabled( ok && canSearch );
|
||||
m_findNext->setEnabled( ok && canSearch );
|
||||
m_findPrev->setEnabled( ok && canSearch );
|
||||
if( m_saveAs ) m_saveAs->setEnabled( ok && (m_document->canSaveChanges() || isDocumentArchive) );
|
||||
if( m_saveCopyAs ) m_saveCopyAs->setEnabled( ok );
|
||||
if( m_save ) m_save->setEnabled( ok && !( isstdin || mime.inherits( "inode/directory" ) ) );
|
||||
if( m_saveAs ) m_saveAs->setEnabled( ok && !( isstdin || mime.inherits( "inode/directory" ) ) );
|
||||
emit enablePrintAction( ok && m_document->printingSupport() != Okular::Document::NoPrinting );
|
||||
m_printPreview->setEnabled( ok && m_document->printingSupport() != Okular::Document::NoPrinting );
|
||||
m_showProperties->setEnabled( ok );
|
||||
bool hasEmbeddedFiles = ok && m_document->embeddedFiles() && m_document->embeddedFiles()->count() > 0;
|
||||
if ( m_showEmbeddedFiles ) m_showEmbeddedFiles->setEnabled( hasEmbeddedFiles );
|
||||
m_topMessage->setVisible( hasEmbeddedFiles && Okular::Settings::showOSD() );
|
||||
m_migrationMessage->setVisible( m_document->isDocdataMigrationNeeded() );
|
||||
|
||||
// Warn the user that XFA forms are not supported yet (NOTE: poppler generator only)
|
||||
if ( ok && m_document->metaData( QStringLiteral("HasUnsupportedXfaForm") ).toBool() == true )
|
||||
|
@ -1516,7 +1584,6 @@ bool Part::openFile()
|
|||
#endif
|
||||
}
|
||||
if ( m_exportAsText ) m_exportAsText->setEnabled( ok && m_document->canExportToText() );
|
||||
if ( m_exportAsDocArchive ) m_exportAsDocArchive->setEnabled( ok );
|
||||
if ( m_exportAs ) m_exportAs->setEnabled( ok );
|
||||
#if PURPOSE_FOUND
|
||||
if ( m_share ) m_share->setEnabled( ok );
|
||||
|
@ -1538,9 +1605,7 @@ bool Part::openFile()
|
|||
|
||||
// set the file to the fileWatcher
|
||||
if ( url().isLocalFile() )
|
||||
{
|
||||
addFileToWatcher( m_watcher, localFilePath() );
|
||||
}
|
||||
setFileToWatch( localFilePath() );
|
||||
|
||||
// if the 'OpenTOC' flag is set, open the TOC
|
||||
if ( m_document->metaData( QStringLiteral("OpenTOC") ).toBool() && m_sidebar->isItemEnabled( m_toc ) && !m_sidebar->isCollapsed() && m_sidebar->currentItem() != m_toc )
|
||||
|
@ -1578,8 +1643,17 @@ bool Part::openFile()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Part::openUrl(const QUrl &_url)
|
||||
bool Part::openUrl( const QUrl &url )
|
||||
{
|
||||
return openUrl( url, false /* swapInsteadOfOpening */ );
|
||||
}
|
||||
|
||||
bool Part::openUrl( const QUrl &_url, bool swapInsteadOfOpening )
|
||||
{
|
||||
/* Store swapInsteadOfOpening, so that closeUrl and openFile will be able
|
||||
* to read it */
|
||||
m_swapInsteadOfOpening = swapInsteadOfOpening;
|
||||
|
||||
// Close current document if any
|
||||
if ( !closeUrl() )
|
||||
return false;
|
||||
|
@ -1630,15 +1704,15 @@ bool Part::queryClose()
|
|||
return true;
|
||||
|
||||
const int res = KMessageBox::warningYesNoCancel( widget(),
|
||||
i18n( "Do you want to save your annotation changes or discard them?" ),
|
||||
i18n( "Do you want to save your changes to \"%1\" or discard them?", url().fileName() ),
|
||||
i18n( "Close Document" ),
|
||||
KStandardGuiItem::saveAs(),
|
||||
KStandardGuiItem::save(),
|
||||
KStandardGuiItem::discard() );
|
||||
|
||||
switch ( res )
|
||||
{
|
||||
case KMessageBox::Yes: // Save as
|
||||
slotSaveFileAs();
|
||||
case KMessageBox::Yes: // Save
|
||||
saveFile();
|
||||
return !isModified(); // Only allow closing if file was really saved
|
||||
case KMessageBox::No: // Discard
|
||||
return true;
|
||||
|
@ -1652,7 +1726,14 @@ bool Part::closeUrl(bool promptToSave)
|
|||
if ( promptToSave && !queryClose() )
|
||||
return false;
|
||||
|
||||
setModified( false );
|
||||
if ( m_swapInsteadOfOpening )
|
||||
{
|
||||
// If we're swapping the backing file, we don't want to close the
|
||||
// current one when openUrl() calls us internally
|
||||
return true; // pretend it worked
|
||||
}
|
||||
|
||||
m_document->setHistoryClean( true );
|
||||
|
||||
if (!m_temporaryLocalFile.isNull() && m_temporaryLocalFile != localFilePath())
|
||||
{
|
||||
|
@ -1665,21 +1746,20 @@ bool Part::closeUrl(bool promptToSave)
|
|||
m_find->setEnabled( false );
|
||||
m_findNext->setEnabled( false );
|
||||
m_findPrev->setEnabled( false );
|
||||
if( m_save ) m_save->setEnabled( false );
|
||||
if( m_saveAs ) m_saveAs->setEnabled( false );
|
||||
if( m_saveCopyAs ) m_saveCopyAs->setEnabled( false );
|
||||
m_printPreview->setEnabled( false );
|
||||
m_showProperties->setEnabled( false );
|
||||
if ( m_showEmbeddedFiles ) m_showEmbeddedFiles->setEnabled( false );
|
||||
if ( m_exportAs ) m_exportAs->setEnabled( false );
|
||||
if ( m_exportAsText ) m_exportAsText->setEnabled( false );
|
||||
if ( m_exportAsDocArchive ) m_exportAsDocArchive->setEnabled( false );
|
||||
m_exportFormats.clear();
|
||||
if ( m_exportAs )
|
||||
{
|
||||
QMenu *menu = m_exportAs->menu();
|
||||
QList<QAction*> acts = menu->actions();
|
||||
int num = acts.count();
|
||||
for ( int i = 2; i < num; ++i )
|
||||
for ( int i = 1; i < num; ++i )
|
||||
{
|
||||
menu->removeAction( acts.at(i) );
|
||||
delete acts.at(i);
|
||||
|
@ -1697,12 +1777,7 @@ bool Part::closeUrl(bool promptToSave)
|
|||
emit enablePrintAction(false);
|
||||
m_realUrl = QUrl();
|
||||
if ( url().isLocalFile() )
|
||||
{
|
||||
m_watcher->removeFile( localFilePath() );
|
||||
QFileInfo fi(localFilePath());
|
||||
m_watcher->removeDir( fi.absolutePath() );
|
||||
if ( fi.isSymLink() ) m_watcher->removeFile( fi.readLink() );
|
||||
}
|
||||
unsetFileToWatch();
|
||||
m_fileWasRemoved = false;
|
||||
if ( m_generatorGuiClient )
|
||||
factory()->removeClient( m_generatorGuiClient );
|
||||
|
@ -1714,6 +1789,7 @@ bool Part::closeUrl(bool promptToSave)
|
|||
if ( widget() )
|
||||
{
|
||||
m_searchWidget->clearText();
|
||||
m_migrationMessage->setVisible( false );
|
||||
m_topMessage->setVisible( false );
|
||||
m_formsMessage->setVisible( false );
|
||||
}
|
||||
|
@ -1802,8 +1878,8 @@ void Part::slotFileDirty( const QString& path )
|
|||
else if (m_fileWasRemoved && QFile::exists(localFilePath()))
|
||||
{
|
||||
// we need to watch the new file
|
||||
m_watcher->removeFile(localFilePath());
|
||||
m_watcher->addFile(localFilePath());
|
||||
unsetFileToWatch();
|
||||
setFileToWatch( localFilePath() );
|
||||
m_dirtyHandler->start( 750 );
|
||||
}
|
||||
}
|
||||
|
@ -1817,12 +1893,12 @@ void Part::slotFileDirty( const QString& path )
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void Part::slotDoFileDirty()
|
||||
// Attempt to reload the document, one or more times, optionally from a different URL
|
||||
bool Part::slotAttemptReload( bool oneShot, const QUrl &newUrl )
|
||||
{
|
||||
// Skip reload when another reload is already in progress
|
||||
if ( m_isReloading ) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
QScopedValueRollback<bool> rollback(m_isReloading, true);
|
||||
|
||||
|
@ -1832,7 +1908,7 @@ void Part::slotDoFileDirty()
|
|||
if ( m_viewportDirty.pageNumber == -1 )
|
||||
{
|
||||
// store the url of the current document
|
||||
m_oldUrl = url();
|
||||
m_oldUrl = newUrl.isEmpty() ? url() : newUrl;
|
||||
|
||||
// store the current viewport
|
||||
m_viewportDirty = m_document->viewport();
|
||||
|
@ -1866,7 +1942,7 @@ void Part::slotDoFileDirty()
|
|||
{
|
||||
m_toc->rollbackReload();
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( tocReloadPrepared )
|
||||
|
@ -1875,6 +1951,8 @@ void Part::slotDoFileDirty()
|
|||
// inform the user about the operation in progress
|
||||
m_pageView->displayMessage( i18n("Reloading the document...") );
|
||||
|
||||
bool reloadSucceeded = false;
|
||||
|
||||
if ( KParts::ReadWritePart::openUrl( m_oldUrl ) )
|
||||
{
|
||||
// on successful opening, restore the previous viewport
|
||||
|
@ -1899,13 +1977,17 @@ void Part::slotDoFileDirty()
|
|||
}
|
||||
if (m_wasPresentationOpen) slotShowPresentation();
|
||||
emit enablePrintAction(true && m_document->printingSupport() != Okular::Document::NoPrinting);
|
||||
|
||||
reloadSucceeded = true;
|
||||
}
|
||||
else
|
||||
else if ( !oneShot )
|
||||
{
|
||||
// start watching the file again (since we dropped it on close)
|
||||
addFileToWatcher( m_watcher, localFilePath() );
|
||||
setFileToWatch( localFilePath() );
|
||||
m_dirtyHandler->start( 750 );
|
||||
}
|
||||
|
||||
return reloadSucceeded;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2312,56 +2394,80 @@ void Part::slotFindPrev()
|
|||
|
||||
bool Part::saveFile()
|
||||
{
|
||||
qCDebug(OkularUiDebug) << "Okular part doesn't support saving the file in the location from which it was opened";
|
||||
return false;
|
||||
if ( !isModified() )
|
||||
return true;
|
||||
else
|
||||
return saveAs( url() );
|
||||
}
|
||||
|
||||
void Part::slotSaveFileAs()
|
||||
bool Part::slotSaveFileAs( bool showOkularArchiveAsDefaultFormat )
|
||||
{
|
||||
if ( m_embedMode == PrintPreviewMode )
|
||||
return;
|
||||
return false;
|
||||
|
||||
/* Show a warning before saving if the generator can't save annotations,
|
||||
* unless we are going to save a .okular archive. */
|
||||
if ( !isDocumentArchive && !m_document->canSaveChanges( Document::SaveAnnotationsCapability ) )
|
||||
// Determine the document's mimetype
|
||||
QMimeDatabase db;
|
||||
QMimeType originalMimeType;
|
||||
const QString typeName = m_document->documentInfo().get( DocumentInfo::MimeType );
|
||||
if ( !typeName.isEmpty() )
|
||||
originalMimeType = db.mimeTypeForName( typeName );
|
||||
|
||||
// What data would we lose if we saved natively?
|
||||
bool wontSaveForms, wontSaveAnnotations;
|
||||
checkNativeSaveDataLoss(&wontSaveForms, &wontSaveAnnotations);
|
||||
|
||||
const QMimeType okularArchiveMimeType = db.mimeTypeForName( QStringLiteral("application/vnd.kde.okular-archive") );
|
||||
|
||||
// Prepare "Save As" dialog
|
||||
const QString originalMimeTypeFilter = i18nc("File type name and pattern", "%1 (%2)", originalMimeType.comment(), originalMimeType.globPatterns().join(QLatin1Char(' ')));
|
||||
const QString okularArchiveMimeTypeFilter = i18nc("File type name and pattern", "%1 (%2)", okularArchiveMimeType.comment(), okularArchiveMimeType.globPatterns().join(QLatin1Char(' ')));
|
||||
|
||||
// What format choice should we show as default?
|
||||
QString selectedFilter = (isDocumentArchive || showOkularArchiveAsDefaultFormat ||
|
||||
wontSaveForms || wontSaveAnnotations) ?
|
||||
okularArchiveMimeTypeFilter : originalMimeTypeFilter;
|
||||
|
||||
QString filter = originalMimeTypeFilter + QStringLiteral(";;") + okularArchiveMimeTypeFilter;
|
||||
|
||||
const QUrl saveUrl = QFileDialog::getSaveFileUrl(widget(), i18n("Save As"), url(), filter, &selectedFilter);
|
||||
|
||||
if ( !saveUrl.isValid() || saveUrl.isEmpty() )
|
||||
return false;
|
||||
|
||||
// Has the user chosen to save in .okular archive format?
|
||||
const bool saveAsOkularArchive = ( selectedFilter == okularArchiveMimeTypeFilter );
|
||||
|
||||
return saveAs( saveUrl, saveAsOkularArchive ? SaveAsOkularArchive : NoSaveAsFlags );
|
||||
}
|
||||
|
||||
bool Part::saveAs(const QUrl & saveUrl)
|
||||
{
|
||||
// Save in the same format (.okular vs native) as the current file
|
||||
return saveAs( saveUrl, isDocumentArchive ? SaveAsOkularArchive : NoSaveAsFlags );
|
||||
}
|
||||
|
||||
bool Part::saveAs( const QUrl & saveUrl, SaveAsFlags flags )
|
||||
{
|
||||
bool hasUserAcceptedReload = false;
|
||||
if ( m_documentOpenWithPassword )
|
||||
{
|
||||
/* Search local annotations */
|
||||
bool containsLocalAnnotations = false;
|
||||
const int pagecount = m_document->pages();
|
||||
const int res = KMessageBox::warningYesNo( widget(),
|
||||
i18n( "The current document is protected with a password.<br />In order to save, the file needs to be reloaded. You will be asked for the password again and your undo/redo history will be lost.<br />Do you want to continue?" ),
|
||||
i18n( "Save - Warning" ) );
|
||||
|
||||
for ( int pageno = 0; pageno < pagecount; ++pageno )
|
||||
switch ( res )
|
||||
{
|
||||
const Okular::Page *page = m_document->page( pageno );
|
||||
foreach ( const Okular::Annotation *ann, page->annotations() )
|
||||
{
|
||||
if ( !(ann->flags() & Okular::Annotation::External) )
|
||||
{
|
||||
containsLocalAnnotations = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( containsLocalAnnotations )
|
||||
case KMessageBox::Yes:
|
||||
hasUserAcceptedReload = true;
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
/* Don't show it if there are no local annotations */
|
||||
if ( containsLocalAnnotations )
|
||||
{
|
||||
int res = KMessageBox::warningContinueCancel( widget(), i18n("Your annotations will not be exported.\nYou can export the annotated document using File -> Export As -> Document Archive") );
|
||||
if ( res != KMessageBox::Continue )
|
||||
return; // Canceled
|
||||
case KMessageBox::No: // User said no to continue, so return true even if save didn't happen otherwise we will get an error
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QUrl saveUrl = QFileDialog::getSaveFileUrl( widget(), QString(), url() );
|
||||
if ( !saveUrl.isValid() || saveUrl.isEmpty() )
|
||||
return;
|
||||
bool setModifiedAfterSave = false;
|
||||
|
||||
saveAs( saveUrl );
|
||||
}
|
||||
|
||||
bool Part::saveAs( const QUrl & saveUrl )
|
||||
{
|
||||
QTemporaryFile tf;
|
||||
QString fileName;
|
||||
if ( !tf.open() )
|
||||
|
@ -2372,85 +2478,283 @@ bool Part::saveAs( const QUrl & saveUrl )
|
|||
fileName = tf.fileName();
|
||||
tf.close();
|
||||
|
||||
QString errorText;
|
||||
bool saved;
|
||||
QScopedPointer<QTemporaryFile> tempFile;
|
||||
KIO::Job *copyJob = nullptr; // this will be filled with the job that writes to saveUrl
|
||||
|
||||
if ( isDocumentArchive )
|
||||
saved = m_document->saveDocumentArchive( fileName );
|
||||
else
|
||||
saved = m_document->saveChanges( fileName, &errorText );
|
||||
|
||||
if ( !saved )
|
||||
// Does the user want a .okular archive?
|
||||
if ( flags & SaveAsOkularArchive )
|
||||
{
|
||||
if (errorText.isEmpty())
|
||||
if ( !hasUserAcceptedReload && !m_document->canSwapBackingFile() )
|
||||
{
|
||||
const int res = KMessageBox::warningYesNo( widget(),
|
||||
i18n( "After saving, the current document format requires the file to be reloaded. Your undo/redo history will be lost.<br />Do you want to continue?" ),
|
||||
i18n( "Save - Warning" ) );
|
||||
|
||||
switch ( res )
|
||||
{
|
||||
case KMessageBox::Yes:
|
||||
// do nothing
|
||||
break;
|
||||
case KMessageBox::No: // User said no to continue, so return true even if save didn't happen otherwise we will get an error
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !m_document->saveDocumentArchive( fileName ) )
|
||||
{
|
||||
KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", fileName ) );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
KMessageBox::information( widget(), i18n("File could not be saved in '%1'. %2", fileName, errorText ) );
|
||||
}
|
||||
return false;
|
||||
|
||||
copyJob = KIO::file_copy( QUrl::fromLocalFile( fileName ), saveUrl, -1, KIO::Overwrite );
|
||||
}
|
||||
|
||||
KIO::Job *copyJob = KIO::file_copy( QUrl::fromLocalFile(fileName), saveUrl, -1, KIO::Overwrite );
|
||||
KJobWidgets::setWindow(copyJob, widget());
|
||||
if ( !copyJob->exec() )
|
||||
else
|
||||
{
|
||||
KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", saveUrl.toDisplayString() ) );
|
||||
return false;
|
||||
}
|
||||
bool wontSaveForms, wontSaveAnnotations;
|
||||
checkNativeSaveDataLoss(&wontSaveForms, &wontSaveAnnotations);
|
||||
|
||||
setModified( false );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Part::slotSaveCopyAs()
|
||||
{
|
||||
if ( m_embedMode == PrintPreviewMode )
|
||||
return;
|
||||
|
||||
QUrl saveUrl = QFileDialog::getSaveFileUrl( widget(), QString(), url());
|
||||
|
||||
if ( saveUrl.isValid() && !saveUrl.isEmpty() )
|
||||
{
|
||||
// make use of the already downloaded (in case of remote URLs) file,
|
||||
// no point in downloading that again
|
||||
QUrl srcUrl = QUrl::fromLocalFile( localFilePath() );
|
||||
QTemporaryFile * tempFile = nullptr;
|
||||
// duh, our local file disappeared...
|
||||
if ( !QFile::exists( localFilePath() ) )
|
||||
// If something can't be saved in this format, ask for confirmation
|
||||
QStringList listOfwontSaves;
|
||||
if ( wontSaveForms ) listOfwontSaves << i18n( "Filled form contents" );
|
||||
if ( wontSaveAnnotations ) listOfwontSaves << i18n( "User annotations" );
|
||||
if ( !listOfwontSaves.isEmpty() )
|
||||
{
|
||||
if ( url().isLocalFile() )
|
||||
if ( saveUrl == url() )
|
||||
{
|
||||
#ifdef OKULAR_KEEP_FILE_OPEN
|
||||
// local file: try to get it back from the open handle on it
|
||||
if ( ( tempFile = m_keeper->copyToTemporary() ) )
|
||||
srcUrl = QUrl::fromLocalFile( tempFile->fileName() );
|
||||
#else
|
||||
const QString msg = i18n( "Okular cannot copy %1 to the specified location.\n\nThe document does not exist anymore.", localFilePath() );
|
||||
KMessageBox::sorry( widget(), msg );
|
||||
return;
|
||||
#endif
|
||||
// Save
|
||||
const QString warningMessage = i18n( "You are about to save changes, but the current file format does not support saving the following elements. Please use the <i>Okular document archive</i> format to preserve them." );
|
||||
const int result = KMessageBox::warningYesNoList( widget(),
|
||||
warningMessage,
|
||||
listOfwontSaves, i18n( "Warning" ),
|
||||
KGuiItem( i18n( "Save as Okular document archive..." ), "document-save-as" ), // <- KMessageBox::Yes
|
||||
KStandardGuiItem::cancel() );
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case KMessageBox::Yes: // -> Save as Okular document archive
|
||||
return slotSaveFileAs( true /* showOkularArchiveAsDefaultFormat */ );
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we still have the original remote URL of the document,
|
||||
// so copy the document from there
|
||||
srcUrl = url();
|
||||
// Save as
|
||||
const QString warningMessage = m_document->canSwapBackingFile() ?
|
||||
i18n( "You are about to save changes, but the current file format does not support saving the following elements. Please use the <i>Okular document archive</i> format to preserve them. Click <i>Continue</i> to save the document and discard these elements." ) :
|
||||
i18n( "You are about to save changes, but the current file format does not support saving the following elements. Please use the <i>Okular document archive</i> format to preserve them. Click <i>Continue</i> to save, but you will lose these elements as well as the undo/redo history." );
|
||||
const QString continueMessage = m_document->canSwapBackingFile() ?
|
||||
i18n( "Continue" ) :
|
||||
i18n( "Continue losing changes" );
|
||||
const int result = KMessageBox::warningYesNoCancelList( widget(),
|
||||
warningMessage,
|
||||
listOfwontSaves, i18n( "Warning" ),
|
||||
KGuiItem( i18n( "Save as Okular document archive..." ), "document-save-as" ), // <- KMessageBox::Yes
|
||||
KGuiItem( continueMessage, "arrow-right" ) ); // <- KMessageBox::NO
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case KMessageBox::Yes: // -> Save as Okular document archive
|
||||
return slotSaveFileAs( true /* showOkularArchiveAsDefaultFormat */ );
|
||||
case KMessageBox::No: // -> Continue
|
||||
setModifiedAfterSave = m_document->canSwapBackingFile();
|
||||
break;
|
||||
case KMessageBox::Cancel:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KIO::Job *copyJob = KIO::file_copy( srcUrl, saveUrl, -1, KIO::Overwrite );
|
||||
KJobWidgets::setWindow(copyJob, widget());
|
||||
if ( !copyJob->exec() )
|
||||
KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", saveUrl.toDisplayString() ) );
|
||||
if ( m_document->canSaveChanges() )
|
||||
{
|
||||
// If the generator supports saving changes, save them
|
||||
|
||||
delete tempFile;
|
||||
QString errorText;
|
||||
if ( !m_document->saveChanges( fileName, &errorText ) )
|
||||
{
|
||||
if (errorText.isEmpty())
|
||||
KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", fileName ) );
|
||||
else
|
||||
KMessageBox::information( widget(), i18n("File could not be saved in '%1'. %2", fileName, errorText ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
copyJob = KIO::file_copy( QUrl::fromLocalFile( fileName ), saveUrl, -1, KIO::Overwrite );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the generators doesn't support saving changes, we will
|
||||
// just copy the original file.
|
||||
|
||||
if ( isDocumentArchive )
|
||||
{
|
||||
// Special case: if the user is extracting the contents of a
|
||||
// .okular archive back to the native format, we can't just copy
|
||||
// the open file (which is a .okular). So let's ask to core to
|
||||
// extract and give us the real file
|
||||
|
||||
if ( !m_document->extractArchivedFile( fileName ) )
|
||||
{
|
||||
KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", fileName ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
copyJob = KIO::file_copy( QUrl::fromLocalFile( fileName ), saveUrl, -1, KIO::Overwrite );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise just copy the open file.
|
||||
// make use of the already downloaded (in case of remote URLs) file,
|
||||
// no point in downloading that again
|
||||
QUrl srcUrl = QUrl::fromLocalFile( localFilePath() );
|
||||
// duh, our local file disappeared...
|
||||
if ( !QFile::exists( localFilePath() ) )
|
||||
{
|
||||
if ( url().isLocalFile() )
|
||||
{
|
||||
#ifdef OKULAR_KEEP_FILE_OPEN
|
||||
// local file: try to get it back from the open handle on it
|
||||
tempFile.reset( m_keeper->copyToTemporary() );
|
||||
if ( tempFile )
|
||||
srcUrl = KUrl::fromPath( tempFile->fileName() );
|
||||
#else
|
||||
const QString msg = i18n( "Okular cannot copy %1 to the specified location.\n\nThe document does not exist anymore.", localFilePath() );
|
||||
KMessageBox::sorry( widget(), msg );
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// we still have the original remote URL of the document,
|
||||
// so copy the document from there
|
||||
srcUrl = url();
|
||||
}
|
||||
}
|
||||
|
||||
if ( srcUrl != saveUrl )
|
||||
{
|
||||
copyJob = KIO::file_copy( srcUrl, saveUrl, -1, KIO::Overwrite );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't do a real copy in this case, just update the timestamps
|
||||
copyJob = KIO::setModificationTime( saveUrl, QDateTime::currentDateTime() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop watching for changes while we write the new file (useful when
|
||||
// overwriting)
|
||||
if ( url().isLocalFile() )
|
||||
unsetFileToWatch();
|
||||
|
||||
KJobWidgets::setWindow(copyJob, widget());
|
||||
if ( !copyJob->exec() )
|
||||
{
|
||||
KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Error: '%2'. Try to save it to another location.", saveUrl.toDisplayString(), copyJob->errorString() ) );
|
||||
|
||||
// Restore watcher
|
||||
if ( url().isLocalFile() )
|
||||
setFileToWatch( localFilePath() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_document->setHistoryClean( true );
|
||||
|
||||
if ( m_document->isDocdataMigrationNeeded() )
|
||||
m_document->docdataMigrationDone();
|
||||
|
||||
bool reloadedCorrectly = true;
|
||||
|
||||
// Make the generator use the new new file instead of the old one
|
||||
if ( m_document->canSwapBackingFile() && !m_documentOpenWithPassword )
|
||||
{
|
||||
// this calls openFile internally, which in turn actually calls
|
||||
// m_document->swapBackingFile() instead of the regular loadDocument
|
||||
if ( openUrl( saveUrl, true /* swapInsteadOfOpening */ ) )
|
||||
{
|
||||
if ( setModifiedAfterSave )
|
||||
{
|
||||
m_document->setHistoryClean( false );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reloadedCorrectly = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the generator doesn't support swapping file, then just reload
|
||||
// the document from the new location
|
||||
if ( !slotAttemptReload( true, saveUrl ) )
|
||||
reloadedCorrectly = false;
|
||||
}
|
||||
|
||||
// In case of file swapping errors, close the document to avoid inconsistencies
|
||||
if ( !reloadedCorrectly )
|
||||
{
|
||||
qWarning() << "The document hasn't been reloaded/swapped correctly";
|
||||
closeUrl();
|
||||
}
|
||||
|
||||
// Restore watcher
|
||||
if ( url().isLocalFile() )
|
||||
setFileToWatch( localFilePath() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the user wants to save in the original file's format, some features might
|
||||
// not be available. Find out what cannot be saved in this format
|
||||
void Part::checkNativeSaveDataLoss(bool *out_wontSaveForms, bool *out_wontSaveAnnotations) const
|
||||
{
|
||||
bool wontSaveForms = false;
|
||||
bool wontSaveAnnotations = false;
|
||||
|
||||
if ( !m_document->canSaveChanges( Document::SaveFormsCapability ) )
|
||||
{
|
||||
/* Set wontSaveForms only if there are forms */
|
||||
const int pagecount = m_document->pages();
|
||||
|
||||
for ( int pageno = 0; pageno < pagecount; ++pageno )
|
||||
{
|
||||
const Okular::Page *page = m_document->page( pageno );
|
||||
if ( !page->formFields().empty() )
|
||||
{
|
||||
wontSaveForms = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !m_document->canSaveChanges( Document::SaveAnnotationsCapability ) )
|
||||
{
|
||||
/* Set wontSaveAnnotations only if there are local annotations */
|
||||
const int pagecount = m_document->pages();
|
||||
|
||||
for ( int pageno = 0; pageno < pagecount; ++pageno )
|
||||
{
|
||||
const Okular::Page *page = m_document->page( pageno );
|
||||
foreach ( const Okular::Annotation *ann, page->annotations() )
|
||||
{
|
||||
if ( !(ann->flags() & Okular::Annotation::External) )
|
||||
{
|
||||
wontSaveAnnotations = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( wontSaveAnnotations )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*out_wontSaveForms = wontSaveForms;
|
||||
*out_wontSaveAnnotations = wontSaveAnnotations;
|
||||
}
|
||||
|
||||
void Part::slotGetNewStuff()
|
||||
{
|
||||
|
@ -2763,9 +3067,6 @@ void Part::slotExportAs(QAction * act)
|
|||
case 0:
|
||||
mimeType = mimeDatabase.mimeTypeForName(QStringLiteral("text/plain"));
|
||||
break;
|
||||
case 1:
|
||||
mimeType = mimeDatabase.mimeTypeForName(QStringLiteral("application/vnd.kde.okular-archive"));
|
||||
break;
|
||||
default:
|
||||
mimeType = m_exportFormats.at( id - 2 ).mimeType();
|
||||
break;
|
||||
|
@ -2782,11 +3083,8 @@ void Part::slotExportAs(QAction * act)
|
|||
case 0:
|
||||
saved = m_document->exportToText( fileName );
|
||||
break;
|
||||
case 1:
|
||||
saved = m_document->saveDocumentArchive( fileName );
|
||||
break;
|
||||
default:
|
||||
saved = m_document->exportTo( fileName, m_exportFormats.at( id - 2 ) );
|
||||
saved = m_document->exportTo( fileName, m_exportFormats.at( id - 1 ) );
|
||||
break;
|
||||
}
|
||||
if ( !saved )
|
||||
|
@ -2801,7 +3099,7 @@ void Part::slotReload()
|
|||
// auto-refresh system
|
||||
m_dirtyHandler->stop();
|
||||
|
||||
slotDoFileDirty();
|
||||
slotAttemptReload();
|
||||
}
|
||||
|
||||
|
||||
|
|
29
part.h
29
part.h
|
@ -6,6 +6,9 @@
|
|||
* Copyright (C) 2003 by Laurent Montel <montel@kde.org> *
|
||||
* Copyright (C) 2004 by Dominique Devriese <devriese@kde.org> *
|
||||
* Copyright (C) 2004-2007 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -179,7 +182,6 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
|
|||
void guiActivateEvent(KParts::GUIActivateEvent *event) override;
|
||||
void displayInfoMessage( const QString &message, KMessageWidget::MessageType messageType = KMessageWidget::Information, int duration = -1 );
|
||||
public:
|
||||
bool saveFile() override;
|
||||
bool queryClose() override;
|
||||
bool closeUrl() override;
|
||||
bool closeUrl(bool promptToSave) override;
|
||||
|
@ -202,8 +204,7 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
|
|||
void slotNextBookmark();
|
||||
void slotFindNext();
|
||||
void slotFindPrev();
|
||||
void slotSaveFileAs();
|
||||
void slotSaveCopyAs();
|
||||
bool slotSaveFileAs(bool showOkularArchiveAsDefaultFormat = false);
|
||||
void slotGetNewStuff();
|
||||
void slotNewConfig();
|
||||
void slotShowMenu(const Okular::Page *page, const QPoint &point);
|
||||
|
@ -234,10 +235,11 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
|
|||
void enableLayers( bool enable );
|
||||
|
||||
public Q_SLOTS:
|
||||
bool saveFile() override;
|
||||
// connected to Shell action (and browserExtension), not local one
|
||||
void slotPrint();
|
||||
void slotFileDirty( const QString& );
|
||||
void slotDoFileDirty();
|
||||
bool slotAttemptReload( bool oneShot = false, const QUrl &newUrl = QUrl() );
|
||||
void psTransformEnded(int, QProcess::ExitStatus);
|
||||
KConfigDialog * slotGeneratorPreferences();
|
||||
|
||||
|
@ -252,6 +254,7 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
|
|||
void showMenu(const Okular::Page *page, const QPoint &point, const QString &bookmarkTitle = QString(), const Okular::DocumentViewport &vp = DocumentViewport());
|
||||
bool eventFilter(QObject * watched, QEvent * event) override;
|
||||
Document::OpenResult doOpenFile(const QMimeType &mime, const QString &fileNameToOpen, bool *isCompressedFile);
|
||||
bool openUrl( const QUrl &url, bool swapInsteadOfOpening );
|
||||
|
||||
void setupViewerActions();
|
||||
void setViewerShortcuts();
|
||||
|
@ -266,6 +269,19 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
|
|||
void slotRenameBookmark( const DocumentViewport &viewport );
|
||||
void slotRemoveBookmark( const DocumentViewport &viewport );
|
||||
void resetStartArguments();
|
||||
void checkNativeSaveDataLoss(bool *out_wontSaveForms, bool *out_wontSaveAnnotations) const;
|
||||
|
||||
enum SaveAsFlag
|
||||
{
|
||||
NoSaveAsFlags = 0, ///< No options
|
||||
SaveAsOkularArchive = 1 ///< Save as Okular Archive (.okular) instead of document's native format
|
||||
};
|
||||
Q_DECLARE_FLAGS( SaveAsFlags, SaveAsFlag )
|
||||
|
||||
bool saveAs( const QUrl & saveUrl, SaveAsFlags flags );
|
||||
|
||||
void setFileToWatch( const QString &filePath );
|
||||
void unsetFileToWatch();
|
||||
|
||||
#if PURPOSE_FOUND
|
||||
void slotShareActionFinished(const QJsonObject &output, int error, const QString &message);
|
||||
|
@ -279,11 +295,14 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
|
|||
Okular::Document * m_document;
|
||||
QString m_temporaryLocalFile;
|
||||
bool isDocumentArchive;
|
||||
bool m_documentOpenWithPassword;
|
||||
bool m_swapInsteadOfOpening; // if set, the next open operation will replace the backing file (used when reloading just saved files)
|
||||
|
||||
// main widgets
|
||||
Sidebar *m_sidebar;
|
||||
SearchWidget *m_searchWidget;
|
||||
FindBar * m_findBar;
|
||||
KMessageWidget * m_migrationMessage;
|
||||
KMessageWidget * m_topMessage;
|
||||
KMessageWidget * m_formsMessage;
|
||||
KMessageWidget * m_infoMessage;
|
||||
|
@ -303,6 +322,7 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
|
|||
|
||||
// document watcher (and reloader) variables
|
||||
KDirWatch *m_watcher;
|
||||
QString m_watchedFilePath, m_watchedFileSymlinkTarget;
|
||||
QTimer *m_dirtyHandler;
|
||||
QUrl m_oldUrl;
|
||||
Okular::DocumentViewport m_viewportDirty;
|
||||
|
@ -334,6 +354,7 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
|
|||
QAction *m_find;
|
||||
QAction *m_findNext;
|
||||
QAction *m_findPrev;
|
||||
QAction *m_save;
|
||||
QAction *m_saveAs;
|
||||
QAction *m_saveCopyAs;
|
||||
QAction *m_printPreview;
|
||||
|
|
4
part.rc
4
part.rc
|
@ -1,11 +1,11 @@
|
|||
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
|
||||
<kpartgui name="okular_part" version="37">
|
||||
<kpartgui name="okular_part" version="38">
|
||||
<MenuBar>
|
||||
<Menu name="file"><text>&File</text>
|
||||
<Action name="get_new_stuff" group="file_open"/>
|
||||
<Action name="import_ps" group="file_open"/>
|
||||
<Action name="file_save" group="file_save"/>
|
||||
<Action name="file_save_as" group="file_save"/>
|
||||
<Action name="file_save_copy" group="file_save"/>
|
||||
<Action name="file_reload" group="file_save"/>
|
||||
<Action name="file_print" group="file_print"/>
|
||||
<Action name="file_print_preview" group="file_print"/>
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
* Copyright (C) 2003 by Malcolm Hunter <malcolm.hunter@gmx.co.uk> *
|
||||
* Copyright (C) 2004 by Dominique Devriese <devriese@kde.org> *
|
||||
* Copyright (C) 2004 by Dirk Mueller <mueller@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -265,8 +268,8 @@ void Shell::openUrl( const QUrl & url, const QString &serializedOptions )
|
|||
else
|
||||
{
|
||||
Shell* newShell = new Shell( serializedOptions );
|
||||
newShell->openUrl( url, serializedOptions );
|
||||
newShell->show();
|
||||
newShell->openUrl( url, serializedOptions );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -495,6 +498,28 @@ void Shell::setFullScreen( bool useFullScreen )
|
|||
setWindowState( windowState() & ~Qt::WindowFullScreen ); // reset
|
||||
}
|
||||
|
||||
void Shell::setCaption( const QString &caption )
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
const int activeTab = m_tabWidget->currentIndex();
|
||||
if ( activeTab >= 0 && activeTab < m_tabs.size() )
|
||||
{
|
||||
KParts::ReadWritePart* const activePart = m_tabs[activeTab].part;
|
||||
QString tabCaption = activePart->url().fileName();
|
||||
if ( activePart->isModified() ) {
|
||||
modified = true;
|
||||
if ( !tabCaption.isEmpty() ) {
|
||||
tabCaption.append( QStringLiteral( " *" ) );
|
||||
}
|
||||
}
|
||||
|
||||
m_tabWidget->setTabText( activeTab, tabCaption );
|
||||
}
|
||||
|
||||
setCaption( caption, modified );
|
||||
}
|
||||
|
||||
void Shell::showEvent(QShowEvent *e)
|
||||
{
|
||||
if (!menuBar()->isNativeMenuBar() && m_showMenuBarAction)
|
||||
|
|
|
@ -99,6 +99,10 @@ protected:
|
|||
void readSettings();
|
||||
void writeSettings();
|
||||
void setFullScreen( bool );
|
||||
|
||||
using KParts::MainWindow::setCaption;
|
||||
void setCaption( const QString &caption ) override;
|
||||
|
||||
bool queryClose() override;
|
||||
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Pino Toscano <pino@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -105,10 +108,32 @@ AnnotationModelPrivate::~AnnotationModelPrivate()
|
|||
delete root;
|
||||
}
|
||||
|
||||
static void updateAnnotationPointer( AnnItem *item, const QVector< Okular::Page * > &pages )
|
||||
{
|
||||
if ( item->annotation ) {
|
||||
item->annotation = pages[ item->page ]->annotation( item->annotation->uniqueName() );
|
||||
if ( !item->annotation )
|
||||
qWarning() << "Lost annotation on document save, something went wrong";
|
||||
}
|
||||
|
||||
foreach ( AnnItem *child, item->children )
|
||||
updateAnnotationPointer( child, pages );
|
||||
}
|
||||
|
||||
void AnnotationModelPrivate::notifySetup( const QVector< Okular::Page * > &pages, int setupFlags )
|
||||
{
|
||||
if ( !( setupFlags & Okular::DocumentObserver::DocumentChanged ) )
|
||||
{
|
||||
if ( setupFlags & Okular::DocumentObserver::UrlChanged )
|
||||
{
|
||||
// Here with UrlChanged and no document changed it means we
|
||||
// need to update all the Annotation* otherwise
|
||||
// they still point to the old document ones, luckily the old ones are still
|
||||
// around so we can look for the new ones using unique ids, etc
|
||||
updateAnnotationPointer( root, pages );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
q->beginResetModel();
|
||||
qDeleteAll( root->children );
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Chu Xiaodong <xiaodongchu@gmail.com> *
|
||||
* Copyright (C) 2006 by Pino Toscano <pino@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -241,6 +244,16 @@ AnnotWindow::~AnnotWindow()
|
|||
delete m_latexRenderer;
|
||||
}
|
||||
|
||||
Okular::Annotation * AnnotWindow::annotation() const
|
||||
{
|
||||
return m_annot;
|
||||
}
|
||||
|
||||
void AnnotWindow::updateAnnotation( Okular::Annotation * a )
|
||||
{
|
||||
m_annot = a;
|
||||
}
|
||||
|
||||
void AnnotWindow::reloadInfo()
|
||||
{
|
||||
const QColor newcolor = m_annot->style().color().isValid() ? m_annot->style().color() : Qt::yellow;
|
||||
|
@ -256,6 +269,11 @@ void AnnotWindow::reloadInfo()
|
|||
m_title->setDate( m_annot->modificationDate() );
|
||||
}
|
||||
|
||||
int AnnotWindow::pageNumber() const
|
||||
{
|
||||
return m_page;
|
||||
}
|
||||
|
||||
void AnnotWindow::showEvent( QShowEvent * event )
|
||||
{
|
||||
QFrame::showEvent( event );
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Chu Xiaodong <xiaodongchu@gmail.com> *
|
||||
* Copyright (C) 2006 by Pino Toscano <pino@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -36,6 +39,11 @@ class AnnotWindow : public QFrame
|
|||
|
||||
void reloadInfo();
|
||||
|
||||
Okular::Annotation * annotation() const;
|
||||
int pageNumber() const;
|
||||
|
||||
void updateAnnotation( Okular::Annotation * a );
|
||||
|
||||
private:
|
||||
MovableTitle * m_title;
|
||||
KTextEdit *textEdit;
|
||||
|
|
|
@ -169,7 +169,7 @@ BookmarkList::~BookmarkList()
|
|||
void BookmarkList::notifySetup( const QVector< Okular::Page * > & pages, int setupFlags )
|
||||
{
|
||||
Q_UNUSED( pages );
|
||||
if ( !( setupFlags & Okular::DocumentObserver::DocumentChanged ) )
|
||||
if ( !( setupFlags & Okular::DocumentObserver::UrlChanged ) )
|
||||
return;
|
||||
|
||||
// clear contents
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -78,14 +81,20 @@ void FormWidgetsController::signalAction( Okular::Action *a )
|
|||
emit action( a );
|
||||
}
|
||||
|
||||
QButtonGroup* FormWidgetsController::registerRadioButton( QAbstractButton *button, Okular::FormFieldButton *formButton )
|
||||
void FormWidgetsController::registerRadioButton( FormWidgetIface *fwButton, Okular::FormFieldButton *formButton )
|
||||
{
|
||||
if ( !fwButton )
|
||||
return;
|
||||
|
||||
QAbstractButton *button = dynamic_cast<QAbstractButton *>(fwButton);
|
||||
if ( !button )
|
||||
return nullptr;
|
||||
{
|
||||
qWarning() << "fwButton is not a QAbstractButton" << fwButton;
|
||||
return;
|
||||
}
|
||||
|
||||
QList< RadioData >::iterator it = m_radios.begin(), itEnd = m_radios.end();
|
||||
const int id = formButton->id();
|
||||
m_formButtons.insert( id, formButton );
|
||||
m_buttons.insert( id, button );
|
||||
for ( ; it != itEnd; ++it )
|
||||
{
|
||||
|
@ -95,7 +104,7 @@ QButtonGroup* FormWidgetsController::registerRadioButton( QAbstractButton *butto
|
|||
qCDebug(OkularUiDebug) << "Adding id" << id << "To group including" << (*it).ids;
|
||||
(*it).group->addButton( button );
|
||||
(*it).group->setId( button, id );
|
||||
return (*it).group;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +124,6 @@ QButtonGroup* FormWidgetsController::registerRadioButton( QAbstractButton *butto
|
|||
connect( newdata.group, SIGNAL( buttonClicked(QAbstractButton* ) ),
|
||||
this, SLOT( slotButtonClicked( QAbstractButton* ) ) );
|
||||
m_radios.append( newdata );
|
||||
return newdata.group;
|
||||
}
|
||||
|
||||
void FormWidgetsController::dropRadioButtons()
|
||||
|
@ -127,7 +135,6 @@ void FormWidgetsController::dropRadioButtons()
|
|||
}
|
||||
m_radios.clear();
|
||||
m_buttons.clear();
|
||||
m_formButtons.clear();
|
||||
}
|
||||
|
||||
bool FormWidgetsController::canUndo()
|
||||
|
@ -147,7 +154,8 @@ void FormWidgetsController::slotButtonClicked( QAbstractButton *button )
|
|||
{
|
||||
// Checkboxes need to be uncheckable so if clicking a checked one
|
||||
// disable the exclusive status temporarily and uncheck it
|
||||
if (m_formButtons[check->formField()->id()]->state()) {
|
||||
Okular::FormFieldButton *formButton = static_cast<Okular::FormFieldButton *>( check->formField() );
|
||||
if ( formButton->state() ) {
|
||||
const bool wasExclusive = button->group()->exclusive();
|
||||
button->group()->setExclusive(false);
|
||||
check->setChecked(false);
|
||||
|
@ -168,9 +176,9 @@ void FormWidgetsController::slotButtonClicked( QAbstractButton *button )
|
|||
foreach ( QAbstractButton* button, buttons )
|
||||
{
|
||||
checked.append( button->isChecked() );
|
||||
int id = button->group()->id( button );
|
||||
formButtons.append( m_formButtons[id] );
|
||||
prevChecked.append( m_formButtons[id]->state() );
|
||||
Okular::FormFieldButton *formButton = static_cast<Okular::FormFieldButton *>( dynamic_cast<FormWidgetIface*>(button)->formField() );
|
||||
formButtons.append( formButton );
|
||||
prevChecked.append( formButton->state() );
|
||||
}
|
||||
if (checked != prevChecked)
|
||||
emit formButtonsChangedByWidget( pageNumber, formButtons, checked );
|
||||
|
@ -259,9 +267,11 @@ FormWidgetIface * FormWidgetFactory::createWidget( Okular::FormField * ff, QWidg
|
|||
}
|
||||
|
||||
|
||||
FormWidgetIface::FormWidgetIface( QWidget * w, Okular::FormField * ff )
|
||||
: m_controller( nullptr ), m_widget( w ), m_ff( ff ), m_pageItem( nullptr )
|
||||
FormWidgetIface::FormWidgetIface( QWidget * w, Okular::FormField * ff, bool canBeEnabled )
|
||||
: m_controller( nullptr ), m_ff( ff ), m_widget( w ), m_pageItem( nullptr ),
|
||||
m_canBeEnabled( canBeEnabled )
|
||||
{
|
||||
m_widget->setEnabled( m_canBeEnabled );
|
||||
}
|
||||
|
||||
FormWidgetIface::~FormWidgetIface()
|
||||
|
@ -294,10 +304,7 @@ bool FormWidgetIface::setVisibility( bool visible )
|
|||
|
||||
void FormWidgetIface::setCanBeFilled( bool fill )
|
||||
{
|
||||
if ( m_widget->isEnabled() )
|
||||
{
|
||||
m_widget->setEnabled( fill );
|
||||
}
|
||||
m_widget->setEnabled( fill && m_canBeEnabled );
|
||||
}
|
||||
|
||||
void FormWidgetIface::setPageItem( PageViewItem *pageItem )
|
||||
|
@ -305,6 +312,11 @@ void FormWidgetIface::setPageItem( PageViewItem *pageItem )
|
|||
m_pageItem = pageItem;
|
||||
}
|
||||
|
||||
void FormWidgetIface::setFormField( Okular::FormField *field )
|
||||
{
|
||||
m_ff = field;
|
||||
}
|
||||
|
||||
Okular::FormField* FormWidgetIface::formField() const
|
||||
{
|
||||
return m_ff;
|
||||
|
@ -320,17 +332,12 @@ void FormWidgetIface::setFormWidgetsController( FormWidgetsController *controlle
|
|||
m_controller = controller;
|
||||
}
|
||||
|
||||
QAbstractButton* FormWidgetIface::button()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
PushButtonEdit::PushButtonEdit( Okular::FormFieldButton * button, QWidget * parent )
|
||||
: QPushButton( parent ), FormWidgetIface( this, button ), m_form( button )
|
||||
: QPushButton( parent ), FormWidgetIface( this, button, !button->isReadOnly() )
|
||||
{
|
||||
setText( m_form->caption() );
|
||||
setVisible( m_form->isVisible() );
|
||||
setText( button->caption() );
|
||||
setVisible( button->isVisible() );
|
||||
setCursor( Qt::ArrowCursor );
|
||||
|
||||
connect( this, &QAbstractButton::clicked, this, &PushButtonEdit::slotClicked );
|
||||
|
@ -338,70 +345,64 @@ PushButtonEdit::PushButtonEdit( Okular::FormFieldButton * button, QWidget * pare
|
|||
|
||||
void PushButtonEdit::slotClicked()
|
||||
{
|
||||
if ( m_form->activationAction() )
|
||||
m_controller->signalAction( m_form->activationAction() );
|
||||
if ( m_ff->activationAction() )
|
||||
m_controller->signalAction( m_ff->activationAction() );
|
||||
}
|
||||
|
||||
|
||||
CheckBoxEdit::CheckBoxEdit( Okular::FormFieldButton * button, QWidget * parent )
|
||||
: QCheckBox( parent ), FormWidgetIface( this, button ), m_form( button )
|
||||
: QCheckBox( parent ), FormWidgetIface( this, button, !button->isReadOnly() )
|
||||
{
|
||||
setText( m_form->caption() );
|
||||
setText( button->caption() );
|
||||
|
||||
setVisible( m_form->isVisible() );
|
||||
setVisible( button->isVisible() );
|
||||
setCursor( Qt::ArrowCursor );
|
||||
}
|
||||
|
||||
void CheckBoxEdit::setFormWidgetsController( FormWidgetsController *controller )
|
||||
{
|
||||
Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
|
||||
FormWidgetIface::setFormWidgetsController( controller );
|
||||
m_controller->registerRadioButton( button(), m_form );
|
||||
setChecked( m_form->state() );
|
||||
m_controller->registerRadioButton( this, form );
|
||||
setChecked( form->state() );
|
||||
connect( this, &QCheckBox::stateChanged, this, &CheckBoxEdit::slotStateChanged );
|
||||
}
|
||||
|
||||
QAbstractButton* CheckBoxEdit::button()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void CheckBoxEdit::slotStateChanged( int state )
|
||||
{
|
||||
if ( state == Qt::Checked && m_form->activationAction() )
|
||||
m_controller->signalAction( m_form->activationAction() );
|
||||
Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
|
||||
if ( state == Qt::Checked && form->activationAction() )
|
||||
m_controller->signalAction( form->activationAction() );
|
||||
}
|
||||
|
||||
|
||||
RadioButtonEdit::RadioButtonEdit( Okular::FormFieldButton * button, QWidget * parent )
|
||||
: QRadioButton( parent ), FormWidgetIface( this, button ), m_form( button )
|
||||
: QRadioButton( parent ), FormWidgetIface( this, button, !button->isReadOnly() )
|
||||
{
|
||||
setText( m_form->caption() );
|
||||
setText( button->caption() );
|
||||
|
||||
setVisible( m_form->isVisible() );
|
||||
setVisible( button->isVisible() );
|
||||
setCursor( Qt::ArrowCursor );
|
||||
}
|
||||
|
||||
void RadioButtonEdit::setFormWidgetsController( FormWidgetsController *controller )
|
||||
{
|
||||
Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
|
||||
FormWidgetIface::setFormWidgetsController( controller );
|
||||
m_controller->registerRadioButton( button(), m_form );
|
||||
setChecked( m_form->state() );
|
||||
m_controller->registerRadioButton( this, form );
|
||||
setChecked( form->state() );
|
||||
}
|
||||
|
||||
QAbstractButton* RadioButtonEdit::button()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
FormLineEdit::FormLineEdit( Okular::FormFieldText * text, QWidget * parent )
|
||||
: QLineEdit( parent ), FormWidgetIface( this, text ), m_form( text )
|
||||
: QLineEdit( parent ), FormWidgetIface( this, text, true )
|
||||
{
|
||||
int maxlen = m_form->maximumLength();
|
||||
int maxlen = text->maximumLength();
|
||||
if ( maxlen >= 0 )
|
||||
setMaxLength( maxlen );
|
||||
setAlignment( m_form->textAlignment() );
|
||||
setText( m_form->text() );
|
||||
if ( m_form->isPassword() )
|
||||
setAlignment( text->textAlignment() );
|
||||
setText( text->text() );
|
||||
if ( text->isPassword() )
|
||||
setEchoMode( QLineEdit::Password );
|
||||
|
||||
m_prevCursorPos = cursorPosition();
|
||||
|
@ -410,7 +411,7 @@ FormLineEdit::FormLineEdit( Okular::FormFieldText * text, QWidget * parent )
|
|||
connect( this, &QLineEdit::textEdited, this, &FormLineEdit::slotChanged );
|
||||
connect( this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged );
|
||||
|
||||
setVisible( m_form->isVisible() );
|
||||
setVisible( text->isVisible() );
|
||||
}
|
||||
|
||||
void FormLineEdit::setFormWidgetsController(FormWidgetsController* controller)
|
||||
|
@ -469,12 +470,13 @@ void FormLineEdit::contextMenuEvent( QContextMenuEvent* event )
|
|||
|
||||
void FormLineEdit::slotChanged()
|
||||
{
|
||||
Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
|
||||
QString contents = text();
|
||||
int cursorPos = cursorPosition();
|
||||
if ( contents != m_form->text() )
|
||||
if ( contents != form->text() )
|
||||
{
|
||||
m_controller->formTextChangedByWidget( pageItem()->pageNumber(),
|
||||
m_form,
|
||||
form,
|
||||
contents,
|
||||
cursorPos,
|
||||
m_prevCursorPos,
|
||||
|
@ -499,7 +501,7 @@ void FormLineEdit::slotHandleTextChangedByUndoRedo( int pageNumber,
|
|||
int anchorPos )
|
||||
{
|
||||
Q_UNUSED(pageNumber);
|
||||
if ( textForm != m_form || contents == text() )
|
||||
if ( textForm != m_ff || contents == text() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -514,12 +516,12 @@ void FormLineEdit::slotHandleTextChangedByUndoRedo( int pageNumber,
|
|||
}
|
||||
|
||||
TextAreaEdit::TextAreaEdit( Okular::FormFieldText * text, QWidget * parent )
|
||||
: KTextEdit( parent ), FormWidgetIface( this, text ), m_form( text )
|
||||
: KTextEdit( parent ), FormWidgetIface( this, text, true )
|
||||
{
|
||||
setAcceptRichText( m_form->isRichText() );
|
||||
setCheckSpellingEnabled( m_form->canBeSpellChecked() );
|
||||
setAlignment( m_form->textAlignment() );
|
||||
setPlainText( m_form->text() );
|
||||
setAcceptRichText( text->isRichText() );
|
||||
setCheckSpellingEnabled( text->canBeSpellChecked() );
|
||||
setAlignment( text->textAlignment() );
|
||||
setPlainText( text->text() );
|
||||
setUndoRedoEnabled( false );
|
||||
|
||||
connect( this, &QTextEdit::textChanged, this, &TextAreaEdit::slotChanged );
|
||||
|
@ -528,7 +530,7 @@ TextAreaEdit::TextAreaEdit( Okular::FormFieldText * text, QWidget * parent )
|
|||
this, &TextAreaEdit::slotUpdateUndoAndRedoInContextMenu );
|
||||
m_prevCursorPos = textCursor().position();
|
||||
m_prevAnchorPos = textCursor().anchor();
|
||||
setVisible( m_form->isVisible() );
|
||||
setVisible( text->isVisible() );
|
||||
}
|
||||
|
||||
bool TextAreaEdit::event( QEvent* e )
|
||||
|
@ -589,7 +591,7 @@ void TextAreaEdit::slotHandleTextChangedByUndoRedo( int pageNumber,
|
|||
int anchorPos )
|
||||
{
|
||||
Q_UNUSED(pageNumber);
|
||||
if ( textForm != m_form )
|
||||
if ( textForm != m_ff )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -605,12 +607,13 @@ void TextAreaEdit::slotHandleTextChangedByUndoRedo( int pageNumber,
|
|||
|
||||
void TextAreaEdit::slotChanged()
|
||||
{
|
||||
Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
|
||||
QString contents = toPlainText();
|
||||
int cursorPos = textCursor().position();
|
||||
if (contents != m_form->text())
|
||||
if (contents != form->text())
|
||||
{
|
||||
m_controller->formTextChangedByWidget( pageItem()->pageNumber(),
|
||||
m_form,
|
||||
form,
|
||||
contents,
|
||||
cursorPos,
|
||||
m_prevCursorPos,
|
||||
|
@ -622,19 +625,19 @@ void TextAreaEdit::slotChanged()
|
|||
|
||||
|
||||
FileEdit::FileEdit( Okular::FormFieldText * text, QWidget * parent )
|
||||
: KUrlRequester( parent ), FormWidgetIface( this, text ), m_form( text )
|
||||
: KUrlRequester( parent ), FormWidgetIface( this, text, !text->isReadOnly() )
|
||||
{
|
||||
setMode( KFile::File | KFile::ExistingOnly | KFile::LocalOnly );
|
||||
setFilter( i18n( "*|All Files" ) );
|
||||
setUrl( QUrl::fromUserInput( m_form->text() ) );
|
||||
lineEdit()->setAlignment( m_form->textAlignment() );
|
||||
setUrl( QUrl::fromUserInput( text->text() ) );
|
||||
lineEdit()->setAlignment( text->textAlignment() );
|
||||
|
||||
m_prevCursorPos = lineEdit()->cursorPosition();
|
||||
m_prevAnchorPos = lineEdit()->cursorPosition();
|
||||
|
||||
connect( this, &KUrlRequester::textChanged, this, &FileEdit::slotChanged );
|
||||
connect( lineEdit(), &QLineEdit::cursorPositionChanged, this, &FileEdit::slotChanged );
|
||||
setVisible( m_form->isVisible() );
|
||||
setVisible( text->isVisible() );
|
||||
}
|
||||
|
||||
void FileEdit::setFormWidgetsController( FormWidgetsController* controller )
|
||||
|
@ -701,12 +704,14 @@ void FileEdit::slotChanged()
|
|||
if ( text() != url().toLocalFile() )
|
||||
this->setText( url().toLocalFile() );
|
||||
|
||||
Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
|
||||
|
||||
QString contents = text();
|
||||
int cursorPos = lineEdit()->cursorPosition();
|
||||
if (contents != m_form->text())
|
||||
if (contents != form->text())
|
||||
{
|
||||
m_controller->formTextChangedByWidget( pageItem()->pageNumber(),
|
||||
m_form,
|
||||
form,
|
||||
contents,
|
||||
cursorPos,
|
||||
m_prevCursorPos,
|
||||
|
@ -731,7 +736,7 @@ void FileEdit::slotHandleFileChangedByUndoRedo( int pageNumber,
|
|||
int anchorPos )
|
||||
{
|
||||
Q_UNUSED(pageNumber);
|
||||
if ( form != m_form || contents == text() )
|
||||
if ( form != m_ff || contents == text() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -746,13 +751,13 @@ void FileEdit::slotHandleFileChangedByUndoRedo( int pageNumber,
|
|||
}
|
||||
|
||||
ListEdit::ListEdit( Okular::FormFieldChoice * choice, QWidget * parent )
|
||||
: QListWidget( parent ), FormWidgetIface( this, choice ), m_form( choice )
|
||||
: QListWidget( parent ), FormWidgetIface( this, choice, !choice->isReadOnly() )
|
||||
{
|
||||
addItems( m_form->choices() );
|
||||
setSelectionMode( m_form->multiSelect() ? QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection );
|
||||
addItems( choice->choices() );
|
||||
setSelectionMode( choice->multiSelect() ? QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection );
|
||||
setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
|
||||
QList< int > selectedItems = m_form->currentChoices();
|
||||
if ( m_form->multiSelect() )
|
||||
QList< int > selectedItems = choice->currentChoices();
|
||||
if ( choice->multiSelect() )
|
||||
{
|
||||
foreach ( int index, selectedItems )
|
||||
if ( index >= 0 && index < count() )
|
||||
|
@ -769,7 +774,7 @@ ListEdit::ListEdit( Okular::FormFieldChoice * choice, QWidget * parent )
|
|||
|
||||
connect( this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged );
|
||||
|
||||
setVisible( m_form->isVisible() );
|
||||
setVisible( choice->isVisible() );
|
||||
setCursor( Qt::ArrowCursor );
|
||||
}
|
||||
|
||||
|
@ -787,9 +792,10 @@ void ListEdit::slotSelectionChanged()
|
|||
foreach( const QListWidgetItem * item, selection )
|
||||
rows.append( row( item ) );
|
||||
|
||||
if ( rows != m_form->currentChoices() ) {
|
||||
Okular::FormFieldChoice *form = static_cast<Okular::FormFieldChoice *>(m_ff);
|
||||
if ( rows != form->currentChoices() ) {
|
||||
m_controller->formListChangedByWidget( pageItem()->pageNumber(),
|
||||
m_form,
|
||||
form,
|
||||
rows );
|
||||
}
|
||||
}
|
||||
|
@ -800,7 +806,7 @@ void ListEdit::slotHandleFormListChangedByUndoRedo( int pageNumber,
|
|||
{
|
||||
Q_UNUSED(pageNumber);
|
||||
|
||||
if ( m_form != listForm ) {
|
||||
if ( m_ff != listForm ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -815,24 +821,24 @@ void ListEdit::slotHandleFormListChangedByUndoRedo( int pageNumber,
|
|||
}
|
||||
|
||||
ComboEdit::ComboEdit( Okular::FormFieldChoice * choice, QWidget * parent )
|
||||
: QComboBox( parent ), FormWidgetIface( this, choice ), m_form( choice )
|
||||
: QComboBox( parent ), FormWidgetIface( this, choice, !choice->isReadOnly() )
|
||||
{
|
||||
addItems( m_form->choices() );
|
||||
addItems( choice->choices() );
|
||||
setEditable( true );
|
||||
setInsertPolicy( NoInsert );
|
||||
lineEdit()->setReadOnly( !m_form->isEditable() );
|
||||
QList< int > selectedItems = m_form->currentChoices();
|
||||
lineEdit()->setReadOnly( !choice->isEditable() );
|
||||
QList< int > selectedItems = choice->currentChoices();
|
||||
if ( selectedItems.count() == 1 && selectedItems.at(0) >= 0 && selectedItems.at(0) < count() )
|
||||
setCurrentIndex( selectedItems.at(0) );
|
||||
|
||||
if ( m_form->isEditable() && !m_form->editChoice().isEmpty() )
|
||||
lineEdit()->setText( m_form->editChoice() );
|
||||
if ( choice->isEditable() && !choice->editChoice().isEmpty() )
|
||||
lineEdit()->setText( choice->editChoice() );
|
||||
|
||||
connect( this, SIGNAL(currentIndexChanged(int)), this, SLOT(slotValueChanged()) );
|
||||
connect( this, &QComboBox::editTextChanged, this, &ComboEdit::slotValueChanged );
|
||||
connect( lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged );
|
||||
|
||||
setVisible( m_form->isVisible() );
|
||||
setVisible( choice->isVisible() );
|
||||
setCursor( Qt::ArrowCursor );
|
||||
m_prevCursorPos = lineEdit()->cursorPosition();
|
||||
m_prevAnchorPos = lineEdit()->cursorPosition();
|
||||
|
@ -850,21 +856,23 @@ void ComboEdit::slotValueChanged()
|
|||
{
|
||||
const QString text = lineEdit()->text();
|
||||
|
||||
Okular::FormFieldChoice *form = static_cast<Okular::FormFieldChoice *>(m_ff);
|
||||
|
||||
QString prevText;
|
||||
if ( m_form->currentChoices().isEmpty() )
|
||||
if ( form->currentChoices().isEmpty() )
|
||||
{
|
||||
prevText = m_form->editChoice();
|
||||
prevText = form->editChoice();
|
||||
}
|
||||
else
|
||||
{
|
||||
prevText = m_form->choices()[m_form->currentChoices()[0]];
|
||||
prevText = form->choices()[form->currentChoices()[0]];
|
||||
}
|
||||
|
||||
int cursorPos = lineEdit()->cursorPosition();
|
||||
if ( text != prevText )
|
||||
{
|
||||
m_controller->formComboChangedByWidget( pageItem()->pageNumber(),
|
||||
m_form,
|
||||
form,
|
||||
currentText(),
|
||||
cursorPos,
|
||||
m_prevCursorPos,
|
||||
|
@ -891,7 +899,7 @@ void ComboEdit::slotHandleFormComboChangedByUndoRedo( int pageNumber,
|
|||
{
|
||||
Q_UNUSED(pageNumber);
|
||||
|
||||
if ( m_form != form ) {
|
||||
if ( m_ff != form ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -56,7 +59,7 @@ class FormWidgetsController : public QObject
|
|||
|
||||
void signalAction( Okular::Action *action );
|
||||
|
||||
QButtonGroup* registerRadioButton( QAbstractButton *button, Okular::FormFieldButton *formButton );
|
||||
void registerRadioButton( FormWidgetIface *fwButton, Okular::FormFieldButton *formButton );
|
||||
void dropRadioButtons();
|
||||
bool canUndo();
|
||||
bool canRedo();
|
||||
|
@ -123,7 +126,6 @@ class FormWidgetsController : public QObject
|
|||
friend class ComboEdit;
|
||||
|
||||
QList< RadioData > m_radios;
|
||||
QHash< int, Okular::FormFieldButton* > m_formButtons;
|
||||
QHash< int, QAbstractButton* > m_buttons;
|
||||
Okular::Document* m_doc;
|
||||
};
|
||||
|
@ -139,7 +141,7 @@ class FormWidgetFactory
|
|||
class FormWidgetIface
|
||||
{
|
||||
public:
|
||||
FormWidgetIface( QWidget * w, Okular::FormField * ff );
|
||||
FormWidgetIface( QWidget * w, Okular::FormField * ff, bool canBeEnabled );
|
||||
virtual ~FormWidgetIface();
|
||||
|
||||
Okular::NormalizedRect rect() const;
|
||||
|
@ -149,19 +151,20 @@ class FormWidgetIface
|
|||
void setCanBeFilled( bool fill );
|
||||
|
||||
void setPageItem( PageViewItem *pageItem );
|
||||
Okular::FormField* formField() const;
|
||||
PageViewItem* pageItem() const;
|
||||
void setFormField( Okular::FormField *field );
|
||||
Okular::FormField* formField() const;
|
||||
|
||||
virtual void setFormWidgetsController( FormWidgetsController *controller );
|
||||
virtual QAbstractButton* button();
|
||||
|
||||
protected:
|
||||
FormWidgetsController * m_controller;
|
||||
Okular::FormField * m_ff;
|
||||
|
||||
private:
|
||||
QWidget * m_widget;
|
||||
Okular::FormField * m_ff;
|
||||
PageViewItem * m_pageItem;
|
||||
bool m_canBeEnabled;
|
||||
};
|
||||
|
||||
|
||||
|
@ -174,9 +177,6 @@ class PushButtonEdit : public QPushButton, public FormWidgetIface
|
|||
|
||||
private Q_SLOTS:
|
||||
void slotClicked();
|
||||
|
||||
private:
|
||||
Okular::FormFieldButton * m_form;
|
||||
};
|
||||
|
||||
class CheckBoxEdit : public QCheckBox, public FormWidgetIface
|
||||
|
@ -188,13 +188,9 @@ class CheckBoxEdit : public QCheckBox, public FormWidgetIface
|
|||
|
||||
// reimplemented from FormWidgetIface
|
||||
void setFormWidgetsController( FormWidgetsController *controller ) override;
|
||||
QAbstractButton* button() override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotStateChanged( int state );
|
||||
|
||||
private:
|
||||
Okular::FormFieldButton * m_form;
|
||||
};
|
||||
|
||||
class RadioButtonEdit : public QRadioButton, public FormWidgetIface
|
||||
|
@ -206,10 +202,6 @@ class RadioButtonEdit : public QRadioButton, public FormWidgetIface
|
|||
|
||||
// reimplemented from FormWidgetIface
|
||||
void setFormWidgetsController( FormWidgetsController *controller ) override;
|
||||
QAbstractButton* button() override;
|
||||
|
||||
private:
|
||||
Okular::FormFieldButton * m_form;
|
||||
};
|
||||
|
||||
class FormLineEdit : public QLineEdit, public FormWidgetIface
|
||||
|
@ -233,7 +225,6 @@ class FormLineEdit : public QLineEdit, public FormWidgetIface
|
|||
void slotChanged();
|
||||
|
||||
private:
|
||||
Okular::FormFieldText * m_form;
|
||||
int m_prevCursorPos;
|
||||
int m_prevAnchorPos;
|
||||
};
|
||||
|
@ -260,7 +251,6 @@ class TextAreaEdit : public KTextEdit, public FormWidgetIface
|
|||
void slotChanged();
|
||||
|
||||
private:
|
||||
Okular::FormFieldText * m_form;
|
||||
int m_prevCursorPos;
|
||||
int m_prevAnchorPos;
|
||||
};
|
||||
|
@ -286,7 +276,6 @@ class FileEdit : public KUrlRequester, public FormWidgetIface
|
|||
int cursorPos,
|
||||
int anchorPos );
|
||||
private:
|
||||
Okular::FormFieldText * m_form;
|
||||
int m_prevCursorPos;
|
||||
int m_prevAnchorPos;
|
||||
};
|
||||
|
@ -305,9 +294,6 @@ class ListEdit : public QListWidget, public FormWidgetIface
|
|||
void slotHandleFormListChangedByUndoRedo( int pageNumber,
|
||||
Okular::FormFieldChoice * listForm,
|
||||
const QList< int > & choices );
|
||||
|
||||
private:
|
||||
Okular::FormFieldChoice * m_form;
|
||||
};
|
||||
|
||||
|
||||
|
@ -331,7 +317,6 @@ class ComboEdit : public QComboBox, public FormWidgetIface
|
|||
);
|
||||
|
||||
private:
|
||||
Okular::FormFieldChoice * m_form;
|
||||
int m_prevCursorPos;
|
||||
int m_prevAnchorPos;
|
||||
};
|
||||
|
|
189
ui/pageview.cpp
189
ui/pageview.cpp
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2004-2006 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* With portions of code from kpdf/kpdf_pagewidget.cc by: *
|
||||
* Copyright (C) 2002 by Wilco Greven <greven@kde.org> *
|
||||
|
@ -81,6 +84,7 @@
|
|||
#include "core/document_p.h"
|
||||
#include "core/form.h"
|
||||
#include "core/page.h"
|
||||
#include "core/page_p.h"
|
||||
#include "core/misc.h"
|
||||
#include "core/generator.h"
|
||||
#include "core/movie.h"
|
||||
|
@ -172,7 +176,7 @@ public:
|
|||
// annotations
|
||||
PageViewAnnotator * annotator;
|
||||
//text annotation dialogs list
|
||||
QHash< Okular::Annotation *, AnnotWindow * > m_annowindows;
|
||||
QSet< AnnotWindow * > m_annowindows;
|
||||
// other stuff
|
||||
QTimer * delayResizeEventTimer;
|
||||
bool dirtyLayout;
|
||||
|
@ -451,7 +455,7 @@ PageView::~PageView()
|
|||
|
||||
// We need to assign it to a different list otherwise slotAnnotationWindowDestroyed
|
||||
// will bite us and clear d->m_annowindows
|
||||
QHash< Okular::Annotation *, AnnotWindow * > annowindows = d->m_annowindows;
|
||||
QSet< AnnotWindow * > annowindows = d->m_annowindows;
|
||||
d->m_annowindows.clear();
|
||||
qDeleteAll( annowindows );
|
||||
|
||||
|
@ -756,10 +760,13 @@ void PageView::openAnnotationWindow( Okular::Annotation * annotation, int pageNu
|
|||
|
||||
// find the annot window
|
||||
AnnotWindow* existWindow = nullptr;
|
||||
QHash< Okular::Annotation *, AnnotWindow * >::ConstIterator it = d->m_annowindows.constFind( annotation );
|
||||
if ( it != d->m_annowindows.constEnd() )
|
||||
foreach(AnnotWindow *aw, d->m_annowindows)
|
||||
{
|
||||
existWindow = *it;
|
||||
if ( aw->annotation() == annotation )
|
||||
{
|
||||
existWindow = aw;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( existWindow == nullptr )
|
||||
|
@ -767,7 +774,7 @@ void PageView::openAnnotationWindow( Okular::Annotation * annotation, int pageNu
|
|||
existWindow = new AnnotWindow( this, annotation, d->document, pageNumber );
|
||||
connect(existWindow, &QObject::destroyed, this, &PageView::slotAnnotationWindowDestroyed);
|
||||
|
||||
d->m_annowindows.insert( annotation, existWindow );
|
||||
d->m_annowindows << existWindow;
|
||||
}
|
||||
|
||||
existWindow->show();
|
||||
|
@ -775,19 +782,7 @@ void PageView::openAnnotationWindow( Okular::Annotation * annotation, int pageNu
|
|||
|
||||
void PageView::slotAnnotationWindowDestroyed( QObject * window )
|
||||
{
|
||||
QHash< Okular::Annotation*, AnnotWindow * >::Iterator it = d->m_annowindows.begin();
|
||||
QHash< Okular::Annotation*, AnnotWindow * >::Iterator itEnd = d->m_annowindows.end();
|
||||
while ( it != itEnd )
|
||||
{
|
||||
if ( it.value() == window )
|
||||
{
|
||||
it = d->m_annowindows.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
d->m_annowindows.remove( static_cast<AnnotWindow*>( window ) );
|
||||
}
|
||||
|
||||
void PageView::displayMessage( const QString & message, const QString & details, PageViewMessage::Icon icon, int duration )
|
||||
|
@ -946,19 +941,132 @@ void PageView::selectAll()
|
|||
}
|
||||
}
|
||||
|
||||
void PageView::createAnnotationsVideoWidgets(PageViewItem *item, const QLinkedList< Okular::Annotation * > &annotations)
|
||||
{
|
||||
qDeleteAll( item->videoWidgets() );
|
||||
item->videoWidgets().clear();
|
||||
|
||||
QLinkedList< Okular::Annotation * >::const_iterator aIt = annotations.constBegin(), aEnd = annotations.constEnd();
|
||||
for ( ; aIt != aEnd; ++aIt )
|
||||
{
|
||||
Okular::Annotation * a = *aIt;
|
||||
if ( a->subType() == Okular::Annotation::AMovie )
|
||||
{
|
||||
Okular::MovieAnnotation * movieAnn = static_cast< Okular::MovieAnnotation * >( a );
|
||||
VideoWidget * vw = new VideoWidget( movieAnn, movieAnn->movie(), d->document, viewport() );
|
||||
item->videoWidgets().insert( movieAnn->movie(), vw );
|
||||
vw->pageInitialized();
|
||||
}
|
||||
else if ( a->subType() == Okular::Annotation::ARichMedia )
|
||||
{
|
||||
Okular::RichMediaAnnotation * richMediaAnn = static_cast< Okular::RichMediaAnnotation * >( a );
|
||||
VideoWidget * vw = new VideoWidget( richMediaAnn, richMediaAnn->movie(), d->document, viewport() );
|
||||
item->videoWidgets().insert( richMediaAnn->movie(), vw );
|
||||
vw->pageInitialized();
|
||||
}
|
||||
else if ( a->subType() == Okular::Annotation::AScreen )
|
||||
{
|
||||
const Okular::ScreenAnnotation * screenAnn = static_cast< Okular::ScreenAnnotation * >( a );
|
||||
Okular::Movie *movie = GuiUtils::renditionMovieFromScreenAnnotation( screenAnn );
|
||||
if ( movie )
|
||||
{
|
||||
VideoWidget * vw = new VideoWidget( screenAnn, movie, d->document, viewport() );
|
||||
item->videoWidgets().insert( movie, vw );
|
||||
vw->pageInitialized();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//BEGIN DocumentObserver inherited methods
|
||||
void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setupFlags )
|
||||
{
|
||||
bool documentChanged = setupFlags & Okular::DocumentObserver::DocumentChanged;
|
||||
const bool allownotes = d->document->isAllowed( Okular::AllowNotes );
|
||||
const bool allowfillforms = d->document->isAllowed( Okular::AllowFillForms );
|
||||
|
||||
// allownotes may have changed
|
||||
if ( d->aToggleAnnotator )
|
||||
d->aToggleAnnotator->setEnabled( allownotes );
|
||||
|
||||
// reuse current pages if nothing new
|
||||
if ( ( pageSet.count() == d->items.count() ) && !documentChanged && !( setupFlags & Okular::DocumentObserver::NewLayoutForPages ) )
|
||||
{
|
||||
int count = pageSet.count();
|
||||
for ( int i = 0; (i < count) && !documentChanged; i++ )
|
||||
{
|
||||
if ( (int)pageSet[i]->number() != d->items[i]->pageNumber() )
|
||||
{
|
||||
documentChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// even if the document has not changed, allowfillforms may have
|
||||
// changed, so update all fields' "canBeFilled" flag
|
||||
foreach ( FormWidgetIface * w, d->items[i]->formWidgets() )
|
||||
w->setCanBeFilled( allowfillforms );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !documentChanged )
|
||||
{
|
||||
if ( setupFlags & Okular::DocumentObserver::UrlChanged )
|
||||
{
|
||||
// Here with UrlChanged and no document changed it means we
|
||||
// need to update all the Annotation* and Form* otherwise
|
||||
// they still point to the old document ones, luckily the old ones are still
|
||||
// around so we can look for the new ones using unique ids, etc
|
||||
d->mouseAnnotation->updateAnnotationPointers();
|
||||
|
||||
foreach(AnnotWindow *aw, d->m_annowindows)
|
||||
{
|
||||
Okular::Annotation *newA = d->document->page( aw->pageNumber() )->annotation( aw->annotation()->uniqueName() );
|
||||
aw->updateAnnotation( newA );
|
||||
}
|
||||
|
||||
const QRect viewportRect( horizontalScrollBar()->value(), verticalScrollBar()->value(),
|
||||
viewport()->width(), viewport()->height() );
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
PageViewItem *item = d->items[i];
|
||||
const QSet<FormWidgetIface*> fws = item->formWidgets();
|
||||
foreach ( FormWidgetIface * w, fws )
|
||||
{
|
||||
Okular::FormField *f = Okular::PagePrivate::findEquivalentForm( d->document->page( i ), w->formField() );
|
||||
if (f)
|
||||
{
|
||||
w->setFormField( f );
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Lost form field on document save, something is wrong";
|
||||
item->formWidgets().remove(w);
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
|
||||
// For the video widgets we don't really care about reusing them since they don't contain much info so just
|
||||
// create them again
|
||||
createAnnotationsVideoWidgets( item, pageSet[i]->annotations() );
|
||||
Q_FOREACH ( VideoWidget *vw, item->videoWidgets() )
|
||||
{
|
||||
const Okular::NormalizedRect r = vw->normGeometry();
|
||||
vw->setGeometry(
|
||||
qRound( item->uncroppedGeometry().left() + item->uncroppedWidth() * r.left ) + 1 - viewportRect.left(),
|
||||
qRound( item->uncroppedGeometry().top() + item->uncroppedHeight() * r.top ) + 1 - viewportRect.top(),
|
||||
qRound( fabs( r.right - r.left ) * item->uncroppedGeometry().width() ),
|
||||
qRound( fabs( r.bottom - r.top ) * item->uncroppedGeometry().height() ) );
|
||||
|
||||
// Workaround, otherwise the size somehow gets lost
|
||||
vw->show();
|
||||
vw->hide();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// mouseAnnotation must not access our PageViewItem widgets any longer
|
||||
|
@ -997,42 +1105,13 @@ void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setup
|
|||
w->setPageItem( item );
|
||||
w->setFormWidgetsController( d->formWidgetsController() );
|
||||
w->setVisibility( false );
|
||||
w->setCanBeFilled( d->document->isAllowed( Okular::AllowFillForms ) );
|
||||
item->formWidgets().insert( ff->id(), w );
|
||||
w->setCanBeFilled( allowfillforms );
|
||||
item->formWidgets().insert( w );
|
||||
hasformwidgets = true;
|
||||
}
|
||||
}
|
||||
const QLinkedList< Okular::Annotation * > annotations = (*setIt)->annotations();
|
||||
QLinkedList< Okular::Annotation * >::const_iterator aIt = annotations.constBegin(), aEnd = annotations.constEnd();
|
||||
for ( ; aIt != aEnd; ++aIt )
|
||||
{
|
||||
Okular::Annotation * a = *aIt;
|
||||
if ( a->subType() == Okular::Annotation::AMovie )
|
||||
{
|
||||
Okular::MovieAnnotation * movieAnn = static_cast< Okular::MovieAnnotation * >( a );
|
||||
VideoWidget * vw = new VideoWidget( movieAnn, movieAnn->movie(), d->document, viewport() );
|
||||
item->videoWidgets().insert( movieAnn->movie(), vw );
|
||||
vw->pageInitialized();
|
||||
}
|
||||
else if ( a->subType() == Okular::Annotation::ARichMedia )
|
||||
{
|
||||
Okular::RichMediaAnnotation * richMediaAnn = static_cast< Okular::RichMediaAnnotation * >( a );
|
||||
VideoWidget * vw = new VideoWidget( richMediaAnn, richMediaAnn->movie(), d->document, viewport() );
|
||||
item->videoWidgets().insert( richMediaAnn->movie(), vw );
|
||||
vw->pageInitialized();
|
||||
}
|
||||
else if ( a->subType() == Okular::Annotation::AScreen )
|
||||
{
|
||||
const Okular::ScreenAnnotation * screenAnn = static_cast< Okular::ScreenAnnotation * >( a );
|
||||
Okular::Movie *movie = GuiUtils::renditionMovieFromScreenAnnotation( screenAnn );
|
||||
if ( movie )
|
||||
{
|
||||
VideoWidget * vw = new VideoWidget( screenAnn, movie, d->document, viewport() );
|
||||
item->videoWidgets().insert( movie, vw );
|
||||
vw->pageInitialized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createAnnotationsVideoWidgets( item, (*setIt)->annotations() );
|
||||
}
|
||||
|
||||
// invalidate layout so relayout/repaint will happen on next viewport change
|
||||
|
@ -1069,7 +1148,7 @@ void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setup
|
|||
|
||||
// We need to assign it to a different list otherwise slotAnnotationWindowDestroyed
|
||||
// will bite us and clear d->m_annowindows
|
||||
QHash< Okular::Annotation *, AnnotWindow * > annowindows = d->m_annowindows;
|
||||
QSet< AnnotWindow * > annowindows = d->m_annowindows;
|
||||
d->m_annowindows.clear();
|
||||
qDeleteAll( annowindows );
|
||||
|
||||
|
@ -1312,10 +1391,10 @@ void PageView::notifyPageChanged( int pageNumber, int changedFlags )
|
|||
{
|
||||
const QLinkedList< Okular::Annotation * > annots = d->document->page( pageNumber )->annotations();
|
||||
const QLinkedList< Okular::Annotation * >::ConstIterator annItEnd = annots.end();
|
||||
QHash< Okular::Annotation*, AnnotWindow * >::Iterator it = d->m_annowindows.begin();
|
||||
QSet< AnnotWindow * >::Iterator it = d->m_annowindows.begin();
|
||||
for ( ; it != d->m_annowindows.end(); )
|
||||
{
|
||||
QLinkedList< Okular::Annotation * >::ConstIterator annIt = qFind( annots, it.key() );
|
||||
QLinkedList< Okular::Annotation * >::ConstIterator annIt = qFind( annots, (*it)->annotation() );
|
||||
if ( annIt != annItEnd )
|
||||
{
|
||||
(*it)->reloadInfo();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2004 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* With portions of code from kpdf/kpdf_pagewidget.h by: *
|
||||
* Copyright (C) 2002 by Wilco Greven <greven@kde.org> *
|
||||
|
@ -199,6 +202,8 @@ Q_OBJECT
|
|||
// handle link clicked
|
||||
bool mouseReleaseOverLink( const Okular::ObjectRect * rect ) const;
|
||||
|
||||
void createAnnotationsVideoWidgets(PageViewItem *item, const QLinkedList< Okular::Annotation * > &annotations);
|
||||
|
||||
// don't want to expose classes in here
|
||||
class PageViewPrivate * d;
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
* Copyright (C) 2017 by Tobias Deiminger <haxtibal@t-online.de> *
|
||||
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2004-2006 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* With portions of code from kpdf/kpdf_pagewidget.cc by: *
|
||||
* Copyright (C) 2002 by Wilco Greven <greven@kde.org> *
|
||||
|
@ -400,6 +403,19 @@ Qt::CursorShape MouseAnnotation::cursor() const
|
|||
return Qt::ArrowCursor;
|
||||
}
|
||||
|
||||
void MouseAnnotation::updateAnnotationPointers()
|
||||
{
|
||||
if (m_focusedAnnotation.annotation)
|
||||
{
|
||||
m_focusedAnnotation.annotation = m_document->page( m_focusedAnnotation.pageNumber )->annotation( m_focusedAnnotation.annotation->uniqueName() );
|
||||
}
|
||||
|
||||
if (m_mouseOverAnnotation.annotation)
|
||||
{
|
||||
m_mouseOverAnnotation.annotation = m_document->page( m_mouseOverAnnotation.pageNumber )->annotation( m_mouseOverAnnotation.annotation->uniqueName() );
|
||||
}
|
||||
}
|
||||
|
||||
void MouseAnnotation::cancel()
|
||||
{
|
||||
if ( isActive() )
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
* Copyright (C) 2017 by Tobias Deiminger <haxtibal@t-online.de> *
|
||||
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2004-2006 by Albert Astals Cid <aacid@kde.org> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* With portions of code from kpdf/kpdf_pagewidget.cc by: *
|
||||
* Copyright (C) 2002 by Wilco Greven <greven@kde.org> *
|
||||
|
@ -113,6 +116,9 @@ public:
|
|||
|
||||
Qt::CursorShape cursor() const;
|
||||
|
||||
// needs to be called after document save
|
||||
void updateAnnotationPointers();
|
||||
|
||||
enum MouseAnnotationState {
|
||||
StateInactive,
|
||||
StateFocused,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -51,9 +54,7 @@ PageViewItem::PageViewItem( const Okular::Page * page )
|
|||
|
||||
PageViewItem::~PageViewItem()
|
||||
{
|
||||
QHash<int, FormWidgetIface*>::iterator it = m_formWidgets.begin(), itEnd = m_formWidgets.end();
|
||||
for ( ; it != itEnd; ++it )
|
||||
delete *it;
|
||||
qDeleteAll( m_formWidgets );
|
||||
qDeleteAll( m_videoWidgets );
|
||||
}
|
||||
|
||||
|
@ -122,7 +123,7 @@ bool PageViewItem::isVisible() const
|
|||
return m_visible;
|
||||
}
|
||||
|
||||
QHash<int, FormWidgetIface*>& PageViewItem::formWidgets()
|
||||
QSet<FormWidgetIface*>& PageViewItem::formWidgets()
|
||||
{
|
||||
return m_formWidgets;
|
||||
}
|
||||
|
@ -163,7 +164,7 @@ void PageViewItem::moveTo( int x, int y )
|
|||
m_croppedGeometry.moveTop( y );
|
||||
m_uncroppedGeometry.moveLeft( qRound( x - m_crop.left * m_uncroppedGeometry.width() ) );
|
||||
m_uncroppedGeometry.moveTop( qRound( y - m_crop.top * m_uncroppedGeometry.height() ) );
|
||||
QHash<int, FormWidgetIface*>::iterator it = m_formWidgets.begin(), itEnd = m_formWidgets.end();
|
||||
QSet<FormWidgetIface*>::iterator it = m_formWidgets.begin(), itEnd = m_formWidgets.end();
|
||||
for ( ; it != itEnd; ++it )
|
||||
{
|
||||
Okular::NormalizedRect r = (*it)->rect();
|
||||
|
@ -200,7 +201,7 @@ bool PageViewItem::setFormWidgetsVisible( bool visible )
|
|||
return false;
|
||||
|
||||
bool somehadfocus = false;
|
||||
QHash<int, FormWidgetIface*>::iterator it = m_formWidgets.begin(), itEnd = m_formWidgets.end();
|
||||
QSet<FormWidgetIface*>::iterator it = m_formWidgets.begin(), itEnd = m_formWidgets.end();
|
||||
for ( ; it != itEnd; ++it )
|
||||
{
|
||||
bool hadfocus = (*it)->setVisibility( visible && (*it)->formField()->isVisible() );
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
|
||||
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
|
||||
* company, info@kdab.com. Work sponsored by the *
|
||||
* LiMux project of the city of Munich *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -47,7 +50,7 @@ class PageViewItem
|
|||
int pageNumber() const;
|
||||
double zoomFactor() const;
|
||||
bool isVisible() const;
|
||||
QHash<int, FormWidgetIface*>& formWidgets();
|
||||
QSet<FormWidgetIface*>& formWidgets();
|
||||
QHash< Okular::Movie *, VideoWidget * >& videoWidgets();
|
||||
|
||||
/* The page is cropped as follows: */
|
||||
|
@ -87,7 +90,7 @@ class PageViewItem
|
|||
QRect m_croppedGeometry;
|
||||
QRect m_uncroppedGeometry;
|
||||
Okular::NormalizedRect m_crop;
|
||||
QHash<int, FormWidgetIface*> m_formWidgets;
|
||||
QSet<FormWidgetIface*> m_formWidgets;
|
||||
QHash< Okular::Movie *, VideoWidget * > m_videoWidgets;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue