/******************************************************************************* * Copyright (c) 2009, G. Weirich and Elexis * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Sponsoring: * mediX Notfallpaxis, diepraxen Stauffacher AG, Zürich * * Contributors: * G. Weirich - initial implementation * *******************************************************************************/ package ch.elexis.agenda.ui; import java.util.Calendar; import java.util.LinkedList; import java.util.List; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.ScrollBar; import ch.elexis.actions.Activator; import ch.elexis.agenda.data.Termin; import ch.elexis.agenda.preferences.PreferenceConstants; import ch.elexis.agenda.series.SerienTermin; import ch.elexis.core.data.activator.CoreHub; import ch.elexis.core.ui.UiDesk; import ch.elexis.core.ui.locks.AcquireLockBlockingUi; import ch.elexis.core.ui.locks.ILockHandler; import ch.elexis.core.ui.util.PersistentObjectDropTarget; import ch.elexis.core.ui.util.SWTHelper; import ch.elexis.data.PersistentObject; import ch.elexis.data.Query; import ch.elexis.dialogs.TerminDialog; import ch.rgw.tools.StringTool; import ch.rgw.tools.TimeTool; public class ProportionalSheet extends Composite implements IAgendaLayout { static final int LEFT_OFFSET_DEFAULT = 20; static final int PADDING_DEFAULT = 5; private int left_offset, padding; private AgendaParallel view; private MenuManager contextMenuManager; private List<TerminLabel> tlabels; private double ppm; private int sheetHeight; private String[] resources; private int textWidth; private double sheetWidth; private double widthPerColumn; private boolean ctrlKeyDown = false; private TimeTool setTerminTo(int x, int y){ String resource = ""; //$NON-NLS-1$ for (int i = 0; i < resources.length; i++) { double lower = left_offset + i * (widthPerColumn + padding); double upper = lower + widthPerColumn; if (isBetween(x, lower, upper)) { resource = resources[i]; break; } } String startOfDayTimeInMinutes = CoreHub.globalCfg.get(PreferenceConstants.AG_DAY_PRESENTATION_STARTS_AT, "0000"); int dayStartHour = Integer.parseInt(startOfDayTimeInMinutes.substring(0, 2)); int minute = (int) Math.round(y / ppm); TimeTool tt = new TimeTool(Activator.getDefault().getActDate()); int hour = minute / 60; minute = minute - (60 * hour); int raster = 5; minute = ((minute + (raster >> 1)) / raster) * raster; tt.set(TimeTool.AM_PM, TimeTool.AM); tt.set(TimeTool.HOUR, (dayStartHour + hour)); tt.set(TimeTool.MINUTE, minute); if (resource.length() > 0) { Activator.getDefault().setActResource(resource); } return tt; } public ProportionalSheet(Composite parent, AgendaParallel v){ super(parent, SWT.NO_BACKGROUND); view = v; addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e){ layout(); recalc(); } }); addPaintListener(new TimePainter()); addMouseListener(new MouseAdapter() { @Override public void mouseDoubleClick(MouseEvent e){ TimeTool tt = setTerminTo(e.x, e.y); TerminDialog dlg = new TerminDialog(null); dlg.create(); dlg.setTime(tt); if (dlg.open() == Dialog.OK) { refresh(); } } }); addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e){ if (e.keyCode == SWT.CTRL) { ctrlKeyDown = true; } } @Override public void keyReleased(KeyEvent e){ if (e.keyCode == SWT.CTRL) { ctrlKeyDown = false; } } }); // setBackground(Desk.getColor(Desk.COL_GREEN)); left_offset = LEFT_OFFSET_DEFAULT; padding = PADDING_DEFAULT; new PersistentObjectDropTarget(this, new PersistentObjectDropTarget.IReceiver() { public boolean accept(PersistentObject o){ return true; } public void dropped(PersistentObject o, DropTargetEvent e){ Point pt = Display.getCurrent().map(null, ProportionalSheet.this, e.x, e.y); TimeTool tt = setTerminTo(pt.x, pt.y); if (o instanceof Termin) { Termin t = (Termin) o; if (Termin.overlaps(Activator.getDefault().getActResource(), tt, t.getDauer(), t.getId())) { SWTHelper.showInfo("Termin Kollision", "Termine überschneiden sich"); } else { if (ctrlKeyDown) { // copy ctrlKeyDown = false; Termin tCopy = (Termin) t.clone(); if (t.isRecurringDate() && t.getKontakt() == null) { // take kontakt from root termin tCopy.setKontakt(new SerienTermin(t).getRootTermin().getKontakt()); } AcquireLockBlockingUi.aquireAndRun(tCopy, new ILockHandler() { @Override public void lockFailed(){ tCopy.delete(); } @Override public void lockAcquired(){ tCopy.setStartTime(tt); tCopy.setBereich(Activator.getDefault().getActResource()); } }); } else { // move AcquireLockBlockingUi.aquireAndRun(t, new ILockHandler() { @Override public void lockFailed(){ // do nothing } @Override public void lockAcquired(){ t.setStartTime(tt); t.setBereich(Activator.getDefault().getActResource()); } }); } } refresh(); } } }); } private boolean isBetween(int x, double lower, double upper){ int y = (int) Math.round(lower); int z = (int) Math.round(upper); if ((x >= y) && (x <= z)) { return true; } return false; } @Override public boolean setFocus(){ ctrlKeyDown = false; return super.setFocus(); } public MenuManager getContextMenuManager(){ return contextMenuManager; } public void clear(){ while (tlabels != null && tlabels.size() > 0) { tlabels.remove(0).dispose(); } recalc(); } synchronized void refresh(){ String[] resnames = view.getDisplayedResources(); Query<Termin> qbe = new Query<Termin>(Termin.class); String day = Activator.getDefault().getActDate().toString(TimeTool.DATE_COMPACT); qbe.add("Tag", "=", day); qbe.startGroup(); for (String n : resnames) { qbe.add("BeiWem", "=", n); qbe.or(); } qbe.endGroup(); List<Termin> apps = qbe.execute(); // clear old TerminLabel list if (tlabels != null) { for (TerminLabel terminLabel : tlabels) { terminLabel.dispose(); } tlabels.clear(); } else { tlabels = new LinkedList<TerminLabel>(); } // populate new TerminLabel list for (Termin termin : apps) { String m = termin.getBereich(); int idx = StringTool.getIndex(resnames, m); if (idx != -1) { TerminLabel terminLabel = new TerminLabel(this); terminLabel.set(termin, idx); tlabels.add(terminLabel); } } TerminLabel.checkAllCollisions(tlabels); recalc(); } void recalc(){ if (tlabels != null) { ppm = AgendaParallel.getPixelPerMinute(); String startOfDayTimeInMinutes = CoreHub.globalCfg.get(PreferenceConstants.AG_DAY_PRESENTATION_STARTS_AT, "0000"); int sodtHours = Integer.parseInt(startOfDayTimeInMinutes.substring(0, 2)); int sodtMinutes = Integer.parseInt(startOfDayTimeInMinutes.substring(2)); int sodtM = (sodtHours * 60); sodtM += sodtMinutes; String endOfDayTimeInMinutes = CoreHub.globalCfg.get(PreferenceConstants.AG_DAY_PRESENTATION_ENDS_AT, "2359"); int eodtHours = Integer.parseInt(endOfDayTimeInMinutes.substring(0, 2)); int eodtMinutes = Integer.parseInt(endOfDayTimeInMinutes.substring(2)); int eodtM = (eodtHours * 60); eodtM += eodtMinutes; sheetHeight = (int) Math.round(ppm * (eodtM - sodtM)); ScrolledComposite sc = (ScrolledComposite) getParent(); Point mySize = getSize(); if (mySize.x > 0.0) { if (mySize.y != sheetHeight) { setSize(mySize.x, sheetHeight); sc.setMinSize(getSize()); } ScrollBar bar = sc.getVerticalBar(); int barWidth = 14; if (bar != null) { barWidth = bar.getSize().x; } resources = view.getDisplayedResources(); int count = resources.length; Point textSize = SWTHelper.getStringBounds(this, "88:88"); //$NON-NLS-1$ textWidth = textSize.x; left_offset = textWidth + 2; sheetWidth = mySize.x - 2 * left_offset - barWidth; widthPerColumn = sheetWidth / count; ColumnHeader header = view.getHeader(); header.recalc(widthPerColumn, left_offset, padding, textSize.y); for (TerminLabel l : tlabels) { l.refresh(); } sc.layout(); } } } public double getPixelPerMinute(){ return ppm; } public double getWidthPerColumn(){ return widthPerColumn; } public int getLeftOffset(){ return left_offset; } public int getPadding(){ return padding; } class TimePainter implements PaintListener { public void paintControl(PaintEvent e){ GC gc = e.gc; gc.fillRectangle(e.x, e.y, e.width, e.height); int y = 0; TimeTool runner = new TimeTool(); String dayStartsAt = CoreHub.globalCfg.get(PreferenceConstants.AG_DAY_PRESENTATION_STARTS_AT, "0000"); runner.set(dayStartsAt); //$NON-NLS-1$ String dayEndsAt = CoreHub.globalCfg.get(PreferenceConstants.AG_DAY_PRESENTATION_ENDS_AT, "2359"); TimeTool limit = new TimeTool(dayEndsAt); //$NON-NLS-1$ Point textSize = gc.textExtent("88:88"); //$NON-NLS-1$ int textwidth = textSize.x; int quarter = (int) Math.round(15.0 * AgendaParallel.getPixelPerMinute()); int w = ProportionalSheet.this.getSize().x - 5; int left = 0; int right = w - textwidth; while (runner.isBefore(limit)) { gc.drawLine(left, y, w, y); // volle Linie String time = runner.toString(TimeTool.TIME_SMALL); gc.drawText(time, 0, y + 1); gc.drawText(time, right, y + 1); y += quarter; gc.drawLine(textwidth - 3, y, textwidth, y); gc.drawLine(right, y, right + 3, y); y += quarter; gc.drawLine(textwidth - 6, y, textwidth, y); gc.drawLine(right, y, right + 6, y); y += quarter; gc.drawLine(textwidth - 3, y, textwidth, y); gc.drawLine(right, y, right + 3, y); y += quarter; runner.addHours(1); } addCurrentTimeline(gc, dayStartsAt); } } /** * adds a red horizontal line representing the current time * * @param gc * @param dayStartsAt */ private void addCurrentTimeline(GC gc, String dayStartsAt){ // calculate start of day time in minutes int sodtHours = Integer.parseInt(dayStartsAt.substring(0, 2)); int sodtMinutes = Integer.parseInt(dayStartsAt.substring(2)); int sodtM = (sodtHours * 60); sodtM += sodtMinutes; // calc current time line int w = ProportionalSheet.this.getSize().x - 5; int y = 0; Calendar c = Calendar.getInstance(); int minuteOfDay = c.get(Calendar.HOUR_OF_DAY) * 60 + c.get(Calendar.MINUTE); if (minuteOfDay < sodtM) { y = (int) getPixelPerMinute(); } else { int startMinute = minuteOfDay - sodtM; y = (int) Math.round(startMinute * getPixelPerMinute()); } gc.setForeground(UiDesk.getColor(UiDesk.COL_RED)); gc.drawLine(getLeftOffset() - 5, y, w, y); // create a horizontal red line (about full width) gc.setForeground(UiDesk.getColor(UiDesk.COL_BLACK)); } public Composite getComposite(){ return this; } }