001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.plugins.design.ui.page.client; 021 022 import com.google.gwt.event.dom.client.ClickEvent; 023 import com.google.gwt.event.dom.client.ClickHandler; 024 import com.google.gwt.event.dom.client.DoubleClickEvent; 025 import com.google.gwt.event.dom.client.DoubleClickHandler; 026 import com.google.gwt.i18n.client.Dictionary; 027 import com.google.gwt.user.client.Window; 028 import com.google.gwt.user.client.ui.*; 029 import org.sonar.gwt.Links; 030 import org.sonar.gwt.ui.Icons; 031 032 import java.util.LinkedList; 033 import java.util.List; 034 035 public class Dsm extends Composite { 036 037 /* STYLES */ 038 public static final String DSM = "dsm"; 039 040 public static final String HEADER = "htable"; 041 public static final String HEADER_TITLE = "ht"; 042 public static final String HEADER_SELECTED_SUFFIX = "s"; 043 public static final String HEADER_INDICATOR = "hi"; 044 public static final String HEADER_HIGHER_INDICATOR_SUFFIX = "h"; 045 public static final String HEADER_LOWER_INDICATOR_SUFFIX = "l"; 046 047 public static final String GRID = "gtable"; 048 public static final String GRID_CELL_BOTTOM_LEFT = "cbl"; 049 public static final String GRID_CELL_TOP_RIGHT = "ctr"; 050 public static final String GRID_CELL_DIAGONAL = "cd"; 051 public static final String GRID_CELL_SELECTION1_SUFFIX = "s1"; 052 public static final String GRID_CELL_SELECTION2_SUFFIX = "s2"; 053 public static final String GRID_CELL_COMB1_SUFFIX = "c1"; 054 public static final String GRID_CELL_COMB2_SUFFIX = "c2"; 055 public static final String[] GRID_SUFFIXES = {GRID_CELL_SELECTION1_SUFFIX, GRID_CELL_SELECTION2_SUFFIX, GRID_CELL_COMB1_SUFFIX, GRID_CELL_COMB2_SUFFIX}; 056 057 058 private VerticalPanel dsm = new VerticalPanel(); 059 private DsmData.Rows data; 060 private Label[][] cells; 061 private Label[] titles; 062 private Label[] indicators; 063 private List<Label> highlightedCells = new LinkedList<Label>(); 064 065 public Dsm() { 066 dsm.setStylePrimaryName(DSM); 067 initWidget(dsm); 068 } 069 070 private Widget createLegend() { 071 Dictionary l10n = Dictionary.getDictionary("l10n"); 072 HorizontalPanel legend = new HorizontalPanel(); 073 legend.getElement().setId("dsmlegend"); 074 legend.add(new HTML("<div class='square gray'> </div>")); 075 legend.add(new Label(l10n.get("design.legend.dependencies"))); 076 legend.add(new HTML("<div class='space'></div>")); 077 legend.add(new HTML("<div class='square red'> </div> ")); 078 legend.add(new Label(l10n.get("design.legend.cycles"))); 079 legend.add(new HTML(" <div class='space'></div> ")); 080 legend.add(new HTML("<div class='square green'></div> ")); 081 legend.add(new Label(l10n.get("design.legend.uses"))); 082 legend.add(new HTML("<div class='square blue'></div> ")); 083 legend.add(new Label(l10n.get("design.legend.uses"))); 084 legend.add(new HTML(" <div class='square yellow'></div>")); 085 return legend; 086 } 087 088 public void displayNoData() { 089 dsm.clear(); 090 dsm.add(new Label(Dictionary.getDictionary("l10n").get("noData"))); 091 } 092 093 public void display(DsmData.Rows data) { 094 if (data == null) { 095 displayNoData(); 096 097 } else { 098 this.data = data; 099 dsm.clear(); 100 dsm.add(createHelp()); 101 dsm.add(createLegend()); 102 HorizontalPanel matrix = new HorizontalPanel(); 103 matrix.add(createRowHeader()); 104 matrix.add(createGrid()); 105 dsm.add(matrix); 106 } 107 } 108 109 private Widget createHelp() { 110 HorizontalPanel help = new HorizontalPanel(); 111 help.getElement().setId("dsmhelp"); 112 Dictionary l10n = Dictionary.getDictionary("l10n"); 113 Anchor link = new Anchor(l10n.get("design.help"), "http://docs.codehaus.org/x/QQFhC", "docsonar"); 114 help.add(Icons.get().help().createImage()); 115 help.add(link); 116 return help; 117 } 118 119 private Grid createRowHeader() { 120 Grid header = new Grid(data.size(), 2); 121 header.setCellPadding(0); 122 header.setCellSpacing(0); 123 header.setStylePrimaryName(HEADER); 124 125 titles = new Label[data.size()]; 126 indicators = new Label[data.size()]; 127 for (int indexRow = 0; indexRow < data.size(); indexRow++) { 128 DsmData.Row row = data.get(indexRow); 129 130 HTML title = buildRowTitle(indexRow, row); 131 titles[indexRow] = title; 132 header.setWidget(indexRow, 0, title); 133 134 Label indicator = buildLabel("", HEADER_INDICATOR); 135 header.setWidget(indexRow, 1, indicator); 136 indicators[indexRow] = indicator; 137 } 138 return header; 139 } 140 141 private Grid createGrid() { 142 int rowsCount = data.size(); 143 Grid grid = new Grid(rowsCount, rowsCount); 144 grid.setCellPadding(0); 145 grid.setCellSpacing(0); 146 grid.setStylePrimaryName(GRID); 147 148 return loadGridCells(grid, data); 149 } 150 151 private Grid loadGridCells(Grid grid, DsmData.Rows data) { 152 int size = data.size(); 153 cells = new Label[size][size]; 154 for (int row = 0; row < size; row++) { 155 DsmData.Row resource = data.get(row); 156 for (int col = 0; col < resource.size(); col++) { 157 Label cell = createGridCell(row, col, resource.getWeight(col)); 158 grid.setWidget(row, col, cell); 159 cells[row][col] = cell; 160 } 161 } 162 return grid; 163 } 164 165 166 /* ---------------- ACTIONS -------------------- */ 167 168 public void onCellClicked(int row, int col) { 169 cancelHighlighting(); 170 171 highlightTitle(row); 172 highlightTitle(col); 173 highlightIndicator(row); 174 highlightIndicator(col); 175 176 for (int i = 0; i < cells.length; i++) { 177 for (int j = 0; j < cells.length; j++) { 178 Label cell = cells[i][j]; 179 if (i == row && j == col) { 180 highlightCell(cell, GRID_CELL_SELECTION1_SUFFIX); 181 182 } else if (j == row && i == col) { 183 // opposite 184 highlightCell(cell, GRID_CELL_SELECTION1_SUFFIX); 185 186 } else if (j == col || i == col) { 187 highlightCell(cell, GRID_CELL_COMB1_SUFFIX); 188 189 } else if (i == row || j == row) { 190 highlightCell(cell, GRID_CELL_COMB2_SUFFIX); 191 } 192 } 193 } 194 } 195 196 private void displayDependencyInfo(int row, int col) { 197 DsmData.Cell cell = data.get(row).getCell(col); 198 DependencyInfo.getInstance().showOrPopup(cell.getDependencyId()); 199 } 200 201 public void onTitleClicked(int row) { 202 cancelHighlighting(); 203 highlightTitle(row); 204 highlightIndicator(row); 205 206 // highlight row 207 for (int col = 0; col < cells[row].length; col++) { 208 highlightCell(cells[row][col], GRID_CELL_SELECTION2_SUFFIX); 209 if (col < row && hasWeight(cells[row][col])) { 210 highlightIndicator(col, true); 211 } 212 } 213 214 // highlight column 215 for (int i = 0; i < cells.length; i++) { 216 if (i != row) { 217 highlightCell(cells[i][row], GRID_CELL_SELECTION2_SUFFIX); 218 if (i > row && hasWeight(cells[i][row])) { 219 highlightIndicator(i, false); 220 } 221 } 222 } 223 } 224 225 private boolean hasWeight(Label label) { 226 return label.getText().length() > 0; 227 } 228 229 230 /*--------- EFFECTS ----------*/ 231 private void cancelHighlighting() { 232 cancelGridHighlighting(); 233 cancelIndicatorsHighlighting(); 234 cancelTitlesHighlighting(); 235 } 236 237 private void cancelGridHighlighting() { 238 for (Label cell : highlightedCells) { 239 for (String suffix : GRID_SUFFIXES) { 240 cell.removeStyleDependentName(suffix); 241 } 242 } 243 highlightedCells.clear(); 244 } 245 246 private void highlightCell(Label cell, String style) { 247 cell.addStyleDependentName(style); 248 highlightedCells.add(cell); 249 } 250 251 private void highlightTitle(int row) { 252 titles[row].addStyleDependentName(HEADER_SELECTED_SUFFIX); 253 } 254 255 private void cancelTitlesHighlighting() { 256 for (Label title : titles) { 257 title.removeStyleDependentName(HEADER_SELECTED_SUFFIX); 258 } 259 } 260 261 private void cancelIndicatorsHighlighting() { 262 for (Label indicator : indicators) { 263 indicator.removeStyleDependentName(HEADER_HIGHER_INDICATOR_SUFFIX); 264 indicator.removeStyleDependentName(HEADER_LOWER_INDICATOR_SUFFIX); 265 indicator.removeStyleDependentName(HEADER_SELECTED_SUFFIX); 266 } 267 } 268 269 private void highlightIndicator(int row) { 270 indicators[row].addStyleDependentName(HEADER_SELECTED_SUFFIX); 271 } 272 273 private void highlightIndicator(int row, boolean higher) { 274 indicators[row].addStyleDependentName(higher ? HEADER_HIGHER_INDICATOR_SUFFIX : HEADER_LOWER_INDICATOR_SUFFIX); 275 } 276 277 278 /* ---------- COMPONENTS ------------ */ 279 private Label createGridCell(final int row, final int col, final int weight) { 280 Label cell; 281 if (row == col) { 282 cell = createDiagonalCell(row); 283 284 } else { 285 cell = createNonDiagonalCell(row, col, weight); 286 } 287 return cell; 288 } 289 290 private Label createNonDiagonalCell(final int row, final int col, int weight) { 291 Label cell; 292 cell = buildCell(row, col, weight, (col > row ? GRID_CELL_TOP_RIGHT : GRID_CELL_BOTTOM_LEFT)); 293 294 if (weight > 0) { 295 String tooltip = data.get(col).getName() + " -> " + data.get(row).getName() + " (" + weight + "). " + Dictionary.getDictionary("l10n").get("design.cellTooltip"); 296 cell.setTitle(tooltip); 297 } 298 return cell; 299 } 300 301 private Label createDiagonalCell(final int row) { 302 Label cell; 303 cell = buildLabel("-", GRID_CELL_DIAGONAL); 304 cell.addClickHandler(new ClickHandler() { 305 public void onClick(final ClickEvent event) { 306 onTitleClicked(row); 307 } 308 }); 309 return cell; 310 } 311 312 private HTML buildRowTitle(final int indexRow, final DsmData.Row row) { 313 HTML title = new HTML(Icons.forQualifier(row.getQualifier()).getHTML() + " " + row.getName()) { 314 { 315 addDomHandler(new DoubleClickHandler() { 316 public void onDoubleClick(DoubleClickEvent pEvent) { 317 if (row.getId() != null) { 318 if (!"FIL".equals(row.getQualifier()) && !"CLA".equals(row.getQualifier())) { 319 Window.Location.assign(Links.urlForResourcePage(row.getId(), DesignPage.GWT_ID, null)); 320 } else { 321 Links.openMeasurePopup(row.getId(), null); 322 } 323 } 324 } 325 }, DoubleClickEvent.getType()); 326 } 327 }; 328 title.setStylePrimaryName(HEADER_TITLE); 329 title.setTitle(Dictionary.getDictionary("l10n").get("design.rowTooltip")); 330 final int finalIndexRow = indexRow; 331 title.addClickHandler(new ClickHandler() { 332 public void onClick(ClickEvent event) { 333 onTitleClicked(finalIndexRow); 334 } 335 }); 336 return title; 337 } 338 339 private static Label buildLabel(String text, String primaryStyle) { 340 Label label = new Label(text); 341 label.setStylePrimaryName(primaryStyle); 342 return label; 343 } 344 345 private Label buildCell(final int row, final int col, int weight, String primaryStyle) { 346 String text = ""; 347 if (weight > 0) { 348 text = "<span>" + Integer.toString(weight) + "</span>"; 349 } 350 351 HTML cell = new HTML(text) { 352 { 353 addDomHandler(new DoubleClickHandler() { 354 public void onDoubleClick(DoubleClickEvent pEvent) { 355 displayDependencyInfo(row, col); 356 } 357 }, DoubleClickEvent.getType()); 358 } 359 }; 360 cell.addClickHandler(new ClickHandler() { 361 public void onClick(final ClickEvent event) { 362 onCellClicked(row, col); 363 } 364 }); 365 cell.setStylePrimaryName(primaryStyle); 366 return cell; 367 } 368 369 }