001: /*
002: * Copyright (c) 2004-2006, Jean-François Brazeau. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * 2. Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: *
014: * 3. The name of the author may not be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
018: * IMPLIEDWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
019: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
020: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
021: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
022: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
023: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
024: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
025: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
026: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028: package jfb.tools.activitymgr.ui;
029:
030: import java.math.BigDecimal;
031: import java.util.ArrayList;
032: import java.util.Iterator;
033:
034: import jfb.tools.activitymgr.core.DbException;
035: import jfb.tools.activitymgr.core.ModelMgr;
036: import jfb.tools.activitymgr.core.beans.Duration;
037: import jfb.tools.activitymgr.core.util.StringFormatException;
038: import jfb.tools.activitymgr.core.util.StringHelper;
039: import jfb.tools.activitymgr.ui.DatabaseUI.IDbStatusListener;
040: import jfb.tools.activitymgr.ui.images.ImagesDatas;
041: import jfb.tools.activitymgr.ui.util.AbstractTableMgr;
042: import jfb.tools.activitymgr.ui.util.SWTHelper;
043: import jfb.tools.activitymgr.ui.util.SafeRunner;
044: import jfb.tools.activitymgr.ui.util.TableOrTreeColumnsMgr;
045: import jfb.tools.activitymgr.ui.util.UITechException;
046:
047: import org.apache.log4j.Logger;
048: import org.eclipse.jface.dialogs.Dialog;
049: import org.eclipse.jface.dialogs.IInputValidator;
050: import org.eclipse.jface.dialogs.InputDialog;
051: import org.eclipse.jface.viewers.CellEditor;
052: import org.eclipse.jface.viewers.CheckboxCellEditor;
053: import org.eclipse.jface.viewers.IBaseLabelProvider;
054: import org.eclipse.jface.viewers.ICellModifier;
055: import org.eclipse.jface.viewers.LabelProviderChangedEvent;
056: import org.eclipse.jface.viewers.StructuredSelection;
057: import org.eclipse.jface.viewers.TableViewer;
058: import org.eclipse.jface.viewers.TextCellEditor;
059: import org.eclipse.swt.SWT;
060: import org.eclipse.swt.events.MenuEvent;
061: import org.eclipse.swt.events.MenuListener;
062: import org.eclipse.swt.events.SelectionEvent;
063: import org.eclipse.swt.events.SelectionListener;
064: import org.eclipse.swt.graphics.Image;
065: import org.eclipse.swt.layout.GridData;
066: import org.eclipse.swt.layout.GridLayout;
067: import org.eclipse.swt.widgets.Composite;
068: import org.eclipse.swt.widgets.Menu;
069: import org.eclipse.swt.widgets.MenuItem;
070: import org.eclipse.swt.widgets.TabItem;
071: import org.eclipse.swt.widgets.Table;
072: import org.eclipse.swt.widgets.TableItem;
073:
074: /**
075: * IHM de gestion des durées.
076: */
077: public class DurationsUI extends AbstractTableMgr implements
078: IDbStatusListener, ICellModifier, SelectionListener,
079: MenuListener {
080:
081: /** Logger */
082: private static Logger log = Logger.getLogger(DurationsUI.class);
083:
084: /** Constantes associées aux colonnes */
085: public static final int IS_ACTIVE_COLUMN_IDX = 0;
086: public static final int DURATION_COLUMN_IDX = 1;
087: private static TableOrTreeColumnsMgr tableColsMgr;
088:
089: /**
090: * Interface utilisée pour permettre l'écoute de la suppression ou de
091: * l'ajout de durées.
092: */
093: public static interface IDurationListener {
094:
095: /**
096: * Indique qu'une durée a été ajoutée au référentiel.
097: * @param duration la durée ajoutée.
098: */
099: public void durationAdded(Duration duration);
100:
101: /**
102: * Indique qu'une durée a été supprimée du référentiel.
103: * @param duration la durée supprimée.
104: */
105: public void durationRemoved(Duration duration);
106:
107: /**
108: * Indique qu'une durée a été modifiée dans le référentiel.
109: * @param oldDuration la durée modifiée.
110: * @param newDuration la nouvelle durée.
111: */
112: public void durationUpdated(Duration oldDuration,
113: Duration newDuration);
114:
115: /**
116: * Indique que l'état d'activation d'une durée a été désactivée dans le référentiel.
117: * @param duration la durée modifiée.
118: */
119: public void durationActivationStatusChanged(Duration duration);
120: }
121:
122: /** Viewer */
123: private TableViewer tableViewer;
124:
125: /** Items de menu */
126: private MenuItem newItem;
127: private MenuItem removeItem;
128: private MenuItem exportItem;
129:
130: /** Composant parent */
131: private Composite parent;
132:
133: /** Listeners */
134: private ArrayList listeners = new ArrayList();
135:
136: /** Icone utilisé pour marquer les durées actifs */
137: private Image checkedIcon;
138:
139: /** Icone utilisé pour les durées non actifs */
140: private Image uncheckedIcon;
141:
142: /**
143: * Constructeur permettant de placer l'IHM dans un onglet.
144: * @param tabItem item parent.
145: */
146: public DurationsUI(TabItem tabItem) {
147: this (tabItem.getParent());
148: tabItem.setControl(parent);
149: }
150:
151: /**
152: * Constructeur par défaut.
153: * @param parentComposite composant parent.
154: */
155: public DurationsUI(Composite parentComposite) {
156: // Création du composite parent
157: parent = new Composite(parentComposite, SWT.NONE);
158: parent.setLayout(new GridLayout(1, false));
159:
160: // Table
161: final Table table = new Table(parent, SWT.MULTI
162: | SWT.FULL_SELECTION | SWT.BORDER | SWT.HIDE_SELECTION);
163: GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
164: gridData.heightHint = 75;
165: table.setLayoutData(gridData);
166: table.setLinesVisible(true);
167: table.setHeaderVisible(true);
168: table.setEnabled(true);
169: table
170: .setToolTipText("Use the first column to activate or disactivate the durations\n"
171: + "(disactivated durations are not deleted from the database but are hidden\n"
172: + "in the contributions tab)");
173:
174: // Création du viewer
175: tableViewer = new TableViewer(table);
176: tableViewer.setCellModifier(this );
177: tableViewer.setContentProvider(this );
178: tableViewer.setLabelProvider(this );
179:
180: // Configuration des colonnes
181: tableColsMgr = new TableOrTreeColumnsMgr();
182: tableColsMgr.addColumn("IS_ACTIVE", "!", 20, SWT.CENTER);
183: tableColsMgr.addColumn("DURATION", "Duration", 100, SWT.LEFT);
184: tableColsMgr.configureTable(tableViewer);
185:
186: // Configuration des éditeurs de cellules
187: CellEditor[] editors = new CellEditor[9];
188: editors[IS_ACTIVE_COLUMN_IDX] = new CheckboxCellEditor(table);
189: editors[DURATION_COLUMN_IDX] = new TextCellEditor(table);
190: tableViewer.setCellEditors(editors);
191:
192: // Configuration du menu popup
193: final Menu menu = new Menu(table);
194: menu.addMenuListener(this );
195: newItem = new MenuItem(menu, SWT.CASCADE);
196: newItem.setText("New duration");
197: newItem.addSelectionListener(this );
198: removeItem = new MenuItem(menu, SWT.CASCADE);
199: removeItem.setText("Remove");
200: removeItem.addSelectionListener(this );
201: exportItem = new MenuItem(menu, SWT.CASCADE);
202: exportItem.setText("Export");
203: exportItem.addSelectionListener(this );
204: table.setMenu(menu);
205:
206: // Chargement des icones
207: checkedIcon = new Image(parentComposite.getDisplay(),
208: ImagesDatas.CHECKED_ICON);
209: uncheckedIcon = new Image(parentComposite.getDisplay(),
210: ImagesDatas.UNCHECKED_ICON);
211: }
212:
213: /* (non-Javadoc)
214: * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
215: */
216: public Object[] getElements(Object inputElement) {
217: // Chargement des données
218: SafeRunner safeRunner = new SafeRunner() {
219: public Object runUnsafe() throws Exception {
220: return ModelMgr.getDurations();
221: }
222: };
223: // Exécution
224: return (Object[]) safeRunner.run(parent.getShell());
225: }
226:
227: /* (non-Javadoc)
228: * @see org.eclipse.jface.viewers.ICellModifier#canModify(java.lang.Object, java.lang.String)
229: */
230: public boolean canModify(Object element, String property) {
231: log.debug("ICellModifier.canModify(" + element + ", "
232: + property + ")");
233: return true;
234: }
235:
236: /* (non-Javadoc)
237: * @see org.eclipse.jface.viewers.ICellModifier#getValue(java.lang.Object, java.lang.String)
238: */
239: public Object getValue(final Object element, final String property) {
240: log.debug("ICellModifier.getValue(" + element + ", " + property
241: + ")");
242: SafeRunner safeRunner = new SafeRunner() {
243: public Object runUnsafe() throws Exception {
244: Duration duration = (Duration) element;
245: int columnIndex = tableColsMgr.getColumnIndex(property);
246: Object value = null;
247: switch (columnIndex) {
248: case IS_ACTIVE_COLUMN_IDX:
249: value = duration.getIsActive() ? Boolean.TRUE
250: : Boolean.FALSE;
251: break;
252: case (DURATION_COLUMN_IDX):
253: value = String.valueOf(new BigDecimal(duration
254: .getId()).movePointLeft(2));
255: break;
256: default:
257: throw new Error("Colonne inconnue");
258: }
259: return value;
260: }
261: };
262: // Exécution
263: return safeRunner.run(parent.getShell(), "");
264: }
265:
266: /* (non-Javadoc)
267: * @see org.eclipse.jface.viewers.ICellModifier#modify(java.lang.Object, java.lang.String, java.lang.Object)
268: */
269: public void modify(final Object element, String property,
270: final Object value) {
271: log.debug("ICellModifier.modify(" + element + ", " + property
272: + ", " + value + ")");
273: TableItem item = (TableItem) element;
274: final Duration duration = (Duration) item.getData();
275: final IBaseLabelProvider labelProvider = this ;
276: final int columnIndex = tableColsMgr.getColumnIndex(property);
277: SafeRunner safeRunner = new SafeRunner() {
278: public Object runUnsafe() throws Exception {
279: // Création d'un clone dans le cas ou il s'agît
280: // d'une modification de la valeur de la durée
281: Duration oldDuration = new Duration();
282: oldDuration.setId(duration.getId());
283: oldDuration.setIsActive(duration.getIsActive());
284: // Booléens indiquant quelles notifications doivent être émises
285: boolean mustNotifyUpdateEvent = false;
286: boolean mustNotifyActivationStatusChangeEvent = false;
287: switch (columnIndex) {
288: case (IS_ACTIVE_COLUMN_IDX):
289: Boolean isActive = (Boolean) value;
290: duration.setIsActive(isActive.booleanValue());
291: ModelMgr.updateDuration(duration);
292: mustNotifyActivationStatusChangeEvent = true;
293: break;
294: case (DURATION_COLUMN_IDX):
295: // Mise à jour en base
296: Duration newDuration = new Duration();
297: newDuration.setId(StringHelper
298: .entryToHundredth((String) value));
299: newDuration.setIsActive(duration.getIsActive());
300: newDuration = ModelMgr.updateDuration(oldDuration,
301: newDuration);
302: // Mise à jour dans le modèle
303: duration.setId(newDuration.getId());
304: mustNotifyUpdateEvent = true;
305: // Tri des données
306: sortDurations();
307: break;
308: default:
309: throw new UITechException("Colonne inconnue");
310: }
311: // Notification des listeners
312: notifyLabelProviderListener(new LabelProviderChangedEvent(
313: labelProvider, duration));
314: if (mustNotifyUpdateEvent)
315: notifyDurationUpdated(oldDuration, duration);
316: if (mustNotifyActivationStatusChangeEvent)
317: notifyDurationActivationStatusChanged(duration);
318: return null;
319: }
320: };
321: // Exécution
322: safeRunner.run(parent.getShell());
323: }
324:
325: /* (non-Javadoc)
326: * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
327: */
328: public String getColumnText(final Object element,
329: final int columnIndex) {
330: log.debug("ITableLabelProvider.getColumnText(" + element + ", "
331: + columnIndex + ")");
332: SafeRunner safeRunner = new SafeRunner() {
333: public Object runUnsafe() throws Exception {
334: Duration duration = (Duration) element;
335: String text = null;
336: switch (columnIndex) {
337: case IS_ACTIVE_COLUMN_IDX:
338: text = ""; // colonne indiquant si la durée est active ou non
339: break;
340: case (DURATION_COLUMN_IDX):
341: text = String.valueOf(new BigDecimal(duration
342: .getId()).movePointLeft(2));
343: break;
344: default:
345: throw new Error("Colonne inconnue");
346: }
347: return text;
348: }
349: };
350: // Exécution
351: return (String) safeRunner.run(parent.getShell(), "");
352: }
353:
354: /* (non-Javadoc)
355: * @see jfb.tools.activitymgr.ui.util.AbstractTableMgr#getColumnImage(java.lang.Object, int)
356: */
357: public Image getColumnImage(final Object element,
358: final int columnIndex) {
359: log.debug("ITableLabelProvider.getColumnImage(" + element
360: + ", " + columnIndex + ")");
361: SafeRunner safeRunner = new SafeRunner() {
362: public Object runUnsafe() throws Exception {
363: Duration duration = (Duration) element;
364: Image image = null;
365: switch (columnIndex) {
366: case (IS_ACTIVE_COLUMN_IDX):
367: image = duration.getIsActive() ? checkedIcon
368: : uncheckedIcon;
369: break;
370: case (DURATION_COLUMN_IDX):
371: image = null;
372: break;
373: default:
374: throw new Error("Colonne inconnue");
375: }
376: return image;
377: }
378: };
379: // Exécution
380: return (Image) safeRunner.run(parent.getShell());
381: }
382:
383: /* (non-Javadoc)
384: * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
385: */
386: public void widgetSelected(final SelectionEvent e) {
387: log.debug("SelectionListener.widgetSelected(" + e + ")");
388: final Object source = e.getSource();
389: SafeRunner safeRunner = new SafeRunner() {
390: public Object runUnsafe() throws Exception {
391: //TableItem[] selection = tableViewer.getTable().getSelection();
392: // Cas d'une création
393: if (newItem.equals(source)) {
394: InputDialog dialog = new InputDialog(parent
395: .getShell(), "Input dialog",
396: "Please enter a new duration", "0",
397: new IInputValidator() {
398: public String isValid(String newText) {
399: String errorMsg = null;
400: Duration duration = new Duration();
401: try {
402: // Parsing de la saisie et contrôle du format
403: duration
404: .setId(StringHelper
405: .entryToHundredth(newText));
406: // Vérification de la non existence de la durée
407: if (ModelMgr
408: .durationExists(duration))
409: errorMsg = "This duration exists";
410: } catch (StringFormatException e) {
411: errorMsg = e.getMessage();
412: } catch (DbException e) {
413: errorMsg = "Database connection failure while checking duration existence";
414: }
415: // Retour du résultat
416: return errorMsg;
417: }
418: });
419: // Ouverture du dialogue
420: if (dialog.open() == Dialog.OK) {
421: Duration newDuration = new Duration();
422: newDuration.setId(StringHelper
423: .entryToHundredth(dialog.getValue()));
424: ModelMgr.createDuration(newDuration);
425: newLine(newDuration);
426: // Notification des listeners
427: notifyDurationAdded(newDuration);
428: // Tri des données
429: sortDurations();
430: }
431: }
432: // Cas d'une suppression
433: else if (removeItem.equals(source)) {
434: TableItem[] items = tableViewer.getTable()
435: .getSelection();
436: for (int i = 0; i < items.length; i++) {
437: TableItem item = items[i];
438: Duration duration = (Duration) item.getData();
439: ModelMgr.removeDuration(duration);
440: item.dispose();
441: // Notification des listeners
442: notifyDurationRemoved(duration);
443: }
444: }
445: // Cas d'une demande d'export
446: else if (exportItem.equals(source)) {
447: SWTHelper.exportToWorkBook(tableViewer.getTable());
448: }
449: return null;
450: }
451: };
452: // Exécution
453: safeRunner.run(parent.getShell());
454: }
455:
456: /**
457: * Ajoute une ligne dans le tableau.
458: * @param duration la durée associée à la nouvelle ligne.
459: */
460: private void newLine(Duration duration) {
461: // Ajout dans l'arbre
462: tableViewer.add(duration);
463: tableViewer.setSelection(new StructuredSelection(duration),
464: true);
465: }
466:
467: /* (non-Javadoc)
468: * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
469: */
470: public void widgetDefaultSelected(SelectionEvent e) {
471: widgetSelected(e);
472: }
473:
474: /* (non-Javadoc)
475: * @see org.eclipse.swt.events.MenuListener#menuShown(org.eclipse.swt.events.MenuEvent)
476: */
477: public void menuShown(MenuEvent e) {
478: log.debug("menuShown(" + e + ")");
479: TableItem[] selection = tableViewer.getTable().getSelection();
480: boolean emptySelection = selection.length == 0;
481: boolean singleSelection = selection.length == 1;
482: newItem.setEnabled(emptySelection || singleSelection);
483: removeItem.setEnabled(!emptySelection);
484: exportItem.setEnabled(true);
485: }
486:
487: /* (non-Javadoc)
488: * @see org.eclipse.swt.events.MenuListener#menuHidden(org.eclipse.swt.events.MenuEvent)
489: */
490: public void menuHidden(MenuEvent e) {
491: // Do nothing...
492: }
493:
494: /**
495: * Ajoute un listener.
496: * @param listener le nouveau listener.
497: */
498: public void addDurationListener(IDurationListener listener) {
499: listeners.add(listener);
500: }
501:
502: /**
503: * Ajoute un listener.
504: * @param listener le nouveau listener.
505: */
506: public void removeDurationListener(IDurationListener listener) {
507: listeners.remove(listener);
508: }
509:
510: /**
511: * Notifie les listeners qu'une durée a été ajoutée.
512: * @param newDuration la durée ajoutée.
513: */
514: private void notifyDurationAdded(Duration newDuration) {
515: Iterator it = listeners.iterator();
516: while (it.hasNext()) {
517: IDurationListener listener = (IDurationListener) it.next();
518: listener.durationAdded(newDuration);
519: }
520: }
521:
522: /**
523: * Notifie les listeners qu'une durée a été supprimée.
524: * @param duration la durée supprimée.
525: */
526: private void notifyDurationRemoved(Duration duration) {
527: Iterator it = listeners.iterator();
528: while (it.hasNext()) {
529: IDurationListener listener = (IDurationListener) it.next();
530: listener.durationRemoved(duration);
531: }
532: }
533:
534: /**
535: * Notifie les listeners qu'un collaborateur a été modifié.
536: * @param oldDuration la durée modifiée.
537: * @param newDuration la nouvelle durée.
538: */
539: private void notifyDurationUpdated(Duration oldDuration,
540: Duration newDuration) {
541: Iterator it = listeners.iterator();
542: while (it.hasNext()) {
543: IDurationListener listener = (IDurationListener) it.next();
544: listener.durationUpdated(oldDuration, newDuration);
545: }
546: }
547:
548: /**
549: * Notifie les listeners que l'état d'activation d'une durée a été modifiée.
550: * @param duration la durée modifiée.
551: */
552: private void notifyDurationActivationStatusChanged(Duration duration) {
553: Iterator it = listeners.iterator();
554: while (it.hasNext()) {
555: IDurationListener listener = (IDurationListener) it.next();
556: listener.durationActivationStatusChanged(duration);
557: }
558: }
559:
560: /* (non-Javadoc)
561: * @see jfb.tools.activitymgr.ui.DatabaseUI.DbStatusListener#databaseOpened()
562: */
563: public void databaseOpened() {
564: initUI();
565: }
566:
567: /* (non-Javadoc)
568: * @see jfb.tools.activitymgr.ui.DatabaseUI.DbStatusListener#databaseClosed()
569: */
570: public void databaseClosed() {
571: Table table = tableViewer.getTable();
572: TableItem[] items = table.getItems();
573: for (int i = 0; i < items.length; i++) {
574: items[i].dispose();
575: }
576: }
577:
578: /**
579: * Trie les durées.
580: */
581: private void sortDurations() {
582: // Réinitialisation des donées
583: initUI();
584: }
585:
586: /**
587: * Initialise l'IHM avec les données en base.
588: */
589: private void initUI() {
590: // Initialisation de la table
591: tableViewer.setInput(ROOT_NODE);
592: }
593:
594: }
|