001package org.vishia.commander; 002 003import java.io.File; 004import java.text.DateFormat; 005import java.text.ParseException; 006import java.text.SimpleDateFormat; 007import java.util.Date; 008import java.util.EventObject; 009 010import org.vishia.event.EventCmdtype; 011import org.vishia.event.EventConsumer; 012import org.vishia.event.EventSource; 013import org.vishia.fileRemote.FileRemote; 014import org.vishia.fileRemote.FileRemoteAccessor; 015import org.vishia.gral.base.GralButton; 016import org.vishia.gral.base.GralLed; 017import org.vishia.gral.base.GralPos; 018import org.vishia.gral.base.GralWidget; 019import org.vishia.gral.base.GralWindow; 020import org.vishia.gral.ifc.GralColor; 021import org.vishia.gral.ifc.GralTextField_ifc; 022import org.vishia.gral.ifc.GralUserAction; 023import org.vishia.gral.ifc.GralWindow_ifc; 024import org.vishia.gral.widget.GralFileSelector; 025import org.vishia.util.FileSystem; 026import org.vishia.util.KeyCode; 027 028public class FcmdFileProps 029{ 030 /**Version, history and license 031 * <ul> 032 * <li>2015-05-16 Hartmut bufgix: dateFormat 033 * <li>2012-03-10 Hartmut improved: Now works tested in windows 034 * <li>2012-03-09 Hartmut created, but not used yet 035 * </ul> 036 * 037 * 038 * <b>Copyright/Copyleft</b>: 039 * For this source the LGPL Lesser General Public License, 040 * published by the Free Software Foundation is valid. 041 * It means: 042 * <ol> 043 * <li> You can use this source without any restriction for any desired purpose. 044 * <li> You can redistribute copies of this source to everybody. 045 * <li> Every user of this source, also the user of redistribute copies 046 * with or without payment, must accept this license for further using. 047 * <li> But the LPGL ist not appropriate for a whole software product, 048 * if this source is only a part of them. It means, the user 049 * must publish this part of source, 050 * but don't need to publish the whole source of the own product. 051 * <li> You can study and modify (improve) this source 052 * for own using or for redistribution, but you have to license the 053 * modified sources likewise under this LGPL Lesser General Public License. 054 * You mustn't delete this Copyright/Copyleft inscription in this source file. 055 * </ol> 056 * If you are intent to use this sources without publishing its usage, you can get 057 * a second license subscribing a special contract with the author. 058 * 059 * @author Hartmut Schorrig = hartmut.schorrig@vishia.de 060 * 061 * 062 */ 063 public static final int version = 20120309; 064 065 066 protected final Fcmd main; 067 068 GralWindow_ifc windFileProps; 069 GralTextField_ifc widgName, widgDir, widgLink, widgDate, widgLength; 070 071 GralButton[] widgRd, widgWr, widgEx; 072 GralButton widgUID, widgGID, widgSticky; 073 GralButton widgHidden, widgDirectory; 074 GralButton widgBtnDirBytes; 075 076 /**Action button. */ 077 GralButton widGetAllProps, widgChrRecurs, widgChgFile, widgCopyFile; 078 079 080 DateFormat formatDate; 081 082 final String sWrAble = "wr / ?rd", sRdOnly = "rd / ?wr"; 083 084 final String sHidden = "hidden / ?", sNonHidden = "non hidden / ?"; 085 086 final String sSubdir = "recursive / ?", sNonSubdir = "non recurs/ ?"; 087 088 static final String sCmdChg = "change", sCmdCopy = "copy", sCmdChgRecurs = "chgRecurs" 089 , sCmdQuit = "quit", sCmdAbort = "abort"; 090 091 /**True if it is a unix system. It checks the "OS" environment variable. */ 092 boolean bUnixSystem; 093 094 /**True then the window is opened. Write acutal file into. */ 095 boolean isVisible; 096 097 /**True while a change commission is send and no answer is received yet. */ 098 //boolean busyChanging; 099 100 FileRemote actFile; 101 102 /** 103 * 104 */ 105 final FileRemote.CallbackEvent evChg; 106 107 final FileRemote.CallbackEvent evCntLen; 108 109 110 111 public FcmdFileProps(Fcmd main) 112 { this.main = main; 113 this.formatDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 114 evChg = new FileRemote.CallbackEvent(evSrc, null, null, callbackChgProps, null, evSrc); 115 evCntLen = new FileRemote.CallbackEvent(evSrc, null, null, callbackCntLen, null, evSrc); 116 } 117 118 119 /**Builds the content of the file property window. The window is created static. It is shown 120 * whenever it is used. */ 121 void buildWindow() 122 { String sUnix = System.getenv("OS"); 123 if(sUnix !=null){ 124 sUnix = sUnix.toUpperCase(); 125 if(sUnix.contains("WINDOWS")){ 126 bUnixSystem = false; 127 } else { 128 bUnixSystem = true; 129 } 130 } else { 131 bUnixSystem = System.getenv("WINDIR") == null; 132 } 133 main._gralMng.selectPanel("primaryWindow"); 134 main._gralMng.setPosition(-30, 0, -47, 0, 1, 'r'); //right buttom, about half less display width and hight. 135 int windProps = GralWindow.windConcurrently; 136 GralWindow window = main._gralMng.createWindow("windProp", "file properties - The.file.Commander", windProps); 137 windFileProps = window; 138 main._gralMng.setPosition(3.5f, GralPos.size -3, 1, -1, 0, 'd'); 139 widgLink = main._gralMng.addTextField("link", false, "symbolic link", "t"); 140 widgDir = main._gralMng.addTextField("dir", false, "directory path", "t"); 141 main._gralMng.setPosition(10, GralPos.size -4, 1, -1, 0, 'd'); 142 widgName = main._gralMng.addTextField("name", true, "filename", "t"); 143 main._gralMng.setPosition(14, GralPos.size -3.5f, 1, 24, 0, 'r'); 144 widgLength = main._gralMng.addTextField("length", false, "file-length", "t"); 145 main._gralMng.setPosition(14, GralPos.size -3.5f, 25, -1, 0, 'r'); 146 widgDate = main._gralMng.addTextField("data", true, "last modified", "t"); 147 GralColor colorBack = main._gralMng.propertiesGui.colorBackground_; 148 GralColor colorText = GralColor.getColor("bk"); 149 int ii; 150 GralColor colorOn = GralColor.getColor("lgn"); 151 GralColor colorOff = GralColor.getColor("wh"); 152 GralColor colorDis = GralColor.getColor("gr"); 153 String textOn = "X", textOff = " ", textDis = "?"; 154 155 main._gralMng.setPosition(17, GralPos.size -2, 1, 20, 0, 'r', 0.2f); 156 widgBtnDirBytes = main._gralMng.addButton("dirBytes", actionBtnCntLen, "dirBytes", null, main.idents.buttonFilePropsCntLen); 157 widgBtnDirBytes.setDisableColorText(colorDis, " (file) "); 158 //widgDirectory = main.gralMng.addCheckButton("FileProp:btndir", textOn, textOff, textDis, colorOn, colorOff, colorDis); 159 //main.gralMng.addText("directory"); 160 main._gralMng.setPosition(17, GralPos.size -2, 22, GralPos.size +2, 0, 'r', 0.2f); 161 widgHidden = main._gralMng.addCheckButton("FileProp:btnhidden", textOn, textOff, textDis, colorOn, colorOff, colorDis); 162 main._gralMng.addText("hidden"); 163 164 //bUnixSystem = true; 165 if(bUnixSystem){ 166 main._gralMng.setPosition(20, GralPos.size -2, 10, 28, 0, 'd'); 167 widGetAllProps = main._gralMng.addButton("buttonFilePropsGetAll", actionButton, "getAll", null, main.idents.buttonFilePropsGetAll); 168 169 main._gralMng.setPosition(20, GralPos.size -2, 1, GralPos.size +2, 0, 'd'); 170 main._gralMng.addText("rd"); 171 widgRd = new GralButton[3]; 172 for(ii=0; ii < 3; ++ii){ 173 widgRd[ii] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis); 174 //widgReadonly[ii] = main.gralMng.addSwitchButton("FileProp:btnro", "X", " ", GralColor.getColor("wh"), GralColor.getColor("lgn")); 175 } 176 main._gralMng.setPosition(20, GralPos.size -2, 4, GralPos.size +2, 0, 'd'); 177 main._gralMng.addText("wr"); 178 widgWr = new GralButton[3]; 179 for(ii=0; ii < 3; ++ii){ 180 widgWr[ii] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis); 181 } 182 main._gralMng.setPosition(20, GralPos.size -2, 7, GralPos.size +2, 0, 'd'); 183 main._gralMng.addText("ex"); 184 widgEx = new GralButton[3]; 185 for(ii=0; ii < 3; ++ii){ 186 widgEx[ii] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis); 187 } 188 main._gralMng.setPosition(22, GralPos.size -2, 10, GralPos.size +6, 0, 'd'); 189 main._gralMng.addText("owner"); 190 main._gralMng.addText("group"); 191 main._gralMng.addText("all"); 192 main._gralMng.setPosition(22, GralPos.size -2, 18, GralPos.size +2, 0, 'r', 0.2f); 193 widgUID = main._gralMng.addCheckButton("FileProp:btnUID", textOn, textOff, textDis, colorOn, colorOff, colorDis); 194 main._gralMng.addText("UID"); 195 main._gralMng.setPosition(24, GralPos.size -2, 18, GralPos.size +2, 0, 'r', 0.2f); 196 widgGID = main._gralMng.addCheckButton("FileProp:btnGID", textOn, textOff, textDis, colorOn, colorOff, colorDis); 197 main._gralMng.addText("GID"); 198 main._gralMng.setPosition(26, GralPos.size -2, 18, GralPos.size +2, 0, 'r', 0.2f); 199 widgSticky = main._gralMng.addCheckButton("FileProp:btnSticky", textOn, textOff, textDis, colorOn, colorOff, colorDis); 200 main._gralMng.addText("sticky"); 201 } else { 202 main._gralMng.setPosition(20, GralPos.size -2, 1, GralPos.size +2, 0, 'd'); 203 main._gralMng.addText("rd"); 204 widgRd = new GralButton[1]; 205 widgRd[0] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis); 206 main._gralMng.setPosition(20, GralPos.size -2, 4, GralPos.size +2, 0, 'd'); 207 main._gralMng.addText("wr"); 208 widgWr = new GralButton[1]; 209 widgWr[0] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis); 210 main._gralMng.setPosition(20, GralPos.size -2, 7, GralPos.size +2, 0, 'd'); 211 main._gralMng.addText("ex"); 212 widgEx = new GralButton[1]; 213 widgEx[0] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis); 214 215 } 216 main._gralMng.setPosition(-12, GralPos.size +3, -16, -1, 0, 'd',1); 217 widgCopyFile = main._gralMng.addButton("buttonFilePropsCopy", actionButton, sCmdCopy, null, main.idents.buttonFilePropsCopy); 218 widgChrRecurs = main._gralMng.addButton("buttonFilePropsChgRecursive", actionButton, sCmdChgRecurs, null, main.idents.buttonFilePropsChgRecurisve); 219 widgChgFile = main._gralMng.addButton("buttonFilePropsChg", actionButton, sCmdChg, null, main.idents.buttonFilePropsChg); 220 } 221 222 223 /**Opens the view window and fills its content. 224 * @param src The path which is selected as source. It may be a directory or a file. 225 */ 226 void openDialog(FileRemote src) 227 { //String sSrc, sTrash; 228 isVisible = true; 229 showFileInfos(src); 230 windFileProps.setFocus(); //WindowVisible(true); 231 232 } 233 234 235 void showFileInfos(FileRemote src){ 236 if(isVisible && !evChg.isOccupied()){ 237 actFile = src; 238 //TODO don't access the file system without user activity!!! need: a refresh button!! 239 widgChgFile.setText(main.idents.buttonFilePropsChg); 240 widgChrRecurs.setText(main.idents.buttonFilePropsChgRecurisve); 241 widgCopyFile.setText(main.idents.buttonFilePropsCopy); 242 widgName.setText(src.getName()); 243 widgDir.setText(src.getParent()); 244 String sDate = formatDate.format(new Date(src.lastModified())); 245 widgDate.setText(sDate); 246 String sLength; 247 long length = src.length(); 248 sLength = "" + length; 249 if(length >= 10000 && length < 10000000){ 250 sLength += " = " + length/1000 + "k"; 251 } else if( length >= 10000000){ 252 sLength += " = " + length/1000000 + "M"; 253 } 254 widgLength.setText(sLength); 255 if(src instanceof FileRemote && (src).isSymbolicLink()){ 256 widgLink.setText(FileSystem.getCanonicalPath(src)); 257 } else { 258 widgLink.setText(""); 259 } 260 widgRd[0].setState(src.canRead() ? GralButton.State.On : GralButton.State.Off); 261 widgEx[0].setState(src.canExecute() ? GralButton.State.On : GralButton.State.Off); 262 widgWr[0].setState(src.canWrite() ? GralButton.State.On : GralButton.State.Off); 263 if(bUnixSystem){ 264 widgRd[1].setState(GralButton.State.Disabled); 265 widgRd[2].setState(GralButton.State.Disabled); 266 widgWr[1].setState(GralButton.State.Disabled); 267 widgWr[2].setState(GralButton.State.Disabled); 268 widgEx[1].setState(GralButton.State.Disabled); 269 widgEx[2].setState(GralButton.State.Disabled); 270 widgSticky.setState(GralButton.State.Disabled); 271 widgUID.setState(GralButton.State.Disabled); 272 widgGID.setState(GralButton.State.Disabled); 273 } 274 widgHidden.setState(src.isHidden() ? GralButton.State.On : GralButton.State.Off); 275 widgBtnDirBytes.setState(src.isDirectory() ? GralButton.State.On : GralButton.State.Disabled); 276 //widgDirectory.setState(src.isDirectory() ? GralButton.State.On : GralButton.State.Off); 277 } 278 } 279 280 281 /**Action for Key F2 for view command. 282 */ 283 GralUserAction actionOpenDialog = new GralUserAction("actionOpenDialog") 284 { 285 @Override public boolean userActionGui(int keyCode, GralWidget infos, Object... params){ 286 if(KeyCode.isControlFunctionMouseUpOrMenu(keyCode)){ 287 openDialog(main.currentFile()); 288 } 289 return true; 290 } 291 }; 292 293 294 EventSource evSrc = new EventSource("FcmdFileProps"){ 295 @Override public void notifyDequeued(){ 296 297 } 298 @Override public void notifyConsumed(int ctConsumed){} 299 @Override public void notifyRelinquished(int ctConsumed){} 300 }; 301 302 303 304 /**Action for all buttons of 'file properties' window: 305 */ 306 GralUserAction actionButton = new GralUserAction("actionBtnCntLen") 307 { 308 @Override public boolean userActionGui(int keyCode, GralWidget infos, Object... params) 309 { if(KeyCode.isControlFunctionMouseUpOrMenu(keyCode)){ //not on mouse down but on mouse up. 310 String name = widgName.getText(); 311 if(name.equals(actFile.getName())){ name = null; } //don't change it. 312 int noMask = 0; 313 int val = 0; //actFileRemote.getFlags(); 314 int mask; 315 String sDate = widgDate.getText(); 316 long date; 317 try{ 318 Date date1 = formatDate.parse(sDate); 319 date = date1.getTime(); 320 } catch(ParseException exc){ 321 date = 0; 322 } 323 if(bUnixSystem){ 324 mask = FileRemote.mCanRead | FileRemote.mCanWrite | FileRemote.mExecute; 325 } else { 326 mask = FileRemote.mCanWrite | FileRemote.mHidden; 327 } 328 switch(widgRd[0].getState()){ 329 case Off: val &= ~FileRemote.mCanRead; break; 330 case On: val |= FileRemote.mCanRead; break; 331 case Disabled: mask &= ~FileRemote.mCanRead; break; 332 } 333 switch(widgWr[0].getState()){ 334 case Off: val &= ~FileRemote.mCanWrite; break; 335 case On: val |= FileRemote.mCanWrite; break; 336 case Disabled: mask &= ~FileRemote.mCanWrite; break; 337 } 338 switch(widgEx[0].getState()){ 339 case Off: val &= ~FileRemote.mExecute; break; 340 case On: val |= FileRemote.mExecute; break; 341 case Disabled: mask &= ~FileRemote.mExecute; break; 342 } 343 if(bUnixSystem){ 344 switch(widgRd[1].getState()){ 345 case Off: val &= ~FileRemote.mCanReadGrp; break; 346 case On: val |= FileRemote.mCanReadGrp; break; 347 case Disabled: mask &= ~FileRemote.mCanReadGrp; break; 348 } 349 switch(widgWr[1].getState()){ 350 case Off: val &= ~FileRemote.mCanWriteGrp; break; 351 case On: val |= FileRemote.mCanWriteGrp; break; 352 case Disabled: mask &= ~FileRemote.mCanWriteGrp; break; 353 } 354 switch(widgEx[1].getState()){ 355 case Off: val &= ~FileRemote.mExecuteGrp; break; 356 case On: val |= FileRemote.mExecuteGrp; break; 357 case Disabled: mask &= ~FileRemote.mExecuteGrp; break; 358 } 359 switch(widgRd[2].getState()){ 360 case Off: val &= ~FileRemote.mCanReadAny; break; 361 case On: val |= FileRemote.mCanReadAny; break; 362 case Disabled: mask &= ~FileRemote.mCanReadAny; break; 363 } 364 switch(widgWr[2].getState()){ 365 case Off: val &= ~FileRemote.mCanWriteAny; break; 366 case On: val |= FileRemote.mCanWriteAny; break; 367 case Disabled: mask &= ~FileRemote.mCanWriteAny; break; 368 } 369 switch(widgEx[2].getState()){ 370 case Off: val &= ~FileRemote.mExecuteAny; break; 371 case On: val |= FileRemote.mExecuteAny; break; 372 case Disabled: mask &= ~FileRemote.mExecuteAny; break; 373 } 374 } 375 switch(widgHidden.getState()){ 376 case Off: val &= ~FileRemote.mHidden; break; 377 case On: val |= FileRemote.mHidden; break; 378 case Disabled: mask &= ~FileRemote.mHidden; break; 379 } 380 val &= mask; //only used bits. 381 boolean bAbort = false; 382 if(infos.sCmd.equals(sCmdAbort)){ 383 if(evChg.occupy(evSrc, callbackChgProps, null, true)){ 384 widgChgFile.setText(main.idents.buttonFilePropsChg); 385 infos.sCmd = sCmdChg; 386 } else { 387 System.err.println("chg properties hangs"); 388 } 389 } else if(infos.sCmd.equals(sCmdChg)){ 390 if(evChg.occupy(evSrc, callbackChgProps, null, true)){ 391 //cmds with callback 392 widgChgFile.setText(main.idents.buttonFilePropsChanging); 393 actFile.chgProps(name, mask, val, date, evChg); 394 main.refreshFilePanel(actFile.getParentFile()); //refresh the panel if the directory is shown there 395 } else { bAbort = true; } 396 // 397 } else if(infos.sCmd.equals(sCmdChgRecurs)){ 398 if(evChg.occupy(evSrc, callbackChgProps, null, true)){ 399 //cmds with callback 400 widgChrRecurs.setText(main.idents.buttonFilePropsChanging); 401 actFile.chgPropsRecursive(mask, val, date, evChg); 402 } else { bAbort = true; } 403 // 404 } else if(infos.sCmd.equals(sCmdCopy)){ 405 if(evChg.occupy(evSrc, callbackChgProps, null, true)){ 406 if(name !=null && !name.equals(actFile.getName())){ 407 widgCopyFile.setText(main.idents.buttonFilePropsCopying); 408 FileRemote fileNew = actFile.getParentFile().child(name); 409 actFile.copyTo(fileNew, evChg, FileRemote.modeCopyReadOnlyOverwrite | FileRemote.modeCopyCreateYes | FileRemote.modeCopyExistAll); 410 } else { 411 widgCopyFile.setText("copy - name?"); 412 } 413 } else { bAbort = true; } 414 } 415 if(bAbort){ 416 widgChgFile.setText(main.idents.buttonFilePropsAbort); 417 widgChgFile.sCmd = sCmdAbort; 418 } 419 } 420 return true; 421 // / 422 } 423 }; 424 425 426 427 GralUserAction actionInvisible = new GralUserAction("actionInvisible") 428 { @Override public boolean userActionGui(int keyCode, GralWidget infos, Object... params) 429 { isVisible = false; 430 return true; 431 } 432 }; 433 434 435 EventConsumer callbackChgProps = new EventConsumer() 436 { @Override public int processEvent(EventObject evP) 437 { FileRemote.CallbackEvent ev = (FileRemote.CallbackEvent)evP; 438 if(ev.getCmd() == FileRemote.CallbackCmd.done){ 439 showFileInfos(actFile); 440 widgChgFile.setText(main.idents.buttonFilePropsOk); 441 } else { 442 widgChgFile.setText(main.idents.buttonFilePropsRetry); 443 } 444 ev.relinquish(); 445 return 1; 446 } 447 448 @Override public String toString(){ return "FcmdFileProps-callbackChgProps"; } 449 450 }; 451 452 453 454 455 /**Action for Key F2 for view command. 456 */ 457 GralUserAction actionBtnCntLen = new GralUserAction("actionBtnCntLen") 458 { 459 @Override public boolean userActionGui(int keyCode, GralWidget infos, Object... params) 460 { if(KeyCode.isControlFunctionMouseUpOrMenu(keyCode)){ 461 widgBtnDirBytes.setText("counting ..."); 462 if(0 != evCntLen.occupyRecall(100, evSrc, callbackCntLen, null, true)){ 463 actFile.countAllFileLength(evCntLen); 464 } 465 } 466 return true; 467 } }; 468 469 EventConsumer callbackCntLen = new EventConsumer() 470 { @Override public int processEvent(EventObject evP) 471 { FileRemote.CallbackEvent ev = (FileRemote.CallbackEvent)evP; 472 if(ev.getCmd() == FileRemote.CallbackCmd.done){ 473 String sLen = "" + ev.nrofBytesAll; 474 widgLength.setText(sLen); 475 } else { 476 widgLength.setText("error count bytes"); 477 } 478 widgBtnDirBytes.setText(main.idents.buttonFilePropsCntLen); 479 ev.relinquish(); 480 return 1; 481 } 482 483 @Override public String toString(){ return "FcmdFileProps - callback cnt length"; } 484 485 }; 486 487 488 489}