File indexing completed on 2024-05-19 16:08:03
0001 /* This file is part of the KDE project 0002 Copyright (C) 2006 Tomas Mecir <mecirt@gmail.com> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; only 0007 version 2 of the License. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 */ 0019 0020 0021 #include "SortManipulator.h" 0022 0023 #include "Map.h" 0024 #include "Sheet.h" 0025 #include "ValueCalc.h" 0026 #include "ValueConverter.h" 0027 0028 #include <KLocalizedString> 0029 0030 using namespace Calligra::Sheets; 0031 0032 SortManipulator::SortManipulator() 0033 : AbstractDFManipulator() 0034 , m_cellStorage(0) 0035 { 0036 m_changeformat = false; 0037 m_rows = true; 0038 m_skipfirst = false; 0039 m_usecustomlist = false; 0040 0041 setText(kundo2_i18n("Sort Data")); 0042 } 0043 0044 SortManipulator::~SortManipulator() 0045 { 0046 } 0047 0048 bool SortManipulator::process(Element* element) 0049 { 0050 // process one element - rectangular range 0051 0052 // here we perform the actual sorting, remember the new ordering and 0053 // let AbstractDFManipulator::process do the rest of the work 0054 // the new ordering is used in newValue and newFormat to return proper 0055 // values 0056 0057 // sort 0058 sort(element); 0059 0060 // set values 0061 return AbstractDFManipulator::process(element); 0062 } 0063 0064 bool SortManipulator::preProcessing() 0065 { 0066 // Only on sorting we need to temporarily store the old data. 0067 // On restoring (undo) we return immediately. 0068 if (m_reverse) 0069 return AbstractDFManipulator::preProcessing(); 0070 0071 m_cellStorage = new CellStorage(m_sheet->cellStorage()->subStorage(*this)); 0072 0073 Region::ConstIterator endOfList(cells().constEnd()); 0074 for (Region::ConstIterator it = cells().constBegin(); it != endOfList; ++it) { 0075 QRect range = (*it)->rect(); 0076 for (int col = range.left(); col <= range.right(); ++col) 0077 for (int row = range.top(); row <= range.bottom(); ++row) { 0078 Cell cell = Cell(m_sheet, col, row); 0079 m_styles.insert(cell, cell.style()); 0080 // encode the formula if there is one, so that cell references get updated correctly 0081 if (cell.isFormula()) m_formulas.insert(cell, cell.encodeFormula()); 0082 } 0083 } 0084 0085 // to start undo recording 0086 return AbstractDFManipulator::preProcessing(); 0087 } 0088 0089 bool SortManipulator::postProcessing() 0090 { 0091 delete m_cellStorage; 0092 m_cellStorage = 0; 0093 m_styles.clear(); 0094 m_formulas.clear(); 0095 0096 // to stop undo recording 0097 return AbstractDFManipulator::postProcessing(); 0098 } 0099 0100 void SortManipulator::addCriterion(int index, Qt::SortOrder order, Qt::CaseSensitivity caseSensitivity) 0101 { 0102 Criterion criterion; 0103 criterion.index = index; 0104 criterion.order = order; 0105 criterion.caseSensitivity = caseSensitivity; 0106 m_criteria.append(criterion); 0107 } 0108 0109 void SortManipulator::clearCriteria() 0110 { 0111 m_criteria.clear(); 0112 } 0113 0114 Value SortManipulator::newValue(Element *element, int col, int row, 0115 bool *parse, Format::Type *) 0116 { 0117 QRect range = element->rect(); 0118 int colidx = col - range.left(); 0119 int rowidx = row - range.top(); 0120 if (m_rows) // sort rows 0121 rowidx = sorted[rowidx]; 0122 else 0123 colidx = sorted[colidx]; 0124 rowidx += range.top(); 0125 colidx += range.left(); 0126 0127 // If the cell contained a formula, we need to decode it with the -new- coordinates, so that the references remain intact 0128 Cell orig_cell = Cell(m_sheet, colidx, rowidx); 0129 Cell new_cell = Cell(m_sheet, col, row); 0130 if (m_formulas.contains(orig_cell)) { 0131 *parse = true; 0132 return Value(new_cell.decodeFormula(m_formulas[orig_cell])); 0133 } 0134 0135 *parse = false; 0136 // have to return the stored value, to prevent the earlier calls from disrupting the latter ones 0137 return m_cellStorage->value(colidx, rowidx); 0138 } 0139 0140 Style SortManipulator::newFormat(Element *element, int col, int row) 0141 { 0142 QRect range = element->rect(); 0143 int colidx = col - range.left(); 0144 int rowidx = row - range.top(); 0145 if (m_changeformat) { 0146 if (m_rows) // sort rows 0147 rowidx = sorted[rowidx]; 0148 else 0149 colidx = sorted[colidx]; 0150 } 0151 0152 // have to return stored format, to avoid earlier calls disrupting latter ones 0153 return m_styles.value(Cell(m_sheet, colidx + range.left(), rowidx + range.top())); 0154 } 0155 0156 void SortManipulator::sort(Element *element) 0157 { 0158 // we'll use insert-sort to sort 0159 QRect range = element->rect(); 0160 int max = m_rows ? range.bottom() : range.right(); 0161 int min = m_rows ? range.top() : range.left(); 0162 int count = max - min + 1; 0163 // initially, all values are at their original positions 0164 sorted.clear(); 0165 for (int i = 0; i < count; ++i) sorted[i] = i; 0166 0167 // for each position, find the lowest value and move it there 0168 int start = m_skipfirst ? 1 : 0; 0169 for (int i = start; i < count - 1; ++i) { 0170 int lowest = i; 0171 for (int j = i + 1; j < count; ++j) 0172 if (shouldReorder(element, sorted[lowest], sorted[j])) 0173 lowest = j; 0174 // move lowest to start 0175 int tmp = sorted[i]; 0176 sorted[i] = sorted[lowest]; 0177 sorted[lowest] = tmp; 0178 } 0179 0180 // that's all - process will take care of the rest, together with our 0181 // newValue/newFormat 0182 } 0183 0184 bool SortManipulator::shouldReorder(Element *element, int first, int second) 0185 { 0186 // we use ValueCalc::natural* to compare 0187 // indexes are real indexes, we don't use the sorted array here 0188 0189 ValueCalc *calc = m_sheet->map()->calc(); 0190 ValueConverter *conv = m_sheet->map()->converter(); 0191 0192 QRect range = element->rect(); 0193 int firstrow = range.top(); 0194 int firstcol = range.left(); 0195 0196 for (int i = 0; i < m_criteria.count(); ++i) { 0197 int which = m_criteria[i].index; 0198 bool ascending = m_criteria[i].order == Qt::AscendingOrder; 0199 bool caseSensitive = m_criteria[i].caseSensitivity == Qt::CaseSensitive; 0200 0201 // figure out coordinates of the cells 0202 int row1 = firstrow + (m_rows ? first : which); 0203 int row2 = firstrow + (m_rows ? second : which); 0204 int col1 = firstcol + (m_rows ? which : first); 0205 int col2 = firstcol + (m_rows ? which : second); 0206 Value val1 = Cell(m_sheet, col1, row1).value(); 0207 Value val2 = Cell(m_sheet, col2, row2).value(); 0208 // empty values always go to the end, so if second value is empty and 0209 // first one is not, we don't need to reorder 0210 if (!val1.isEmpty() && val2.isEmpty()) 0211 return false; 0212 if (val1.isEmpty() && !val2.isEmpty()) 0213 return true; 0214 0215 // custom list ? 0216 if (m_usecustomlist) { 0217 QString s1 = conv->asString(val1).asString().toLower(); 0218 QString s2 = conv->asString(val2).asString().toLower(); 0219 int pos1 = -1, pos2 = -1; 0220 int pos = 0; 0221 // Try to locate our two strings in the list. If both are there, assume 0222 // ordering as specified by the list. 0223 for (QStringList::ConstIterator it = m_customlist.constBegin(); it != m_customlist.constEnd(); ++it) { 0224 if ((pos1 == -1) && ((*it).toLower() == s1)) 0225 pos1 = pos; 0226 if ((pos2 == -1) && ((*it).toLower() == s2)) 0227 pos2 = pos; 0228 pos++; 0229 } 0230 if ((pos1 >= 0) && (pos2 >= 0) && (pos1 != pos2)) 0231 // both are in the list, not the same 0232 return (pos1 > pos2); 0233 } 0234 0235 if (calc->naturalGreater(val1, val2, caseSensitive)) 0236 // first one greater - must reorder if ascending, don't reorder if not 0237 return ascending; 0238 if (calc->naturalLower(val1, val2, caseSensitive)) 0239 // first one lower - don't reorder if ascending, reorder if not 0240 return !ascending; 0241 // equal - don't know yet, continue 0242 } 0243 0244 // no difference found, they're the same - no need to reorder 0245 return false; 0246 }