0001: /*
0002: * Copyright (c) 2004-2006, Jean-François Brazeau. All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * 1. Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * 2. Redistributions in binary form must reproduce the above copyright
0011: * notice, this list of conditions and the following disclaimer in the
0012: * documentation and/or other materials provided with the distribution.
0013: *
0014: * 3. The name of the author may not be used to endorse or promote products
0015: * derived from this software without specific prior written permission.
0016: *
0017: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0018: * IMPLIEDWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0019: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0020: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0021: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
0022: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
0023: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0024: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
0025: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
0026: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0027: */
0028: package jfb.tools.activitymgr.ui;
0029:
0030: import java.util.ArrayList;
0031: import java.util.HashMap;
0032: import java.util.Iterator;
0033:
0034: import jfb.tools.activitymgr.core.DbException;
0035: import jfb.tools.activitymgr.core.ModelException;
0036: import jfb.tools.activitymgr.core.ModelMgr;
0037: import jfb.tools.activitymgr.core.beans.Task;
0038: import jfb.tools.activitymgr.core.beans.TaskSums;
0039: import jfb.tools.activitymgr.core.util.StringHelper;
0040: import jfb.tools.activitymgr.ui.DatabaseUI.IDbStatusListener;
0041: import jfb.tools.activitymgr.ui.dialogs.ContributionsViewerDialog;
0042: import jfb.tools.activitymgr.ui.dialogs.DialogException;
0043: import jfb.tools.activitymgr.ui.dialogs.ITaskChooserValidator;
0044: import jfb.tools.activitymgr.ui.dialogs.TaskChooserTreeWithHistoryDialog;
0045: import jfb.tools.activitymgr.ui.util.AbstractTableMgr;
0046: import jfb.tools.activitymgr.ui.util.ITaskSelectionListener;
0047: import jfb.tools.activitymgr.ui.util.SWTHelper;
0048: import jfb.tools.activitymgr.ui.util.SafeRunner;
0049: import jfb.tools.activitymgr.ui.util.TableOrTreeColumnsMgr;
0050: import jfb.tools.activitymgr.ui.util.TaskFinderPanel;
0051: import jfb.tools.activitymgr.ui.util.UITechException;
0052:
0053: import org.apache.log4j.Logger;
0054: import org.eclipse.jface.dialogs.Dialog;
0055: import org.eclipse.jface.dialogs.MessageDialog;
0056: import org.eclipse.jface.viewers.AbstractTreeViewer;
0057: import org.eclipse.jface.viewers.CellEditor;
0058: import org.eclipse.jface.viewers.IBaseLabelProvider;
0059: import org.eclipse.jface.viewers.ICellModifier;
0060: import org.eclipse.jface.viewers.ITableColorProvider;
0061: import org.eclipse.jface.viewers.ITreeContentProvider;
0062: import org.eclipse.jface.viewers.LabelProviderChangedEvent;
0063: import org.eclipse.jface.viewers.StructuredSelection;
0064: import org.eclipse.jface.viewers.TextCellEditor;
0065: import org.eclipse.jface.viewers.TreeViewer;
0066: import org.eclipse.swt.SWT;
0067: import org.eclipse.swt.dnd.Clipboard;
0068: import org.eclipse.swt.dnd.TextTransfer;
0069: import org.eclipse.swt.dnd.Transfer;
0070: import org.eclipse.swt.events.KeyAdapter;
0071: import org.eclipse.swt.events.KeyEvent;
0072: import org.eclipse.swt.events.KeyListener;
0073: import org.eclipse.swt.events.MenuEvent;
0074: import org.eclipse.swt.events.MenuListener;
0075: import org.eclipse.swt.events.MouseAdapter;
0076: import org.eclipse.swt.events.MouseEvent;
0077: import org.eclipse.swt.events.SelectionEvent;
0078: import org.eclipse.swt.events.SelectionListener;
0079: import org.eclipse.swt.graphics.Color;
0080: import org.eclipse.swt.graphics.Point;
0081: import org.eclipse.swt.layout.GridData;
0082: import org.eclipse.swt.layout.GridLayout;
0083: import org.eclipse.swt.widgets.Composite;
0084: import org.eclipse.swt.widgets.Event;
0085: import org.eclipse.swt.widgets.Menu;
0086: import org.eclipse.swt.widgets.MenuItem;
0087: import org.eclipse.swt.widgets.TabItem;
0088: import org.eclipse.swt.widgets.Tree;
0089: import org.eclipse.swt.widgets.TreeItem;
0090: import org.eclipse.swt.widgets.Widget;
0091:
0092: /**
0093: * IHM de gestion des tâches.
0094: */
0095: public class TasksUI extends AbstractTableMgr implements
0096: IDbStatusListener, ICellModifier, SelectionListener,
0097: MenuListener, ITreeContentProvider, ITableColorProvider {
0098:
0099: /** Logger */
0100: private static Logger log = Logger.getLogger(TasksUI.class);
0101:
0102: /** Constantes associées aux colonnes */
0103: public static final int NAME_COLUMN_IDX = 0;
0104: public static final int CODE_COLUMN_IDX = 1;
0105: public static final int INITIAL_FUND_COLUMN_IDX = 2;
0106: public static final int INITIALLY_CONSUMED_COLUMN_IDX = 3;
0107: public static final int CONSUMED_COLUMN_IDX = 4;
0108: public static final int TODO_COLUMN_IDX = 5;
0109: public static final int DELTA_COLUMN_IDX = 6;
0110: public static final int COMMENT_COLUMN_IDX = 7;
0111: private static TableOrTreeColumnsMgr treeColsMgr;
0112:
0113: /**
0114: * Interface utilisée pour permettre l'écoute de la suppression ou de
0115: * l'ajout de taches.
0116: */
0117: public static interface ITaskListener {
0118:
0119: /**
0120: * Indique qu'une tache a été ajoutée au référentiel.
0121: * @param task la tache ajoutée.
0122: */
0123: public void taskAdded(Task task);
0124:
0125: /**
0126: * Indique qu'une tache a été supprimée du référentiel.
0127: * @param task la tache supprimée.
0128: */
0129: public void taskRemoved(Task task);
0130:
0131: /**
0132: * Indique qu'une tache a été modifiée duans le référentiel.
0133: * @param task la tache modifiée.
0134: */
0135: public void taskUpdated(Task task);
0136:
0137: /**
0138: * Indique qu'une tache a été déplacée duans le référentiel.
0139: * @param oldTaskFullpath ancien chemin de la tache.
0140: * @param task la tache déplacée.
0141: */
0142: public void taskMoved(String oldTaskFullpath, Task task);
0143: }
0144:
0145: /** Listeners */
0146: private ArrayList listeners = new ArrayList();
0147:
0148: /** Viewer */
0149: private TreeViewer treeViewer;
0150:
0151: /** Items de menu */
0152: private MenuItem newTaskItem;
0153: private MenuItem newSubtaskItem;
0154: private MenuItem moveUpItem;
0155: private MenuItem moveDownItem;
0156: private MenuItem moveToAnotherTaskItem;
0157: private MenuItem moveToRootItem;
0158: private MenuItem copyItem;
0159: private MenuItem removeItem;
0160: private MenuItem expandItem;
0161: private MenuItem collapseItem;
0162: private MenuItem listTaskContributionsItem;
0163: private MenuItem refreshItem;
0164: private MenuItem exportItem;
0165:
0166: /** Composant parent */
0167: private Composite parent;
0168:
0169: /** Table contenant les sommes associées aux taches */
0170: private HashMap tasksSums = new HashMap();
0171:
0172: /** Popup permettant de choisir une tache */
0173: private TaskChooserTreeWithHistoryDialog taskChooserDialog;
0174:
0175: /** Popup permettant de lister les contributions d'une tache */
0176: private ContributionsViewerDialog contribsViewerDialog;
0177:
0178: /** Panneau de recherche de tache */
0179: private TaskFinderPanel taskFinderPanel;
0180:
0181: /** Couleur de fond utilisée pour les zones non modifiables */
0182: private Color disabledBGColor;
0183:
0184: /** Couleur de police de caractère utilisée pour les zones non modifiables */
0185: private Color disabledFGColor;
0186:
0187: /**
0188: * Constructeur permettant de placer l'IHM dans un onglet.
0189: * @param tabItem item parent.
0190: */
0191: public TasksUI(TabItem tabItem) {
0192: this (tabItem.getParent());
0193: tabItem.setControl(parent);
0194: }
0195:
0196: /**
0197: * Constructeur par défaut.
0198: * @param parentComposite composant parent.
0199: */
0200: public TasksUI(Composite parentComposite) {
0201: // Création du composite parent
0202: parent = new Composite(parentComposite, SWT.NONE);
0203: parent.setLayout(new GridLayout(1, false));
0204:
0205: // Panneau permettant de recherche une tache
0206: taskFinderPanel = new TaskFinderPanel(parent);
0207: GridData gridData = new GridData(SWT.FILL, SWT.NONE, true,
0208: false);
0209: taskFinderPanel.setLayoutData(gridData);
0210: taskFinderPanel.addTaskListener(new ITaskSelectionListener() {
0211: public void taskSelected(Task selectedTask) {
0212: treeViewer.setSelection(new StructuredSelection(
0213: selectedTask));
0214: treeViewer.getTree().setFocus();
0215: }
0216: });
0217:
0218: // Arbre tableau
0219: final Tree tree = new Tree(parent, SWT.MULTI
0220: | SWT.FULL_SELECTION | SWT.BORDER | SWT.HIDE_SELECTION);
0221: gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
0222: gridData.heightHint = 75;
0223: tree.setLayoutData(gridData);
0224: tree.setLinesVisible(true);
0225: tree.setHeaderVisible(true);
0226: tree.setEnabled(true);
0227: tree.addMouseListener(new MouseAdapter() {
0228: public void mouseDoubleClick(MouseEvent e) {
0229: Tree tree = (Tree) e.widget;
0230: MessageDialog.openInformation(parent.getShell(),
0231: "title", "Widget : " + e.widget + ", source="
0232: + e.getSource() + ", x=" + e.x + ", y="
0233: + e.y + ", item="
0234: + tree.getItem(new Point(e.x, e.y)));
0235: }
0236: });
0237:
0238: // Création du viewer
0239: treeViewer = new TreeViewer(tree);
0240: treeViewer.setCellModifier(this );
0241: treeViewer.setContentProvider(this );
0242: treeViewer.setLabelProvider(this );
0243:
0244: // Création des polices de caractère
0245: disabledBGColor = tree.getDisplay().getSystemColor(
0246: SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
0247: disabledFGColor = tree.getDisplay().getSystemColor(
0248: SWT.COLOR_WIDGET_DARK_SHADOW);
0249:
0250: // Configuration des colonnes
0251: treeColsMgr = new TableOrTreeColumnsMgr();
0252: treeColsMgr.addColumn("NAME", "Task name", 200, SWT.LEFT);
0253: treeColsMgr.addColumn("CODE", "Task code", 70, SWT.LEFT);
0254: treeColsMgr.addColumn("BUDGET", "Budget", 70, SWT.RIGHT);
0255: treeColsMgr.addColumn("INI_CONS", "Initially consumed", 70,
0256: SWT.RIGHT);
0257: treeColsMgr.addColumn("CONSUMED", "Consumed", 70, SWT.RIGHT);
0258: treeColsMgr.addColumn("TODO", "Todo", 70, SWT.RIGHT);
0259: treeColsMgr.addColumn("DELTA", "Delta", 70, SWT.RIGHT);
0260: treeColsMgr.addColumn("COMMENT", "Comment", 200, SWT.LEFT);
0261: treeColsMgr.configureTree(treeViewer);
0262:
0263: // Configuration des éditeurs de cellules
0264: CellEditor[] editors = new CellEditor[8];
0265: editors[NAME_COLUMN_IDX] = new TextCellEditor(tree);
0266: editors[CODE_COLUMN_IDX] = new TextCellEditor(tree);
0267: editors[INITIAL_FUND_COLUMN_IDX] = new TextCellEditor(tree);
0268: editors[INITIALLY_CONSUMED_COLUMN_IDX] = new TextCellEditor(
0269: tree);
0270: editors[CONSUMED_COLUMN_IDX] = null;
0271: editors[TODO_COLUMN_IDX] = new TextCellEditor(tree);
0272: editors[DELTA_COLUMN_IDX] = null;
0273: editors[COMMENT_COLUMN_IDX] = new TextCellEditor(tree);
0274: treeViewer.setCellEditors(editors);
0275:
0276: // Initialisation des popups
0277: taskChooserDialog = new TaskChooserTreeWithHistoryDialog(parent
0278: .getShell());
0279: contribsViewerDialog = new ContributionsViewerDialog(parent
0280: .getShell());
0281:
0282: // Configuration du menu popup
0283: final Menu menu = new Menu(tree);
0284: menu.addMenuListener(this );
0285: // Sous-menu 'Nouveau'
0286: MenuItem newItem = new MenuItem(menu, SWT.CASCADE);
0287: newItem.setText("New");
0288: Menu newMenu = new Menu(newItem);
0289: newItem.setMenu(newMenu);
0290: newTaskItem = new MenuItem(newMenu, SWT.CASCADE);
0291: newTaskItem.setText("task (same level)");
0292: newTaskItem.addSelectionListener(this );
0293: newSubtaskItem = new MenuItem(newMenu, SWT.CASCADE);
0294: newSubtaskItem.setText("subtask");
0295: newSubtaskItem.addSelectionListener(this );
0296: // Sous-menu 'Déplacer'
0297: MenuItem moveToItem = new MenuItem(menu, SWT.CASCADE);
0298: moveToItem.setText("Move");
0299: Menu moveToMenu = new Menu(moveToItem);
0300: moveToItem.setMenu(moveToMenu);
0301: moveUpItem = new MenuItem(moveToMenu, SWT.CASCADE);
0302: moveUpItem.setText("up (Ctrl + up arrow)");
0303: moveUpItem.addSelectionListener(this );
0304: moveDownItem = new MenuItem(moveToMenu, SWT.CASCADE);
0305: moveDownItem.setText("down (Ctrl + down arrow)");
0306: moveDownItem.addSelectionListener(this );
0307: moveToAnotherTaskItem = new MenuItem(moveToMenu, SWT.CASCADE);
0308: moveToAnotherTaskItem.setText("under another task");
0309: moveToAnotherTaskItem.addSelectionListener(this );
0310: moveToRootItem = new MenuItem(moveToMenu, SWT.CASCADE);
0311: moveToRootItem.setText("under root");
0312: moveToRootItem.addSelectionListener(this );
0313: copyItem = new MenuItem(menu, SWT.CASCADE);
0314: copyItem.setText("Copy (Ctrl + c)");
0315: copyItem.addSelectionListener(this );
0316: removeItem = new MenuItem(menu, SWT.CASCADE);
0317: removeItem.setText("Remove");
0318: removeItem.addSelectionListener(this );
0319: expandItem = new MenuItem(menu, SWT.CASCADE);
0320: expandItem.setText("Expand all");
0321: expandItem.addSelectionListener(this );
0322: collapseItem = new MenuItem(menu, SWT.CASCADE);
0323: collapseItem.setText("Collapse all");
0324: collapseItem.addSelectionListener(this );
0325: listTaskContributionsItem = new MenuItem(menu, SWT.CASCADE);
0326: listTaskContributionsItem.setText("List contrib.");
0327: listTaskContributionsItem.addSelectionListener(this );
0328: refreshItem = new MenuItem(menu, SWT.CASCADE);
0329: refreshItem.setText("Refresh");
0330: refreshItem.addSelectionListener(this );
0331: exportItem = new MenuItem(menu, SWT.CASCADE);
0332: exportItem.setText("Export");
0333: exportItem.addSelectionListener(this );
0334: tree.setMenu(menu);
0335:
0336: log.debug("UI initialization done");
0337: // Ajout de KeyListeners pour faciliter le déplacement vers le bas et
0338: // vers le haut des taches
0339: // (Rq: les accélérateurs sont ignorés dans les menus contextuels)
0340: KeyListener keyListener = new KeyAdapter() {
0341: public void keyReleased(KeyEvent e) {
0342: Widget simulatedWidget = null;
0343: if ((e.keyCode == SWT.ARROW_UP)
0344: && (e.stateMask == SWT.CTRL))
0345: simulatedWidget = moveUpItem;
0346: else if ((e.keyCode == SWT.ARROW_DOWN)
0347: && (e.stateMask == SWT.CTRL))
0348: simulatedWidget = moveDownItem;
0349: else if ((e.keyCode == 'c')
0350: && (e.stateMask == SWT.CTRL))
0351: simulatedWidget = copyItem;
0352: if (simulatedWidget != null) {
0353: Event event = new Event();
0354: event.widget = simulatedWidget;
0355: SelectionEvent se = new SelectionEvent(event);
0356: widgetSelected(se);
0357: }
0358: }
0359: };
0360: parentComposite.addKeyListener(keyListener);
0361: tree.addKeyListener(keyListener);
0362: }
0363:
0364: /* (non-Javadoc)
0365: * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
0366: */
0367: public Object[] getElements(Object inputElement) {
0368: return getChildren(null);
0369: }
0370:
0371: /**
0372: * Retourne les sommes associées à la tache spécifiée.
0373: * @param task la tache pour laquelle on désire connaître les cumuls.
0374: * @return les sommes associées à la tache.
0375: * @throws ModelException levé en cas de détection d'incohérence au niveau
0376: * du modèle.
0377: * @throws DbException levé en cas d'incident technique d'accès à la base
0378: * de données.
0379: */
0380: private TaskSums getTasksSums(Task task) throws ModelException,
0381: DbException {
0382: // 1° lecture dans le cache
0383: TaskSums taskSums = (TaskSums) tasksSums.get(task);
0384: if (taskSums == null) {
0385: synchronized (tasksSums) {
0386: // 2° lecture dans le cache (synchronisée)
0387: taskSums = (TaskSums) tasksSums.get(task);
0388: if (taskSums == null) {
0389: taskSums = ModelMgr.getTaskSums(task);
0390: // Dépot dans le cache
0391: tasksSums.put(task, taskSums);
0392: }
0393: }
0394: }
0395: return taskSums;
0396: }
0397:
0398: /* (non-Javadoc)
0399: * @see org.eclipse.jface.viewers.ICellModifier#canModify(java.lang.Object, java.lang.String)
0400: */
0401: public boolean canModify(Object element, String property) {
0402: log.debug("ICellModifier.canModify(" + element + ", "
0403: + property + ")");
0404: final Task task = (Task) element;
0405: final int propertyIdx = treeColsMgr.getColumnIndex(property);
0406: SafeRunner safeRunner = new SafeRunner() {
0407: public Object runUnsafe() throws Exception {
0408: boolean hasChilds = (task.getSubTasksCount() > 0);
0409: boolean canModify = false;
0410: switch (propertyIdx) {
0411: case (NAME_COLUMN_IDX):
0412: case (CODE_COLUMN_IDX):
0413: canModify = true;
0414: break;
0415: case (INITIAL_FUND_COLUMN_IDX):
0416: case (INITIALLY_CONSUMED_COLUMN_IDX):
0417: case (TODO_COLUMN_IDX):
0418: canModify = !hasChilds;
0419: break;
0420: case (CONSUMED_COLUMN_IDX):
0421: case (DELTA_COLUMN_IDX):
0422: canModify = false;
0423: break;
0424: case (COMMENT_COLUMN_IDX):
0425: canModify = true;
0426: break;
0427: default:
0428: throw new UITechException("Colonne inconnue");
0429: }
0430: return canModify ? Boolean.TRUE : Boolean.FALSE;
0431: }
0432: };
0433: // Retour du résultat
0434: return ((Boolean) safeRunner.run(parent.getShell()))
0435: .booleanValue();
0436: }
0437:
0438: /* (non-Javadoc)
0439: * @see org.eclipse.jface.viewers.ICellModifier#getValue(java.lang.Object, java.lang.String)
0440: */
0441: public Object getValue(Object element, String property) {
0442: log.debug("ICellModifier.getValue(" + element + ", " + property
0443: + ")");
0444: final Task task = (Task) element;
0445: final int propertyIdx = treeColsMgr.getColumnIndex(property);
0446: SafeRunner safeRunner = new SafeRunner() {
0447: public Object runUnsafe() throws Exception {
0448: TaskSums taskSums = getTasksSums(task);
0449: boolean hasChilds = (task.getSubTasksCount() > 0);
0450: String value = null;
0451: switch (propertyIdx) {
0452: case (NAME_COLUMN_IDX):
0453: value = task.getName();
0454: break;
0455: case (CODE_COLUMN_IDX):
0456: value = task.getCode();
0457: break;
0458: case (INITIAL_FUND_COLUMN_IDX):
0459: value = StringHelper
0460: .hundredthToEntry(!hasChilds ? task
0461: .getBudget() : taskSums
0462: .getBudgetSum());
0463: break;
0464: case (INITIALLY_CONSUMED_COLUMN_IDX):
0465: value = StringHelper
0466: .hundredthToEntry(!hasChilds ? task
0467: .getInitiallyConsumed() : taskSums
0468: .getInitiallyConsumedSum());
0469: break;
0470: case (CONSUMED_COLUMN_IDX):
0471: value = StringHelper.hundredthToEntry(taskSums
0472: .getConsumedSum()
0473: + taskSums.getInitiallyConsumedSum());
0474: break;
0475: case (TODO_COLUMN_IDX):
0476: value = StringHelper
0477: .hundredthToEntry(!hasChilds ? task
0478: .getTodo() : taskSums.getTodoSum());
0479: break;
0480: case (DELTA_COLUMN_IDX):
0481: long delta = taskSums.getBudgetSum()
0482: - taskSums.getInitiallyConsumedSum()
0483: - taskSums.getConsumedSum()
0484: - taskSums.getTodoSum();
0485: value = StringHelper.hundredthToEntry(delta);
0486: break;
0487: case (COMMENT_COLUMN_IDX):
0488: value = task.getComment() != null ? task
0489: .getComment() : "";
0490: break;
0491: default:
0492: throw new UITechException("Colonne inconnue");
0493: }
0494: // Retour du résultat
0495: return value;
0496: }
0497: };
0498: // Exécution
0499: return safeRunner.run(parent.getShell());
0500: }
0501:
0502: /* (non-Javadoc)
0503: * @see org.eclipse.jface.viewers.ICellModifier#modify(java.lang.Object, java.lang.String, java.lang.Object)
0504: */
0505: public void modify(Object element, String property,
0506: final Object value) {
0507: log.debug("ICellModifier.modify(" + element + ", " + property
0508: + ", " + value + ")");
0509: final TreeItem item = (TreeItem) element;
0510: final Task task = (Task) item.getData();
0511: final int columnIdx = treeColsMgr.getColumnIndex(property);
0512: final IBaseLabelProvider labelProvider = this ;
0513: SafeRunner safeRunner = new SafeRunner() {
0514: public Object runUnsafe() throws Exception {
0515: boolean parentsMustBeRefreshed = false;
0516: switch (columnIdx) {
0517: case (NAME_COLUMN_IDX):
0518: task.setName((String) value);
0519: break;
0520: case (CODE_COLUMN_IDX):
0521: task.setCode((String) value);
0522: break;
0523: case (INITIAL_FUND_COLUMN_IDX):
0524: long newInitialFund = StringHelper
0525: .entryToHundredth((String) value);
0526: task.setBudget(newInitialFund);
0527: parentsMustBeRefreshed = true;
0528: break;
0529: case (INITIALLY_CONSUMED_COLUMN_IDX):
0530: long newInitiallyConsumed = StringHelper
0531: .entryToHundredth((String) value);
0532: task.setInitiallyConsumed(newInitiallyConsumed);
0533: parentsMustBeRefreshed = true;
0534: break;
0535: case (TODO_COLUMN_IDX):
0536: long newTodo = StringHelper
0537: .entryToHundredth((String) value);
0538: task.setTodo(newTodo);
0539: parentsMustBeRefreshed = true;
0540: break;
0541: case (COMMENT_COLUMN_IDX):
0542: String comment = (String) value;
0543: if (comment != null)
0544: comment = comment.trim();
0545: // Si le commentaire est vide, il devient nul
0546: if ("".equals(comment))
0547: comment = null;
0548: task.setComment((String) value);
0549: break;
0550: case (CONSUMED_COLUMN_IDX):
0551: case (DELTA_COLUMN_IDX):
0552: throw new UITechException(
0553: "Cette colonne ne peut pas être modifiée");
0554: default:
0555: throw new UITechException("Colonne inconnue");
0556: }
0557: // Mise à jour en base
0558: ModelMgr.updateTask(task);
0559: // Mise à jour des labels
0560: if (parentsMustBeRefreshed) {
0561: // Mise à jour des sommes des taches parentes
0562: updateBranchItemsSums(item);
0563: } else {
0564: // Notification de la mise à jour uniquement pour la tache
0565: notifyLabelProviderListener(new LabelProviderChangedEvent(
0566: labelProvider, new Object[] { task }));
0567: }
0568: // Notification de la mise à jour de la tache pour les listeners
0569: notifyTaskUpdated(task);
0570: return null;
0571: }
0572: };
0573: // Exécution
0574: safeRunner.run(parent.getShell());
0575: }
0576:
0577: /**
0578: * Met à jour les sommes associés aux taches de la branche associée à l'item spécifié.
0579: * @param item l'item de tableau.
0580: * @throws ModelException levée en cas d'invalidité associée au modèle.
0581: * @throws DbException levé en cas d'incident technique avec la base de données.
0582: */
0583: private void updateBranchItemsSums(TreeItem item)
0584: throws ModelException, DbException {
0585: ArrayList list = new ArrayList();
0586: // Nettoyage du cache
0587: TreeItem cursor = item;
0588: while (cursor != null) {
0589: Task taskCursor = (Task) cursor.getData();
0590: log.debug("Update task " + taskCursor.getName());
0591: tasksSums.remove(taskCursor);
0592: list.add(0, taskCursor);
0593: cursor = cursor.getParentItem();
0594: }
0595: // Notification de la mise à jour (ce qui recharge automatiquement
0596: // le cache des sommes de taches)
0597: notifyLabelProviderListener(new LabelProviderChangedEvent(this ,
0598: list.toArray()));
0599: }
0600:
0601: /* (non-Javadoc)
0602: * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
0603: */
0604: public boolean hasChildren(Object element) {
0605: log.debug("ITreeContentProvider.getChildren(" + element + ")");
0606: final Task task = (Task) element;
0607: return task.getSubTasksCount() > 0;
0608: }
0609:
0610: /* (non-Javadoc)
0611: * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
0612: */
0613: public Object[] getChildren(Object parentElement) {
0614: log.debug("ITreeContentProvider.getChildren(" + parentElement
0615: + ")");
0616: final Task parentTask = (Task) parentElement;
0617: SafeRunner safeRunner = new SafeRunner() {
0618: public Object runUnsafe() throws Exception {
0619: Task[] subTasks = ModelMgr.getSubtasks(parentTask);
0620: return subTasks;
0621: }
0622: };
0623: Object[] result = (Object[]) safeRunner.run(parent.getShell(),
0624: new Object[] {});
0625: return result;
0626: }
0627:
0628: /* (non-Javadoc)
0629: * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
0630: */
0631: public Object getParent(Object element) {
0632: log.debug("ITreeContentProvider.getParent(" + element + ")");
0633: final Task task = (Task) element;
0634: SafeRunner safeRunner = new SafeRunner() {
0635: public Object runUnsafe() throws Exception {
0636: Task parentTask = ModelMgr.getParentTask(task);
0637: return parentTask == null ? treeViewer.getInput()
0638: : parentTask;
0639: }
0640: };
0641: // Exécution du traitement
0642: Object result = (Object) safeRunner.run(parent.getShell());
0643: return result;
0644: }
0645:
0646: /* (non-Javadoc)
0647: * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
0648: */
0649: public String getColumnText(Object element, int columnIndex) {
0650: log.debug("ITableLabelProvider.getColumnText(" + element + ", "
0651: + columnIndex + ")");
0652: return (String) getValue(element, treeColsMgr
0653: .getColumnCode(columnIndex));
0654: }
0655:
0656: /* (non-Javadoc)
0657: * @see org.eclipse.jface.viewers.ITableColorProvider#getBackground(java.lang.Object, int)
0658: */
0659: public Color getBackground(Object element, int columnIndex) {
0660: return canModify(element, treeColsMgr
0661: .getColumnCode(columnIndex)) ? treeViewer.getTree()
0662: .getBackground() : disabledBGColor;
0663: }
0664:
0665: /* (non-Javadoc)
0666: * @see org.eclipse.jface.viewers.ITableColorProvider#getForeground(java.lang.Object, int)
0667: */
0668: public Color getForeground(Object element, int columnIndex) {
0669: return canModify(element, treeColsMgr
0670: .getColumnCode(columnIndex)) ? treeViewer.getTree()
0671: .getForeground() : disabledFGColor;
0672: }
0673:
0674: /* (non-Javadoc)
0675: * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
0676: */
0677: public void widgetSelected(final SelectionEvent e) {
0678: log.debug("SelectionListener.widgetSelected(" + e + ")");
0679: final Object source = e.getSource();
0680: SafeRunner safeRunner = new SafeRunner() {
0681: public Object runUnsafe() throws Exception {
0682: TreeItem[] selection = treeViewer.getTree()
0683: .getSelection();
0684: // Cas d'une création (même niveau)
0685: if (newTaskItem.equals(source)) {
0686: // Récupération du noeud parent
0687: TreeItem parentItem = selection.length > 0 ? selection[0]
0688: .getParentItem()
0689: : null;
0690: Task parentTask = parentItem == null ? null
0691: : (Task) parentItem.getData();
0692: // Création de la tache
0693: Task newTask = newTask(parentTask);
0694: // Notification des listeners
0695: notifyTaskAdded(newTask);
0696: }
0697: // Cas d'une création de sous tache
0698: else if (newSubtaskItem.equals(source)) {
0699: Task selectedTask = (Task) selection[0].getData();
0700: Task newTask = newTask(selectedTask);
0701: // Notification des listeners
0702: notifyTaskAdded(newTask);
0703: }
0704: // Cas d'une demande de déplacement vers le haut
0705: else if (moveUpItem.equals(source)) {
0706: Task selectedTask = (Task) selection[0].getData();
0707: String oldTaskFullpath = selectedTask.getFullPath();
0708: ModelMgr.moveUpTask(selectedTask);
0709: // Mise à jour de l'IHM
0710: treeViewer.refresh(selection[0].getParent()
0711: .getData(), true);
0712: // Notification des listeners
0713: notifyTaskMoved(oldTaskFullpath, selectedTask);
0714: }
0715: // Cas d'une demande de déplacement vers le haut
0716: else if (moveDownItem.equals(source)) {
0717: Task selectedTask = (Task) selection[0].getData();
0718: String oldTaskFullpath = selectedTask.getFullPath();
0719: ModelMgr.moveDownTask(selectedTask);
0720: // Mise à jour de l'IHM
0721: treeViewer.refresh(selection[0].getParent()
0722: .getData(), true);
0723: // Notification des listeners
0724: notifyTaskMoved(oldTaskFullpath, selectedTask);
0725: }
0726: // Cas d'une demande de déplacement vers une autre tache
0727: else if (moveToAnotherTaskItem.equals(source)) {
0728: Task taskToMove = (Task) selection[0].getData();
0729: // Récupération du noeud parent
0730: TreeItem parentItem = selection[0].getParentItem();
0731: final Task srcParentTask = (parentItem != null) ? (Task) parentItem
0732: .getData()
0733: : null;
0734: // Création du valideur
0735: taskChooserDialog
0736: .setValidator(new ITaskChooserValidator() {
0737: public void validateChoosenTask(
0738: Task selectedTask)
0739: throws DialogException {
0740: if (srcParentTask != null
0741: && srcParentTask
0742: .equals(selectedTask))
0743: throw new DialogException(
0744: "This parent task is the source parent task.",
0745: null);
0746: try {
0747: ModelMgr
0748: .checkAcceptsSubtasks(selectedTask);
0749: } catch (ModelException e) {
0750: throw new DialogException(e
0751: .getMessage(), null);
0752: }
0753: // TODO Ajouter au ITaskChooserValidator la levée d'exception techniques pour ne plus avoir ce catch
0754: catch (DbException e) {
0755: throw new DialogException(
0756: "Incident technique : '"
0757: + e
0758: .getMessage()
0759: + "'", null);
0760: }
0761: }
0762: });
0763: // Affichage du popup
0764: Task newParentTask = null;
0765: if (taskChooserDialog.open() == Dialog.OK) {
0766: newParentTask = (Task) taskChooserDialog
0767: .getValue();
0768: log.debug("Selected parent task="
0769: + newParentTask);
0770: String oldTaskFullpath = taskToMove
0771: .getFullPath();
0772: ModelMgr.moveTask(taskToMove, newParentTask);
0773: // Rafraichir l'ancien et le nouveau parent ne suffit pas
0774: // dans le cas ou le parent destination change de numéro
0775: // (ex : déplacement d'une tache A vers une tache B avec
0776: // A et B initialement soeurs, A étant placé avant B)
0777: //treeViewer.refresh(newParentTask);
0778: //treeViewer.refresh(srcParentTask);
0779: treeViewer.refresh();
0780: // Notification des listeners
0781: notifyTaskMoved(oldTaskFullpath, taskToMove);
0782: }
0783: }
0784: // Cas d'une demande de déplacement vers la racine
0785: else if (moveToRootItem.equals(source)) {
0786: TreeItem selectedItem = selection[0];
0787: Task taskToMove = (Task) selectedItem.getData();
0788: String oldTaskFullpath = taskToMove.getFullPath();
0789: // Déplacement
0790: ModelMgr.moveTask(taskToMove, null);
0791: treeViewer.refresh();
0792: // Notification des listeners
0793: notifyTaskMoved(oldTaskFullpath, taskToMove);
0794: }
0795: // Cas d'une demande de copie des taches sélectionnées (on met dans le presse papier
0796: // le chemin de la tache)
0797: else if (copyItem.equals(source)) {
0798: // Implémentation en multi sélection => pour l'instant on ne veut gérer qu'une seule tache à la fois
0799: //TreeItem[] selectedItems = treeViewer.getTree().getSelection();
0800: //Clipboard clipboard = new Clipboard(parent.getDisplay());
0801: //String[] taskCodePaths = new String[selectedItems.length];
0802: //Transfer[] transfers = new Transfer[selectedItems.length];
0803: //for (int i=0; i<selectedItems.length; i++) {
0804: // Task task = (Task) selectedItems[i].getData();
0805: // taskCodePaths[i] = ModelMgr.getTaskCodePath(task);
0806: // transfers[i] = TextTransfer.getInstance();
0807: //}
0808: //clipboard.setContents(taskCodePaths, transfers);
0809: //clipboard.dispose();
0810:
0811: // Implémentation en sélection simple
0812: if (selection != null && selection.length > 0) {
0813: TreeItem selectedItem = selection[0];
0814: Clipboard clipboard = new Clipboard(parent
0815: .getDisplay());
0816: Task task = (Task) selectedItem.getData();
0817: String taskCodePath = ModelMgr
0818: .getTaskCodePath(task);
0819: clipboard.setContents(
0820: new String[] { taskCodePath },
0821: new Transfer[] { TextTransfer
0822: .getInstance() });
0823: clipboard.dispose();
0824: }
0825: }
0826: // Cas d'une suppression
0827: else if (removeItem.equals(source)) {
0828: TreeItem selectedItem = selection[0];
0829: TreeItem parentItem = selectedItem.getParentItem();
0830: Task selectedTask = (Task) selectedItem.getData();
0831: Task parentTask = (parentItem != null) ? (Task) parentItem
0832: .getData()
0833: : null;
0834: // Suppression
0835: ModelMgr.removeTask(selectedTask);
0836: // Suppression dans l'arbre
0837: treeViewer.remove(selectedTask);
0838: // Mise à jour des sommes des taches parentes
0839: updateBranchItemsSums(parentItem);
0840: // Mise à jour des taches soeurs
0841: if (parentTask != null)
0842: treeViewer.refresh(parentTask);
0843: else
0844: treeViewer.refresh();
0845: // Notification des listeners
0846: notifyTaskRemoved(selectedTask);
0847: }
0848: // Cas d'une demande d'expansion d'un noeud
0849: else if (expandItem.equals(source)) {
0850: TreeItem selectedItem = treeViewer.getTree()
0851: .getSelection()[0];
0852: treeViewer.expandToLevel(selectedItem.getData(),
0853: AbstractTreeViewer.ALL_LEVELS);
0854: }
0855: // Cas d'une demande de fermeture d'un noeud
0856: else if (collapseItem.equals(source)) {
0857: TreeItem selectedItem = treeViewer.getTree()
0858: .getSelection()[0];
0859: treeViewer.collapseToLevel(selectedItem.getData(),
0860: AbstractTreeViewer.ALL_LEVELS);
0861: }
0862: // Cas d'une demande de liste des contributions
0863: else if (listTaskContributionsItem.equals(source)) {
0864: TreeItem selectedItem = selection[0];
0865: Task selectedTask = (Task) selectedItem.getData();
0866: contribsViewerDialog.setFilter(selectedTask, null,
0867: null, null, null);
0868: // Ouverture du dialogue
0869: contribsViewerDialog.open();
0870: }
0871: // Cas d'une demande de rafraichissement
0872: else if (refreshItem.equals(source)) {
0873: // Récupération du noeud parent
0874: TreeItem selectedItem = selection[0];
0875: TreeItem parentItem = selectedItem.getParentItem();
0876: Task parentTask = (parentItem != null) ? (Task) parentItem
0877: .getData()
0878: : null;
0879: // Mise à jour
0880: if (parentTask != null)
0881: treeViewer.refresh(parentTask);
0882: else
0883: treeViewer.refresh();
0884: }
0885: // Cas d'une demande d'export du tableau
0886: else if (exportItem.equals(source)) {
0887: // Export du tableau
0888: SWTHelper.exportToWorkBook(treeViewer.getTree());
0889: }
0890: return null;
0891: }
0892: };
0893: // Exécution
0894: safeRunner.run(parent.getShell());
0895: }
0896:
0897: /* (non-Javadoc)
0898: * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
0899: */
0900: public void widgetDefaultSelected(SelectionEvent e) {
0901: widgetSelected(e);
0902: }
0903:
0904: /**
0905: * Ajoute une tache.
0906: * @param parentTask la tache parent ou null pour une tache racine.
0907: * @throws DbException levé en cas d'incident associé à la persistence.
0908: * @throws ModelException levé en cas de violation du modèle de données.
0909: * @return la tache créée.
0910: */
0911: private Task newTask(Task parentTask) throws DbException,
0912: ModelException {
0913: log.debug("newTask(" + parentTask + ")");
0914: // Création de la nouvelle tache
0915: Task newTask = ModelMgr.createNewTask(parentTask);
0916: // Ajout dans l'arbre et création en base
0917: treeViewer.add(parentTask == null ? treeViewer.getInput()
0918: : parentTask, newTask);
0919: treeViewer.setSelection(new StructuredSelection(newTask), true);
0920: return newTask;
0921: }
0922:
0923: /* (non-Javadoc)
0924: * @see org.eclipse.swt.events.MenuListener#menuShown(org.eclipse.swt.events.MenuEvent)
0925: */
0926: public void menuShown(MenuEvent e) {
0927: log.debug("menuShown(" + e + ")");
0928: TreeItem[] selection = treeViewer.getTree().getSelection();
0929: boolean emptySelection = selection.length == 0;
0930: boolean singleSelection = selection.length == 1;
0931: boolean rootSingleSelection = singleSelection
0932: && (selection[0] == null);
0933: newTaskItem.setEnabled(emptySelection || singleSelection);
0934: newSubtaskItem.setEnabled(singleSelection);
0935: moveUpItem.setEnabled(singleSelection);
0936: moveDownItem.setEnabled(singleSelection);
0937: moveToAnotherTaskItem.setEnabled(singleSelection);
0938: moveToRootItem.setEnabled(singleSelection
0939: && !rootSingleSelection);
0940: copyItem.setEnabled(!emptySelection);
0941: removeItem.setEnabled(singleSelection);
0942: expandItem.setEnabled(singleSelection);
0943: collapseItem.setEnabled(singleSelection);
0944: listTaskContributionsItem.setEnabled(singleSelection);
0945: refreshItem.setEnabled(singleSelection);
0946: exportItem.setEnabled(true);
0947: }
0948:
0949: /* (non-Javadoc)
0950: * @see org.eclipse.swt.events.MenuListener#menuHidden(org.eclipse.swt.events.MenuEvent)
0951: */
0952: public void menuHidden(MenuEvent e) {
0953: // Do nothing...
0954: }
0955:
0956: /* (non-Javadoc)
0957: * @see jfb.tools.activitymgr.ui.DatabaseUI.DbStatusListener#databaseOpened()
0958: */
0959: public void databaseOpened() {
0960: // Création d'une racine fictive
0961: treeViewer.setInput(ROOT_NODE);
0962: }
0963:
0964: /* (non-Javadoc)
0965: * @see jfb.tools.activitymgr.ui.DatabaseUI.DbStatusListener#databaseClosed()
0966: */
0967: public void databaseClosed() {
0968: Tree tree = treeViewer.getTree();
0969: TreeItem[] items = tree.getItems();
0970: for (int i = 0; i < items.length; i++) {
0971: items[i].dispose();
0972: }
0973: }
0974:
0975: /**
0976: * Ajoute un listener.
0977: * @param listener le nouveau listener.
0978: */
0979: public void addTaskListener(ITaskListener listener) {
0980: listeners.add(listener);
0981: }
0982:
0983: /**
0984: * Ajoute un listener.
0985: * @param listener le nouveau listener.
0986: */
0987: public void removeTaskListener(ITaskListener listener) {
0988: listeners.remove(listener);
0989: }
0990:
0991: /**
0992: * Notifie les listeners qu'une tache a été ajoutée.
0993: * @param newTask la tache ajouté.
0994: */
0995: private void notifyTaskAdded(Task newTask) {
0996: Iterator it = listeners.iterator();
0997: while (it.hasNext()) {
0998: ITaskListener listener = (ITaskListener) it.next();
0999: listener.taskAdded(newTask);
1000: }
1001: // Transfert de la notification au popup de choix de tache
1002: taskChooserDialog.taskAdded(newTask);
1003: }
1004:
1005: /**
1006: * Notifie les listeners qu'une tache a été supprimée.
1007: * @param task la tache supprimée.
1008: */
1009: private void notifyTaskRemoved(Task task) {
1010: Iterator it = listeners.iterator();
1011: while (it.hasNext()) {
1012: ITaskListener listener = (ITaskListener) it.next();
1013: listener.taskRemoved(task);
1014: }
1015: // Transfert de la notification au popup de choix de tache
1016: taskChooserDialog.taskRemoved(task);
1017: }
1018:
1019: /**
1020: * Notifie les listeners qu'une tache a été modifiée.
1021: * @param task la tache modifiée.
1022: */
1023: private void notifyTaskUpdated(Task task) {
1024: Iterator it = listeners.iterator();
1025: while (it.hasNext()) {
1026: ITaskListener listener = (ITaskListener) it.next();
1027: listener.taskUpdated(task);
1028: }
1029: // Transfert de la notification au popup de choix de tache
1030: taskChooserDialog.taskUpdated(task);
1031: }
1032:
1033: /**
1034: * Notifie les listeners qu'une tache a été déplacée.
1035: * @param task la tache modifiée.
1036: */
1037: private void notifyTaskMoved(String oldTaskPath, Task task) {
1038: Iterator it = listeners.iterator();
1039: while (it.hasNext()) {
1040: ITaskListener listener = (ITaskListener) it.next();
1041: listener.taskMoved(oldTaskPath, task);
1042: }
1043: // Transfert de la notification au popup de choix de tache
1044: taskChooserDialog.taskMoved(oldTaskPath, task);
1045: }
1046:
1047: }
|