1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <vector>
21 
22 #include <symbol.hxx>
23 #include <utility.hxx>
24 #include "cfgitem.hxx"
25 #include <smmod.hxx>
26 #include <sal/log.hxx>
27 #include <osl/diagnose.h>
28 
29 
SmSym()30 SmSym::SmSym() :
31     m_aName(OUString("unknown")),
32     m_aSetName(OUString("unknown")),
33     m_cChar('\0'),
34     m_bPredefined(false)
35 {
36     m_aExportName = m_aName;
37     m_aFace.SetTransparent(true);
38     m_aFace.SetAlignment(ALIGN_BASELINE);
39 }
40 
41 
SmSym(const SmSym & rSymbol)42 SmSym::SmSym(const SmSym& rSymbol)
43 {
44     *this = rSymbol;
45 }
46 
47 
SmSym(const OUString & rName,const vcl::Font & rFont,sal_UCS4 cChar,const OUString & rSet,bool bIsPredefined)48 SmSym::SmSym(const OUString& rName, const vcl::Font& rFont, sal_UCS4 cChar,
49              const OUString& rSet, bool bIsPredefined)
50 {
51     m_aName     = m_aExportName   = rName;
52 
53     m_aFace     = rFont;
54     m_aFace.SetTransparent(true);
55     m_aFace.SetAlignment(ALIGN_BASELINE);
56 
57     m_cChar         = cChar;
58     m_aSetName      = rSet;
59     m_bPredefined   = bIsPredefined;
60 }
61 
62 
operator =(const SmSym & rSymbol)63 SmSym& SmSym::operator = (const SmSym& rSymbol)
64 {
65     m_aName         = rSymbol.m_aName;
66     m_aExportName   = rSymbol.m_aExportName;
67     m_cChar         = rSymbol.m_cChar;
68     m_aFace         = rSymbol.m_aFace;
69     m_aSetName      = rSymbol.m_aSetName;
70     m_bPredefined   = rSymbol.m_bPredefined;
71 
72     SM_MOD()->GetSymbolManager().SetModified(true);
73 
74     return *this;
75 }
76 
77 
IsEqualInUI(const SmSym & rSymbol) const78 bool SmSym::IsEqualInUI( const SmSym& rSymbol ) const
79 {
80     return  m_aName == rSymbol.m_aName &&
81             m_aFace == rSymbol.m_aFace &&
82             m_cChar == rSymbol.m_cChar;
83 }
84 
85 /**************************************************************************/
86 
87 
SmSymbolManager()88 SmSymbolManager::SmSymbolManager()
89 {
90     m_bModified     = false;
91 }
92 
93 
SmSymbolManager(const SmSymbolManager & rSymbolSetManager)94 SmSymbolManager::SmSymbolManager(const SmSymbolManager& rSymbolSetManager)
95 {
96     m_aSymbols      = rSymbolSetManager.m_aSymbols;
97     m_bModified     = true;
98 }
99 
100 
~SmSymbolManager()101 SmSymbolManager::~SmSymbolManager()
102 {
103 }
104 
105 
operator =(const SmSymbolManager & rSymbolSetManager)106 SmSymbolManager& SmSymbolManager::operator = (const SmSymbolManager& rSymbolSetManager)
107 {
108     m_aSymbols      = rSymbolSetManager.m_aSymbols;
109     m_bModified     = true;
110     return *this;
111 }
112 
113 
GetSymbolByName(const OUString & rSymbolName)114 SmSym *SmSymbolManager::GetSymbolByName(const OUString& rSymbolName)
115 {
116     SmSym *pRes = nullptr;
117     SymbolMap_t::iterator aIt( m_aSymbols.find( rSymbolName ) );
118     if (aIt != m_aSymbols.end())
119         pRes = &aIt->second;
120     return pRes;
121 }
122 
123 
GetSymbols() const124 SymbolPtrVec_t SmSymbolManager::GetSymbols() const
125 {
126     SymbolPtrVec_t aRes;
127     for (const auto& rEntry : m_aSymbols)
128         aRes.push_back( &rEntry.second );
129 //    OSL_ENSURE( sSymbols.size() == m_aSymbols.size(), "number of symbols mismatch " );
130     return aRes;
131 }
132 
133 
AddOrReplaceSymbol(const SmSym & rSymbol,bool bForceChange)134 bool SmSymbolManager::AddOrReplaceSymbol( const SmSym &rSymbol, bool bForceChange )
135 {
136     bool bAdded = false;
137 
138     const OUString& aSymbolName( rSymbol.GetName() );
139     if (!aSymbolName.isEmpty() && !rSymbol.GetSymbolSetName().isEmpty())
140     {
141         const SmSym *pFound = GetSymbolByName( aSymbolName );
142         const bool bSymbolConflict = pFound && !pFound->IsEqualInUI( rSymbol );
143 
144         // avoid having the same symbol name twice but with different symbols in use
145         if (!pFound || bForceChange)
146         {
147             m_aSymbols[ aSymbolName ] = rSymbol;
148             bAdded = true;
149         }
150         else if (bSymbolConflict)
151         {
152             // TODO: to solve this a document owned symbol manager would be required ...
153                 SAL_WARN("starmath", "symbol conflict, different symbol with same name found!");
154             // symbols in all formulas. A copy of the global one would be needed here
155             // and then the new symbol has to be forcefully applied. This would keep
156             // the current formula intact but will leave the set of symbols in the
157             // global symbol manager somewhat to chance.
158         }
159 
160         OSL_ENSURE( bAdded, "failed to add symbol" );
161         if (bAdded)
162             m_bModified = true;
163         OSL_ENSURE( bAdded || (pFound && !bSymbolConflict), "AddOrReplaceSymbol: unresolved symbol conflict" );
164     }
165 
166     return bAdded;
167 }
168 
169 
RemoveSymbol(const OUString & rSymbolName)170 void SmSymbolManager::RemoveSymbol( const OUString & rSymbolName )
171 {
172     if (!rSymbolName.isEmpty())
173     {
174         size_t nOldSize = m_aSymbols.size();
175         m_aSymbols.erase( rSymbolName );
176         m_bModified = nOldSize != m_aSymbols.size();
177     }
178 }
179 
180 
GetSymbolSetNames() const181 std::set< OUString > SmSymbolManager::GetSymbolSetNames() const
182 {
183     std::set< OUString >  aRes;
184     for (const auto& rEntry : m_aSymbols)
185         aRes.insert( rEntry.second.GetSymbolSetName() );
186     return aRes;
187 }
188 
189 
GetSymbolSet(const OUString & rSymbolSetName)190 SymbolPtrVec_t SmSymbolManager::GetSymbolSet( const OUString& rSymbolSetName )
191 {
192     SymbolPtrVec_t aRes;
193     if (!rSymbolSetName.isEmpty())
194     {
195         for (const auto& rEntry : m_aSymbols)
196         {
197             if (rEntry.second.GetSymbolSetName() == rSymbolSetName)
198                 aRes.push_back( &rEntry.second );
199         }
200     }
201     return aRes;
202 }
203 
204 
Load()205 void SmSymbolManager::Load()
206 {
207     std::vector< SmSym > aSymbols;
208     SmMathConfig &rCfg = *SM_MOD()->GetConfig();
209     rCfg.GetSymbols( aSymbols );
210     size_t nSymbolCount = aSymbols.size();
211 
212     m_aSymbols.clear();
213     for (size_t i = 0;  i < nSymbolCount;  ++i)
214     {
215         const SmSym &rSym = aSymbols[i];
216         OSL_ENSURE( !rSym.GetName().isEmpty(), "symbol without name!" );
217         if (!rSym.GetName().isEmpty())
218             AddOrReplaceSymbol( rSym );
219     }
220     m_bModified = true;
221 
222     if (0 == nSymbolCount)
223     {
224         SAL_WARN("starmath", "no symbol set found");
225         m_bModified = false;
226     }
227 
228     // now add a %i... symbol to the 'iGreek' set for every symbol found in the 'Greek' set.
229     const OUString aGreekSymbolSetName(SmLocalizedSymbolData::GetUiSymbolSetName("Greek"));
230     const SymbolPtrVec_t    aGreekSymbols( GetSymbolSet( aGreekSymbolSetName ) );
231     OUString aSymbolSetName = "i" + aGreekSymbolSetName;
232     size_t nSymbols = aGreekSymbols.size();
233     for (size_t i = 0;  i < nSymbols;  ++i)
234     {
235         // make the new symbol a copy but with ITALIC_NORMAL, and add it to iGreek
236         const SmSym &rSym = *aGreekSymbols[i];
237         vcl::Font aFont( rSym.GetFace() );
238         OSL_ENSURE( aFont.GetItalic() == ITALIC_NONE, "expected Font with ITALIC_NONE, failed." );
239         aFont.SetItalic( ITALIC_NORMAL );
240         OUString aSymbolName = "i" + rSym.GetName();
241         SmSym aSymbol( aSymbolName, aFont, rSym.GetCharacter(),
242                 aSymbolSetName, true /*bIsPredefined*/ );
243 
244         AddOrReplaceSymbol( aSymbol );
245     }
246 }
247 
Save()248 void SmSymbolManager::Save()
249 {
250     if (!m_bModified)
251         return;
252 
253     SmMathConfig &rCfg = *SM_MOD()->GetConfig();
254 
255     // prepare to skip symbols from iGreek on saving
256     OUString aSymbolSetName = "i" +
257         SmLocalizedSymbolData::GetUiSymbolSetName("Greek");
258 
259     SymbolPtrVec_t aTmp( GetSymbols() );
260     std::vector< SmSym > aSymbols;
261     for (const SmSym* i : aTmp)
262     {
263         // skip symbols from iGreek set since those symbols always get added
264         // by computational means in SmSymbolManager::Load
265         if (i->GetSymbolSetName() != aSymbolSetName)
266             aSymbols.push_back( *i );
267     }
268     rCfg.SetSymbols( aSymbols );
269 
270     m_bModified = false;
271 }
272 
273 
274 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
275