40 |
|
|
41 |
import java.awt.FontMetrics; |
import java.awt.FontMetrics; |
42 |
import java.awt.Graphics; |
import java.awt.Graphics; |
43 |
|
import java.text.BreakIterator; |
44 |
|
|
45 |
/** |
/** |
46 |
* A set of utilities to deal with text. This is used by several other classes |
* A set of utilities to deal with text. This is used by several other classes |
232 |
// At the end of the for loop, this holds the requested model location |
// At the end of the for loop, this holds the requested model location |
233 |
int pos; |
int pos; |
234 |
int currentX = x0; |
int currentX = x0; |
235 |
for (pos = p0; pos < s.getEndIndex(); pos++) |
|
236 |
|
for (pos = p0; pos < s.count; pos++) |
237 |
{ |
{ |
238 |
char nextChar = s.array[pos]; |
char nextChar = s.array[s.offset+pos]; |
239 |
if (nextChar != '\n') |
if (nextChar == 0) |
240 |
|
{ |
241 |
|
if (! round) |
242 |
|
pos--; |
243 |
|
break; |
244 |
|
} |
245 |
|
if (nextChar != '\t') |
246 |
currentX += fm.charWidth(nextChar); |
currentX += fm.charWidth(nextChar); |
247 |
else |
else |
248 |
{ |
{ |
251 |
else |
else |
252 |
currentX = (int) te.nextTabStop(currentX, pos); |
currentX = (int) te.nextTabStop(currentX, pos); |
253 |
} |
} |
254 |
if (currentX >= x) |
if (currentX > x) |
255 |
{ |
{ |
256 |
if (! round) |
if (! round) |
257 |
pos--; |
pos--; |
288 |
{ |
{ |
289 |
return getTabbedTextOffset(s, fm, x0, x, te, p0, true); |
return getTabbedTextOffset(s, fm, x0, x, te, p0, true); |
290 |
} |
} |
291 |
|
|
292 |
|
/** |
293 |
|
* Finds the start of the next word for the given offset. |
294 |
|
* |
295 |
|
* @param c |
296 |
|
* the text component |
297 |
|
* @param offs |
298 |
|
* the offset in the document |
299 |
|
* @return the location in the model of the start of the next word. |
300 |
|
* @throws BadLocationException |
301 |
|
* if the offset is invalid. |
302 |
|
*/ |
303 |
|
public static final int getNextWord(JTextComponent c, int offs) |
304 |
|
throws BadLocationException |
305 |
|
{ |
306 |
|
if (offs < 0 || offs > (c.getText().length() - 1)) |
307 |
|
throw new BadLocationException("invalid offset specified", offs); |
308 |
|
String text = c.getText(); |
309 |
|
BreakIterator wb = BreakIterator.getWordInstance(); |
310 |
|
wb.setText(text); |
311 |
|
int last = wb.following(offs); |
312 |
|
int current = wb.next(); |
313 |
|
while (current != BreakIterator.DONE) |
314 |
|
{ |
315 |
|
for (int i = last; i < current; i++) |
316 |
|
{ |
317 |
|
// FIXME: Should use isLetter(int) and text.codePointAt(int) |
318 |
|
// instead, but isLetter(int) isn't implemented yet |
319 |
|
if (Character.isLetter(text.charAt(i))) |
320 |
|
return last; |
321 |
|
} |
322 |
|
last = current; |
323 |
|
current = wb.next(); |
324 |
|
} |
325 |
|
return BreakIterator.DONE; |
326 |
|
} |
327 |
|
|
328 |
|
/** |
329 |
|
* Finds the start of the previous word for the given offset. |
330 |
|
* |
331 |
|
* @param c |
332 |
|
* the text component |
333 |
|
* @param offs |
334 |
|
* the offset in the document |
335 |
|
* @return the location in the model of the start of the previous word. |
336 |
|
* @throws BadLocationException |
337 |
|
* if the offset is invalid. |
338 |
|
*/ |
339 |
|
public static final int getPreviousWord(JTextComponent c, int offs) |
340 |
|
throws BadLocationException |
341 |
|
{ |
342 |
|
if (offs < 0 || offs > (c.getText().length() - 1)) |
343 |
|
throw new BadLocationException("invalid offset specified", offs); |
344 |
|
String text = c.getText(); |
345 |
|
BreakIterator wb = BreakIterator.getWordInstance(); |
346 |
|
wb.setText(text); |
347 |
|
int last = wb.preceding(offs); |
348 |
|
int current = wb.previous(); |
349 |
|
|
350 |
|
while (current != BreakIterator.DONE) |
351 |
|
{ |
352 |
|
for (int i = last; i < offs; i++) |
353 |
|
{ |
354 |
|
// FIXME: Should use isLetter(int) and text.codePointAt(int) |
355 |
|
// instead, but isLetter(int) isn't implemented yet |
356 |
|
if (Character.isLetter(text.charAt(i))) |
357 |
|
return last; |
358 |
|
} |
359 |
|
last = current; |
360 |
|
current = wb.previous(); |
361 |
|
} |
362 |
|
return 0; |
363 |
|
} |
364 |
|
|
365 |
|
/** |
366 |
|
* Finds the start of a word for the given location. |
367 |
|
* @param c the text component |
368 |
|
* @param offs the offset location |
369 |
|
* @return the location of the word beginning |
370 |
|
* @throws BadLocationException if the offset location is invalid |
371 |
|
*/ |
372 |
|
public static final int getWordStart(JTextComponent c, int offs) |
373 |
|
throws BadLocationException |
374 |
|
{ |
375 |
|
if (offs < 0 || offs >= c.getText().length()) |
376 |
|
throw new BadLocationException("invalid offset specified", offs); |
377 |
|
|
378 |
|
String text = c.getText(); |
379 |
|
BreakIterator wb = BreakIterator.getWordInstance(); |
380 |
|
wb.setText(text); |
381 |
|
if (wb.isBoundary(offs)) |
382 |
|
return offs; |
383 |
|
return wb.preceding(offs); |
384 |
|
} |
385 |
|
|
386 |
|
/** |
387 |
|
* Finds the end of a word for the given location. |
388 |
|
* @param c the text component |
389 |
|
* @param offs the offset location |
390 |
|
* @return the location of the word end |
391 |
|
* @throws BadLocationException if the offset location is invalid |
392 |
|
*/ |
393 |
|
public static final int getWordEnd(JTextComponent c, int offs) |
394 |
|
throws BadLocationException |
395 |
|
{ |
396 |
|
if (offs < 0 || offs >= c.getText().length()) |
397 |
|
throw new BadLocationException("invalid offset specified", offs); |
398 |
|
|
399 |
|
String text = c.getText(); |
400 |
|
BreakIterator wb = BreakIterator.getWordInstance(); |
401 |
|
wb.setText(text); |
402 |
|
return wb.following(offs); |
403 |
|
} |
404 |
|
|
405 |
|
/** |
406 |
|
* Get the model position of the end of the row that contains the |
407 |
|
* specified model position. Return null if the given JTextComponent |
408 |
|
* does not have a size. |
409 |
|
* @param c the JTextComponent |
410 |
|
* @param offs the model position |
411 |
|
* @return the model position of the end of the row containing the given |
412 |
|
* offset |
413 |
|
* @throws BadLocationException if the offset is invalid |
414 |
|
*/ |
415 |
|
public static final int getRowEnd(JTextComponent c, int offs) |
416 |
|
throws BadLocationException |
417 |
|
{ |
418 |
|
String text = c.getText(); |
419 |
|
if (text == null) |
420 |
|
return -1; |
421 |
|
|
422 |
|
// Do a binary search for the smallest position X > offs |
423 |
|
// such that that character at positino X is not on the same |
424 |
|
// line as the character at position offs |
425 |
|
int high = offs + ((text.length() - 1 - offs) / 2); |
426 |
|
int low = offs; |
427 |
|
int oldHigh = text.length() + 1; |
428 |
|
while (true) |
429 |
|
{ |
430 |
|
if (c.modelToView(high).y != c.modelToView(offs).y) |
431 |
|
{ |
432 |
|
oldHigh = high; |
433 |
|
high = low + ((high + 1 - low) / 2); |
434 |
|
if (oldHigh == high) |
435 |
|
return high - 1; |
436 |
|
} |
437 |
|
else |
438 |
|
{ |
439 |
|
low = high; |
440 |
|
high += ((oldHigh - high) / 2); |
441 |
|
if (low == high) |
442 |
|
return low; |
443 |
|
} |
444 |
|
} |
445 |
|
} |
446 |
|
|
447 |
|
/** |
448 |
|
* Get the model position of the start of the row that contains the specified |
449 |
|
* model position. Return null if the given JTextComponent does not have a |
450 |
|
* size. |
451 |
|
* |
452 |
|
* @param c the JTextComponent |
453 |
|
* @param offs the model position |
454 |
|
* @return the model position of the start of the row containing the given |
455 |
|
* offset |
456 |
|
* @throws BadLocationException if the offset is invalid |
457 |
|
*/ |
458 |
|
public static final int getRowStart(JTextComponent c, int offs) |
459 |
|
throws BadLocationException |
460 |
|
{ |
461 |
|
String text = c.getText(); |
462 |
|
if (text == null) |
463 |
|
return -1; |
464 |
|
|
465 |
|
// Do a binary search for the greatest position X < offs |
466 |
|
// such that the character at position X is not on the same |
467 |
|
// row as the character at position offs |
468 |
|
int high = offs; |
469 |
|
int low = 0; |
470 |
|
int oldLow = 0; |
471 |
|
while (true) |
472 |
|
{ |
473 |
|
if (c.modelToView(low).y != c.modelToView(offs).y) |
474 |
|
{ |
475 |
|
oldLow = low; |
476 |
|
low = high - ((high + 1 - low) / 2); |
477 |
|
if (oldLow == low) |
478 |
|
return low + 1; |
479 |
|
} |
480 |
|
else |
481 |
|
{ |
482 |
|
high = low; |
483 |
|
low -= ((low - oldLow) / 2); |
484 |
|
if (low == high) |
485 |
|
return low; |
486 |
|
} |
487 |
|
} |
488 |
|
} |
489 |
|
|
490 |
|
/** |
491 |
|
* Determine where to break the text in the given Segment, attempting to find |
492 |
|
* a word boundary. |
493 |
|
* @param s the Segment that holds the text |
494 |
|
* @param metrics the font metrics used for calculating the break point |
495 |
|
* @param x0 starting view location representing the start of the text |
496 |
|
* @param x the target view location |
497 |
|
* @param e the TabExpander used for expanding tabs (if this is null tabs |
498 |
|
* are expanded to 1 space) |
499 |
|
* @param startOffset the offset in the Document of the start of the text |
500 |
|
* @return the offset at which we should break the text |
501 |
|
*/ |
502 |
|
public static final int getBreakLocation(Segment s, FontMetrics metrics, |
503 |
|
int x0, int x, TabExpander e, |
504 |
|
int startOffset) |
505 |
|
{ |
506 |
|
int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset); |
507 |
|
BreakIterator breaker = BreakIterator.getWordInstance(); |
508 |
|
breaker.setText(s.toString()); |
509 |
|
|
510 |
|
// If mark is equal to the end of the string, just use that position |
511 |
|
if (mark == s.count) |
512 |
|
return mark; |
513 |
|
|
514 |
|
// Try to find a word boundary previous to the mark at which we |
515 |
|
// can break the text |
516 |
|
int preceding = breaker.preceding(mark + 1); |
517 |
|
|
518 |
|
if (preceding != 0) |
519 |
|
return preceding; |
520 |
|
else |
521 |
|
// If preceding is 0 we couldn't find a suitable word-boundary so |
522 |
|
// just break it on the character boundary |
523 |
|
return mark; |
524 |
|
} |
525 |
} |
} |