|
Added
Link Here
|
| 1 |
/******************************************************************************* |
| 2 |
* Copyright (c) 2004 - 2006 Mylar committers and others. |
| 3 |
* All rights reserved. This program and the accompanying materials |
| 4 |
* are made available under the terms of the Eclipse Public License v1.0 |
| 5 |
* which accompanies this distribution, and is available at |
| 6 |
* http://www.eclipse.org/legal/epl-v10.html |
| 7 |
*******************************************************************************/ |
| 8 |
|
| 9 |
package org.eclipse.mylar.internal.tasks.ui.wizards; |
| 10 |
|
| 11 |
import java.util.Iterator; |
| 12 |
import java.util.LinkedList; |
| 13 |
import java.util.List; |
| 14 |
import java.util.StringTokenizer; |
| 15 |
|
| 16 |
import org.eclipse.jface.wizard.WizardPage; |
| 17 |
import org.eclipse.mylar.tasks.core.AbstractRepositoryTask; |
| 18 |
import org.eclipse.mylar.tasks.core.RepositoryTaskData; |
| 19 |
import org.eclipse.mylar.tasks.ui.TasksUiPlugin; |
| 20 |
import org.eclipse.swt.SWT; |
| 21 |
import org.eclipse.swt.events.SelectionEvent; |
| 22 |
import org.eclipse.swt.events.SelectionListener; |
| 23 |
import org.eclipse.swt.events.TreeEvent; |
| 24 |
import org.eclipse.swt.events.TreeListener; |
| 25 |
import org.eclipse.swt.graphics.GC; |
| 26 |
import org.eclipse.swt.graphics.Point; |
| 27 |
import org.eclipse.swt.graphics.Rectangle; |
| 28 |
import org.eclipse.swt.layout.FillLayout; |
| 29 |
import org.eclipse.swt.layout.GridData; |
| 30 |
import org.eclipse.swt.layout.GridLayout; |
| 31 |
import org.eclipse.swt.layout.RowLayout; |
| 32 |
import org.eclipse.swt.widgets.Composite; |
| 33 |
import org.eclipse.swt.widgets.Display; |
| 34 |
import org.eclipse.swt.widgets.Event; |
| 35 |
import org.eclipse.swt.widgets.Label; |
| 36 |
import org.eclipse.swt.widgets.Listener; |
| 37 |
import org.eclipse.swt.widgets.Shell; |
| 38 |
import org.eclipse.swt.widgets.TableItem; |
| 39 |
import org.eclipse.swt.widgets.Tree; |
| 40 |
import org.eclipse.swt.widgets.TreeColumn; |
| 41 |
import org.eclipse.swt.widgets.TreeItem; |
| 42 |
|
| 43 |
/** |
| 44 |
* |
| 45 |
* @author Jeff Pound |
| 46 |
*/ |
| 47 |
public class DisplayRelatedReportsPage extends WizardPage { |
| 48 |
|
| 49 |
private static final String PAGE_DESCRIPTION = "Select duplicate report candidates to open and comment on them. Otherwise press finish to create new one."; |
| 50 |
|
| 51 |
static final String PAGE_NAME = "DisplayRelatedReportsPage"; |
| 52 |
|
| 53 |
private static final String PAGE_TITLE = "Related Reports"; |
| 54 |
|
| 55 |
private static final int LINES_PER_ITEM = 3; |
| 56 |
|
| 57 |
private List<AbstractRepositoryTask> relatedTasks; |
| 58 |
|
| 59 |
private String[] columnHeaders = { "Related Reports" }; |
| 60 |
|
| 61 |
private Tree duplicatesTree; |
| 62 |
|
| 63 |
private int[] columnWidths = { 550 }; |
| 64 |
|
| 65 |
protected DisplayRelatedReportsPage() { |
| 66 |
super(PAGE_NAME); |
| 67 |
setTitle(PAGE_TITLE); |
| 68 |
setDescription(PAGE_DESCRIPTION); |
| 69 |
// Description doesn't show up without an image present TODO: proper image. |
| 70 |
setImageDescriptor(TasksUiPlugin.imageDescriptorFromPlugin("org.eclipse.mylar.bugzilla.ui", |
| 71 |
"icons/wizban/bug-wizard.gif")); |
| 72 |
} |
| 73 |
|
| 74 |
public void createControl(Composite parent) { |
| 75 |
Composite composite = new Composite(parent, SWT.NONE); |
| 76 |
composite.setLayout(new GridLayout()); |
| 77 |
setControl(composite); |
| 78 |
|
| 79 |
duplicatesTree = new Tree(composite, SWT.MULTI | SWT.CHECK | SWT.FULL_SELECTION); |
| 80 |
duplicatesTree.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| 81 |
|
| 82 |
RowLayout gl = new RowLayout(); |
| 83 |
gl.spacing = 30; |
| 84 |
duplicatesTree.setLayout(gl); |
| 85 |
|
| 86 |
duplicatesTree.setHeaderVisible(true); |
| 87 |
duplicatesTree.setLinesVisible(true); |
| 88 |
|
| 89 |
for (int i = 0; i < columnHeaders.length; i++) { |
| 90 |
TreeColumn column = new TreeColumn(duplicatesTree, SWT.NONE); |
| 91 |
column.setText(columnHeaders[i]); |
| 92 |
} |
| 93 |
|
| 94 |
/* |
| 95 |
* Adapted from snippet 227 |
| 96 |
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet227.java?rev=HEAD&content-type=text/vnd.viewcvs-markup |
| 97 |
*/ |
| 98 |
Listener paintListener = new Listener() { |
| 99 |
public void handleEvent(Event event) { |
| 100 |
switch (event.type) { |
| 101 |
case SWT.MeasureItem: { |
| 102 |
String text = ((TreeItem) event.item).getText(); |
| 103 |
Point size = event.gc.textExtent(text); |
| 104 |
event.width = size.x; |
| 105 |
event.height = Math.max(event.height, size.y); |
| 106 |
break; |
| 107 |
} |
| 108 |
case SWT.PaintItem: { |
| 109 |
String text = ((TreeItem) event.item).getText(); |
| 110 |
Point size = event.gc.textExtent(text); |
| 111 |
int offset2 = event.index == 0 ? Math.max(0, (event.height - size.y) / 2) : 0; |
| 112 |
event.gc.drawText(text, event.x, event.y + offset2, true); |
| 113 |
break; |
| 114 |
} |
| 115 |
case SWT.EraseItem: { |
| 116 |
event.detail &= ~SWT.FOREGROUND; |
| 117 |
break; |
| 118 |
} |
| 119 |
} |
| 120 |
} |
| 121 |
}; |
| 122 |
duplicatesTree.addListener(SWT.MeasureItem, paintListener); |
| 123 |
duplicatesTree.addListener(SWT.PaintItem, paintListener); |
| 124 |
duplicatesTree.addListener(SWT.EraseItem, paintListener); |
| 125 |
duplicatesTree.addTreeListener(new TreeListener() { |
| 126 |
public void treeCollapsed(TreeEvent arg0) { |
| 127 |
// packTable(); |
| 128 |
} |
| 129 |
|
| 130 |
public void treeExpanded(TreeEvent arg0) { |
| 131 |
// packTable(); |
| 132 |
} |
| 133 |
}); |
| 134 |
|
| 135 |
registerToolTipListeners(); |
| 136 |
} |
| 137 |
|
| 138 |
/* |
| 139 |
* Adapted from SWT snippet 125 |
| 140 |
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?rev=HEAD&content-type=text/vnd.viewcvs-markup |
| 141 |
*/ |
| 142 |
private void registerToolTipListeners() { |
| 143 |
final Shell shell = duplicatesTree.getShell(); |
| 144 |
final Display display = duplicatesTree.getDisplay(); |
| 145 |
|
| 146 |
// Disable native tooltip |
| 147 |
duplicatesTree.setToolTipText(""); |
| 148 |
|
| 149 |
// Implement a "fake" tooltip |
| 150 |
final Listener labelListener = new Listener() { |
| 151 |
public void handleEvent(Event event) { |
| 152 |
Label label = (Label) event.widget; |
| 153 |
Shell shell = label.getShell(); |
| 154 |
switch (event.type) { |
| 155 |
case SWT.MouseDown: |
| 156 |
Event e = new Event(); |
| 157 |
e.item = (TableItem) label.getData("_TABLEITEM"); |
| 158 |
// Assuming table is single select, set the selection as if |
| 159 |
// the mouse down event went through to the table |
| 160 |
duplicatesTree.setSelection(new TreeItem[] { (TreeItem) e.item }); |
| 161 |
duplicatesTree.notifyListeners(SWT.Selection, e); |
| 162 |
// fall through |
| 163 |
case SWT.MouseExit: |
| 164 |
shell.dispose(); |
| 165 |
break; |
| 166 |
} |
| 167 |
} |
| 168 |
}; |
| 169 |
|
| 170 |
Listener tableListener = new Listener() { |
| 171 |
Shell tip = null; |
| 172 |
|
| 173 |
Label label = null; |
| 174 |
|
| 175 |
public void handleEvent(Event event) { |
| 176 |
switch (event.type) { |
| 177 |
case SWT.Dispose: |
| 178 |
case SWT.KeyDown: |
| 179 |
case SWT.MouseMove: { |
| 180 |
if (tip == null) |
| 181 |
break; |
| 182 |
tip.dispose(); |
| 183 |
tip = null; |
| 184 |
label = null; |
| 185 |
break; |
| 186 |
} |
| 187 |
case SWT.MouseHover: { |
| 188 |
TreeItem item = duplicatesTree.getItem(new Point(event.x, event.y)); |
| 189 |
if (item != null) { |
| 190 |
if (tip != null && !tip.isDisposed()) |
| 191 |
tip.dispose(); |
| 192 |
tip = new Shell(shell, SWT.ON_TOP | SWT.TOOL); |
| 193 |
tip.setLayout(new FillLayout()); |
| 194 |
label = new Label(tip, SWT.NONE); |
| 195 |
label.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); |
| 196 |
label.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); |
| 197 |
label.setData("_TABLEITEM", item); |
| 198 |
label.setText((String) item.getData()); |
| 199 |
label.addListener(SWT.MouseExit, labelListener); |
| 200 |
label.addListener(SWT.MouseDown, labelListener); |
| 201 |
Point size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT); |
| 202 |
Rectangle rect = item.getBounds(0); |
| 203 |
Point pt = duplicatesTree.toDisplay(rect.x, rect.y); |
| 204 |
tip.setBounds(pt.x, pt.y, size.x, size.y); |
| 205 |
tip.setVisible(true); |
| 206 |
} |
| 207 |
} |
| 208 |
} |
| 209 |
} |
| 210 |
}; |
| 211 |
duplicatesTree.addListener(SWT.Dispose, tableListener); |
| 212 |
duplicatesTree.addListener(SWT.KeyDown, tableListener); |
| 213 |
duplicatesTree.addListener(SWT.MouseMove, tableListener); |
| 214 |
duplicatesTree.addListener(SWT.MouseHover, tableListener); |
| 215 |
|
| 216 |
} |
| 217 |
|
| 218 |
private void packTable() { |
| 219 |
for (int i = 0; i < columnHeaders.length; i++) { |
| 220 |
// duplicatesTree.getColumn(i).pack(); |
| 221 |
duplicatesTree.getColumn(i).setWidth(columnWidths[i]); |
| 222 |
} |
| 223 |
duplicatesTree.redraw(); |
| 224 |
} |
| 225 |
|
| 226 |
public List<AbstractRepositoryTask> getRelatedTasks() { |
| 227 |
return relatedTasks; |
| 228 |
} |
| 229 |
|
| 230 |
public void setRelatedTasks(List<AbstractRepositoryTask> relatedTasks) { |
| 231 |
duplicatesTree.removeAll(); |
| 232 |
this.relatedTasks = relatedTasks; |
| 233 |
if (duplicatesTree == null || this.relatedTasks == null) { |
| 234 |
return; |
| 235 |
} |
| 236 |
|
| 237 |
SelectionListener selectAllListener = new SelectionListener() { |
| 238 |
public void widgetDefaultSelected(SelectionEvent arg0) { |
| 239 |
// ignore |
| 240 |
} |
| 241 |
|
| 242 |
public void widgetSelected(SelectionEvent event) { |
| 243 |
TreeItem item = (TreeItem) event.item; |
| 244 |
if (item.getParentItem() != null) { |
| 245 |
item.getParentItem().setChecked(item.getChecked()); |
| 246 |
} |
| 247 |
if (item.getItems() != null && item.getItems().length > 0) { |
| 248 |
item.getItems()[0].setChecked(item.getChecked()); |
| 249 |
} |
| 250 |
} |
| 251 |
}; |
| 252 |
duplicatesTree.addSelectionListener(selectAllListener); |
| 253 |
|
| 254 |
// update the table |
| 255 |
Iterator<AbstractRepositoryTask> iter = this.relatedTasks.iterator(); |
| 256 |
while (iter.hasNext()) { |
| 257 |
AbstractRepositoryTask task = iter.next(); |
| 258 |
RepositoryTaskData taskData = task.getTaskData(); |
| 259 |
if (taskData == null) { |
| 260 |
iter.remove(); |
| 261 |
continue; |
| 262 |
} |
| 263 |
TreeItem item = new TreeItem(duplicatesTree, SWT.NONE); |
| 264 |
item.setText(new String[] { formatTreeText(taskData.getSummary(), LINES_PER_ITEM - 1) }); |
| 265 |
TreeItem descItem = new TreeItem(item, SWT.NONE); |
| 266 |
descItem.setText(formatTreeText(taskData.getDescription())); |
| 267 |
descItem.setGrayed(true); |
| 268 |
|
| 269 |
String toolTip = "Report Id: " + taskData.getId() + "\nCreated on: " + taskData.getCreated() |
| 270 |
+ "\nCreated by: " + taskData.getReporter() + "\nAssigned to: " + taskData.getAssignedTo(); |
| 271 |
item.setData(toolTip); |
| 272 |
descItem.setData(toolTip); |
| 273 |
} |
| 274 |
packTable(); |
| 275 |
} |
| 276 |
|
| 277 |
private String formatTreeText(String text) { |
| 278 |
return formatTreeText(text, LINES_PER_ITEM); |
| 279 |
} |
| 280 |
|
| 281 |
private String formatTreeText(String text, int maxLines) { |
| 282 |
GC gc = new GC(duplicatesTree); |
| 283 |
int avgCharWidth = gc.getFontMetrics().getAverageCharWidth(); |
| 284 |
gc.dispose(); |
| 285 |
|
| 286 |
text = text.replace("\n", " "); |
| 287 |
StringTokenizer strtok = new StringTokenizer(text); |
| 288 |
StringBuffer formatText = new StringBuffer(); |
| 289 |
int charsPerLine = columnWidths[0] / avgCharWidth; |
| 290 |
int lines = 0; |
| 291 |
|
| 292 |
// character wrap |
| 293 |
// while (strtok.hasMoreTokens() && lines < maxLines) { |
| 294 |
// String line = strtok.nextToken(); |
| 295 |
// while (line.length() > charsPerLine && lines < maxLines) { |
| 296 |
// String trimmedLine = line.substring(0, charsPerLine); |
| 297 |
// formatText.append(trimmedLine + "\n"); |
| 298 |
// lines++; |
| 299 |
// line = line.substring(charsPerLine); |
| 300 |
// } |
| 301 |
// formatText.append(line + "\n"); |
| 302 |
// lines++; |
| 303 |
// } |
| 304 |
|
| 305 |
// word wrap |
| 306 |
StringBuffer line = new StringBuffer(); |
| 307 |
while (strtok.hasMoreTokens() && lines < maxLines) { |
| 308 |
String word = strtok.nextToken(); |
| 309 |
while (strtok.hasMoreTokens() && line.length() + word.length() < charsPerLine) { |
| 310 |
line.append(word + ((line.length() + word.length() + 1 > charsPerLine) ? "" : " ")); |
| 311 |
word = strtok.nextToken(); |
| 312 |
} |
| 313 |
if (!strtok.hasMoreTokens()) { |
| 314 |
line.append(word); |
| 315 |
} |
| 316 |
formatText.append(((formatText.length() == 0) ? "" : "\n") + line.toString()); |
| 317 |
lines++; |
| 318 |
line.delete(0, line.length()); |
| 319 |
line.append(word + " "); |
| 320 |
} |
| 321 |
|
| 322 |
// pad elements to ensure no weird repaint artifacts |
| 323 |
// this also centres text in the element (aligned with tree arrow) |
| 324 |
if (lines < LINES_PER_ITEM) { |
| 325 |
int diff = LINES_PER_ITEM - lines; |
| 326 |
for (int i = 0; i < diff / 2; i++) { |
| 327 |
formatText.append("\n "); |
| 328 |
lines++; |
| 329 |
} |
| 330 |
while (lines < LINES_PER_ITEM) { |
| 331 |
formatText.insert(0, " \n"); |
| 332 |
lines++; |
| 333 |
} |
| 334 |
lines++; |
| 335 |
} |
| 336 |
|
| 337 |
// add "..." if we're cutting off the text |
| 338 |
if (strtok.hasMoreTokens()) { |
| 339 |
formatText.replace(formatText.length() - 4, formatText.length(), "..."); |
| 340 |
} |
| 341 |
|
| 342 |
return formatText.toString(); |
| 343 |
} |
| 344 |
|
| 345 |
public List<AbstractRepositoryTask> getSelectedReports() { |
| 346 |
List<AbstractRepositoryTask> selected = new LinkedList<AbstractRepositoryTask>(); |
| 347 |
TreeItem[] items = duplicatesTree.getItems(); |
| 348 |
|
| 349 |
for (int i = 0; i < items.length; i++) { |
| 350 |
if (items[i].getChecked()) { |
| 351 |
selected.add(relatedTasks.get(i)); |
| 352 |
} |
| 353 |
} |
| 354 |
|
| 355 |
return selected; |
| 356 |
} |
| 357 |
} |