Changeset 2109 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2009-09-12T23:13:00+02:00 (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/MapStatus.java
r2039 r2109 7 7 import java.awt.AWTEvent; 8 8 import java.awt.Color; 9 import java.awt.Component; 9 10 import java.awt.Cursor; 10 11 import java.awt.Dimension; … … 13 14 import java.awt.GridBagLayout; 14 15 import java.awt.Point; 16 import java.awt.SystemColor; 15 17 import java.awt.Toolkit; 16 18 import java.awt.event.AWTEventListener; … … 23 25 import java.awt.event.MouseListener; 24 26 import java.awt.event.MouseMotionListener; 25 import java. lang.reflect.InvocationTargetException;27 import java.util.ArrayList; 26 28 import java.util.Collection; 27 29 import java.util.ConcurrentModificationException; 30 import java.util.List; 28 31 import java.util.Map.Entry; 29 32 … … 113 116 private final class Collector implements Runnable { 114 117 /** 115 * The last object displayed in status line. 116 */ 117 Collection<OsmPrimitive> osmStatus; 118 /** 119 * The old modifiers that was pressed the last time this collector ran. 120 */ 121 private int oldModifiers; 118 * the mouse position of the previous iteration. This is used to show 119 * the popup until the cursor is moved. 120 */ 121 private Point oldMousePos; 122 /** 123 * Contains the labels that are currently shown in the information 124 * popup 125 */ 126 private List<JLabel> popupLabels = null; 122 127 /** 123 128 * The popup displayed to show additional information … … 144 149 if (parent != Main.map) 145 150 return; // exit, if new parent. 146 if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0 || ms.mousePos == null) { 147 continue; // freeze display when holding down ctrl 148 } 149 150 if (mv.center == null) { 151 152 // Do nothing, if required data is missing 153 if(ms.mousePos == null || mv.center == null) { 154 continue; 155 } 156 157 // Freeze display when holding down CTRL 158 if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0) { 159 // update the information popup's labels though, because 160 // the selection might have changed from the outside 161 popupUpdateLabels(); 151 162 continue; 152 163 } … … 157 168 // the data. 158 169 try { 159 OsmPrimitive osmNearest = null;160 170 // Set the text label in the bottom status bar 161 osmNearest = mv.getNearest(ms.mousePos); 162 if (osmNearest != null) { 163 nameText.setText(osmNearest.getDisplayName(DefaultNameFormatter.getInstance())); 164 } else { 165 nameText.setText(tr("(no object)")); 166 } 171 statusBarElementUpdate(ms); 172 173 // The popup != null check is required because a left-click 174 // produces several events as well, which would make this 175 // variable true. Of course we only want the popup to show 176 // if the middle mouse button has been pressed in the first 177 // place 178 boolean isAtOldPosition = (oldMousePos != null 179 && oldMousePos.equals(ms.mousePos) 180 && popup != null); 181 boolean middleMouseDown = (ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0; 182 167 183 168 184 // Popup Information 169 if ((ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0 ) { 185 // display them if the middle mouse button is pressed and 186 // keep them until the mouse is moved 187 if (middleMouseDown || isAtOldPosition) 188 { 170 189 Collection<OsmPrimitive> osms = mv.getAllNearest(ms.mousePos); 171 190 … … 173 192 continue; 174 193 } 175 if (osms.equals(osmStatus) && ms.modifiers == oldModifiers) { 176 continue; 194 195 JPanel c = new JPanel(new GridBagLayout()); 196 c.setBorder(BorderFactory.createRaisedBevelBorder()); 197 final JLabel lbl = new JLabel( 198 "<html>"+tr("Middle click again, to cycle through.<br>"+ 199 "Hold CTRL to select something from this list.<hr>")+"</html>", 200 null, 201 JLabel.HORIZONTAL 202 ); 203 lbl.setHorizontalAlignment(JLabel.LEFT); 204 c.add(lbl, GBC.eol().insets(2, 0, 2, 0)); 205 206 // Only cycle if the mouse has not been moved and the 207 // middle mouse button has been pressed at least twice 208 // (the reason for this is the popup != null check for 209 // isAtOldPosition, see above. This is a nice side 210 // effect though, because it does not change selection 211 // of the first middle click) 212 if(isAtOldPosition && middleMouseDown) { 213 popupCycleSelection(osms); 177 214 } 178 215 179 if (popup != null) { 180 try { 181 EventQueue.invokeAndWait(new Runnable() { 182 public void run() { 183 popup.hide(); 184 } 185 }); 186 } catch (InterruptedException e) { 187 } catch (InvocationTargetException e) { 188 throw new RuntimeException(e); 189 } 216 // These labels may need to be updated from the outside 217 // so collect them 218 List<JLabel> lbls = new ArrayList<JLabel>(); 219 for (final OsmPrimitive osm : osms) { 220 JLabel l = popupBuildPrimitiveLabels(osm); 221 lbls.add(l); 222 c.add(l, GBC.eol().fill(GBC.HORIZONTAL).insets(2, 0, 2, 2)); 190 223 } 191 224 192 JPanel c = new JPanel(new GridBagLayout()); 193 for (final OsmPrimitive osm : osms) { 194 final StringBuilder text = new StringBuilder(); 195 String name = osm.getDisplayName(DefaultNameFormatter.getInstance()); 196 if (osm.getId() == 0 || osm.isModified()) { 197 name = "<i><b>"+ osm.getDisplayName(DefaultNameFormatter.getInstance())+"*</b></i>"; 198 } 199 text.append(name); 200 if (osm.getId() != 0) { 201 text.append("<br>id="+osm.getId()); 202 } 203 for (Entry<String, String> e : osm.entrySet()) { 204 text.append("<br>"+e.getKey()+"="+e.getValue()); 205 } 206 final JLabel l = new JLabel( 207 "<html>"+text.toString()+"</html>", 208 ImageProvider.get(OsmPrimitiveType.from(osm)), 209 JLabel.HORIZONTAL 210 ); 211 l.setFont(l.getFont().deriveFont(Font.PLAIN)); 212 l.setVerticalTextPosition(JLabel.TOP); 213 l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 214 l.addMouseListener(new MouseAdapter(){ 215 @Override public void mouseEntered(MouseEvent e) { 216 l.setText("<html><u color='blue'>"+text.toString()+"</u></html>"); 217 } 218 @Override public void mouseExited(MouseEvent e) { 219 l.setText("<html>"+text.toString()+"</html>"); 220 } 221 @Override public void mouseClicked(MouseEvent e) { 222 Main.main.getCurrentDataSet().setSelected(osm); 223 mv.repaint(); 224 } 225 }); 226 c.add(l, GBC.eol()); 227 } 228 229 Point p = mv.getLocationOnScreen(); 230 popup = PopupFactory.getSharedInstance().getPopup(mv, c, p.x+ms.mousePos.x+16, p.y+ms.mousePos.y+16); 231 final Popup staticPopup = popup; 232 EventQueue.invokeLater(new Runnable(){ 233 public void run() { 234 staticPopup.show(); 235 } 236 }); 237 } else if (popup != null) { 238 final Popup staticPopup = popup; 239 popup = null; 240 EventQueue.invokeLater(new Runnable(){ 241 public void run() { 242 staticPopup.hide(); 243 } 244 }); 225 popupShowPopup(popupCreatePopup(c, ms), lbls); 226 } else { 227 popupHidePopup(); 245 228 } 229 230 oldMousePos = ms.mousePos; 246 231 } catch (ConcurrentModificationException x) { 232 //x.printStackTrace(); 247 233 } catch (NullPointerException x) { 248 } 249 } 234 //x.printStackTrace(); 235 } 236 } 237 } 238 239 /** 240 * Creates a popup for the given content next to the cursor. Tries to 241 * keep the popup on screen. 242 * @param content 243 * @param ms 244 * @return popup 245 */ 246 private final Popup popupCreatePopup(Component content, MouseState ms) { 247 Point p = mv.getLocationOnScreen(); 248 Dimension scrn = Toolkit.getDefaultToolkit().getScreenSize(); 249 Dimension dim = content.getPreferredSize(); 250 251 int xPos = p.x + ms.mousePos.x + 16; 252 // Display the popup to the left of the cursor if it would be cut 253 // off on its right 254 if(xPos + dim.width > scrn.width) { 255 xPos = p.x + ms.mousePos.x - 4 - dim.width; 256 } 257 int yPos = p.y + ms.mousePos.y + 16; 258 // Move the popup up if it would be cut off at its bottom 259 if(yPos + dim.height > scrn.height - 5) { 260 yPos = scrn.height - dim.height - 5; 261 } 262 263 PopupFactory pf = PopupFactory.getSharedInstance(); 264 return pf.getPopup(mv, content, xPos, yPos); 265 } 266 267 /** 268 * Calls this to update the element that is shown in the statusbar 269 * @param ms 270 */ 271 private final void statusBarElementUpdate(MouseState ms) { 272 final OsmPrimitive osmNearest = mv.getNearest(ms.mousePos); 273 if (osmNearest != null) { 274 nameText.setText(osmNearest.getDisplayName(DefaultNameFormatter.getInstance())); 275 } else { 276 nameText.setText(tr("(no object)")); 277 } 278 } 279 280 /** 281 * Call this with a set of primitives to cycle through them. Method 282 * will automatically select the next item and update the map 283 * @param osms 284 */ 285 private final void popupCycleSelection(Collection<OsmPrimitive> osms) { 286 // Find some items that are required for cycling through 287 OsmPrimitive firstItem = null; 288 OsmPrimitive firstSelected = null; 289 OsmPrimitive nextSelected = null; 290 for (final OsmPrimitive osm : osms) { 291 if(firstItem == null) { 292 firstItem = osm; 293 } 294 if(firstSelected != null && nextSelected == null) { 295 nextSelected = osm; 296 } 297 if(firstSelected == null && osm.isSelected()) { 298 firstSelected = osm; 299 } 300 } 301 302 // This will cycle through the available items. 303 if(firstSelected == null) { 304 firstItem.setSelected(true); 305 } else { 306 firstSelected.setSelected(false); 307 if(nextSelected != null) { 308 nextSelected.setSelected(true); 309 } 310 } 311 mv.repaint(); 312 } 313 314 /** 315 * Tries to hide the given popup 316 * @param popup 317 */ 318 private final void popupHidePopup() { 319 popupLabels = null; 320 if(popup == null) 321 return; 322 final Popup staticPopup = popup; 323 popup = null; 324 EventQueue.invokeLater(new Runnable(){ 325 public void run() { staticPopup.hide(); }}); 326 } 327 328 /** 329 * Tries to show the given popup, can be hidden using popupHideOldPopup 330 * If an old popup exists, it will be automatically hidden 331 * @param popup 332 */ 333 private final void popupShowPopup(Popup newPopup, List<JLabel> lbls) { 334 final Popup staticPopup = newPopup; 335 if(this.popup != null) { 336 // If an old popup exists, remove it when the new popup has been 337 // drawn to keep flickering to a minimum 338 final Popup staticOldPopup = this.popup; 339 EventQueue.invokeLater(new Runnable(){ 340 public void run() { 341 staticPopup.show(); 342 staticOldPopup.hide(); 343 } 344 }); 345 } else { 346 // There is no old popup 347 EventQueue.invokeLater(new Runnable(){ 348 public void run() { staticPopup.show(); }}); 349 } 350 this.popupLabels = lbls; 351 this.popup = newPopup; 352 } 353 354 /** 355 * This method should be called if the selection may have changed from 356 * outside of this class. This is the case when CTRL is pressed and the 357 * user clicks on the map instead of the popup. 358 */ 359 private final void popupUpdateLabels() { 360 if(this.popup == null || this.popupLabels == null) 361 return; 362 for(JLabel l : this.popupLabels) { 363 l.validate(); 364 } 365 } 366 367 /** 368 * Sets the colors for the given label depending on the selected status of 369 * the given OsmPrimitive 370 * 371 * @param lbl The label to color 372 * @param osm The primitive to derive the colors from 373 */ 374 private final void popupSetLabelColors(JLabel lbl, OsmPrimitive osm) { 375 if(osm.isSelected()) { 376 lbl.setBackground(SystemColor.textHighlight); 377 lbl.setForeground(SystemColor.textHighlightText); 378 } else { 379 lbl.setBackground(SystemColor.control); 380 lbl.setForeground(SystemColor.controlText); 381 } 382 } 383 384 /** 385 * Builds the labels with all necessary listeners for the info popup for the 386 * given OsmPrimitive 387 * @param osm The primitive to create the label for 388 * @return 389 */ 390 private final JLabel popupBuildPrimitiveLabels(final OsmPrimitive osm) { 391 final StringBuilder text = new StringBuilder(); 392 String name = osm.getDisplayName(DefaultNameFormatter.getInstance()); 393 if (osm.getId() == 0 || osm.isModified()) { 394 name = "<i><b>"+ osm.getDisplayName(DefaultNameFormatter.getInstance())+"*</b></i>"; 395 } 396 text.append(name); 397 if (osm.getId() != 0) { 398 text.append("<br>id="+osm.getId()); 399 } 400 for (Entry<String, String> e1 : osm.entrySet()) { 401 text.append("<br>"+e1.getKey()+"="+e1.getValue()); 402 } 403 404 final JLabel l = new JLabel( 405 "<html>"+text.toString()+"</html>", 406 ImageProvider.get(OsmPrimitiveType.from(osm)), 407 JLabel.HORIZONTAL 408 ) { 409 // This is necessary so the label updates its colors when the 410 // selection is changed from the outside 411 @Override public void validate() { 412 super.validate(); 413 popupSetLabelColors(this, osm); 414 } 415 }; 416 l.setOpaque(true); 417 popupSetLabelColors(l, osm); 418 l.setFont(l.getFont().deriveFont(Font.PLAIN)); 419 l.setVerticalTextPosition(JLabel.TOP); 420 l.setHorizontalAlignment(JLabel.LEFT); 421 l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 422 l.addMouseListener(new MouseAdapter(){ 423 @Override public void mouseEntered(MouseEvent e) { 424 l.setBackground(SystemColor.info); 425 l.setForeground(SystemColor.infoText); 426 } 427 @Override public void mouseExited(MouseEvent e) { 428 popupSetLabelColors(l, osm); 429 } 430 @Override public void mouseClicked(MouseEvent e) { 431 // Let the user toggle the selection 432 osm.setSelected(!osm.isSelected()); 433 mv.repaint(); 434 l.validate(); 435 } 436 }); 437 // Sometimes the mouseEntered event is not catched, thus the label 438 // will not be highlighted, making it confusing. The MotionListener 439 // can correct this defect. 440 l.addMouseMotionListener(new MouseMotionListener() { 441 public void mouseMoved(MouseEvent e) { 442 l.setBackground(SystemColor.info); 443 l.setForeground(SystemColor.infoText); 444 } 445 public void mouseDragged(MouseEvent e) { 446 l.setBackground(SystemColor.info); 447 l.setForeground(SystemColor.infoText); 448 } 449 }); 450 return l; 250 451 } 251 452 }
Note:
See TracChangeset
for help on using the changeset viewer.