/plugins/ProjectViewer/tags/pv_2_1_3_6/projectviewer/vpt/IconComposer.java

# · Java · 365 lines · 226 code · 46 blank · 93 comment · 41 complexity · 4dd070b4e05e535cf047a6eb42a8c74a MD5 · raw file

  1. /*
  2. * :tabSize=4:indentSize=4:noTabs=false:
  3. * :folding=explicit:collapseFolds=1:
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version 2
  8. * of the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19. package projectviewer.vpt;
  20. //{{{ Imports
  21. import java.io.File;
  22. import java.util.HashMap;
  23. import java.awt.Graphics;
  24. import java.awt.Image;
  25. import java.awt.image.BufferedImage;
  26. import java.awt.image.ColorModel;
  27. import java.awt.image.MemoryImageSource;
  28. import java.awt.image.PixelGrabber;
  29. import java.awt.Toolkit;
  30. import javax.swing.Icon;
  31. import javax.swing.ImageIcon;
  32. import org.gjt.sp.jedit.Buffer;
  33. import org.gjt.sp.jedit.jEdit;
  34. import org.gjt.sp.util.Log;
  35. import errorlist.ErrorSource;
  36. import projectviewer.config.ProjectViewerConfig;
  37. //}}}
  38. /**
  39. * Create decorated icons for VPT nodes
  40. *
  41. * @author Stefan Kost
  42. * @version $Id: IconComposer.java 8932 2007-02-09 05:29:17Z vanza $
  43. */
  44. public final class IconComposer {
  45. //{{{ Constants
  46. public final static int FILE_STATE_NORMAL = 0;
  47. public final static int FILE_STATE_CHANGED = 1;
  48. public final static int FILE_STATE_READONLY = 2;
  49. public final static int FILE_STATE_NOT_FOUND = 3;
  50. public final static int VC_STATE_NONE = 0;
  51. public final static int VC_STATE_LOCAL_MOD = 1;
  52. public final static int VC_STATE_LOCAL_ADD = 2;
  53. public final static int VC_STATE_LOCAL_RM = 3;
  54. public final static int VC_STATE_NEED_UPDATE = 4;
  55. public final static int VC_STATE_CONFLICT = 5;
  56. public final static int MSG_STATE_NONE = 0;
  57. public final static int MSG_STATE_MESSAGES = 1;
  58. public final static int MSG_STATE_ERRORS = 2;
  59. //}}}
  60. //{{{ Attributes
  61. private final static HashMap iconCache = new HashMap();
  62. private final static Icon FILE_STATE_CHANGED_IMG =
  63. new ImageIcon(IconComposer.class.getResource("/projectviewer/images/file_state_changed.png"));
  64. private final static Icon FILE_STATE_READONLY_IMG =
  65. new ImageIcon(IconComposer.class.getResource("/projectviewer/images/file_state_readonly.png"));
  66. private final static Icon FILE_STATE_NOT_FOUND_IMG =
  67. new ImageIcon(IconComposer.class.getResource("/projectviewer/images/file_state_not_found.png"));
  68. private final static Icon MSG_STATE_MESSAGES_IMG =
  69. new ImageIcon(IconComposer.class.getResource("/projectviewer/images/msg_state_messages.png"));
  70. private final static Icon MSG_STATE_ERRORS_IMG =
  71. new ImageIcon(IconComposer.class.getResource("/projectviewer/images/msg_state_errors.png"));
  72. private static VCProvider vcProvider = null;
  73. //}}}
  74. //{{{ Public methods
  75. //{{{ +_setVersionControlProvider(VCProvider)_ : void
  76. public static void setVersionControlProvider(VCProvider vc) {
  77. iconCache.clear();
  78. vcProvider = vc;
  79. } //}}}
  80. //{{{ +_composeIcon(File, String, Icon)_ : Icon
  81. public static Icon composeIcon(File f, String path, Icon baseIcon) {
  82. Icon[][][][] cache = getIconCache(baseIcon);
  83. int msg_state = MSG_STATE_NONE;
  84. if (ProjectViewerConfig.getInstance().isErrorListAvailable()) {
  85. msg_state = Helper.getMessageState(path);
  86. }
  87. int file_state = FILE_STATE_NORMAL;
  88. if (f != null)
  89. file_state = getFileState(f, path);
  90. int vc_state = VC_STATE_NONE;
  91. if (vcProvider != null) {
  92. vc_state = vcProvider.getFileState(f, path);
  93. }
  94. try {
  95. if(cache[vc_state][0][file_state][msg_state] == null) {
  96. Icon tl = null; // vc_state
  97. Icon tr = null; // unused
  98. Icon bl = null; // file_state
  99. switch(file_state) {
  100. case FILE_STATE_CHANGED:
  101. bl = FILE_STATE_CHANGED_IMG;
  102. break;
  103. case FILE_STATE_READONLY:
  104. bl = FILE_STATE_READONLY_IMG;
  105. break;
  106. case FILE_STATE_NOT_FOUND:
  107. bl = FILE_STATE_NOT_FOUND_IMG;
  108. break;
  109. }
  110. Icon br = null; // msg_state
  111. switch(msg_state) {
  112. case MSG_STATE_MESSAGES:
  113. br = MSG_STATE_MESSAGES_IMG;
  114. break;
  115. case MSG_STATE_ERRORS:
  116. br = MSG_STATE_ERRORS_IMG;
  117. break;
  118. }
  119. if (vcProvider != null)
  120. tl = vcProvider.getIcon(vc_state);
  121. cache[vc_state][0][file_state][msg_state] =
  122. composeIcons(baseIcon, tl, tr, bl, br);
  123. }
  124. baseIcon = cache[vc_state][0][file_state][msg_state];
  125. } catch(ArrayIndexOutOfBoundsException ex) {
  126. Log.log(Log.WARNING, null, ex);
  127. }
  128. return baseIcon;
  129. } //}}}
  130. //}}}
  131. //{{{ Private methods
  132. //{{{ -_composeIcons(Icon, Icon, Icon, Icon, Icon)_ : Icon
  133. private static Icon composeIcons(Icon baseIcon, Icon tl, Icon tr, Icon bl, Icon br) {
  134. // copy base image
  135. int baseWidth = baseIcon.getIconWidth();
  136. int baseHeight = baseIcon.getIconHeight();
  137. if (tl != null) {
  138. baseIcon = composeIcons(baseIcon, tl, 0, 0);
  139. }
  140. if (tr != null) {
  141. baseIcon = composeIcons(baseIcon, tr, baseWidth - tr.getIconWidth(), 0);
  142. }
  143. if (bl != null) {
  144. baseIcon = composeIcons(baseIcon, bl, 0, baseHeight - bl.getIconHeight());
  145. }
  146. if (br != null) {
  147. baseIcon = composeIcons(baseIcon, br,
  148. baseWidth - br.getIconWidth(), baseHeight - br.getIconHeight());
  149. }
  150. return baseIcon;
  151. } //}}}
  152. //{{{ -_composeIcons(Icon, Icon, int, int)_ : Icon
  153. private static Icon composeIcons(Icon baseIcon, Icon decoIcon, int px, int py) {
  154. Image baseImage;
  155. if (baseIcon instanceof ImageIcon) {
  156. baseImage = ((ImageIcon)baseIcon).getImage();
  157. } else {
  158. // not an image icon. do this ugly thing.
  159. baseImage = new BufferedImage(baseIcon.getIconWidth(),
  160. baseIcon.getIconHeight(),
  161. BufferedImage.TYPE_INT_ARGB);
  162. Graphics g = baseImage.getGraphics();
  163. baseIcon.paintIcon(null, g, 0, 0);
  164. g.dispose();
  165. }
  166. int baseWidth=baseIcon.getIconWidth();
  167. int baseHeight=baseIcon.getIconHeight();
  168. int [] base = new int[baseWidth*baseHeight];
  169. PixelGrabber basePG = new PixelGrabber(baseImage, 0, 0, baseWidth, baseHeight, base, 0, baseWidth);
  170. try { basePG.grabPixels(); } catch (Exception ie1) { }
  171. Image decoImage=((ImageIcon)decoIcon).getImage();
  172. int decoWidth=decoIcon.getIconWidth();
  173. int decoHeight=decoIcon.getIconHeight();
  174. int [] deco = new int[decoWidth*decoHeight];
  175. PixelGrabber decoPG = new PixelGrabber(decoImage, 0, 0, decoWidth, decoHeight, deco, 0, decoWidth);
  176. try { decoPG.grabPixels(); } catch (Exception ie1) { }
  177. //Log.log(Log.DEBUG, null, "baseSize :["+baseWidth+"x"+baseHeight+"]");
  178. //Log.log(Log.DEBUG, null, "decoSize :["+decoWidth+"x"+decoHeight+"]");
  179. // overlay base icon with deco icon
  180. int baseIx,decoIx;
  181. int p,bb,bg,br,ba,db,dg,dr,da,r,g,b,a;
  182. double bw,dw;
  183. for(int y = 0; y < decoHeight; y++) {
  184. for(int x = 0; x < decoWidth; x++) {
  185. decoIx = y * decoWidth + x;
  186. baseIx = (py+y) * baseWidth +(px+x);
  187. // read pixels
  188. p=base[baseIx];
  189. bb = p & 0x00ff;
  190. bg = (p >> 8) & 0x00ff;
  191. br = (p >> 16) & 0x00ff;
  192. ba = (p >> 24) & 0x00ff;
  193. p=deco[decoIx];
  194. db = p & 0x00ff;
  195. dg = (p >> 8) & 0x00ff;
  196. dr = (p >> 16) & 0x00ff;
  197. da = (p >> 24) & 0x00ff;
  198. // combining the pixels
  199. /*
  200. dw = (da / 255.0);
  201. r = ((int)(br + dr * dw)) >> 1;
  202. r = (r < 0)?(0):((r>255)?(255):(r));
  203. g = ((int)(bg + dg * dw)) >> 1;
  204. g = (g < 0)?(0):((g>255)?(255):(g));
  205. b = ((int)(bb + db * dw)) >> 1;
  206. b = (b < 0)?(0):((b>255)?(255):(b));
  207. a = ((int)(ba + da * dw)) >> 1;
  208. a = (a < 0)?(0):((a>255)?(255):(a));
  209. */
  210. dw = (da / 255.0);bw = 1.0 - dw;
  211. r = (int)(br * bw + dr * dw);
  212. r = (r < 0)?(0):((r>255)?(255):(r));
  213. g = (int)(bg * bw + dg * dw);
  214. g = (g < 0)?(0):((g>255)?(255):(g));
  215. b = (int)(bb * bw + db * dw);
  216. b = (b < 0)?(0):((b>255)?(255):(b));
  217. a = (int)(ba * bw + da * dw);
  218. a = (a < 0)?(0):((a>255)?(255):(a));
  219. p = (((((a << 8) + (r & 0x0ff)) << 8) + (g & 0x0ff)) << 8) + (b & 0x0ff);
  220. // save the pixel
  221. base[baseIx] = p;
  222. }
  223. }
  224. ColorModel cm = ColorModel.getRGBdefault();
  225. MemoryImageSource mis =
  226. new MemoryImageSource(baseWidth, baseHeight, cm, base, 0, baseWidth);
  227. Image compositeImage = Toolkit.getDefaultToolkit().createImage(mis);
  228. return (new ImageIcon(compositeImage));
  229. } //}}}
  230. //{{{ -_getIconCache(Icon)_ : Icon[][][][][]
  231. private static Icon[][][][] getIconCache(Icon icon) {
  232. Icon[][][][] cache = (Icon[][][][]) iconCache.get(icon);
  233. if (cache == null) {
  234. if (vcProvider == null)
  235. cache = new Icon[1][1][4][3];
  236. else
  237. cache = new Icon[6][1][4][3];
  238. iconCache.put(icon, cache);
  239. }
  240. return cache;
  241. } //}}}
  242. //{{{ -_getFileState(File, String)_ : int
  243. private static int getFileState(File f, String path) {
  244. if (f != null && !f.exists())
  245. return FILE_STATE_NOT_FOUND;
  246. Buffer buffer = jEdit.getBuffer(path);
  247. int file_state = IconComposer.FILE_STATE_NORMAL;
  248. if (buffer != null) {
  249. if(buffer.isDirty()) {
  250. return FILE_STATE_CHANGED;
  251. } else if (!buffer.isEditable()) {
  252. return FILE_STATE_READONLY;
  253. }
  254. } else if (!f.canWrite()) {
  255. return FILE_STATE_READONLY;
  256. }
  257. return FILE_STATE_NORMAL;
  258. } //}}}
  259. //}}}
  260. //{{{ -class _Helper_
  261. /**
  262. * Class to hold references to classes that may not be available, so this
  263. * class can behave well when called from a BeanShell script.
  264. */
  265. private static class Helper {
  266. //{{{ +_getMessageState(String)_ : int
  267. public static int getMessageState(String path) {
  268. int msg_state = IconComposer.MSG_STATE_NONE;
  269. ErrorSource[] sources = ErrorSource.getErrorSources();
  270. for(int i = 0; i < sources.length; i++) {
  271. if (sources[i].getFileErrorCount(path) > 0) {
  272. msg_state = IconComposer.MSG_STATE_MESSAGES;
  273. ErrorSource.Error[] errors = sources[i].getAllErrors();
  274. for(int j=0; j < errors.length; j++) {
  275. if(errors[j].getErrorType() == ErrorSource.ERROR
  276. && errors[j].getFilePath().equals(path)) {
  277. msg_state = IconComposer.MSG_STATE_ERRORS;
  278. break;
  279. }
  280. }
  281. break;
  282. }
  283. }
  284. return msg_state;
  285. } //}}}
  286. } //}}}
  287. //{{{ +class _VCProvider_
  288. /**
  289. * Version control plugins that want to provide file status info to PV
  290. * should implement this interface.
  291. *
  292. * @since PV 2.1.0
  293. */
  294. public static interface VCProvider {
  295. //{{{ +*getFileState(File, String)* : int
  296. /**
  297. * This method should return one of the VC_STATE possible values
  298. * for the given file. The file argument may be null, in which
  299. * case the path should be used to identify the file (this will
  300. * be the case, for example, with files from a VFS).
  301. *
  302. * <p>No range checking is performed on the returned values, so
  303. * make sure that the value is one of the defined constants.</p>
  304. *
  305. * @param f The file, if it's a local file.
  306. * @param path The path to the file (absolute path if local, VFS
  307. * URL otherwise).
  308. */
  309. public int getFileState(File f, String path); //}}}
  310. //{{{ +*getIcon(int)* : Icon
  311. /**
  312. * This should return the icon to be used to represent the requested
  313. * state.
  314. *
  315. * @param state One of the defined VC_STATE_* constants.
  316. */
  317. public Icon getIcon(int state); //}}}
  318. } //}}}
  319. }