You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TextFrame.java 37KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181
  1. /*
  2. * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.addons.ui_swing.components.frames;
  23. import com.dmdirc.FrameContainer;
  24. import com.dmdirc.actions.ActionManager;
  25. import com.dmdirc.actions.CoreActionType;
  26. import com.dmdirc.addons.ui_swing.SwingController;
  27. import com.dmdirc.addons.ui_swing.UIUtilities;
  28. import com.dmdirc.addons.ui_swing.actions.ChannelCopyAction;
  29. import com.dmdirc.addons.ui_swing.actions.CommandAction;
  30. import com.dmdirc.addons.ui_swing.actions.HyperlinkCopyAction;
  31. import com.dmdirc.addons.ui_swing.actions.NicknameCopyAction;
  32. import com.dmdirc.addons.ui_swing.actions.SearchAction;
  33. import com.dmdirc.addons.ui_swing.actions.TextPaneCopyAction;
  34. import com.dmdirc.addons.ui_swing.components.LoggingSwingWorker;
  35. import com.dmdirc.addons.ui_swing.components.SwingSearchBar;
  36. import com.dmdirc.addons.ui_swing.textpane.ClickType;
  37. import com.dmdirc.addons.ui_swing.textpane.LineInfo;
  38. import com.dmdirc.addons.ui_swing.textpane.TextPane;
  39. import com.dmdirc.addons.ui_swing.textpane.TextPanePageDownAction;
  40. import com.dmdirc.addons.ui_swing.textpane.TextPanePageUpAction;
  41. import com.dmdirc.commandparser.PopupManager;
  42. import com.dmdirc.commandparser.PopupMenu;
  43. import com.dmdirc.commandparser.PopupMenuItem;
  44. import com.dmdirc.commandparser.PopupType;
  45. import com.dmdirc.commandparser.parsers.GlobalCommandParser;
  46. import com.dmdirc.util.StringTranscoder;
  47. import com.dmdirc.config.ConfigManager;
  48. import com.dmdirc.interfaces.ConfigChangeListener;
  49. import com.dmdirc.interfaces.FrameInfoAdapter;
  50. import com.dmdirc.logger.ErrorLevel;
  51. import com.dmdirc.logger.Logger;
  52. import com.dmdirc.ui.IconManager;
  53. import com.dmdirc.ui.WindowManager;
  54. import com.dmdirc.ui.interfaces.InputWindow;
  55. import com.dmdirc.ui.interfaces.Window;
  56. import com.dmdirc.ui.messages.Formatter;
  57. import com.dmdirc.util.URLHandler;
  58. import java.awt.Dimension;
  59. import java.awt.Point;
  60. import java.awt.event.KeyEvent;
  61. import java.awt.event.KeyListener;
  62. import java.awt.event.MouseEvent;
  63. import java.awt.event.MouseListener;
  64. import java.beans.PropertyChangeEvent;
  65. import java.beans.PropertyChangeListener;
  66. import java.beans.PropertyVetoException;
  67. import java.lang.reflect.Constructor;
  68. import java.lang.reflect.InvocationTargetException;
  69. import java.nio.charset.Charset;
  70. import java.nio.charset.IllegalCharsetNameException;
  71. import java.nio.charset.UnsupportedCharsetException;
  72. import java.util.Date;
  73. import java.util.LinkedList;
  74. import java.util.List;
  75. import java.util.concurrent.atomic.AtomicBoolean;
  76. import javax.swing.BorderFactory;
  77. import javax.swing.JComponent;
  78. import javax.swing.JInternalFrame;
  79. import javax.swing.JMenu;
  80. import javax.swing.JMenuItem;
  81. import javax.swing.JPopupMenu;
  82. import javax.swing.JSeparator;
  83. import javax.swing.KeyStroke;
  84. import javax.swing.SwingUtilities;
  85. import javax.swing.UIManager;
  86. import javax.swing.WindowConstants;
  87. import javax.swing.event.InternalFrameEvent;
  88. import javax.swing.event.InternalFrameListener;
  89. import javax.swing.plaf.basic.BasicInternalFrameUI;
  90. import javax.swing.plaf.synth.SynthLookAndFeel;
  91. /**
  92. * Implements a generic (internal) frame.
  93. */
  94. public abstract class TextFrame extends JInternalFrame implements Window,
  95. PropertyChangeListener, InternalFrameListener,
  96. MouseListener, KeyListener, ConfigChangeListener {
  97. /** Logger to use. */
  98. private static final java.util.logging.Logger LOGGER =
  99. java.util.logging.Logger.getLogger(TextFrame.class.getName());
  100. /**
  101. * A version number for this class. It should be changed whenever the class
  102. * structure is changed (or anything else that would prevent serialized
  103. * objects being unserialized with the new class).
  104. */
  105. private static final long serialVersionUID = 5;
  106. /** The channel object that owns this frame. */
  107. protected final FrameContainer frameParent;
  108. /** Frame output pane. */
  109. private TextPane textPane;
  110. /** search bar. */
  111. private SwingSearchBar searchBar;
  112. /** String transcoder. */
  113. private StringTranscoder transcoder;
  114. /** Frame buffer size. */
  115. private int frameBufferSize;
  116. /** Quick copy? */
  117. private boolean quickCopy;
  118. /** Are we closing? */
  119. private boolean closing = false;
  120. /** Input window for popup commands. */
  121. private Window inputWindow;
  122. /** Swing controller. */
  123. private SwingController controller;
  124. /** Are we maximising/restoring? */
  125. private AtomicBoolean maximiseRestoreInProgress = new AtomicBoolean(false);
  126. /** Click types. */
  127. public enum MouseClickType {
  128. /** Clicked. */
  129. CLICKED,
  130. /** Released. */
  131. RELEASED,
  132. /** Pressed. */
  133. PRESSED,
  134. }
  135. /**
  136. * Creates a new instance of Frame.
  137. *
  138. * @param owner FrameContainer owning this frame.
  139. * @param controller Swing controller
  140. */
  141. public TextFrame(final FrameContainer owner,
  142. final SwingController controller) {
  143. super();
  144. this.controller = controller;
  145. frameParent = owner;
  146. final ConfigManager config = owner.getConfigManager();
  147. frameBufferSize = config.getOptionInt("ui", "frameBufferSize");
  148. quickCopy = config.getOptionBool("ui", "quickCopy");
  149. setFrameIcon(IconManager.getIconManager().getIcon(owner.getIcon()));
  150. owner.addFrameInfoListener(new FrameInfoAdapter() {
  151. /** {@inheritDoc} */
  152. @Override
  153. public void iconChanged(final Window window, final String icon) {
  154. setFrameIcon(IconManager.getIconManager().getIcon(icon));
  155. }
  156. });
  157. try {
  158. transcoder = new StringTranscoder(Charset.forName(
  159. config.getOption("channel", "encoding")));
  160. } catch (UnsupportedCharsetException ex) {
  161. transcoder = new StringTranscoder(Charset.forName("UTF-8"));
  162. } catch (IllegalCharsetNameException ex) {
  163. transcoder = new StringTranscoder(Charset.forName("UTF-8"));
  164. } catch (IllegalArgumentException ex) {
  165. transcoder = new StringTranscoder(Charset.forName("UTF-8"));
  166. }
  167. inputWindow = this;
  168. while (!(inputWindow instanceof InputWindow) && inputWindow != null) {
  169. inputWindow = WindowManager.getParent(inputWindow);
  170. }
  171. initComponents();
  172. setMaximizable(true);
  173. setClosable(true);
  174. setResizable(true);
  175. setIconifiable(true);
  176. setFocusable(true);
  177. setPreferredSize(new Dimension(controller.getMainFrame().getWidth() /
  178. 2, controller.getMainFrame().getHeight() / 3));
  179. setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
  180. addPropertyChangeListener("UI", this);
  181. addInternalFrameListener(this);
  182. getTextPane().setBackground(config.getOptionColour("ui",
  183. "backgroundcolour"));
  184. getTextPane().setForeground(config.getOptionColour("ui",
  185. "foregroundcolour"));
  186. config.addChangeListener("ui", "foregroundcolour", this);
  187. config.addChangeListener("ui", "backgroundcolour", this);
  188. config.addChangeListener("ui", "quickCopy", this);
  189. config.addChangeListener("ui", "frameBufferSize", this);
  190. addPropertyChangeListener("maximum", this);
  191. }
  192. /**
  193. * Returns this text frames swing controller.
  194. *
  195. * @return Swing controller
  196. */
  197. public SwingController getController() {
  198. return controller;
  199. }
  200. /** {@inheritDoc} */
  201. @Override
  202. public void setTitle(final String title) {
  203. UIUtilities.invokeLater(new Runnable() {
  204. /** {@inheritDoc} */
  205. @Override
  206. public void run() {
  207. TextFrame.super.setTitle(title);
  208. }
  209. });
  210. }
  211. /** {@inheritDoc} */
  212. @Override
  213. public void setVisible(final boolean isVisible) {
  214. UIUtilities.invokeLater(new Runnable() {
  215. @Override
  216. public void run() {
  217. if (isVisible() && isVisible) {
  218. open();
  219. } else {
  220. TextFrame.super.setVisible(isVisible);
  221. }
  222. }
  223. });
  224. }
  225. /** {@inheritDoc} */
  226. @Override
  227. public void open() {
  228. final boolean pref = frameParent.getConfigManager().getOptionBool("ui",
  229. "maximisewindows");
  230. UIUtilities.invokeLater(new Runnable() {
  231. @Override
  232. public void run() {
  233. if (!isVisible()) {
  234. TextFrame.super.setVisible(true);
  235. }
  236. try {
  237. if (pref || controller.getMainFrame().getMaximised()) {
  238. if (!isMaximum()) {
  239. setMaximum(true);
  240. }
  241. }
  242. } catch (PropertyVetoException ex) {
  243. //Ignore
  244. }
  245. try {
  246. if (!isSelected()) {
  247. setSelected(true);
  248. }
  249. } catch (PropertyVetoException ex) {
  250. //Ignore
  251. }
  252. }
  253. });
  254. }
  255. /** {@inheritDoc} */
  256. @Override
  257. public void activateFrame() {
  258. UIUtilities.invokeLater(new Runnable() {
  259. @Override
  260. public void run() {
  261. try {
  262. if (isIcon()) {
  263. setIcon(false);
  264. }
  265. if (!isVisible()) {
  266. TextFrame.super.setVisible(true);
  267. }
  268. if (!isSelected()) {
  269. new LoggingSwingWorker() {
  270. /** {@inheritDoc} */
  271. @Override
  272. protected Object doInBackground() throws Exception {
  273. ActionManager.processEvent(
  274. CoreActionType.CLIENT_FRAME_CHANGED,
  275. null, getContainer());
  276. return null;
  277. }
  278. }.execute();
  279. setSelected(true);
  280. }
  281. } catch (PropertyVetoException ex) {
  282. //Ignore
  283. }
  284. }
  285. });
  286. }
  287. /** Closes this frame. */
  288. @Override
  289. public void close() {
  290. UIUtilities.invokeLater(new Runnable() {
  291. @Override
  292. public void run() {
  293. if (closing) {
  294. return;
  295. }
  296. closing = true;
  297. try {
  298. if (!isClosed()) {
  299. setClosed(true);
  300. }
  301. } catch (PropertyVetoException ex) {
  302. Logger.userError(ErrorLevel.LOW, "Unable to close frame");
  303. }
  304. }
  305. });
  306. }
  307. /** {@inheritDoc} */
  308. @Override
  309. public void minimise() {
  310. UIUtilities.invokeLater(new Runnable() {
  311. @Override
  312. public void run() {
  313. try {
  314. if (!isIcon()) {
  315. setIcon(true);
  316. }
  317. } catch (PropertyVetoException ex) {
  318. Logger.userError(ErrorLevel.LOW, "Unable to minimise frame");
  319. }
  320. }
  321. });
  322. }
  323. /** {@inheritDoc} */
  324. @Override
  325. public void maximise() {
  326. LOGGER.finest("maximise(): About to invokeAndWait");
  327. UIUtilities.invokeLater(new Runnable() {
  328. /** {@inheritDoc} */
  329. @Override
  330. public void run() {
  331. LOGGER.finest("maximise(): Running");
  332. try {
  333. if (isMaximum()) {
  334. return;
  335. }
  336. maximiseRestoreInProgress.set(true);
  337. LOGGER.finest("maximise(): About to set icon");
  338. if (isIcon()) {
  339. setIcon(false);
  340. }
  341. LOGGER.finest("maximise(): About to set maximum");
  342. setMaximum(true);
  343. LOGGER.finest("maximise(): Done?");
  344. } catch (PropertyVetoException ex) {
  345. Logger.userError(ErrorLevel.LOW, "Unable to minimise frame");
  346. }
  347. maximiseRestoreInProgress.set(false);
  348. LOGGER.finest("maximise(): Done running");
  349. }
  350. });
  351. LOGGER.finest("maximise(): Done");
  352. }
  353. /** {@inheritDoc} */
  354. @Override
  355. public void restore() {
  356. UIUtilities.invokeLater(new Runnable() {
  357. /** {@inheritDoc} */
  358. @Override
  359. public void run() {
  360. try {
  361. if (!isMaximum()) {
  362. return;
  363. }
  364. maximiseRestoreInProgress.set(true);
  365. if (isIcon()) {
  366. setIcon(false);
  367. }
  368. setMaximum(false);
  369. } catch (PropertyVetoException ex) {
  370. Logger.userError(ErrorLevel.LOW, "Unable to minimise frame");
  371. }
  372. maximiseRestoreInProgress.set(false);
  373. }
  374. });
  375. }
  376. /** {@inheritDoc} */
  377. @Override
  378. public void toggleMaximise() {
  379. UIUtilities.invokeLater(new Runnable() {
  380. /** {@inheritDoc} */
  381. @Override
  382. public void run() {
  383. if (isMaximum()) {
  384. restore();
  385. } else {
  386. maximise();
  387. }
  388. }
  389. });
  390. }
  391. /** {@inheritDoc} */
  392. @Override
  393. public final void addLine(final String line, final boolean timestamp) {
  394. final String encodedLine = transcoder.decode(line);
  395. UIUtilities.invokeLater(new Runnable() {
  396. /** {@inheritDoc} */
  397. @Override
  398. public void run() {
  399. final List<String[]> lines = new LinkedList<String[]>();
  400. for (final String myLine : encodedLine.split("\n")) {
  401. if (timestamp) {
  402. lines.add(new String[]{
  403. Formatter.formatMessage(getConfigManager(),
  404. "timestamp", new Date()), myLine,});
  405. } else {
  406. lines.add(new String[]{myLine,});
  407. }
  408. new LoggingSwingWorker() {
  409. /** {@inheritDoc} */
  410. @Override
  411. protected Object doInBackground() throws Exception {
  412. ActionManager.processEvent(
  413. CoreActionType.CLIENT_LINE_ADDED,
  414. null, getContainer(), myLine);
  415. return null;
  416. }
  417. }.execute();
  418. }
  419. textPane.getDocument().addText(lines);
  420. if (frameBufferSize > 0) {
  421. textPane.trim(frameBufferSize);
  422. }
  423. }
  424. });
  425. }
  426. /** {@inheritDoc} */
  427. @Override
  428. public final void addLine(final String messageType,
  429. final Object... args) {
  430. if (!messageType.isEmpty()) {
  431. addLine(Formatter.formatMessage(getConfigManager(), messageType,
  432. args), true);
  433. }
  434. }
  435. /** {@inheritDoc} */
  436. @Override
  437. public final void addLine(final StringBuffer messageType,
  438. final Object... args) {
  439. if (messageType != null) {
  440. addLine(messageType.toString(), args);
  441. }
  442. }
  443. /** {@inheritDoc} */
  444. @Override
  445. public final void clear() {
  446. UIUtilities.invokeLater(new Runnable() {
  447. /** {@inheritDoc} */
  448. @Override
  449. public void run() {
  450. getTextPane().clear();
  451. }
  452. });
  453. }
  454. /**
  455. * Initialises the components for this frame.
  456. */
  457. private void initComponents() {
  458. setTextPane(new TextPane(getContainer()));
  459. getTextPane().addMouseListener(this);
  460. getTextPane().addKeyListener(this);
  461. searchBar = new SwingSearchBar(this, controller.getMainFrame());
  462. searchBar.setVisible(false);
  463. searchBar.addKeyListener(this);
  464. getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
  465. put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
  466. "pageUpAction");
  467. getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
  468. put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
  469. "pageDownAction");
  470. getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
  471. put(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), "searchAction");
  472. getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
  473. put(KeyStroke.getKeyStroke(KeyEvent.VK_F,
  474. UIUtilities.getCtrlDownMask()), "searchAction");
  475. getActionMap().put("pageUpAction",
  476. new TextPanePageUpAction(getTextPane()));
  477. getActionMap().put("pageDownAction",
  478. new TextPanePageDownAction(getTextPane()));
  479. getActionMap().put("searchAction", new SearchAction(searchBar));
  480. }
  481. /**
  482. * Removes and reinserts the border of an internal frame on maximising.
  483. * {@inheritDoc}
  484. *
  485. * @param event Property change event
  486. */
  487. @Override
  488. public final void propertyChange(final PropertyChangeEvent event) {
  489. LOGGER.finer("Property change: name: " + event.getPropertyName() +
  490. " value: " + event.getOldValue() + "->" + event.getNewValue());
  491. if ("UI".equals(event.getPropertyName())) {
  492. if (isMaximum()) {
  493. hideTitlebar();
  494. }
  495. } else {
  496. if ((Boolean) event.getNewValue()) {
  497. hideTitlebar();
  498. } else {
  499. showTitlebar();
  500. }
  501. }
  502. LOGGER.finest("Done property change");
  503. }
  504. /** Hides the titlebar for this frame. */
  505. private void hideTitlebar() {
  506. UIUtilities.invokeLater(new Runnable() {
  507. /** {@inheritDoc} */
  508. @Override
  509. public void run() {
  510. setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  511. ((BasicInternalFrameUI) getUI()).setNorthPane(null);
  512. }
  513. });
  514. }
  515. /** Shows the titlebar for this frame. */
  516. private void showTitlebar() {
  517. UIUtilities.invokeLater(new Runnable() {
  518. /** {@inheritDoc} */
  519. @Override
  520. public void run() {
  521. final Class<?> c;
  522. Object temp = null;
  523. Constructor<?> constructor;
  524. final String componentUI = (String) UIManager.get(
  525. "InternalFrameUI");
  526. if ("javax.swing.plaf.synth.SynthLookAndFeel".equals(componentUI)) {
  527. temp = SynthLookAndFeel.createUI(TextFrame.this);
  528. } else {
  529. try {
  530. c = getClass().getClassLoader().loadClass(componentUI);
  531. constructor =
  532. c.getConstructor(new Class[]{
  533. javax.swing.JInternalFrame.class});
  534. temp =
  535. constructor.newInstance(new Object[]{
  536. TextFrame.this});
  537. } catch (ClassNotFoundException ex) {
  538. Logger.appError(ErrorLevel.MEDIUM,
  539. "Unable to readd titlebar",
  540. ex);
  541. } catch (NoSuchMethodException ex) {
  542. Logger.appError(ErrorLevel.MEDIUM,
  543. "Unable to readd titlebar",
  544. ex);
  545. } catch (InstantiationException ex) {
  546. Logger.appError(ErrorLevel.MEDIUM,
  547. "Unable to readd titlebar",
  548. ex);
  549. } catch (IllegalAccessException ex) {
  550. Logger.appError(ErrorLevel.MEDIUM,
  551. "Unable to readd titlebar",
  552. ex);
  553. } catch (InvocationTargetException ex) {
  554. Logger.appError(ErrorLevel.MEDIUM,
  555. "Unable to readd titlebar",
  556. ex);
  557. }
  558. }
  559. setBorder(UIManager.getBorder("InternalFrame.border"));
  560. if (temp == null) {
  561. temp = new BasicInternalFrameUI(TextFrame.this);
  562. }
  563. setUI((BasicInternalFrameUI) temp);
  564. }
  565. });
  566. }
  567. /**
  568. * Not needed for this class. {@inheritDoc}
  569. *
  570. * @param event Internal frame event
  571. */
  572. @Override
  573. public void internalFrameOpened(final InternalFrameEvent event) {
  574. new LoggingSwingWorker() {
  575. /** {@inheritDoc} */
  576. @Override
  577. protected Object doInBackground() throws Exception {
  578. frameParent.windowOpened();
  579. return null;
  580. }
  581. }.execute();
  582. }
  583. /**
  584. * Not needed for this class. {@inheritDoc}
  585. *
  586. * @param event Internal frame event
  587. */
  588. @Override
  589. public void internalFrameClosing(final InternalFrameEvent event) {
  590. new LoggingSwingWorker() {
  591. /** {@inheritDoc} */
  592. @Override
  593. protected Object doInBackground() throws Exception {
  594. frameParent.windowClosing();
  595. return null;
  596. }
  597. }.execute();
  598. }
  599. /**
  600. * Not needed for this class. {@inheritDoc}
  601. *
  602. * @param event Internal frame event
  603. */
  604. @Override
  605. public void internalFrameClosed(final InternalFrameEvent event) {
  606. new LoggingSwingWorker() {
  607. /** {@inheritDoc} */
  608. @Override
  609. protected Object doInBackground() throws Exception {
  610. frameParent.windowClosed();
  611. return null;
  612. }
  613. }.execute();
  614. }
  615. /**
  616. * Makes the internal frame invisible. {@inheritDoc}
  617. *
  618. * @param event Internal frame event
  619. */
  620. @Override
  621. public void internalFrameIconified(final InternalFrameEvent event) {
  622. UIUtilities.invokeLater(new Runnable() {
  623. @Override
  624. public void run() {
  625. event.getInternalFrame().setVisible(false);
  626. }
  627. });
  628. }
  629. /**
  630. * Not needed for this class. {@inheritDoc}
  631. *
  632. * @param event Internal frame event
  633. */
  634. @Override
  635. public void internalFrameDeiconified(final InternalFrameEvent event) {
  636. //Ignore.
  637. }
  638. /**
  639. * Activates the input field on frame focus. {@inheritDoc}
  640. *
  641. * @param event Internal frame event
  642. */
  643. @Override
  644. public void internalFrameActivated(final InternalFrameEvent event) {
  645. LOGGER.finer(getName() + ": internalFrameActivated()");
  646. if (maximiseRestoreInProgress.get()) {
  647. return;
  648. }
  649. new LoggingSwingWorker() {
  650. /** {@inheritDoc} */
  651. @Override
  652. protected Object doInBackground() throws Exception {
  653. LOGGER.finer(getName() +
  654. ": internalFrameActivated(): doInBackground");
  655. frameParent.windowActivated();
  656. return null;
  657. }
  658. }.execute();
  659. }
  660. /**
  661. * Not needed for this class. {@inheritDoc}
  662. *
  663. * @param event Internal frame event
  664. */
  665. @Override
  666. public void internalFrameDeactivated(final InternalFrameEvent event) {
  667. if (maximiseRestoreInProgress.get()) {
  668. return;
  669. }
  670. new LoggingSwingWorker() {
  671. /** {@inheritDoc} */
  672. @Override
  673. protected Object doInBackground() throws Exception {
  674. frameParent.windowDeactivated();
  675. return null;
  676. }
  677. }.execute();
  678. }
  679. /** {@inheritDoc} */
  680. @Override
  681. public FrameContainer getContainer() {
  682. return frameParent;
  683. }
  684. /** {@inheritDoc} */
  685. @Override
  686. public ConfigManager getConfigManager() {
  687. return getContainer().getConfigManager();
  688. }
  689. /**
  690. * Returns the text pane for this frame.
  691. *
  692. * @return Text pane for this frame
  693. */
  694. public final TextPane getTextPane() {
  695. return textPane;
  696. }
  697. /**
  698. * Returns the transcoder for this frame.
  699. *
  700. * @return String transcoder for this frame
  701. */
  702. @Override
  703. public StringTranscoder getTranscoder() {
  704. return transcoder;
  705. }
  706. /** {@inheritDoc} */
  707. @Override
  708. public final String getName() {
  709. if (frameParent == null) {
  710. return "";
  711. }
  712. return frameParent.toString();
  713. }
  714. /**
  715. * Sets the frames text pane.
  716. *
  717. * @param newTextPane new text pane to use
  718. */
  719. protected final void setTextPane(final TextPane newTextPane) {
  720. this.textPane = newTextPane;
  721. }
  722. /**
  723. * {@inheritDoc}
  724. *
  725. * @param mouseEvent Mouse event
  726. */
  727. @Override
  728. public void mouseClicked(final MouseEvent mouseEvent) {
  729. if (mouseEvent.getSource() == getTextPane()) {
  730. processMouseClickEvent(mouseEvent, MouseClickType.CLICKED);
  731. }
  732. }
  733. /**
  734. * {@inheritDoc}
  735. *
  736. * @param mouseEvent Mouse event
  737. */
  738. @Override
  739. public void mousePressed(final MouseEvent mouseEvent) {
  740. processMouseClickEvent(mouseEvent, MouseClickType.PRESSED);
  741. }
  742. /**
  743. * {@inheritDoc}
  744. *
  745. * @param mouseEvent Mouse event
  746. */
  747. @Override
  748. public void mouseReleased(final MouseEvent mouseEvent) {
  749. if (quickCopy && mouseEvent.getSource() == getTextPane()) {
  750. getTextPane().copy();
  751. getTextPane().clearSelection();
  752. }
  753. processMouseClickEvent(mouseEvent, MouseClickType.RELEASED);
  754. }
  755. /**
  756. * {@inheritDoc}
  757. *
  758. * @param mouseEvent Mouse event
  759. */
  760. @Override
  761. public void mouseEntered(final MouseEvent mouseEvent) {
  762. //Ignore.
  763. }
  764. /**
  765. * {@inheritDoc}
  766. *
  767. * @param mouseEvent Mouse event
  768. */
  769. @Override
  770. public void mouseExited(final MouseEvent mouseEvent) {
  771. //Ignore.
  772. }
  773. /**
  774. * Processes every mouse button event to check for a popup trigger.
  775. *
  776. * @param e mouse event
  777. * @param type
  778. */
  779. public void processMouseClickEvent(final MouseEvent e,
  780. final MouseClickType type) {
  781. final Point point = e.getLocationOnScreen();
  782. SwingUtilities.convertPointFromScreen(point, this);
  783. if (e.getSource() == getTextPane() && point != null) {
  784. final LineInfo lineInfo = getTextPane().getClickPosition(textPane.
  785. getMousePosition());
  786. final ClickType clickType = getTextPane().getClickType(lineInfo);
  787. final String attribute = (String) getTextPane().
  788. getAttributeValueAtPoint(lineInfo);
  789. if (e.isPopupTrigger()) {
  790. showPopupMenuInternal(clickType, point, attribute);
  791. } else {
  792. if (type == MouseClickType.CLICKED) {
  793. switch (clickType) {
  794. case CHANNEL:
  795. ActionManager.processEvent(CoreActionType.LINK_CHANNEL_CLICKED, null, attribute);
  796. frameParent.getServer().join(attribute);
  797. break;
  798. case HYPERLINK:
  799. ActionManager.processEvent(CoreActionType.LINK_URL_CLICKED, null, attribute);
  800. URLHandler.getURLHander().launchApp(attribute);
  801. break;
  802. case NICKNAME:
  803. ActionManager.processEvent(CoreActionType.LINK_NICKNAME_CLICKED, null, attribute);
  804. if (getContainer().getServer().hasQuery(attribute)) {
  805. getContainer().getServer().getQuery(attribute).
  806. activateFrame();
  807. } else {
  808. getContainer().getServer().addQuery(attribute);
  809. getContainer().getServer().getQuery(attribute).
  810. show();
  811. }
  812. break;
  813. default:
  814. break;
  815. }
  816. }
  817. }
  818. }
  819. super.processMouseEvent(e);
  820. }
  821. /**
  822. * What popup type should be used for popup menus for nicknames
  823. *
  824. * @return Appropriate popuptype for this frame
  825. */
  826. public abstract PopupType getNicknamePopupType();
  827. /**
  828. * What popup type should be used for popup menus for channels
  829. *
  830. * @return Appropriate popuptype for this frame
  831. */
  832. public abstract PopupType getChannelPopupType();
  833. /**
  834. * What popup type should be used for popup menus for hyperlinks
  835. *
  836. * @return Appropriate popuptype for this frame
  837. */
  838. public abstract PopupType getHyperlinkPopupType();
  839. /**
  840. * What popup type should be used for popup menus for normal clicks
  841. *
  842. * @return Appropriate popuptype for this frame
  843. */
  844. public abstract PopupType getNormalPopupType();
  845. /**
  846. * A method called to add custom popup items.
  847. *
  848. * @param popupMenu Popup menu to add popup items to
  849. */
  850. public abstract void addCustomPopupItems(final JPopupMenu popupMenu);
  851. /**
  852. * Shows a popup menu at the specified point for the specified click type
  853. *
  854. * @param type ClickType Click type
  855. * @param point Point Point of the click
  856. * @param argument Word under the click
  857. */
  858. private void showPopupMenuInternal(final ClickType type,
  859. final Point point,
  860. final String argument) {
  861. final JPopupMenu popupMenu;
  862. switch (type) {
  863. case CHANNEL:
  864. popupMenu = getPopupMenu(getChannelPopupType(), argument);
  865. popupMenu.add(new ChannelCopyAction(argument));
  866. if (popupMenu.getComponentCount() > 1) {
  867. popupMenu.addSeparator();
  868. }
  869. break;
  870. case HYPERLINK:
  871. popupMenu = getPopupMenu(getHyperlinkPopupType(), argument);
  872. popupMenu.add(new HyperlinkCopyAction(argument));
  873. if (popupMenu.getComponentCount() > 1) {
  874. popupMenu.addSeparator();
  875. }
  876. break;
  877. case NICKNAME:
  878. popupMenu = getPopupMenu(getNicknamePopupType(), argument);
  879. if (popupMenu.getComponentCount() > 0) {
  880. popupMenu.addSeparator();
  881. }
  882. popupMenu.add(new NicknameCopyAction(argument));
  883. break;
  884. default:
  885. popupMenu = getPopupMenu(null, argument);
  886. break;
  887. }
  888. popupMenu.add(new TextPaneCopyAction(getTextPane()));
  889. addCustomPopupItems(popupMenu);
  890. popupMenu.show(this, (int) point.getX(), (int) point.getY());
  891. }
  892. /**
  893. * Shows a popup menu at the specified point for the specified click type
  894. *
  895. * @param type ClickType Click type
  896. * @param point Point Point of the click (Must be screen coords)
  897. * @param argument Word under the click
  898. */
  899. public void showPopupMenu(final ClickType type,
  900. final Point point,
  901. final String argument) {
  902. SwingUtilities.convertPointFromScreen(point, this);
  903. final JPopupMenu popupMenu;
  904. switch (type) {
  905. case CHANNEL:
  906. popupMenu = getPopupMenu(getChannelPopupType(), argument);
  907. popupMenu.add(new ChannelCopyAction(argument));
  908. if (popupMenu.getComponentCount() > 1) {
  909. popupMenu.addSeparator();
  910. }
  911. break;
  912. case HYPERLINK:
  913. popupMenu = getPopupMenu(getHyperlinkPopupType(), argument);
  914. popupMenu.add(new HyperlinkCopyAction(argument));
  915. if (popupMenu.getComponentCount() > 1) {
  916. popupMenu.addSeparator();
  917. }
  918. break;
  919. case NICKNAME:
  920. popupMenu = getPopupMenu(getNicknamePopupType(), argument);
  921. if (popupMenu.getComponentCount() > 0) {
  922. popupMenu.addSeparator();
  923. }
  924. popupMenu.add(new NicknameCopyAction(argument));
  925. break;
  926. default:
  927. popupMenu = getPopupMenu(null, argument);
  928. break;
  929. }
  930. popupMenu.show(this, (int) point.getX(), (int) point.getY());
  931. }
  932. /**
  933. * Builds a popup menu of a specified type
  934. *
  935. * @param type type of menu to build
  936. * @param arguments Arguments for the command
  937. *
  938. * @return PopupMenu
  939. */
  940. public JPopupMenu getPopupMenu(
  941. final PopupType type,
  942. final Object... arguments) {
  943. JPopupMenu popupMenu = new JPopupMenu();
  944. if (type != null) {
  945. popupMenu = (JPopupMenu) populatePopupMenu(popupMenu,
  946. PopupManager.getMenu(type, getConfigManager()),
  947. arguments);
  948. }
  949. return popupMenu;
  950. }
  951. /**
  952. * Populates the specified popupmenu
  953. *
  954. * @param menu Menu component
  955. * @param popup Popup to get info from
  956. * @param arguments Arguments for the command
  957. *
  958. * @return Populated popup
  959. */
  960. private JComponent populatePopupMenu(final JComponent menu,
  961. final PopupMenu popup,
  962. final Object... arguments) {
  963. for (PopupMenuItem menuItem : popup.getItems()) {
  964. if (menuItem.isDivider()) {
  965. menu.add(new JSeparator());
  966. } else if (menuItem.isSubMenu()) {
  967. menu.add(populatePopupMenu(new JMenu(menuItem.getName()),
  968. menuItem.getSubMenu(), arguments));
  969. } else {
  970. menu.add(new JMenuItem(new CommandAction(inputWindow == null ? GlobalCommandParser.
  971. getGlobalCommandParser()
  972. : ((InputWindow) inputWindow).getCommandParser(),
  973. (InputWindow) inputWindow, menuItem.getName(),
  974. menuItem.getCommand(arguments))));
  975. }
  976. }
  977. return menu;
  978. }
  979. /**
  980. * {@inheritDoc}
  981. *
  982. * @param event Key event
  983. */
  984. @Override
  985. public void keyTyped(final KeyEvent event) {
  986. //Ignore.
  987. }
  988. /**
  989. * {@inheritDoc}
  990. *
  991. * @param event Key event
  992. */
  993. @Override
  994. public void keyPressed(final KeyEvent event) {
  995. if (!quickCopy && (event.getModifiers() & UIUtilities.getCtrlMask()) !=
  996. 0 &&
  997. event.getKeyCode() == KeyEvent.VK_C) {
  998. getTextPane().copy();
  999. }
  1000. }
  1001. /**
  1002. * {@inheritDoc}
  1003. *
  1004. * @param event Key event
  1005. */
  1006. @Override
  1007. public void keyReleased(final KeyEvent event) {
  1008. //Ignore.
  1009. }
  1010. /**
  1011. * Gets the search bar.
  1012. *
  1013. * @return the frames search bar
  1014. */
  1015. public final SwingSearchBar getSearchBar() {
  1016. return searchBar;
  1017. }
  1018. /** {@inheritDoc} */
  1019. @Override
  1020. public void configChanged(final String domain,
  1021. final String key) {
  1022. if (getConfigManager() == null) {
  1023. return;
  1024. }
  1025. if ("ui".equals(domain)) {
  1026. if ("foregroundcolour".equals(key) && getTextPane() != null) {
  1027. getTextPane().setForeground(getConfigManager().
  1028. getOptionColour("ui", "foregroundcolour"));
  1029. } else if ("backgroundcolour".equals(key) && getTextPane() != null) {
  1030. getTextPane().setBackground(getConfigManager().
  1031. getOptionColour("ui", "backgroundcolour"));
  1032. } else if ("frameBufferSize".equals(key)) {
  1033. frameBufferSize = getContainer().getConfigManager().
  1034. getOptionInt("ui", "frameBufferSize");
  1035. } else if ("quickCopy".equals(key)) {
  1036. quickCopy = getContainer().getConfigManager().
  1037. getOptionBool("ui", "quickCopy");
  1038. }
  1039. }
  1040. }
  1041. }