1 // SPDX-FileCopyrightText: 2003 Dominique Devriese <devriese@kde.org>
2 
3 // SPDX-License-Identifier: GPL-2.0-or-later
4 
5 #include "lists.h"
6 
7 #include "object_constructor.h"
8 #include "guiaction.h"
9 #include "object_hierarchy.h"
10 #include "../kig/kig_part.h"
11 #include "kig_version.h"
12 
13 #include <KMessageBox>
14 #include <QFile>
15 #include <QTextStream>
16 #include <qdom.h>
17 #include <QRegExp>
18 #include <algorithm>
19 #include <iterator>
20 using namespace std;
21 
22 template<typename T>
vect_remove(std::vector<T> & v,const T & t)23 void vect_remove( std::vector<T>& v, const T& t )
24 {
25   typename std::vector<T>::iterator new_end = std::remove( v.begin(), v.end(), t );
26   v.erase( new_end, v.end() );
27 }
28 
instance()29 GUIActionList* GUIActionList::instance()
30 {
31   static GUIActionList l;
32   return &l;
33 }
34 
~GUIActionList()35 GUIActionList::~GUIActionList()
36 {
37   for ( avectype::iterator i = mactions.begin(); i != mactions.end(); ++i )
38     delete *i;
39 }
40 
GUIActionList()41 GUIActionList::GUIActionList()
42 {
43 }
44 
regDoc(KigPart * d)45 void GUIActionList::regDoc( KigPart* d )
46 {
47   mdocs.insert( d );
48 }
49 
unregDoc(KigPart * d)50 void GUIActionList::unregDoc( KigPart* d )
51 {
52   mdocs.erase( d );
53 }
54 
add(const std::vector<GUIAction * > & a)55 void GUIActionList::add( const std::vector<GUIAction*>& a )
56 {
57   copy( a.begin(), a.end(), inserter( mactions, mactions.begin() ) );
58   for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i )
59   {
60     KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate();
61     for ( uint j = 0; j < a.size(); ++j )
62       (*i)->actionAdded( a[j], t );
63     (*i)->endGUIActionUpdate( t );
64   };
65 }
66 
add(GUIAction * a)67 void GUIActionList::add( GUIAction* a )
68 {
69   mactions.insert( a );
70   for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i )
71   {
72     KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate();
73     (*i)->actionAdded( a, t );
74     (*i)->endGUIActionUpdate( t );
75   };
76 }
77 
remove(const std::vector<GUIAction * > & a)78 void GUIActionList::remove( const std::vector<GUIAction*>& a )
79 {
80   for ( uint i = 0; i < a.size(); ++i )
81   {
82     mactions.erase( a[i] );
83   };
84   for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i )
85   {
86     KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate();
87     for ( uint j = 0; j < a.size(); ++j )
88       (*i)->actionRemoved( a[j], t );
89     (*i)->endGUIActionUpdate( t );
90   };
91   delete_all( a.begin(), a.end() );
92 }
93 
remove(GUIAction * a)94 void GUIActionList::remove( GUIAction* a )
95 {
96   mactions.erase( a );
97   for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i )
98   {
99     KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate();
100     (*i)->actionRemoved( a, t );
101     (*i)->endGUIActionUpdate( t );
102   };
103   delete a;
104 }
105 
ObjectConstructorList()106 ObjectConstructorList::ObjectConstructorList()
107 {
108 }
109 
~ObjectConstructorList()110 ObjectConstructorList::~ObjectConstructorList()
111 {
112   for ( vectype::iterator i = mctors.begin(); i != mctors.end(); ++i )
113     delete *i;
114 }
115 
instance()116 ObjectConstructorList* ObjectConstructorList::instance()
117 {
118   static ObjectConstructorList s;
119   return &s;
120 }
121 
ctorsThatWantArgs(const std::vector<ObjectCalcer * > & os,const KigDocument & d,const KigWidget & w,bool co) const122 ObjectConstructorList::vectype ObjectConstructorList::ctorsThatWantArgs(
123   const std::vector<ObjectCalcer*>& os, const KigDocument& d,
124   const KigWidget& w, bool co ) const
125 {
126   vectype ret;
127   for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i )
128   {
129     int r = (*i)->wantArgs( os, d, w );
130     if ( r == ArgsParser::Complete || ( !co && r == ArgsParser::Valid ) )
131       ret.push_back( *i );
132   };
133   return ret;
134 }
135 
remove(ObjectConstructor * a)136 void ObjectConstructorList::remove( ObjectConstructor* a )
137 {
138   vect_remove( mctors, a );
139   delete a;
140 }
141 
add(ObjectConstructor * a)142 void ObjectConstructorList::add( ObjectConstructor* a )
143 {
144   mctors.push_back( a );
145 }
146 
Macro(GUIAction * a,MacroConstructor * c)147 Macro::Macro( GUIAction* a, MacroConstructor* c )
148   : action( a ), ctor( c )
149 {
150 }
151 
operator ==(const Macro & l,const Macro & r)152 bool operator==( const Macro& l, const Macro& r )
153 {
154   return ( l.action->descriptiveName() == r.action->descriptiveName() ) &&
155          ( l.action->description() == r.action->description() ) &&
156          ( l.action->iconFileName() == r.action->iconFileName() );
157 }
158 
MacroList()159 MacroList::MacroList()
160 {
161 }
162 
~MacroList()163 MacroList::~MacroList()
164 {
165   std::vector<GUIAction*> actions;
166   std::vector<ObjectConstructor*> ctors;
167   for ( vectype::iterator i = mdata.begin(); i != mdata.end(); ++i )
168   {
169     Macro* m = *i;
170     GUIAction* a = m->action;
171     actions.push_back( a );
172     ObjectConstructor* c = m->ctor;
173     ctors.push_back( c );
174     delete m;
175   };
176   mdata.clear();
177   GUIActionList::instance()->remove( actions );
178   for ( uint i = 0; i < ctors.size(); ++i )
179     ObjectConstructorList::instance()->remove( ctors[i] );
180 }
181 
instance()182 MacroList* MacroList::instance()
183 {
184   static MacroList t;
185   return &t;
186 }
187 
add(const std::vector<Macro * > & ms)188 void MacroList::add( const std::vector<Macro*>& ms )
189 {
190   copy( ms.begin(), ms.end(), back_inserter( mdata ) );
191   std::vector<GUIAction*> acts;
192   for ( uint i = 0; i < ms.size(); ++i )
193   {
194     ObjectConstructorList::instance()->add( ms[i]->ctor );
195     acts.push_back( ms[i]->action );
196   };
197   GUIActionList::instance()->add( acts );
198 }
199 
add(Macro * m)200 void MacroList::add( Macro* m )
201 {
202   mdata.push_back( m );
203   ObjectConstructorList::instance()->add( m->ctor );
204   GUIActionList::instance()->add( m->action );
205 }
206 
remove(Macro * m)207 void MacroList::remove( Macro* m )
208 {
209   GUIAction* a = m->action;
210   ObjectConstructor* c = m->ctor;
211   mdata.erase( std::remove( mdata.begin(), mdata.end(), m ),
212                mdata.end() );
213   delete m;
214   GUIActionList::instance()->remove( a );
215   ObjectConstructorList::instance()->remove( c );
216 }
217 
macros() const218 const MacroList::vectype& MacroList::macros() const
219 {
220   return mdata;
221 }
222 
~Macro()223 Macro::~Macro()
224 {
225 }
226 
save(Macro * m,const QString & f)227 bool MacroList::save( Macro* m, const QString& f )
228 {
229   std::vector<Macro*> ms;
230   ms.push_back( m );
231   return save( ms, f );
232 }
233 
save(const std::vector<Macro * > & ms,const QString & f)234 bool MacroList::save( const std::vector<Macro*>& ms, const QString& f )
235 {
236   QDomDocument doc( QStringLiteral("KigMacroFile") );
237 
238   QDomElement docelem = doc.createElement( QStringLiteral("KigMacroFile") );
239   docelem.setAttribute( QStringLiteral("Version"), KIG_VERSION_STRING );
240   docelem.setAttribute( QStringLiteral("Number"), static_cast<uint>( ms.size() ) );
241 
242   for ( uint i = 0; i < ms.size(); ++i )
243   {
244     MacroConstructor* ctor = ms[i]->ctor;
245 
246     QDomElement macroelem = doc.createElement( QStringLiteral("Macro") );
247 
248     // name
249     QDomElement nameelem = doc.createElement( QStringLiteral("Name") );
250     nameelem.appendChild( doc.createTextNode( ctor->descriptiveName() ) );
251     macroelem.appendChild( nameelem );
252 
253     // desc
254     QDomElement descelem = doc.createElement( QStringLiteral("Description") );
255     descelem.appendChild( doc.createTextNode( ctor->description() ) );
256     macroelem.appendChild( descelem );
257 
258     // icon
259     QByteArray icon = ctor->iconFileName( true );
260     if ( !icon.isNull() )
261     {
262       QDomElement descelem = doc.createElement( QStringLiteral("IconFileName") );
263       descelem.appendChild( doc.createTextNode( icon ) );
264       macroelem.appendChild( descelem );
265     }
266 
267     // data
268     QDomElement hierelem = doc.createElement( QStringLiteral("Construction") );
269     ctor->hierarchy().serialize( hierelem, doc );
270     macroelem.appendChild( hierelem );
271 
272     docelem.appendChild( macroelem );
273   };
274 
275   doc.appendChild( docelem );
276 
277   QFile file( f );
278   if ( ! file.open( QIODevice::WriteOnly ) )
279     return false;
280   QTextStream stream( &file );
281   stream << doc.toByteArray();
282   return true;
283 }
284 
load(const QString & f,std::vector<Macro * > & ret,const KigPart & kdoc)285 bool MacroList::load( const QString& f, std::vector<Macro*>& ret, const KigPart& kdoc )
286 {
287   QFile file( f );
288   if ( ! file.open( QIODevice::ReadOnly ) )
289   {
290     KMessageBox::sorry( 0, i18n( "Could not open macro file '%1'", f ) );
291     return false;
292   }
293   QDomDocument doc( QStringLiteral("KigMacroFile") );
294   if ( !doc.setContent( &file ) )
295   {
296     KMessageBox::sorry( 0, i18n( "Could not open macro file '%1'", f ) );
297     return false;
298   }
299   file.close();
300   QDomElement main = doc.documentElement();
301 
302   if ( main.tagName() == QLatin1String("KigMacroFile") )
303     return loadNew( main, ret, kdoc );
304   else
305   {
306     KMessageBox::detailedSorry(
307       0, i18n( "Kig cannot open the macro file \"%1\".", f ),
308       i18n( "This file was created by a very old Kig version (pre-0.4). "
309             "Support for this format has been removed from recent Kig versions. "
310             "You can try to import this macro using a previous Kig version "
311             "(0.4 to 0.6) and then export it again in the new format." ),
312       i18n( "Not Supported" ) );
313     return false;
314   }
315 }
316 
loadNew(const QDomElement & docelem,std::vector<Macro * > & ret,const KigPart &)317 bool MacroList::loadNew( const QDomElement& docelem, std::vector<Macro*>& ret, const KigPart& )
318 {
319   bool sok = true;
320   // unused..
321 //  int number = docelem.attribute( "Number" ).toInt( &sok );
322   if ( ! sok ) return false;
323 
324   QString version = docelem.attribute( QStringLiteral("Version") );
325 //  QRegExp re( "(\\d+)\\.(\\d+)\\.(\\d+)" );
326 //  re.match( version );
327   // unused..
328 //  int major = re.cap( 1 ).toInt( &sok );
329 //  int minor = re.cap( 2 ).toInt( &sok );
330 //  int mminor = re.cap( 3 ).toInt( &sok );
331 //  if ( ! sok ) return false;
332 
333   int unnamedindex = 1;
334   QString tmp;
335 
336   for ( QDomElement macroelem = docelem.firstChild().toElement();
337         ! macroelem.isNull(); macroelem = macroelem.nextSibling().toElement() )
338   {
339     QString name, description;
340     ObjectHierarchy* hierarchy = 0;
341     QByteArray actionname;
342     QByteArray iconfile( "system-run" );
343     if ( macroelem.tagName() != QLatin1String("Macro") ) continue; // forward compat ?
344     for ( QDomElement dataelem = macroelem.firstChild().toElement();
345           ! dataelem.isNull(); dataelem = dataelem.nextSibling().toElement() )
346     {
347       if ( dataelem.tagName() == QLatin1String("Name") )
348         name = dataelem.text();
349       else if ( dataelem.tagName() == QLatin1String("Description") )
350         description = dataelem.text();
351       else if ( dataelem.tagName() == QLatin1String("Construction") )
352         hierarchy = ObjectHierarchy::buildSafeObjectHierarchy( dataelem, tmp );
353       else if ( dataelem.tagName() == QLatin1String("ActionName") )
354         actionname = dataelem.text().toLatin1();
355       else if ( dataelem.tagName() == QLatin1String("IconFileName") )
356         iconfile = dataelem.text().toLatin1();
357       else continue;
358     };
359     assert( hierarchy );
360     // if the macro has no name, we give it a bogus name...
361     bool name_i18ned = false;
362     if ( name.isEmpty() )
363     {
364       name = i18n( "Unnamed Macro #%1", unnamedindex++ );
365       name_i18ned = true;
366     }
367     MacroConstructor* ctor =
368       new MacroConstructor( *hierarchy, name_i18ned ? name : i18n( name.toUtf8() ),
369                             description.isEmpty() ? QString() : i18n( description.toUtf8() ),
370                             iconfile );
371     delete hierarchy;
372     GUIAction* act = new ConstructibleAction( ctor, actionname );
373     Macro* macro = new Macro( act, ctor );
374     ret.push_back( macro );
375   };
376   return true;
377 }
378 
constructors() const379 const ObjectConstructorList::vectype& ObjectConstructorList::constructors() const
380 {
381   return mctors;
382 }
383