*/}}
瀏覽代碼

Rack template, and other things

YimingWu 1 年之前
父節點
當前提交
9b3ca04198
共有 8 個文件被更改,包括 128 次插入56 次删除
  1. 5 4
      la_data.c
  2. 1 1
      la_data.h
  3. 7 0
      la_interface.h
  4. 0 2
      la_kernel.c
  5. 62 11
      resources/la_operators.c
  6. 5 0
      resources/la_properties.c
  7. 47 37
      resources/la_templates.c
  8. 1 1
      resources/la_widgets.c

+ 5 - 4
la_data.c

@@ -3453,7 +3453,7 @@ void la_MakeDummyManagedUDF(){
     MAIN.DummyManageUDF=la_EnsureManagedUDF("< Save as a new file >", 1);
     if(!MAIN.DummyManageUDFSingle){
         MAIN.DummyManageUDFSingle=memAcquire(sizeof(laManagedUDF)); strSafeSet(&MAIN.DummyManageUDFSingle->BaseName, "< Choose file >");
-        MAIN.DummyManageUDFSingleForce=memAcquire(sizeof(laManagedUDF)); strSafeSet(&MAIN.DummyManageUDFSingleForce->BaseName, "Force");
+        MAIN.DummyManageUDFSingleForce=memAcquire(sizeof(laManagedUDF)); strSafeSet(&MAIN.DummyManageUDFSingleForce->BaseName, "< Force >");
     }
 }
 void laSaveProp(char* path){
@@ -3594,7 +3594,7 @@ laUDF *laOpenUDF(char *FileName, int ReadToMemory, laUDFRegistry* ReadRegistryRe
     if(ReadToMemory){ la_ReadUDFToMemory(udf); fclose(udf->DiskFile); udf->DiskFile = 0; }
 
     la_ReadBuffer(udf, sizeof(LA_UDF_IDENTIFIER) - 1, Identifier);
-    if (!strSame(Identifier, LA_UDF_IDENTIFIER)){ laCloseUDF(udf); logPrintNew("\"%s\" is not a UDF file.\n"); return 0; }
+    if (!strSame(Identifier, LA_UDF_IDENTIFIER)){ laCloseUDF(udf); logPrintNew("\"%s\" is not a UDF file.\n", FileName); return 0; }
 
     if(UseManaged){
         laManagedUDF* m=la_EnsureManagedUDF(FileName, 0);
@@ -3698,6 +3698,7 @@ void laRefreshUDFResourcesIn(char* rootpath){
         struct dirent* d = NameList[i]; int dlen;
         char *format = strGetLastSegment(d->d_name, '.'); int file_okay=0;
         for(laExtensionType* et=MAIN.ExtraExtensions.pFirst;et;et=et->Item.pNext){ if(et->FileType==LA_FILETYPE_UDF && strSame(et->Extension,format)){file_okay=1;break;} }
+        if(!file_okay) continue;
 
         struct stat s;
         sprintf(Final, "%s%s",rootpath,d->d_name);
@@ -3779,8 +3780,8 @@ void laPushDifferenceOnly(char* Description, u64bit hint){
     laDiff* d=memAcquire(sizeof(laDiff));
     lstAppendItem(&MAIN.Differences, d);
     if(MAIN.HeadDifference && Description) strSafeSet(&MAIN.HeadDifference->Description,Description);
-    d->Hint=hint;
-    MAIN.HeadDifference=d;
+    d->Hint=hint; MAIN.HeadDifference=d;
+    laNotifyUsers("la.differences");
 }
 void laPushDifferences(char* Description, u64bit hint){
     memFreeRemainingLeftNodes();

+ 1 - 1
la_data.h

@@ -639,7 +639,7 @@ STRUCTURE(laDiffPost){
 #define LA_UDF_EXTENSION_FLOAT32 (1<<0)
 #define LA_UDF_EXTENSION_RAW (1<<1)
 
-#define LA_UDF_EXTENSION_BITS 0
+#define LA_UDF_EXTENSION_BITS (LA_UDF_EXTENSION_RAW)
 
 #define LA_UDF_MARK_TIMESTAMP 3
 

+ 7 - 0
la_interface.h

@@ -1642,6 +1642,11 @@ STRUCTURE(laDiskItem){
     real Total_GB;
     real Free_GB;
 };
+STRUCTURE(laBookmarkedFolder){
+    laListItem Item;
+    char Name[128];
+    char Path[1024];
+};
 
 STRUCTURE(laFileBrowser){
     laListItem Hyper;
@@ -1650,12 +1655,14 @@ STRUCTURE(laFileBrowser){
     laDiskItem *RootDisk;
     laFileItem *Active;
     laListHandle FileList;
+    laListHandle Bookmarks;
     laListHandle Disks;
 
     int SelectFolder;
     int WarnFileExists;
     int StatusWaitingWarning;
     char UseExtension[64];
+    int UseType;
     int FilterType;
     laStringSplitor* ss_filter_extensions;
 };

+ 0 - 2
la_kernel.c

@@ -473,8 +473,6 @@ int laGetReady(){
 
     la_RegenerateWireColors();
 
-    laSaveProp("la.input_mapping");
-
     logPrint("Initialization Completed\n");
     MAIN.InitDone=1;
 }

+ 62 - 11
resources/la_operators.c

@@ -181,8 +181,7 @@ void la_FileBrowserRebuildList(laFileBrowser *fb){
     struct dirent **NameList=0;
     int NumFiles=scandir(fb->Path,&NameList,0,alphasort);
 
-    while (fi = lstPopItem(&fb->FileList))
-        memFree(fi);
+    while (fi = lstPopItem(&fb->FileList)) memFree(fi);
 
     for(int i=0;i<NumFiles;i++){
         struct dirent* d = NameList[i];
@@ -222,8 +221,7 @@ void la_FileBrowserRebuildList(laFileBrowser *fb){
     
     for (int i=0;i<NumFiles;i++){ free(NameList[i]); } free(NameList);
 
-    while (dl = lstPopItem(&fb->Disks))
-        memFree(dl);
+    while (dl = lstPopItem(&fb->Disks)) memFree(dl);
 
     //NumDisks = GetLogicalDriveStrings(256, DiskLabels) / 4;
 
@@ -259,6 +257,19 @@ laFileBrowser *la_FileBrowserInit(laOperator *a){
     if ((arg=strGetArgumentString(a->ExtraInstructionsP, "filter_extensions"))){ fb->ss_filter_extensions=strSplitPath(arg,','); }
     if ((arg=strGetArgumentString(a->ExtraInstructionsP, "use_extension"))){ strcpy(fb->UseExtension, arg); }
     if ((arg=strGetArgumentString(a->ExtraInstructionsP, "filter_type"))){ sscanf(arg,"%d",&fb->FilterType); }
+    if ((arg=strGetArgumentString(a->ExtraInstructionsP, "use_type"))){ sscanf(arg,"%d",&fb->UseType); }
+
+    char BookmarkPath[1024];
+    strcat(strcpy(BookmarkPath, getenv("HOME")), "/.config/gtk-3.0/bookmarks");
+    FILE* f=fopen(BookmarkPath, "r");
+    if(f){ char entry[1024]={0};
+        while(fgets(entry,1024,f)){ laBookmarkedFolder* bf=memAcquireSimple(sizeof(laBookmarkedFolder));
+            entry[strlen(entry)-1]=0;
+            strcpy(bf->Path,&entry[7]);
+            strcpy(bf->Name,strGetLastSegment(&entry[7],'/')); lstAppendItem(&fb->Bookmarks,bf);
+        }
+        fclose(f);
+    }
 
     la_FileBrowserRebuildList(fb);
     fb->FileName[0] = 0;
@@ -319,12 +330,41 @@ void *laset_FileBrowserActiveDisk(laFileBrowser *fb, laDiskItem *di, int UNUSED_
 void laset_FileBrowserPath(laFileBrowser *fb, char *content){
     strCopyFull(fb->Path, content); la_FileBrowserRebuildList(fb); fb->FileName[0] = 0; laRecalcCurrentPanel();
 }
+void *laset_FileBrowserBookmark(laFileBrowser *fb, laBookmarkedFolder *bf){
+    strcpy(fb->Path,bf->Path); la_FileBrowserRebuildList(fb); fb->FileName[0] = 0;
+}
 void laset_FileBrowserFileName(laFileBrowser *fb, char *content){
-    strCopyFull(fb->FileName, content);
-    if(fb->UseExtension[0] && strcmp(strGetLastSegment(fb->FileName,'.'),fb->UseExtension)){ strcat(fb->FileName,"."); strcat(fb->FileName,fb->UseExtension); }
+    strCopyFull(fb->FileName, content); int file_okay=0;
+    char* ext=strGetLastSegment(fb->FileName,'.');
+    if(fb->UseExtension[0] && strcmp(ext,fb->UseExtension)){ strcat(fb->FileName,"."); strcat(fb->FileName,fb->UseExtension); }
+    else if(fb->UseType){ int file_okay=0; laExtensionType* FirstET=0;
+        for(laExtensionType*et=MAIN.ExtraExtensions.pFirst;et;et=et->Item.pNext){
+            if((!FirstET) && et->FileType==fb->UseType) FirstET=et;
+            if(et->FileType==fb->UseType && strSame(ext,et->Extension)){ file_okay=1; break; }
+        }
+        if((!file_okay) && FirstET){ strcat(fb->FileName,"."); strcat(fb->FileName,FirstET->Extension); }
+    }
     la_FileBrowserRebuildList(fb);
     laRecalcCurrentPanel();
 }
+void* laget_FileBrowserAcceptedExtensionsFrist(laFileBrowser* fb, laPropIterator* pi){
+    if(!fb->UseType){ return 0; }
+    for(laExtensionType*et=MAIN.ExtraExtensions.pFirst;et;et=et->Item.pNext){
+        if(et->FileType==fb->UseType){ return et; }
+    }
+}
+void* laget_FileBrowserAcceptedExtensionsNext(laExtensionType* et, laPropIterator* pi){
+    for(laExtensionType*iet=et->Item.pNext;iet;iet=iet->Item.pNext){
+        if(et->FileType==iet->FileType){ return iet; }
+    }
+}
+void* laset_FileBrowserExtension(laFileBrowser* fb, laExtensionType* et){
+    if(fb->UseType && fb->FileName[0] && et){ char* ext=strGetLastSegment(fb->FileName,'.');
+        if(!ext){ sprintf(fb->FileName,".%s",ext); }
+        elif(strcmp(et->Extension,ext)){ sprintf(ext,"%s",et->Extension); }
+        la_FileBrowserRebuildList(fb); laRecalcCurrentPanel();
+    }
+}
 void la_FileBrowserUpLevel(laFileBrowser *fb){
     char *p = fb->Path;
     char *LastP = 0;
@@ -345,8 +385,12 @@ int OPINV_FileBrowser(laOperator *a, laEvent *e){
     return LA_RUNNING;
 }
 int OPEXT_FileBrowser(laOperator *a, int mark){
-    memFree(a->CustomData);
-
+    laFileBrowser *fb = a->CustomData;
+    void* f;
+    while (f=lstPopItem(&fb->Disks)) memFree(f);
+    while (f=lstPopItem(&fb->FileList)) memFree(f);
+    while (f=lstPopItem(&fb->Bookmarks)) memFree(f);
+    memFree(fb);
     return 0;
 }
 int OPMOD_FileBrowser(laOperator *a, laEvent *e){
@@ -522,7 +566,7 @@ int OPINV_ManagedSaveNewFile(laOperator *a, laEvent *e){
     if(MAIN.SetUDFPending) return LA_FINISHED;
     MAIN.SetUDFPending=1;
     a->CustomData = memAcquireSimple(sizeof(laUDFPreviewExtra));
-    laInvoke(a, "LA_file_dialog", e, 0, 0, 0);
+    laInvoke(a, "LA_file_dialog", e, 0, "use_type=1;filter_type=1", 0);
     return LA_RUNNING;
 }
 int OPMOD_ManagedSaveNewFile(laOperator *a, laEvent *e){
@@ -1998,7 +2042,10 @@ void la_RegisterBuiltinOperators(){
     laAddStringProperty(pc, "path", "Path", "Directort Path", 0, 0, 0, "/", 0, offsetof(laFileBrowser, Path), 0, 0, laset_FileBrowserPath, 0, LA_UDF_LOCAL);
     laAddStringProperty(pc, "file_name", "File Name", "File Name", 0, 0, 0, 0, 0, offsetof(laFileBrowser, FileName), 0, 0, laset_FileBrowserFileName, 0, LA_UDF_LOCAL);
     laAddSubGroup(pc, "file_list", "File List", "List Of Files And Directories Under A Specific Path", "file_item",0,0,laui_FileBrowserFileItem, -1, 0, laget_FileBrowserActiveFile, 0, 0, 0, laset_FileBrowserSelectFile, offsetof(laFileBrowser, FileList), 0);
-    laAddSubGroup(pc, "disk_list", "Disk List", "List Of All Logical Drives (In Windows)", "disk_item",0, 0, 0, -offsetof(laFileBrowser, RootDisk), 0, 0, 0, 0, 0, laset_FileBrowserActiveDisk, offsetof(laFileBrowser, Disks), 0);
+    laAddSubGroup(pc, "disk_list", "Disk List", "List Of All Logical Drives (In Windows)", "disk_item",0, 0, 0, offsetof(laFileBrowser, RootDisk), 0, 0, 0, 0, 0, laset_FileBrowserActiveDisk, offsetof(laFileBrowser, Disks), 0);
+    laAddSubGroup(pc, "bookmarks", "Bookmarks", "Bookmarked directories in GTK3", "bookmarked_folder",0, 0, 0, 0, 0, 0, 0, laset_FileBrowserBookmark, 0, 0, offsetof(laFileBrowser, Bookmarks), 0);
+    laAddSubGroup(pc, "available_extensions", "Available Extensions", "List of all available extensions", "la_extension_type",0, 0, 0, 0, laget_FileBrowserAcceptedExtensionsFrist, 0, laget_FileBrowserAcceptedExtensionsNext,laset_FileBrowserExtension,0,0,0,0);
+    laAddIntProperty(pc,"use_type","Use Type","Use file type",0,0,0,0,0,0,0,0,offsetof(laFileBrowser,UseType),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
     ep = laAddEnumProperty(pc, "select_what", "Select What", "Select folder or file", 0, 0, 0, 0, 0, offsetof(laFileBrowser, SelectFolder), 0, 0, 0, 0, 0, 0, 0, 0, 0,LA_READ_ONLY);{
         laAddEnumItemAs(ep, "file", "File", "File", LA_FILE_SELECT_FILE, L'📁');
         laAddEnumItemAs(ep, "folder", "Folder", "Folder", LA_FILE_SELECT_FOLDER, L'🖹');
@@ -2035,7 +2082,11 @@ void la_RegisterBuiltinOperators(){
             laAddEnumItemAs(ep, "lasdexchange", "LaSDExchange", "LA Scene Descriptive Exchange File", LA_FILETYPE_LASDEXCHANGE, 0);
         }
     }
-
+    p = laAddPropertyContainer("bookmarked_folder", "Bookmarked_folder", "A bookmarked folder from GTK3", 0, laui_IdentifierOnly, sizeof(laBookmarkedFolder), 0, 0, 0);{
+        laAddStringProperty(p, "name", "Name", "Bookmark Name", 0, 0, 0, 0, 0, offsetof(laBookmarkedFolder, Name), 0, 0, 0, 0, LA_AS_IDENTIFIER|LA_UDF_LOCAL);
+        laAddStringProperty(p, "path", "Path", "Bookmark path", 0, 0, 0, 0, 0, offsetof(laBookmarkedFolder, Path), 0, 0, 0, 0, LA_UDF_LOCAL);
+    }
+    
     at = laCreateOperatorType("LA_udf_read", "Read", "Read a UDF file", 0, 0, OPEXT_UDFOperation, OPINV_UDFAppend, OPMOD_UDFAppend, L'📑', LA_ACTUATOR_SYSTEM);
     pc = laDefineOperatorProps(at, 0);
     at->UiDefine = laui_LinkerPanel;

+ 5 - 0
resources/la_properties.c

@@ -959,6 +959,7 @@ void la_RegisterInternalProps(){
         p = laAddPropertyContainer("la_rack_page", "Rack Page", "A page of nodes", 0, laui_IdentifierOnly, sizeof(laRackPage), 0, 0, 2);{
             laAddStringProperty(p, "name", "Name", "Name of the page", 0, 0, 0, 0, 1, offsetof(laRackPage, Name), 0, 0, 0, 0, LA_AS_IDENTIFIER);
             laAddIntProperty(p,"type", "Type", "Type of the rack", 0,0,0,0,0,0,0,0,offsetof(laRackPage,RackType),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
+            laAddIntProperty(p,"has_rack", "Has Rack", "Has rack", 0,0,0,0,0,0,0,0,offsetof(laRackPage,Racks.pFirst),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY|LA_UDF_IGNORE);
             laAddSubGroup(p, "racks", "Racks", "Racks for nodes","la_node_rack",0,0,0,-1,0,0,0,0,0,0,offsetof(laRackPage,Racks),0);
             laAddOperatorProperty(p,"add_rack","Add Rack", "Add a rack into the page", "LA_add_rack", '+', 0);
         }
@@ -1081,6 +1082,10 @@ void la_RegisterInternalProps(){
             laAddStringProperty(p, "name", "Name", "The name of this language", 0, 0, 0, "Unknown", 1, offsetof(laTranslationNode, LanguageName), 0, 0, 0, 0, LA_AS_IDENTIFIER);
         }
 
+        p = laAddPropertyContainer("la_extension_type", "Extension Type", "File extension and its matching type for file filtering", 0, 0, sizeof(laExtensionType), 0, 0, 0);{
+            laAddStringProperty(p, "extension", "Extension", "File extension string", LA_WIDGET_STRING_PLAIN, 0, 0, 0, 0, offsetof(laExtensionType, Extension), 0, 0, 0, 0, LA_AS_IDENTIFIER|LA_READ_ONLY);
+        }
+
         // UI WINDOW ========================================================================================
 
         p = laAddPropertyContainer("ui_window", "Window Node", "Property Container For A System Window", 0, laui_SubPropInfoDefault, sizeof(laWindow), lapost_Window, 0, 2);{

+ 47 - 37
resources/la_templates.c

@@ -479,6 +479,8 @@ void laui_DefaultMenuButtonsEditEntries(laUiList *uil, laPropPack *pp, laPropPac
     laShowLabel(uil, c, "History", 0, 0)->Flags|=LA_TEXT_MONO|LA_UI_FLAGS_DISABLED;
     laShowItem(uil, c, 0, "LA_undo")->Flags|=LA_UI_FLAGS_NO_CONFIRM;
     laShowItem(uil, c, 0, "LA_redo")->Flags|=LA_UI_FLAGS_NO_CONFIRM;
+    laShowSeparator(uil,c);
+    laShowItemFull(uil, c, 0, "LA_panel_activator",0,"panel_id=LAUI_histories;text=Histories",0,0);
 }
 void laui_DefaultMenuButtonsOptionEntries(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){
     laColumn* c=laFirstColumn(uil);
@@ -503,7 +505,7 @@ void laui_DefaultMenuExtras(laUiList *uil, laPropPack *pp, laPropPack *actinst,
     laColumn *c = laFirstColumn(uil);
     laShowLabel(uil, c, MAIN.MenuProgramName, 0, 0)->Expand=1;
 }
-        
+
 void laui_DefaultMenuBarActual(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){
     laUiList *muil;
     laColumn *c,*mc;
@@ -931,9 +933,8 @@ void laui_FileBrowserDiskItem(laUiList *uil, laPropPack *This, laPropPack *OP_UN
     laShowItem(uil, crr, This, "free_gb");
 }
 void laui_FileBrowserFileList(laUiList *uil, laPropPack *THIS_UNUSED, laPropPack *Operator, laColumn *UNUSED, int context){
-    laColumn *col = laFirstColumn(uil), *c, *cl, *cr, *crl, *crr, *cll, *clr, *clrl, *clrr, *clrrl, *clrrr;
-    laUiList *u;
-    laUiItem *b;
+    laColumn *col = laFirstColumn(uil), *c, *cl, *cr, *crl, *crr, *cll, *clr, *clrl, *clrr, *clrrl, *clrrr, *ulc;
+    laUiList *u; laUiItem *b; laUiList *ul;
 
     laSplitColumn(uil, col, 0.25);
     cl = laLeftColumn(col, 10);
@@ -949,30 +950,29 @@ void laui_FileBrowserFileList(laUiList *uil, laPropPack *THIS_UNUSED, laPropPack
 
     laShowSeparator(uil,col);
 
-    u = laMakeGroup(uil, cl, "Logic Drives", 0)->Page;
-    c = laFirstColumn(u);
-    laSplitColumn(u, c, 0.2);
-    cll = laLeftColumn(c, LA_2RH);
-    clr = laRightColumn(c, 0);
-    laSplitColumn(u, clr, 0.5);
-    clrl = laLeftColumn(clr, 0);
-    clrr = laRightColumn(clr, 0);
+    laUiItem* left=laMakeEmptyGroup(uil, cl, "left", 0); ul=left->Page; left->Flags|=LA_UI_FLAGS_NO_DECAL; ul->HeightCoeff=-1;
+    ulc=laFirstColumn(ul);
+
+    b=laOnConditionThat(ul,ulc,laPropExpression(Operator,"use_type"));{
+        u = laMakeGroup(ul, ulc, "Use Format", 0)->Page; c = laFirstColumn(u);
+        laShowItemFull(u,c,Operator,"available_extensions",0,0,laui_IdentifierOnly,0)->Flags|=LA_UI_FLAGS_NO_DECAL;
+    }laEndCondition(ul,b);
+
+    u = laMakeFoldableGroup(ul, ulc, "Bookmarks", 0, 0, 0)->Page; c = laFirstColumn(u);
+    laShowItemFull(u,c,Operator,"bookmarks",0,0,laui_IdentifierOnly,0)->Flags|=LA_UI_FLAGS_NO_DECAL;
+
+    u = laMakeGroup(ul, ulc, "Logic Drives", 0)->Page;
+    c = laFirstColumn(u); laSplitColumn(u, c, 0.2); cll = laLeftColumn(c, LA_2RH); clr = laRightColumn(c, 0);
+    laSplitColumn(u, clr, 0.5); clrl = laLeftColumn(clr, 0); clrr = laRightColumn(clr, 0);
     laShowColumnAdjuster(u, c);
     laShowItem(u, c, Operator, "disk_list");
 
     b = laMakeGroup(uil, cr, "File List", 0);b->State=LA_UI_ACTIVE; u=b->Page;
     u->HeightCoeff = -1;
-    c = laFirstColumn(u);
+    c = laFirstColumn(u); laSplitColumn(u, c, 0.1); cll = laLeftColumn(c, 1); clr = laRightColumn(c, 0);
+    laSplitColumn(u, clr, 0.7); clrl = laLeftColumn(clr, 0); clrr = laRightColumn(clr, 0);
+    laSplitColumn(u, clrr, 0.5); clrrl = laLeftColumn(clrr, 0); clrrr = laRightColumn(clrr, 0);
     laShowColumnAdjuster(u, c);
-    laSplitColumn(u, c, 0.1);
-    cll = laLeftColumn(c, 1);
-    clr = laRightColumn(c, 0);
-    laSplitColumn(u, clr, 0.7);
-    clrl = laLeftColumn(clr, 0);
-    clrr = laRightColumn(clr, 0);
-    laSplitColumn(u, clrr, 0.5);
-    clrrl = laLeftColumn(clrr, 0);
-    clrrr = laRightColumn(clrr, 0);
     laShowLabel(u, clrl, "File Name", 0, 0);
     laShowLabel(u, clrrl, "Last Modified On", 0, 0);
     laShowLabel(u, clrrr, "File Size", 0, 0);
@@ -1041,7 +1041,7 @@ void laui_ManagedPropInstance(laUiList *uil, laPropPack *Base, laPropPack *Opera
     if(!Base ||! Base->EndInstance ||Base->LastPs->p->PropertyType!=LA_PROP_SUB) return;
     laColumn *c=laFirstColumn(uil);
     laSplitColumn(uil,c,0.3); laColumn *cl=laLeftColumn(c, 1); laColumn *cr=laRightColumn(c,0);
-    laSplitColumn(uil,cr,0.6); laColumn *crl=laLeftColumn(cr, 30); laColumn *crr=laRightColumn(cr,0);
+    laSplitColumn(uil,cr,0.6); laColumn *crl=laLeftColumn(cr, 0); laColumn *crr=laRightColumn(cr,30);
 
     laPropContainer* pc=la_EnsureSubTarget(Base->LastPs->p, Base->EndInstance);
 
@@ -1055,7 +1055,7 @@ void laui_ManagedPropInstance(laUiList *uil, laPropPack *Base, laPropPack *Opera
         //laShowLabel(uil,cr,"(Item not allocated by memAcquire)",0,0)->Expand=1;
     }else{
         if(pc->Hyper==2){
-            laShowItem(uil,cl,Base,"__modified"); laSplitColumn(uil,crl,0.4); laColumn *crll=laLeftColumn(crl, 0); laColumn *crlr=laRightColumn(crl,10);
+            laShowItem(uil,cl,Base,"__modified"); laSplitColumn(uil,crl,0.4); laColumn *crll=laLeftColumn(crl, 0); laColumn *crlr=laRightColumn(crl,15);
             laUiItem* idui=laShowItem(uil,crll,Base,"identifier");idui->Flags|=LA_UI_FLAGS_PLAIN;idui->Expand=1;
             laShowItem(uil,crlr,Base,"__uid")->Flags|=LA_UI_FLAGS_NO_DECAL;
             if(pc->UDFPropagate){
@@ -1088,8 +1088,7 @@ void laui_ManagedPropInstance(laUiList *uil, laPropPack *Base, laPropPack *Opera
             laShowItem(uil,crr,Base,"__single_saver_dummy.__file");
             continue;
         }
-        char buf[128]; sprintf(buf,"🞄 %s",p->Identifier);
-        laUiItem* b=laOnConditionToggle(uil,c,0,0,0,0,0);{ strSafePrint(&b->ExtraInstructions,"text=🞄 %s;",p->Identifier);
+        laUiItem* b=laOnConditionToggle(uil,c,0,0,0,0,0);{ strSafePrint(&b->ExtraInstructions,"text=⮡ %s;",p->Identifier);
             b->Flags|=LA_UI_FLAGS_NO_DECAL|LA_TEXT_ALIGN_LEFT; b->State=LA_BT_ACTIVE;
             laShowItemFull(uil,cr,Base,p->Identifier,0, 0,laui_ManagedPropInstance,0)->Flags|=LA_UI_FLAGS_NO_DECAL|LA_UI_COLLECTION_NO_HIGHLIGHT;
         }laEndCondition(uil,b);
@@ -1178,11 +1177,14 @@ void laui_NodeRack(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn
 }
 void laui_RackPage(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laRackPage* r=This->EndInstance;
-    laUiItem* g=laMakeGroup(uil,c,"Racks",0);{ g->Flags|=/*LA_UI_FLAGS_NO_GAP|LA_UI_FLAGS_NO_DECAL|*/LA_UI_FLAGS_NODE_CONTAINER;
-        laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); gu->AllowScale=1; gu->HeightCoeff=-3; g->State=LA_UI_ACTIVE;
-        laUiItem* hui=laShowItemFull(gu,gc,This,"racks",0,0,laui_NodeRack,r?r->RackType:0); hui->Expand=15; hui->Flags|=LA_UI_FLAGS_NO_DECAL;
-    }
-    laShowItemFull(uil,c,This,"add_rack",0,0,0,0);
+    laUiItem* b=laOnConditionThat(uil,c,laPropExpression(This,"has_rack"));{
+        laUiItem* g=laMakeGroup(uil,c,"Racks",0);{ g->Flags|=/*LA_UI_FLAGS_NO_GAP|LA_UI_FLAGS_NO_DECAL|*/LA_UI_FLAGS_NODE_CONTAINER;
+            laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); gu->AllowScale=1; gu->HeightCoeff=-1; g->State=LA_UI_ACTIVE;
+            laUiItem* hui=laShowItemFull(gu,gc,This,"racks",0,0,laui_NodeRack,r?r->RackType:0); hui->Expand=15; hui->Flags|=LA_UI_FLAGS_NO_DECAL;
+        }
+    }laElse(uil,b);{
+        laShowItemFull(uil,c,This,"add_rack",0,0,0,0);
+    }laEndCondition(uil,b);
 }
 void laui_NodeCategory(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laNodeCategory* nc=This->EndInstance;
@@ -1194,7 +1196,6 @@ void laui_NodeCategory(laUiList *uil, laPropPack *This, laPropPack *Extra, laCol
     }
 }
 
-
 void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laColumn *ExtraColumns, int context){
     laColumn* c = laFirstColumn(uil),*cl,*cr; laSplitColumn(uil,c,0.5);cl=laLeftColumn(c,0);cr=laRightColumn(c,0);
     laUiList *muil;
@@ -1397,7 +1398,7 @@ void laui_About(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laCol
         gu = g->Page;{
             gc = laFirstColumn(gu);
             laShowLabel(gu,gc,"LaGUI application framework is made by Wu Yiming.",0,0)->Flags|=LA_TEXT_LINE_WRAP;
-            laUiItem* b=laBeginRow(gu,gc,1,0);
+            laUiItem* b=laBeginRow(gu,gc,0,0);
             laShowItemFull(gu, gc, 0, "LA_open_internet_link", 0, "link=http://www.ChengduLittleA.com/lagui;text=Details", 0, 0);
             laShowItemFull(gu, gc, 0, "LA_open_internet_link", 0, "link=http://www.ChengduLittleA.com;text=Yiming's Blog", 0, 0);
             laEndRow(gu,b);
@@ -1406,7 +1407,15 @@ void laui_About(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laCol
         t->Page = first;
     }
 }
-void laui_terminal(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laColumn *ExtraColumns, int context){
+void laui_UndoHistories(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laColumn *ExtraColumns, int context){
+    laColumn* c=laFirstColumn(uil),*cl,*cr; laSplitColumn(uil,c,0.5);cl=laLeftColumn(c,0);cr=laRightColumn(c,0);
+    laShowItem(uil,cl,0,"LA_undo"); laShowItem(uil,cr,0,"LA_redo")->Flags|=LA_TEXT_ALIGN_RIGHT;
+    laUiItem* g=laMakeGroup(uil,c,"123",0);{ g->State=LA_UI_ACTIVE; g->Flags|=LA_UI_FLAGS_PREFER_BOTTOM;
+        laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); gu->HeightCoeff=-1;
+        laShowItem(gu,gc,0,"la.differences")->Flags|=LA_UI_FLAGS_NO_DECAL;
+    }
+}
+void laui_Terminal(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laColumn *ExtraColumns, int context){
     laColumn* c=laFirstColumn(uil);
     laUiItem* g=laMakeGroup(uil,c,"123",0);{ g->State=LA_UI_ACTIVE; g->Flags|=LA_UI_FLAGS_PREFER_BOTTOM;
         laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); gu->HeightCoeff=-1;
@@ -1488,7 +1497,7 @@ void laui_InputMapper(laUiList *uil, laPropPack *This, laPropPack *Extra, laColu
         laShowItemFull(uil,cr,0,"la.input_mapping.pages",LA_WIDGET_COLLECTION_SELECTOR,0,0,0);
     }laEndCondition(uil,b2);
     
-    laShowItemFull(uil,c,0,"la.input_mapping.current_page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0);
+    laShowItemFull(uil,c,0,"la.input_mapping.current_page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0)->Flags|=LA_UI_FLAGS_NO_DECAL;;
 }
 void lauidetached_Drivers(laPanel* p){
     la_MakeDetachedProp(p, "la.drivers.current_page", "page");
@@ -1510,7 +1519,7 @@ void laui_Drivers(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *
         laShowItemFull(uil,cr,Extra,"page",LA_WIDGET_COLLECTION_SELECTOR,0,0,0);
     }laEndCondition(uil,b2);
     
-    laShowItemFull(uil,c,Extra,"page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0);
+    laShowItemFull(uil,c,Extra,"page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0)->Flags|=LA_UI_FLAGS_NO_DECAL;;
 }
 
 
@@ -1522,5 +1531,6 @@ void la_RegisterBuiltinTemplates(){
     laRegisterUiTemplate("LAUI_about", "About", laui_About, 0, 0, 0, 0);
     laRegisterUiTemplate("LAUI_texture_inspector", "Texture Inspector", laui_TextureInspector, lauidetached_TextureInspector, 0, 0, 0);
     laRegisterUiTemplate("LAUI_data_manager", "Data Manager", laui_IdleDataManager, lauidetached_IdleDataManager, 0, 0, 0);
-    laRegisterUiTemplate("LAUI_terminal", "Terminal", laui_terminal, 0, 0, 0, 0);
+    laRegisterUiTemplate("LAUI_histories", "Histories", laui_UndoHistories, 0, 0, 0, 0);
+    laRegisterUiTemplate("LAUI_terminal", "Terminal", laui_Terminal, 0, 0, 0, 0);
 }

+ 1 - 1
resources/la_widgets.c

@@ -2586,7 +2586,7 @@ int OPMOD_Collection(laOperator *a, laEvent *e){
         ui->PP.EndInstance = Active;
     }
 
-    if (e->Type == LA_L_MOUSE_DOWN){
+    if (e->Type == LA_L_MOUSE_DOWN && (!laIsPropertyReadOnly(&ui->PP))){
         laUiList *uil;
         for (uil = ui->Subs.pFirst; uil; uil = uil->Item.pNext){
             if (a->ConfirmData) return LA_RUNNING;