18 |
ilm |
1 |
/*
|
|
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
|
|
3 |
*
|
182 |
ilm |
4 |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
|
18 |
ilm |
5 |
*
|
|
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
|
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
|
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
|
|
9 |
* language governing permissions and limitations under the License.
|
|
|
10 |
*
|
|
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
|
|
12 |
*/
|
|
|
13 |
|
|
|
14 |
package org.openconcerto.erp.core.sales.pos.ui;
|
|
|
15 |
|
|
|
16 |
import org.openconcerto.erp.core.sales.pos.model.Article;
|
|
|
17 |
import org.openconcerto.erp.core.sales.pos.model.Categorie;
|
|
|
18 |
import org.openconcerto.ui.touch.ScrollableList;
|
182 |
ilm |
19 |
import org.openconcerto.utils.QuickOrderedMap;
|
|
|
20 |
import org.openconcerto.utils.StringUtils;
|
18 |
ilm |
21 |
|
|
|
22 |
import java.awt.Color;
|
142 |
ilm |
23 |
import java.awt.FlowLayout;
|
18 |
ilm |
24 |
import java.awt.Font;
|
|
|
25 |
import java.awt.Graphics;
|
|
|
26 |
import java.awt.Graphics2D;
|
|
|
27 |
import java.awt.GridBagConstraints;
|
|
|
28 |
import java.awt.GridBagLayout;
|
|
|
29 |
import java.awt.RenderingHints;
|
142 |
ilm |
30 |
import java.awt.event.ActionEvent;
|
|
|
31 |
import java.awt.event.ActionListener;
|
18 |
ilm |
32 |
import java.awt.event.MouseAdapter;
|
|
|
33 |
import java.awt.event.MouseEvent;
|
83 |
ilm |
34 |
import java.math.BigDecimal;
|
67 |
ilm |
35 |
import java.math.RoundingMode;
|
18 |
ilm |
36 |
|
|
|
37 |
import javax.swing.JPanel;
|
|
|
38 |
import javax.swing.event.ListSelectionEvent;
|
|
|
39 |
import javax.swing.event.ListSelectionListener;
|
|
|
40 |
|
174 |
ilm |
41 |
public class ArticleSelector extends JPanel implements CaisseListener {
|
18 |
ilm |
42 |
private ArticleModel model;
|
|
|
43 |
private ScrollableList list;
|
|
|
44 |
private StatusBar comp;
|
|
|
45 |
private CaisseControler controller;
|
|
|
46 |
|
|
|
47 |
ArticleSelector(final CaisseControler controller) {
|
|
|
48 |
this.controller = controller;
|
|
|
49 |
this.controller.addCaisseListener(this);
|
|
|
50 |
|
|
|
51 |
this.setLayout(new GridBagLayout());
|
|
|
52 |
GridBagConstraints c = new GridBagConstraints();
|
|
|
53 |
c.gridx = 0;
|
|
|
54 |
c.gridy = 0;
|
|
|
55 |
c.weightx = 1;
|
|
|
56 |
c.fill = GridBagConstraints.BOTH;
|
174 |
ilm |
57 |
this.comp = new StatusBar();
|
|
|
58 |
this.comp.setLayout(new FlowLayout(FlowLayout.LEFT));
|
|
|
59 |
this.comp.setTitle("Articles");
|
142 |
ilm |
60 |
final POSButton bSwitch = new POSButton("-");
|
|
|
61 |
bSwitch.setForeground(Color.WHITE);
|
|
|
62 |
bSwitch.setBackground(CaissePanel.DARK_BLUE);
|
174 |
ilm |
63 |
this.comp.add(bSwitch);
|
|
|
64 |
this.add(this.comp, c);
|
18 |
ilm |
65 |
|
|
|
66 |
c.weighty = 1;
|
|
|
67 |
c.gridy++;
|
174 |
ilm |
68 |
this.model = new ArticleModel();
|
|
|
69 |
this.model.setCategorie(null);
|
18 |
ilm |
70 |
|
174 |
ilm |
71 |
final Font f;
|
|
|
72 |
if (controller.getPOSConf().getScreenWidth() < 1280) {
|
|
|
73 |
f = new Font("Arial", Font.PLAIN, 18);
|
|
|
74 |
} else {
|
|
|
75 |
f = new Font("Arial", Font.PLAIN, 21);
|
|
|
76 |
}
|
|
|
77 |
|
|
|
78 |
this.list = new ScrollableList(this.model) {
|
83 |
ilm |
79 |
int maxStringWidth = 0;
|
|
|
80 |
|
18 |
ilm |
81 |
@Override
|
|
|
82 |
public void paint(Graphics g) {
|
83 |
ilm |
83 |
|
174 |
ilm |
84 |
if (this.maxStringWidth == 0) {
|
83 |
ilm |
85 |
g.setFont(f);
|
|
|
86 |
int w = this.getWidth();
|
|
|
87 |
int priceWidth = (int) g.getFontMetrics(f).getStringBounds(getPrice(BigDecimal.valueOf(999)), g).getWidth();
|
|
|
88 |
int maxW = w - priceWidth - getLeftMargin();
|
|
|
89 |
String str = "a";
|
|
|
90 |
int strW;
|
|
|
91 |
do {
|
|
|
92 |
strW = (int) g.getFontMetrics(f).getStringBounds(str, g).getWidth();
|
|
|
93 |
str += "a";
|
|
|
94 |
} while (strW < maxW);
|
|
|
95 |
|
174 |
ilm |
96 |
this.maxStringWidth = Math.max(1, str.length() - 1);
|
83 |
ilm |
97 |
|
|
|
98 |
}
|
18 |
ilm |
99 |
super.paint(g);
|
|
|
100 |
g.setColor(Color.GRAY);
|
|
|
101 |
g.drawLine(0, 0, 0, this.getHeight());
|
|
|
102 |
}
|
|
|
103 |
|
|
|
104 |
@Override
|
|
|
105 |
public void paintCell(Graphics g, Object object, int index, boolean isSelected, int posY) {
|
|
|
106 |
Article article = (Article) object;
|
182 |
ilm |
107 |
paintArticle(f, g, article, isSelected, posY, this.getWidth(), this.getCellHeight(), this.maxStringWidth, getLeftMargin(), false);
|
18 |
ilm |
108 |
|
|
|
109 |
}
|
|
|
110 |
};
|
|
|
111 |
|
174 |
ilm |
112 |
this.list.setFixedCellHeight(64);
|
|
|
113 |
this.list.setOpaque(true);
|
|
|
114 |
this.add(this.list, c);
|
|
|
115 |
this.list.addListSelectionListener(new ListSelectionListener() {
|
18 |
ilm |
116 |
|
|
|
117 |
@Override
|
174 |
ilm |
118 |
public void valueChanged(ListSelectionEvent e) {
|
|
|
119 |
Object sel = ArticleSelector.this.list.getSelectedValue();
|
|
|
120 |
if (sel != null && !e.getValueIsAdjusting()) {
|
|
|
121 |
Article article = (Article) sel;
|
|
|
122 |
controller.setArticleSelected(article);
|
|
|
123 |
}
|
|
|
124 |
|
|
|
125 |
}
|
|
|
126 |
});
|
|
|
127 |
|
|
|
128 |
this.list.addMouseListener(new MouseAdapter() {
|
|
|
129 |
@Override
|
18 |
ilm |
130 |
public void mouseClicked(MouseEvent e) {
|
174 |
ilm |
131 |
|
|
|
132 |
Object sel = ArticleSelector.this.list.getSelectedValue();
|
|
|
133 |
if (sel != null) {
|
|
|
134 |
int nb = e.getClickCount();
|
|
|
135 |
if (nb == 1) {
|
18 |
ilm |
136 |
Article article = (Article) sel;
|
174 |
ilm |
137 |
controller.addArticle(article);
|
|
|
138 |
} else if (nb > 1) {
|
|
|
139 |
Article article = (Article) sel;
|
18 |
ilm |
140 |
controller.incrementArticle(article);
|
|
|
141 |
controller.setArticleSelected(article);
|
|
|
142 |
}
|
|
|
143 |
}
|
|
|
144 |
}
|
|
|
145 |
});
|
142 |
ilm |
146 |
|
|
|
147 |
bSwitch.addActionListener(new ActionListener() {
|
|
|
148 |
|
83 |
ilm |
149 |
@Override
|
142 |
ilm |
150 |
public void actionPerformed(ActionEvent e) {
|
83 |
ilm |
151 |
controller.switchListMode();
|
142 |
ilm |
152 |
|
83 |
ilm |
153 |
}
|
|
|
154 |
});
|
174 |
ilm |
155 |
this.comp.addMouseListener(new MouseAdapter() {
|
149 |
ilm |
156 |
@Override
|
|
|
157 |
public void mousePressed(MouseEvent e) {
|
174 |
ilm |
158 |
ArticleSelector.this.list.scrollToOffset(0);
|
149 |
ilm |
159 |
}
|
|
|
160 |
});
|
18 |
ilm |
161 |
}
|
|
|
162 |
|
|
|
163 |
public ArticleModel getModel() {
|
|
|
164 |
return this.model;
|
|
|
165 |
}
|
|
|
166 |
|
|
|
167 |
@Override
|
|
|
168 |
public void caisseStateChanged() {
|
|
|
169 |
|
174 |
ilm |
170 |
final Article articleSelected = this.controller.getArticleSelected();
|
18 |
ilm |
171 |
if (articleSelected == null) {
|
174 |
ilm |
172 |
this.list.clearSelection();
|
18 |
ilm |
173 |
return;
|
|
|
174 |
}
|
|
|
175 |
|
|
|
176 |
Object selectedValue = null;
|
|
|
177 |
try {
|
174 |
ilm |
178 |
selectedValue = this.list.getSelectedValue();
|
18 |
ilm |
179 |
} catch (Exception e) {
|
|
|
180 |
e.printStackTrace();
|
|
|
181 |
}
|
149 |
ilm |
182 |
if (!articleSelected.equals(selectedValue)) {
|
18 |
ilm |
183 |
Categorie c = articleSelected.getCategorie();
|
174 |
ilm |
184 |
this.model.setCategorie(c);
|
|
|
185 |
this.list.setSelectedValue(articleSelected, true);
|
18 |
ilm |
186 |
}
|
|
|
187 |
|
|
|
188 |
}
|
|
|
189 |
|
182 |
ilm |
190 |
public static void paintArticle(final Font f, Graphics g, Article article, boolean isSelected, int posY, int cellWidth, int cellHeight, int maxWidth, int leftMargin, boolean showCode) {
|
83 |
ilm |
191 |
|
|
|
192 |
g.setFont(f);
|
|
|
193 |
|
|
|
194 |
if (isSelected) {
|
|
|
195 |
g.setColor(new Color(232, 242, 254));
|
|
|
196 |
} else {
|
|
|
197 |
g.setColor(Color.WHITE);
|
|
|
198 |
}
|
|
|
199 |
g.fillRect(0, posY, cellWidth, cellHeight);
|
|
|
200 |
|
|
|
201 |
//
|
|
|
202 |
g.setColor(Color.GRAY);
|
|
|
203 |
g.drawLine(0, posY + cellHeight - 1, cellWidth, posY + cellHeight - 1);
|
|
|
204 |
|
|
|
205 |
if (isSelected) {
|
|
|
206 |
g.setColor(Color.BLACK);
|
|
|
207 |
} else {
|
|
|
208 |
g.setColor(Color.GRAY);
|
|
|
209 |
}
|
|
|
210 |
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
211 |
|
|
|
212 |
String label = article.getName();
|
182 |
ilm |
213 |
if (showCode && article.getCode() != null && !article.getCode().isEmpty()) {
|
|
|
214 |
label += " [" + article.getCode() + "]";
|
|
|
215 |
}
|
|
|
216 |
StringBuilder declinaisons = null;
|
|
|
217 |
QuickOrderedMap<String, String> decls = article.getDeclinaisons();
|
|
|
218 |
if (decls != null && !decls.isEmpty()) {
|
|
|
219 |
declinaisons = new StringBuilder();
|
|
|
220 |
for (int i = 0; i < decls.size(); i++) {
|
|
|
221 |
declinaisons.append(StringUtils.firstUpThenLow(decls.getKey(i)));
|
|
|
222 |
declinaisons.append(": ");
|
|
|
223 |
declinaisons.append(decls.getValue(i));
|
|
|
224 |
declinaisons.append(" ");
|
|
|
225 |
}
|
|
|
226 |
}
|
83 |
ilm |
227 |
|
|
|
228 |
if (label.length() > maxWidth * 2) {
|
|
|
229 |
label = label.substring(0, maxWidth * 2) + "...";
|
|
|
230 |
}
|
|
|
231 |
String label2 = null;
|
|
|
232 |
if (label.length() > maxWidth) {
|
|
|
233 |
String t = label.substring(0, maxWidth).trim();
|
|
|
234 |
int lastSpace = t.lastIndexOf(' ');
|
|
|
235 |
if (lastSpace <= 0) {
|
|
|
236 |
lastSpace = maxWidth;
|
|
|
237 |
}
|
|
|
238 |
label2 = label.substring(lastSpace).trim();
|
|
|
239 |
label = label.substring(0, lastSpace).trim();
|
|
|
240 |
if (label2.length() > maxWidth) {
|
|
|
241 |
label2 = label2.substring(0, maxWidth) + "...";
|
|
|
242 |
}
|
|
|
243 |
}
|
182 |
ilm |
244 |
if (declinaisons != null) {
|
|
|
245 |
String textDeclinaisons = declinaisons.toString();
|
|
|
246 |
if (label2 == null) {
|
|
|
247 |
label2 = textDeclinaisons;
|
|
|
248 |
} else {
|
|
|
249 |
if (label2.length() + textDeclinaisons.length() > maxWidth) {
|
|
|
250 |
label2 = label2.substring(0, maxWidth - textDeclinaisons.length()) + "...";
|
|
|
251 |
}
|
|
|
252 |
label2 = label2 + " " + textDeclinaisons;
|
|
|
253 |
}
|
83 |
ilm |
254 |
|
182 |
ilm |
255 |
}
|
|
|
256 |
|
|
|
257 |
final BigDecimal priceInCents = article.getPriceWithTax(BigDecimal.ONE);
|
83 |
ilm |
258 |
String euro = getPrice(priceInCents);
|
|
|
259 |
|
|
|
260 |
int wEuro = (int) g.getFontMetrics().getStringBounds(euro, g).getWidth();
|
|
|
261 |
if (label2 == null) {
|
|
|
262 |
g.drawString(label, leftMargin, posY + 39);
|
|
|
263 |
} else {
|
|
|
264 |
g.drawString(label, leftMargin, posY + 26);
|
|
|
265 |
g.drawString(label2, leftMargin, posY + 52);
|
|
|
266 |
}
|
174 |
ilm |
267 |
|
|
|
268 |
if (isSelected) {
|
|
|
269 |
g.setColor(new Color(232, 242, 254));
|
|
|
270 |
} else {
|
|
|
271 |
g.setColor(Color.WHITE);
|
|
|
272 |
}
|
|
|
273 |
g.fillRect(cellWidth - wEuro - 12, posY + 1, wEuro + 12, cellHeight - 2);
|
|
|
274 |
|
|
|
275 |
// Price
|
|
|
276 |
if (isSelected) {
|
|
|
277 |
g.setColor(Color.BLACK);
|
|
|
278 |
} else {
|
|
|
279 |
g.setColor(Color.GRAY);
|
|
|
280 |
}
|
|
|
281 |
if (article.getSalesUnit() != null) {
|
|
|
282 |
String unit = "/" + article.getSalesUnit();
|
|
|
283 |
int wUnit = (int) g.getFontMetrics().getStringBounds(unit, g).getWidth();
|
|
|
284 |
g.drawString(euro, cellWidth - 5 - wEuro, posY + 28);
|
|
|
285 |
g.drawString(unit, cellWidth - 5 - wUnit, posY + 50);
|
|
|
286 |
} else {
|
|
|
287 |
g.drawString(euro, cellWidth - 5 - wEuro, posY + 39);
|
|
|
288 |
}
|
|
|
289 |
|
83 |
ilm |
290 |
}
|
|
|
291 |
|
|
|
292 |
public int getLeftMargin() {
|
174 |
ilm |
293 |
if (this.controller.getPOSConf().getScreenWidth() < 1280) {
|
|
|
294 |
return 3;
|
|
|
295 |
}
|
83 |
ilm |
296 |
return 10;
|
|
|
297 |
}
|
|
|
298 |
|
|
|
299 |
public static String getPrice(final BigDecimal price) {
|
|
|
300 |
return TicketCellRenderer.centsToString(price.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue()) + "€";
|
|
|
301 |
}
|
|
|
302 |
|
18 |
ilm |
303 |
}
|