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

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