diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 50f65572b..7c203d51b 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -843,19 +843,23 @@ PREFERENCES_TP_LABEL;Panneau des outils: PREFERENCES_TP_USEICONORTEXT;Utiliser des icônes au lieu de textes PREFERENCES_TP_VSCROLLBAR;Cacher la barre de défilement verticale PREFERENCES_TUNNELMETADATA;Copier les données IPTC/XMP sans les\nchanger dans fichier de sortie +PREFERENCES_USEBUNDLEDPROFILES;Utiliser les profiles fournis PREFERENCES_USESYSTEMTHEME;Utiliser le thème système PREFERENCES_VIEW;Réglage du point blanc du périphérique sortie (moniteur, TV, projecteur,...) PREFERENCES_WORKFLOW;Habitudes de travail PROFILEPANEL_COPYPPASTE;Paramètres à copier PROFILEPANEL_FILEDLGFILTERANY;Tous les fichiers PROFILEPANEL_FILEDLGFILTERPP;Profils de post-traitement +PROFILEPANEL_GLOBALPROFILES;Profils fournis PROFILEPANEL_LABEL;Profils de post-traitement PROFILEPANEL_LOADDLGLABEL;Charger les paramètres de post-traitement... PROFILEPANEL_LOADPPASTE;Paramètres à charger PROFILEPANEL_MODE_TIP;Mode de complètement des profils de traitement.\n\nBouton pressé: les profils partiels seront convertis en profils complets; les valeurs manquantes seront remplacées par les valeurs internes par défaut\n\nBouton relevé: les profils seront appliqués tel quel, altérant seulement les paramètres qu'ils contiennent. +PROFILEPANEL_MYPROFILES;Mes profils PROFILEPANEL_PASTEPPASTE;Paramètres à coller PROFILEPANEL_PCUSTOM;Personnel PROFILEPANEL_PFILE;Depuis le fichier +PROFILEPANEL_PINTERNAL;Neutre PROFILEPANEL_PLASTPHOTO;Photo précédente PROFILEPANEL_PLASTSAVED;Dernière sauvegarde PROFILEPANEL_PROFILE;Profil diff --git a/rtdata/languages/default b/rtdata/languages/default index fd385517e..57bee0871 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -838,19 +838,23 @@ PREFERENCES_TP_LABEL;Tool panel: PREFERENCES_TP_USEICONORTEXT;Use tab icons instead of text PREFERENCES_TP_VSCROLLBAR;Hide vertical scrollbar PREFERENCES_TUNNELMETADATA;Copy IPTC/XMP unchanged to output file (when tagging with other program) +PREFERENCES_USEBUNDLEDPROFILES;Use bundled profiles PREFERENCES_USESYSTEMTHEME;Use system theme PREFERENCES_VIEW;Output device's white balance setting (monitor, TV, projector, viewing, etc.) PREFERENCES_WORKFLOW;Layout PROFILEPANEL_COPYPPASTE;Parameters to copy PROFILEPANEL_FILEDLGFILTERANY;Any files PROFILEPANEL_FILEDLGFILTERPP;Processing profiles +PROFILEPANEL_GLOBALPROFILES;Bundled profiles PROFILEPANEL_LABEL;Processing Profiles PROFILEPANEL_LOADDLGLABEL;Load Processing Parameters... PROFILEPANEL_LOADPPASTE;Parameters to load PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. +PROFILEPANEL_MYPROFILES;My profiles PROFILEPANEL_PASTEPPASTE;Parameters to paste PROFILEPANEL_PCUSTOM;Custom PROFILEPANEL_PFILE;From file +PROFILEPANEL_PINTERNAL;Neutral PROFILEPANEL_PLASTPHOTO;Last Photo PROFILEPANEL_PLASTSAVED;Last Saved PROFILEPANEL_PROFILE;Profile diff --git a/rtdata/profiles/BW 1.pp3 b/rtdata/profiles/BW/BW 1.pp3 similarity index 100% rename from rtdata/profiles/BW 1.pp3 rename to rtdata/profiles/BW/BW 1.pp3 diff --git a/rtdata/profiles/BW 2.pp3 b/rtdata/profiles/BW/BW 2.pp3 similarity index 100% rename from rtdata/profiles/BW 2.pp3 rename to rtdata/profiles/BW/BW 2.pp3 diff --git a/rtdata/profiles/BW 3.pp3 b/rtdata/profiles/BW/BW 3.pp3 similarity index 100% rename from rtdata/profiles/BW 3.pp3 rename to rtdata/profiles/BW/BW 3.pp3 diff --git a/rtdata/profiles/BW 4.pp3 b/rtdata/profiles/BW/BW 4.pp3 similarity index 100% rename from rtdata/profiles/BW 4.pp3 rename to rtdata/profiles/BW/BW 4.pp3 diff --git a/rtdata/profiles/Deep Shadows.pp3 b/rtdata/profiles/Constrasty/Deep Shadows.pp3 similarity index 100% rename from rtdata/profiles/Deep Shadows.pp3 rename to rtdata/profiles/Constrasty/Deep Shadows.pp3 diff --git a/rtdata/profiles/Punchy 1.pp3 b/rtdata/profiles/Constrasty/Punchy 1.pp3 similarity index 100% rename from rtdata/profiles/Punchy 1.pp3 rename to rtdata/profiles/Constrasty/Punchy 1.pp3 diff --git a/rtdata/profiles/Punchy 2.pp3 b/rtdata/profiles/Constrasty/Punchy 2.pp3 similarity index 100% rename from rtdata/profiles/Punchy 2.pp3 rename to rtdata/profiles/Constrasty/Punchy 2.pp3 diff --git a/rtdata/profiles/Faded Amber 1 TM Bright.pp3 b/rtdata/profiles/Faded/Amber/Faded Amber 1 TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Faded Amber 1 TM Bright.pp3 rename to rtdata/profiles/Faded/Amber/Faded Amber 1 TM Bright.pp3 diff --git a/rtdata/profiles/Faded Amber 1 TM.pp3 b/rtdata/profiles/Faded/Amber/Faded Amber 1 TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Amber 1 TM.pp3 rename to rtdata/profiles/Faded/Amber/Faded Amber 1 TM.pp3 diff --git a/rtdata/profiles/Faded Amber 1.pp3 b/rtdata/profiles/Faded/Amber/Faded Amber 1.pp3 similarity index 100% rename from rtdata/profiles/Faded Amber 1.pp3 rename to rtdata/profiles/Faded/Amber/Faded Amber 1.pp3 diff --git a/rtdata/profiles/Faded Blue 1 TM Bright.pp3 b/rtdata/profiles/Faded/Blue/Faded Blue 1 TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Faded Blue 1 TM Bright.pp3 rename to rtdata/profiles/Faded/Blue/Faded Blue 1 TM Bright.pp3 diff --git a/rtdata/profiles/Faded Blue 1 TM.pp3 b/rtdata/profiles/Faded/Blue/Faded Blue 1 TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Blue 1 TM.pp3 rename to rtdata/profiles/Faded/Blue/Faded Blue 1 TM.pp3 diff --git a/rtdata/profiles/Faded Blue 1.pp3 b/rtdata/profiles/Faded/Blue/Faded Blue 1.pp3 similarity index 100% rename from rtdata/profiles/Faded Blue 1.pp3 rename to rtdata/profiles/Faded/Blue/Faded Blue 1.pp3 diff --git a/rtdata/profiles/Faded Blue Pink TM.pp3 b/rtdata/profiles/Faded/Blue/Faded Blue Pink TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Blue Pink TM.pp3 rename to rtdata/profiles/Faded/Blue/Faded Blue Pink TM.pp3 diff --git a/rtdata/profiles/Faded Blue Pink.pp3 b/rtdata/profiles/Faded/Blue/Faded Blue Pink.pp3 similarity index 100% rename from rtdata/profiles/Faded Blue Pink.pp3 rename to rtdata/profiles/Faded/Blue/Faded Blue Pink.pp3 diff --git a/rtdata/profiles/Faded Chocolate 1 TM Bright.pp3 b/rtdata/profiles/Faded/Chocolate/Faded Chocolate 1 TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Faded Chocolate 1 TM Bright.pp3 rename to rtdata/profiles/Faded/Chocolate/Faded Chocolate 1 TM Bright.pp3 diff --git a/rtdata/profiles/Faded Chocolate 2 TM Bright.pp3 b/rtdata/profiles/Faded/Chocolate/Faded Chocolate 2 TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Faded Chocolate 2 TM Bright.pp3 rename to rtdata/profiles/Faded/Chocolate/Faded Chocolate 2 TM Bright.pp3 diff --git a/rtdata/profiles/Faded Golden 1.pp3 b/rtdata/profiles/Faded/Chocolate/Faded Golden 1.pp3 similarity index 100% rename from rtdata/profiles/Faded Golden 1.pp3 rename to rtdata/profiles/Faded/Chocolate/Faded Golden 1.pp3 diff --git a/rtdata/profiles/Faded Golden 2.pp3 b/rtdata/profiles/Faded/Chocolate/Faded Golden 2.pp3 similarity index 100% rename from rtdata/profiles/Faded Golden 2.pp3 rename to rtdata/profiles/Faded/Chocolate/Faded Golden 2.pp3 diff --git a/rtdata/profiles/Faded/Golden/Faded Golden 1.pp3 b/rtdata/profiles/Faded/Golden/Faded Golden 1.pp3 new file mode 100644 index 000000000..6196b5cdd --- /dev/null +++ b/rtdata/profiles/Faded/Golden/Faded Golden 1.pp3 @@ -0,0 +1,105 @@ + +[Version] +AppVersion=4.0.11 +Version=308 + +[Exposure] +Auto=false +Clip=0.02 + +Brightness=0 +Contrast=0 +Saturation=0 +Black=0 +HighlightCompr=70 +HighlightComprThreshold=0 +ShadowCompr=50 +CurveMode=SatAndValueBlending +CurveMode2=SatAndValueBlending +Curve=3;0;0;0.23320158102766808;0.58893280632411094;0.69169960474308201;0.96047430830039549;1;1; +Curve2=3;0;0;0.12213438735177862;0.012648221343873525;0.53359683794466362;0.72870684529014551;1;1; + +[Channel Mixer] +Red=60;0;20; +Green=0;60;20; +Blue=-20;20;40; + +[Luminance Curve] +Brightness=0 +Contrast=0 +Chromaticity=56 +AvoidColorShift=true +RedAndSkinTonesProtection=0 +BWtoning=false +LCredsk=true +LCurve=0; +aCurve=0; +bCurve=0; +ccCurve=0; +chCurve=0; +LcCurve=0; + +[Vibrance] +Enabled=false +Pastels=0 +Saturated=0 +PSThreshold=0;75; +ProtectSkins=false +AvoidColorShift=true +PastSatTog=true +SkinTonesCurve=0; + + +[Color appearance] +Enabled=false + + +[Directional Pyramid Denoising] +Enabled=true +Luma=0 +Ldetail=50 +Chroma=20 +Method=RGB +Redchro=0 +Bluechro=0 +Gamma=1.7 + +[EPD] +Enabled=false +Strength=0.25 +EdgeStopping=1.40 +Scale=1 +ReweightingIterates=0 + +[Shadows & Highlights] +Enabled=false + + +[HLRecovery] +Enabled=false +Method=Blend + +[Color Management] + +ToneCurve=false +BlendCMSMatrix=false +PreferredProfile=true +WorkingProfile=ProPhoto +OutputProfile=RT_sRGB +Gammafree=default +Freegamma=false +GammaValue=2.2200000000000002 +GammaSlope=4.5 + +[HSV Equalizer] +HCurve=0; +SCurve=0; +VCurve=0; + +[RGB Curves] +LumaMode=false +rCurve=3;0;0;0.086956521739130432;0.090909090909090856;1;1; +gCurve=3;0;0;0.20266764462192638;0.12923950395936107;1;1; +bCurve=1;0;0;0.25296442687747034;0.22529644268774709;0.8656126482213431;0.80632411067193566;1;1; + + diff --git a/rtdata/profiles/Faded/Golden/Faded Golden 2.pp3 b/rtdata/profiles/Faded/Golden/Faded Golden 2.pp3 new file mode 100644 index 000000000..345aef888 --- /dev/null +++ b/rtdata/profiles/Faded/Golden/Faded Golden 2.pp3 @@ -0,0 +1,105 @@ + +[Version] +AppVersion=4.0.11 +Version=308 + +[Exposure] +Auto=false +Clip=0.02 + +Brightness=0 +Contrast=0 +Saturation=0 +Black=0 +HighlightCompr=70 +HighlightComprThreshold=0 +ShadowCompr=50 +CurveMode=SatAndValueBlending +CurveMode2=SatAndValueBlending +Curve=3;0;0;0.23320158102766808;0.58893280632411094;0.69169960474308201;0.96047430830039549;1;1; +Curve2=3;0;0;0.12213438735177862;0.012648221343873525;0.53359683794466362;0.72870684529014551;1;1; + +[Channel Mixer] +Red=60;0;20; +Green=0;60;20; +Blue=-20;20;40; + +[Luminance Curve] +Brightness=0 +Contrast=-25 +Chromaticity=56 +AvoidColorShift=true +RedAndSkinTonesProtection=0 +BWtoning=false +LCredsk=true +LCurve=0; +aCurve=0; +bCurve=0; +ccCurve=0; +chCurve=0; +LcCurve=0; + +[Vibrance] +Enabled=false +Pastels=0 +Saturated=0 +PSThreshold=0;75; +ProtectSkins=false +AvoidColorShift=true +PastSatTog=true +SkinTonesCurve=0; + + +[Color appearance] +Enabled=false + + +[Directional Pyramid Denoising] +Enabled=true +Luma=0 +Ldetail=50 +Chroma=20 +Method=RGB +Redchro=0 +Bluechro=0 +Gamma=1.7 + +[EPD] +Enabled=false +Strength=0.25 +EdgeStopping=1.40 +Scale=1 +ReweightingIterates=0 + +[Shadows & Highlights] +Enabled=false + + +[HLRecovery] +Enabled=false +Method=Blend + +[Color Management] + +ToneCurve=false +BlendCMSMatrix=false +PreferredProfile=true +WorkingProfile=ProPhoto +OutputProfile=RT_sRGB +Gammafree=default +Freegamma=false +GammaValue=2.2200000000000002 +GammaSlope=4.5 + +[HSV Equalizer] +HCurve=0; +SCurve=0; +VCurve=0; + +[RGB Curves] +LumaMode=false +rCurve=3;0;0;0.086956521739130432;0.090909090909090856;0.94466403162055268;0.89328063241106692;1;1; +gCurve=3;0;0;0.20266764462192638;0.12923950395936107;0.90118577075098805;0.88537549407114613;1;1; +bCurve=1;0;0;0.25296442687747034;0.22529644268774709;0.8656126482213431;0.80632411067193566;1;1; + + diff --git a/rtdata/profiles/Faded Green 1 TM Bright.pp3 b/rtdata/profiles/Faded/Green/Faded Green 1 TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Faded Green 1 TM Bright.pp3 rename to rtdata/profiles/Faded/Green/Faded Green 1 TM Bright.pp3 diff --git a/rtdata/profiles/Faded Green 1 TM.pp3 b/rtdata/profiles/Faded/Green/Faded Green 1 TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Green 1 TM.pp3 rename to rtdata/profiles/Faded/Green/Faded Green 1 TM.pp3 diff --git a/rtdata/profiles/Faded Green 1.pp3 b/rtdata/profiles/Faded/Green/Faded Green 1.pp3 similarity index 100% rename from rtdata/profiles/Faded Green 1.pp3 rename to rtdata/profiles/Faded/Green/Faded Green 1.pp3 diff --git a/rtdata/profiles/Faded Green 2.pp3 b/rtdata/profiles/Faded/Green/Faded Green 2.pp3 similarity index 100% rename from rtdata/profiles/Faded Green 2.pp3 rename to rtdata/profiles/Faded/Green/Faded Green 2.pp3 diff --git a/rtdata/profiles/Faded Green 3.pp3 b/rtdata/profiles/Faded/Green/Faded Green 3.pp3 similarity index 100% rename from rtdata/profiles/Faded Green 3.pp3 rename to rtdata/profiles/Faded/Green/Faded Green 3.pp3 diff --git a/rtdata/profiles/Faded Neutral TM.pp3 b/rtdata/profiles/Faded/Neutral/Faded Neutral TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Neutral TM.pp3 rename to rtdata/profiles/Faded/Neutral/Faded Neutral TM.pp3 diff --git a/rtdata/profiles/Faded Neutral.pp3 b/rtdata/profiles/Faded/Neutral/Faded Neutral.pp3 similarity index 100% rename from rtdata/profiles/Faded Neutral.pp3 rename to rtdata/profiles/Faded/Neutral/Faded Neutral.pp3 diff --git a/rtdata/profiles/Faded Purple 1 TM Bright.pp3 b/rtdata/profiles/Faded/Purple/Faded Purple 1 TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Faded Purple 1 TM Bright.pp3 rename to rtdata/profiles/Faded/Purple/Faded Purple 1 TM Bright.pp3 diff --git a/rtdata/profiles/Faded Purple 1 TM.pp3 b/rtdata/profiles/Faded/Purple/Faded Purple 1 TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Purple 1 TM.pp3 rename to rtdata/profiles/Faded/Purple/Faded Purple 1 TM.pp3 diff --git a/rtdata/profiles/Faded Purple 1.pp3 b/rtdata/profiles/Faded/Purple/Faded Purple 1.pp3 similarity index 100% rename from rtdata/profiles/Faded Purple 1.pp3 rename to rtdata/profiles/Faded/Purple/Faded Purple 1.pp3 diff --git a/rtdata/profiles/Faded Purple 2 TM.pp3 b/rtdata/profiles/Faded/Purple/Faded Purple 2 TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Purple 2 TM.pp3 rename to rtdata/profiles/Faded/Purple/Faded Purple 2 TM.pp3 diff --git a/rtdata/profiles/Faded Purple 2.pp3 b/rtdata/profiles/Faded/Purple/Faded Purple 2.pp3 similarity index 100% rename from rtdata/profiles/Faded Purple 2.pp3 rename to rtdata/profiles/Faded/Purple/Faded Purple 2.pp3 diff --git a/rtdata/profiles/Faded Teal Orange TM Bright.pp3 b/rtdata/profiles/Faded/Teal/Faded Teal Orange TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Faded Teal Orange TM Bright.pp3 rename to rtdata/profiles/Faded/Teal/Faded Teal Orange TM Bright.pp3 diff --git a/rtdata/profiles/Faded Teal Orange TM.pp3 b/rtdata/profiles/Faded/Teal/Faded Teal Orange TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Teal Orange TM.pp3 rename to rtdata/profiles/Faded/Teal/Faded Teal Orange TM.pp3 diff --git a/rtdata/profiles/Faded Teal Orange.pp3 b/rtdata/profiles/Faded/Teal/Faded Teal Orange.pp3 similarity index 100% rename from rtdata/profiles/Faded Teal Orange.pp3 rename to rtdata/profiles/Faded/Teal/Faded Teal Orange.pp3 diff --git a/rtdata/profiles/Faded Warm 1 TM Bright.pp3 b/rtdata/profiles/Faded/Warm/Faded Warm 1 TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Faded Warm 1 TM Bright.pp3 rename to rtdata/profiles/Faded/Warm/Faded Warm 1 TM Bright.pp3 diff --git a/rtdata/profiles/Faded Warm 1 TM.pp3 b/rtdata/profiles/Faded/Warm/Faded Warm 1 TM.pp3 similarity index 100% rename from rtdata/profiles/Faded Warm 1 TM.pp3 rename to rtdata/profiles/Faded/Warm/Faded Warm 1 TM.pp3 diff --git a/rtdata/profiles/Faded Warm 1.pp3 b/rtdata/profiles/Faded/Warm/Faded Warm 1.pp3 similarity index 100% rename from rtdata/profiles/Faded Warm 1.pp3 rename to rtdata/profiles/Faded/Warm/Faded Warm 1.pp3 diff --git a/rtdata/profiles/Faded Warm 2.pp3 b/rtdata/profiles/Faded/Warm/Faded Warm 2.pp3 similarity index 100% rename from rtdata/profiles/Faded Warm 2.pp3 rename to rtdata/profiles/Faded/Warm/Faded Warm 2.pp3 diff --git a/rtdata/profiles/Faded Warm 3.pp3 b/rtdata/profiles/Faded/Warm/Faded Warm 3.pp3 similarity index 100% rename from rtdata/profiles/Faded Warm 3.pp3 rename to rtdata/profiles/Faded/Warm/Faded Warm 3.pp3 diff --git a/rtdata/profiles/Neutral.pp3 b/rtdata/profiles/Neutral.pp3 deleted file mode 100644 index 014cee165..000000000 --- a/rtdata/profiles/Neutral.pp3 +++ /dev/null @@ -1,267 +0,0 @@ -[Version] -AppVersion=4.0.11.20 -Version=309 - -[Exposure] -Auto=false -Clip=0.02 -Compensation=0 -Brightness=0 -Contrast=0 -Saturation=0 -Black=0 -HighlightCompr=0 -HighlightComprThreshold=0 -ShadowCompr=50 -CurveMode=Standard -Curve=0; -CurveMode2=Standard -Curve2=0; - -[Channel Mixer] -Red=100;0;0; -Green=0;100;0; -Blue=0;0;100; - -[Luminance Curve] -Brightness=0 -Contrast=0 -Chromaticity=0 -BWtoning=false -AvoidColorShift=false -RedAndSkinTonesProtection=0 -LCredsk=true -LCurve=0; -aCurve=0; -bCurve=0; -ccCurve=0; -chCurve=0; -LcCurve=0; - -[Sharpening] -Enabled=false -Method=usm -Radius=0.5 -Amount=200 -Threshold=20;80;2000;1200; -OnlyEdges=false -EdgedetectionRadius=1.9 -EdgeTolerance=1800 -HalocontrolEnabled=false -HalocontrolAmount=85 -DeconvRadius=0.75 -DeconvAmount=75 -DeconvDamping=20 -DeconvIterations=30 - -[Vibrance] -Enabled=false -Pastels=50 -Saturated=50 -PSThreshold=0;75; -ProtectSkins=false -AvoidColorShift=true -PastSatTog=true -SkinTonesCurve=0; - -[SharpenEdge] -Enabled=false -Passes=2 -Strength=50 -ThreeChannels=false - -[SharpenMicro] -Enabled=false -Matrix=false -Strength=20 -Uniformity=50 - -[White Balance] -Setting=Camera -Temperature=5745 -Green=1.0 -Equal=1.0 - -[Color appearance] -Enabled=false -Degree=90 -AutoDegree=true -Surround=Average -AdaptLum=16 -Model=RawT -Algorithm=JC -J-Light=0 -Q-Bright=0 -C-Chroma=0 -S-Chroma=0 -M-Chroma=0 -J-Contrast=0 -Q-Contrast=0 -H-Hue=0 -RSTProtection=0 -AdaptScene=2000 -AutoAdapscen=true -SurrSource=false -Gamut=true -Datacie=false -Tonecie=false -CurveMode=Lightness -CurveMode2=Lightness -CurveMode3=Chroma -Curve=0; -Curve2=0; -Curve3=0; - -[Impulse Denoising] -Enabled=false -Threshold=50 - -[Defringing] -Enabled=false -Radius=2.0 -Threshold=25 -HueCurve=1;0.166666667;0;0.35;0.35;0.347;0;0.35;0.35;0.513667426;0;0.35;0.35;0.668944571;0;0.35;0.35;0.8287775246;0.97835991;0.35;0.35;0.9908883827;0;0.35;0.35; - -[Directional Pyramid Denoising] -Enabled=false -Method=RGB -Luma=0 -Ldetail=50 -Chroma=15 -Redchro=0 -Bluechro=0 -Gamma=1.7 - -[EPD] -Enabled=false -Strength=0.25 -EdgeStopping=1.4 -Scale=1 -ReweightingIterates=0 - -[Shadows & Highlights] -Enabled=false -HighQuality=false -Highlights=10 -HighlightTonalWidth=80 -Shadows=10 -ShadowTonalWidth=80 -LocalContrast=0 -Radius=30 - -[Crop] -Enabled=false -X=0 -Y=0 -W=7360 -H=4912 -FixedRatio=false -Ratio=3:2 -Orientation=Landscape -Guide=None - -[Coarse Transformation] -Rotate=0 -HorizontalFlip=false -VerticalFlip=false - -[Common Properties for Transformations] -AutoFill=true - -[Rotation] -Degree=0 - -[Distortion] -Amount=0 - -[LensProfile] -LCPFile= -UseDistortion=true -UseVignette=true -UseCA=false - -[Perspective] -Horizontal=0 -Vertical=0 - -[CACorrection] -Red=0 -Blue=0 - -[Vignetting Correction] -Amount=0 -Radius=50 -Strength=1 -CenterX=0 -CenterY=0 - -[HLRecovery] -Enabled=false -Method=Blend - -[Resize] -Enabled=false -Scale=1 -AppliesTo=Cropped area -Method=Lanczos -DataSpecified=3 -Width=900 -Height=900 - -[Color Management] -InputProfile=(cameraICC) -ToneCurve=false -BlendCMSMatrix=true -PreferredProfile=true -WorkingProfile=sRGB -OutputProfile=RT_sRGB -Gammafree=default -Freegamma=false -GammaValue=2.22 -GammaSlope=4.5 - -[Directional Pyramid Equalizer] -Enabled=false -Mult0=1 -Mult1=1 -Mult2=1 -Mult3=1 -Mult4=0.2 - -[HSV Equalizer] -HCurve=0; -SCurve=0; -VCurve=0; - -[RGB Curves] -LumaMode=false -rCurve=0; -gCurve=0; -bCurve=0; - -[RAW] -DarkFrame= -DarkFrameAuto=false -FlatFieldFile= -FlatFieldAutoSelect=false -FlatFieldBlurRadius=32 -FlatFieldBlurType=Area Flatfield -CA=false -CARed=0 -CABlue=0 -HotDeadPixels=false -HotDeadPixelThresh=40 -LineDenoise=0 -GreenEqThreshold=0 -CcSteps=0 -Method=amaze -DCBIterations=2 -DCBEnhance=false -ALLEnhance=false -PreExposure=1 -PrePreserv=0 -PreBlackzero=0 -PreBlackone=0 -PreBlacktwo=0 -PreBlackthree=0 -PreTwoGreen=true diff --git a/rtdata/profiles/Pop 1.pp3 b/rtdata/profiles/Pop/Pop 1.pp3 similarity index 100% rename from rtdata/profiles/Pop 1.pp3 rename to rtdata/profiles/Pop/Pop 1.pp3 diff --git a/rtdata/profiles/Pop 2 L.pp3 b/rtdata/profiles/Pop/Pop 2 L.pp3 similarity index 100% rename from rtdata/profiles/Pop 2 L.pp3 rename to rtdata/profiles/Pop/Pop 2 L.pp3 diff --git a/rtdata/profiles/Pop 3 Skin.pp3 b/rtdata/profiles/Pop/Pop 3 Skin.pp3 similarity index 100% rename from rtdata/profiles/Pop 3 Skin.pp3 rename to rtdata/profiles/Pop/Pop 3 Skin.pp3 diff --git a/rtdata/profiles/Pop 4 BW.pp3 b/rtdata/profiles/Pop/Pop 4 BW.pp3 similarity index 100% rename from rtdata/profiles/Pop 4 BW.pp3 rename to rtdata/profiles/Pop/Pop 4 BW.pp3 diff --git a/rtdata/profiles/Portrait Lejto.pp3 b/rtdata/profiles/Portrait/Portrait Lejto.pp3 similarity index 100% rename from rtdata/profiles/Portrait Lejto.pp3 rename to rtdata/profiles/Portrait/Portrait Lejto.pp3 diff --git a/rtdata/profiles/Portrait Smooth.pp3 b/rtdata/profiles/Portrait/Portrait Smooth.pp3 similarity index 100% rename from rtdata/profiles/Portrait Smooth.pp3 rename to rtdata/profiles/Portrait/Portrait Smooth.pp3 diff --git a/rtdata/profiles/Skintones - Natural TM.pp3 b/rtdata/profiles/Skintones/Skintones - Natural TM.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Natural TM.pp3 rename to rtdata/profiles/Skintones/Skintones - Natural TM.pp3 diff --git a/rtdata/profiles/Skintones - Natural.pp3 b/rtdata/profiles/Skintones/Skintones - Natural.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Natural.pp3 rename to rtdata/profiles/Skintones/Skintones - Natural.pp3 diff --git a/rtdata/profiles/Skintones - Pale TM Bright.pp3 b/rtdata/profiles/Skintones/Skintones - Pale TM Bright.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Pale TM Bright.pp3 rename to rtdata/profiles/Skintones/Skintones - Pale TM Bright.pp3 diff --git a/rtdata/profiles/Skintones - Pale TM.pp3 b/rtdata/profiles/Skintones/Skintones - Pale TM.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Pale TM.pp3 rename to rtdata/profiles/Skintones/Skintones - Pale TM.pp3 diff --git a/rtdata/profiles/Skintones - Pale.pp3 b/rtdata/profiles/Skintones/Skintones - Pale.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Pale.pp3 rename to rtdata/profiles/Skintones/Skintones - Pale.pp3 diff --git a/rtdata/profiles/Skintones - Soft Texture.pp3 b/rtdata/profiles/Skintones/Skintones - Soft Texture.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Soft Texture.pp3 rename to rtdata/profiles/Skintones/Skintones - Soft Texture.pp3 diff --git a/rtdata/profiles/Skintones - Strong Texture.pp3 b/rtdata/profiles/Skintones/Skintones - Strong Texture.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Strong Texture.pp3 rename to rtdata/profiles/Skintones/Skintones - Strong Texture.pp3 diff --git a/rtdata/profiles/Skintones - Studio TM.pp3 b/rtdata/profiles/Skintones/Skintones - Studio TM.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Studio TM.pp3 rename to rtdata/profiles/Skintones/Skintones - Studio TM.pp3 diff --git a/rtdata/profiles/Skintones - Studio.pp3 b/rtdata/profiles/Skintones/Skintones - Studio.pp3 similarity index 100% rename from rtdata/profiles/Skintones - Studio.pp3 rename to rtdata/profiles/Skintones/Skintones - Studio.pp3 diff --git a/rtdata/profiles/Skintones - StudioBase 1 TM.pp3 b/rtdata/profiles/Skintones/Skintones - StudioBase 1 TM.pp3 similarity index 100% rename from rtdata/profiles/Skintones - StudioBase 1 TM.pp3 rename to rtdata/profiles/Skintones/Skintones - StudioBase 1 TM.pp3 diff --git a/rtdata/profiles/Skintones - StudioBase 1.pp3 b/rtdata/profiles/Skintones/Skintones - StudioBase 1.pp3 similarity index 100% rename from rtdata/profiles/Skintones - StudioBase 1.pp3 rename to rtdata/profiles/Skintones/Skintones - StudioBase 1.pp3 diff --git a/rtengine/procparams.h b/rtengine/procparams.h index dbb700458..dcc457d4d 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -863,6 +863,17 @@ class PartialProfile { const void applyTo (ProcParams *destParams) const ; }; +/** + * This class automatically create the pparams and pedited instance in the constructor, + * and automatically delete them in the destructor. This class has been mostly created + * to be used with vectors, which use the default constructor/destructor + */ +class AutoPartialProfile : public PartialProfile { + public: + AutoPartialProfile() : PartialProfile(true) {} + ~AutoPartialProfile() { deleteInstance(); } +}; + } } #endif diff --git a/rtgui/filebrowser.cc b/rtgui/filebrowser.cc index 67eede569..e6881d994 100644 --- a/rtgui/filebrowser.cc +++ b/rtgui/filebrowser.cc @@ -23,7 +23,6 @@ #include "options.h" #include "multilangmgr.h" #include "clipboard.h" -#include "profilestore.h" #include "procparamchangers.h" #include "batchqueue.h" #include "../rtengine/dfmanager.h" @@ -41,6 +40,8 @@ FileBrowser::FileBrowser () fbih->destroyed = false; fbih->pending = 0; + profileStore.addListener(this); + signal_style_changed().connect( sigc::mem_fun(*this, &FileBrowser::styleChanged) ); int p = 0; @@ -288,6 +289,9 @@ FileBrowser::FileBrowser () } pmenuColorLabels->show_all (); + // Has to be located after creation of applyprof and applypartprof + updateProfileList (); + // Bind to event handlers for (int i=0; i<=5; i++) colorlabel_pop[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuColorlabelActivated), colorlabel_pop[i])); @@ -295,6 +299,7 @@ FileBrowser::FileBrowser () FileBrowser::~FileBrowser () { + profileStore.removeListener(this); delete pmenu; delete pmenuColorLabels; delete[] amiExtProg; @@ -303,7 +308,6 @@ FileBrowser::~FileBrowser () void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) { { - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -327,32 +331,8 @@ void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) { clearprof->set_sensitive (!selected.empty()); } - // submenu applmenu - int p = 0; - Gtk::Menu* applmenu = Gtk::manage (new Gtk::Menu ()); - std::vector profnames = profileStore.getProfileNames (); - for (size_t i=0; iattach (*mi, 0, 1, p, p+1); p++; - mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyMenuItemActivated), profnames[i])); - mi->show (); - } - applyprof->set_submenu (*applmenu); - - // submenu applpartmenu - p = 0; - Gtk::Menu* applpartmenu = Gtk::manage (new Gtk::Menu ()); - //std::vector profnames = profileStore.getProfileNames (); // this is already created for submenu applmenu above - for (size_t i=0; iattach (*mi, 0, 1, p, p+1); p++; - mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyPartialMenuItemActivated), profnames[i])); - mi->show (); - } - applypartprof->set_submenu (*applpartmenu); - // submenuDF - p = 0; + int p = 0; Gtk::Menu* submenuDF = Gtk::manage (new Gtk::Menu ()); submenuDF->attach (*Gtk::manage(selectDF = new Gtk::MenuItem (M("FILEBROWSER_SELECTDARKFRAME"))), 0, 1, p, p+1); p++; submenuDF->attach (*Gtk::manage(autoDF = new Gtk::MenuItem (M("FILEBROWSER_AUTODARKFRAME"))), 0, 1, p, p+1); p++; @@ -451,7 +431,6 @@ void FileBrowser::addEntry_ (FileBrowserEntry* entry) { // find place in abc order { - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK(l, entryRW); #endif @@ -468,7 +447,6 @@ void FileBrowser::addEntry_ (FileBrowserEntry* entry) { } FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname) { - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK(l, entryRW); #endif @@ -511,21 +489,18 @@ void FileBrowser::close () { fbih->pending = 0; { - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK(l, entryRW); #endif selected.clear (); - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK_RELEASE(l); // notifySelectionListener will need read access! #endif notifySelectionListener (); - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK_ACQUIRE(l); #endif @@ -558,7 +533,6 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) { std::vector mselected; { - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -627,7 +601,6 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) { else if (m==selall) { lastClicked = NULL; { - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK(l, entryRW); #endif @@ -816,7 +789,6 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) { } void FileBrowser::copyProfile () { - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -830,7 +802,6 @@ void FileBrowser::pasteProfile () { if (clipboard.hasProcParams()) { std::vector mselected; { - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -866,7 +837,6 @@ void FileBrowser::partPasteProfile () { std::vector mselected; { - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -909,7 +879,6 @@ void FileBrowser::openDefaultViewer (int destination) { bool success=true; { - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -1078,14 +1047,13 @@ int FileBrowser::getThumbnailHeight () { return std::max(std::min(options.thumbSize, 800), 10); } -void FileBrowser::applyMenuItemActivated (Glib::ustring ppname) { +void FileBrowser::applyMenuItemActivated (ProfileStoreLabel *label) { - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif - const rtengine::procparams::PartialProfile* partProfile = profileStore.getProfile (ppname); + const rtengine::procparams::PartialProfile* partProfile = profileStore.getProfile (label->entry); if (partProfile->pparams && !selected.empty()) { if (bppcl) bppcl->beginBatchPParamsChange(selected.size()); @@ -1097,7 +1065,7 @@ void FileBrowser::applyMenuItemActivated (Glib::ustring ppname) { } } -void FileBrowser::applyPartialMenuItemActivated (Glib::ustring ppname) { +void FileBrowser::applyPartialMenuItemActivated (ProfileStoreLabel *label) { { #if PROTECT_VECTORS @@ -1108,7 +1076,7 @@ void FileBrowser::applyPartialMenuItemActivated (Glib::ustring ppname) { return; } - const rtengine::procparams::PartialProfile* srcProfiles = profileStore.getProfile (ppname); + const rtengine::procparams::PartialProfile* srcProfiles = profileStore.getProfile (label->entry); if (srcProfiles->pparams) { if (partialPasteDlg.run()==Gtk::RESPONSE_OK) { @@ -1145,7 +1113,6 @@ void FileBrowser::applyFilter (const BrowserFilter& filter) { bool selchanged = false; numFiltered=0; { - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK(l, entryRW); // Don't make this a writer lock! HOMBRE: Why? 'selected' is modified here #endif @@ -1334,7 +1301,6 @@ void FileBrowser::colorlabelRequested (std::vector tbe, int c } void FileBrowser::requestRanking(int rank){ - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -1346,7 +1312,6 @@ void FileBrowser::requestRanking(int rank){ } void FileBrowser::requestColorLabel(int colorlabel){ - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -1386,7 +1351,6 @@ void FileBrowser::buttonPressed (LWButton* button, int actionCode, void* actionD } void FileBrowser::openNextImage () { - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK(l, entryRW); #endif @@ -1454,7 +1418,6 @@ void FileBrowser::openNextImage () { } void FileBrowser::openPrevImage () { - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK(l, entryRW); #endif @@ -1526,7 +1489,6 @@ void FileBrowser::selectImage (Glib::ustring fname) { // need to clear the filter in filecatalog - // TODO: Check for Linux #if PROTECT_VECTORS MYWRITERLOCK(l, entryRW); #endif @@ -1607,7 +1569,6 @@ void FileBrowser::selectionChanged () { void FileBrowser::notifySelectionListener () { if (tbl) { - // TODO: Check for Linux #if PROTECT_VECTORS MYREADERLOCK(l, entryRW); #endif @@ -1639,3 +1600,82 @@ void FileBrowser::setExportPanel (ExportPanel* expanel) { exportPanel->set_sensitive (false); exportPanel->setExportPanelListener (this); } + +void FileBrowser::updateProfileList () { + // submenu applmenu + int p = 0; + + const std::vector *profEntries = profileStore.getFileList(); // lock and get a pointer to the profiles' list + + std::map subMenuList; // store the Gtk::Menu that Gtk::MenuItem will have to be attached to + + subMenuList[0] = Gtk::manage (new Gtk::Menu ()); // adding the root submenu + + // iterate the profile store's profile list + for (size_t i=0; isize(); i++) { + // create a new label for the current entry (be it a folder or file) + ProfileStoreLabel *currLabel = Gtk::manage(new ProfileStoreLabel( profEntries->at(i) )); + + // create the MenuItem object + Gtk::MenuItem* mi = Gtk::manage (new Gtk::MenuItem (*currLabel)); + + // create a new Menu object if the entry is a folder and not the root one + if (currLabel->entry->type == PSET_FOLDER) { + // creating the new sub-menu + Gtk::Menu* subMenu = Gtk::manage (new Gtk::Menu ()); + + // add it to the menu list + subMenuList[currLabel->entry->folderId] = subMenu; + + // add it to the parent MenuItem + mi->set_submenu(*subMenu); + } + + // Hombre: ... does parentMenuId sounds like a hack? ... Yes. + int parentMenuId = !options.useBundledProfiles && currLabel->entry->parentFolderId==1 ? 0 : currLabel->entry->parentFolderId; + subMenuList[parentMenuId]->attach (*mi, 0, 1, p, p+1); p++; + if (currLabel->entry->type == PSET_FILE) + mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyMenuItemActivated), currLabel)); + mi->show (); + } + + if (subMenuList.size() && applyprof) + // TODO: Check that the previous one has been deleted, including all childrens + applyprof->set_submenu (*(subMenuList.at(0))); + + subMenuList.clear(); + subMenuList[0] = Gtk::manage (new Gtk::Menu ()); // adding the root submenu + // keep profEntries list + + // submenu applpartmenu + p = 0; + for (size_t i=0; isize(); i++) { + ProfileStoreLabel *currLabel = Gtk::manage(new ProfileStoreLabel( profEntries->at(i) )); + + Gtk::MenuItem* mi = Gtk::manage (new Gtk::MenuItem (*currLabel)); + + if (currLabel->entry->type == PSET_FOLDER) { + // creating the new sub-menu + Gtk::Menu* subMenu = Gtk::manage (new Gtk::Menu ()); + + // add it to the menu list + subMenuList[currLabel->entry->folderId] = subMenu; + + // add it to the parent MenuItem + mi->set_submenu(*subMenu); + } + // Hombre: ... does parentMenuId sounds like a hack? ... yes. + int parentMenuId = !options.useBundledProfiles && currLabel->entry->parentFolderId==1 ? 0 : currLabel->entry->parentFolderId; + subMenuList[parentMenuId]->attach (*mi, 0, 1, p, p+1); p++; + if (currLabel->entry->type == PSET_FILE) + mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyPartialMenuItemActivated), currLabel)); + mi->show (); + } + + if (subMenuList.size() && applypartprof) + // TODO: Check that the previous one has been deleted, including all childrens + applypartprof->set_submenu (*(subMenuList.at(0))); + + profileStore.releaseFileList(); + subMenuList.clear(); +} diff --git a/rtgui/filebrowser.h b/rtgui/filebrowser.h index 4486e70c4..a939f38ed 100644 --- a/rtgui/filebrowser.h +++ b/rtgui/filebrowser.h @@ -29,7 +29,9 @@ #include "partialpastedlg.h" #include "exportpanel.h" #include "extprog.h" +#include "profilestore.h" +class ProfileStoreLabel; class FileBrowser; class FileBrowserEntry; class FileBrowserListener { @@ -56,7 +58,8 @@ struct FileBrowserIdleHelper { */ class FileBrowser : public ThumbBrowserBase, public LWButtonListener, - public ExportPanelListener{ + public ExportPanelListener, + public ProfileStoreListener { typedef sigc::signal type_trash_changed; @@ -147,8 +150,8 @@ class FileBrowser : public ThumbBrowserBase, void setFileBrowserListener (FileBrowserListener* l) { tbl = l; } void menuItemActivated (Gtk::MenuItem* m); - void applyMenuItemActivated (Glib::ustring ppname); - void applyPartialMenuItemActivated (Glib::ustring ppname); + void applyMenuItemActivated (ProfileStoreLabel *label); + void applyPartialMenuItemActivated (ProfileStoreLabel *label); void applyFilter (const BrowserFilter& filter); int getNumFiltered(){ return numFiltered;} @@ -182,6 +185,8 @@ class FileBrowser : public ThumbBrowserBase, // exportpanel interface void exportRequested(); + void updateProfileList (); + type_trash_changed trash_changed(); }; diff --git a/rtgui/main.cc b/rtgui/main.cc index 3aa5e2a89..24b453c7f 100644 --- a/rtgui/main.cc +++ b/rtgui/main.cc @@ -91,6 +91,9 @@ int main(int argc, char **argv) { setlocale(LC_ALL,""); + // Uncomment the following line if you want to use the "--g-fatal-warnings" command line flag + //gtk_init (&argc, &argv); + Glib::thread_init(); gdk_threads_set_lock_functions(G_CALLBACK(myGdkLockEnter), (G_CALLBACK(myGdkLockLeave))); gdk_threads_init(); diff --git a/rtgui/options.cc b/rtgui/options.cc index a78ef628d..23e8ffc1c 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -95,12 +95,10 @@ void Options::updatePaths() { if (checkDirPath (profilePath, "Error: the specified user's profiles' path doesn't point to a directory or doesn't exist!\n")) { if (multiUser) { userProfilePath = profilePath; - if (useBundledProfiles) { - tmpPath = Glib::build_filename(argv0, "profiles"); - if(checkDirPath (tmpPath, "Error: the global's profiles' path doesn't point to a directory or doesn't exist!\n")) { - if (userProfilePath != tmpPath) - globalProfilePath = tmpPath; - } + tmpPath = Glib::build_filename(argv0, "profiles"); + if(checkDirPath (tmpPath, "Error: the global's profiles' path doesn't point to a directory or doesn't exist!\n")) { + if (userProfilePath != tmpPath) + globalProfilePath = tmpPath; } } else { @@ -121,16 +119,14 @@ void Options::updatePaths() { if (!checkDirPath (tmpPath, "")) { int retVal = safe_g_mkdir_with_parents (tmpPath, 511); if (!retVal) - printf("Error: user's profiles' directory \"%s\" creation failed\n", tmpPath.c_str()); + printf("Error: user's profiles' directory \"%s\" creation failed\n", tmpPath.c_str()); } if(checkDirPath (tmpPath, "Error: the specified user's profiles' path doesn't point to a directory!\n")) { userProfilePath = tmpPath; } - if (useBundledProfiles) { - tmpPath = Glib::build_filename(argv0, "profiles"); - if(checkDirPath (tmpPath, "Error: the specified user's profiles' path doesn't point to a directory or doesn't exist!\n")) { - globalProfilePath = tmpPath; - } + tmpPath = Glib::build_filename(argv0, "profiles"); + if(checkDirPath (tmpPath, "Error: the specified user's profiles' path doesn't point to a directory or doesn't exist!\n")) { + globalProfilePath = tmpPath; } } else { @@ -172,21 +168,53 @@ Glib::ustring Options::getPreferredProfilePath() { return ""; } +/** @brief Get the absolute path of the given filename or the "Internal" special value + * + *@param profName path + filename of the procparam to look for. A filename without path can be provided for backward compatibility. + * In this case, this parameter will be update with the new format. + *@return Send back the absolute path of the given filename or "Internal" if "Internal" has been set to profName. Implementor will have + * to test for this particular value. If the absolute path is invalid (e.g. the file doesn't exist), it will return an empty string. + */ Glib::ustring Options::findProfilePath(Glib::ustring &profName) { if (profName.empty()) return ""; - Glib::ustring p = getUserProfilePath(); - Glib::ustring fullPath = Glib::build_filename(p, profName + paramFileExtension); - if (!p.empty() && safe_file_test (fullPath, Glib::FILE_TEST_EXISTS)) - return p; + if (profName == DEFPROFILE_INTERNAL) + return profName; - p = getGlobalProfilePath(); - fullPath = Glib::build_filename(p, profName + paramFileExtension); - if (!p.empty() && safe_file_test (fullPath, Glib::FILE_TEST_EXISTS)) - return p; - else - return ""; + Glib::ustring p = profName.substr(0, 4); + if (p=="${U}") { + // the path starts by the User virtual path + p = getUserProfilePath(); + Glib::ustring fullPath = p + profName.substr(4) + paramFileExtension; + if (!p.empty() && safe_file_test (fullPath, Glib::FILE_TEST_EXISTS)) + return Glib::path_get_dirname(fullPath); + } + else if (p=="${G}") { + // the path starts by the User virtual path + p = getGlobalProfilePath(); + Glib::ustring fullPath = p + profName.substr(4) + paramFileExtension; + if (!p.empty() && safe_file_test (fullPath, Glib::FILE_TEST_EXISTS)) + return Glib::path_get_dirname(fullPath); + } + else { + // compatibility case -> convert the path to the new format + p = getUserProfilePath(); + Glib::ustring fullPath = Glib::build_filename(p, profName + paramFileExtension); + if (!p.empty() && safe_file_test (fullPath, Glib::FILE_TEST_EXISTS)) { + // update the profile path + profName = Glib::build_filename("${U}", profName); + return Glib::path_get_dirname(fullPath); + } + + p = getGlobalProfilePath(); + fullPath = Glib::build_filename(p, profName + paramFileExtension); + if (!p.empty() && safe_file_test (fullPath, Glib::FILE_TEST_EXISTS)) { + profName = Glib::build_filename("${G}", profName); + return Glib::path_get_dirname(fullPath); + } + } + return ""; } @@ -1076,7 +1104,7 @@ void Options::load () { // If the local option file does not exist or is broken, and the local cache folder does not exist, recreate it if (r && !safe_g_mkdir_with_parents (rtdir, 511)) { // Save the option file - options.saveToFile (rtdir + "/options"); + options.saveToFile (rtdir + "/options"); } // Modify the path of the cache folder to the user's personal folder #ifdef WIN32 @@ -1091,7 +1119,7 @@ void Options::load () { // Check default Raw and Img procparams existence if (options.defProfRaw.empty()) - options.defProfRaw = DEFPROFILE_INTERNAL; + options.defProfRaw = DEFPROFILE_INTERNAL; else { Glib::ustring tmpFName = options.findProfilePath(options.defProfRaw); if (!tmpFName.empty()) { diff --git a/rtgui/options.h b/rtgui/options.h index 1ec326b50..4be423884 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -28,11 +28,11 @@ #define STARTUPDIR_LAST 3 // Default bundled profile name to use for Raw images -#define DEFPROFILE_RAW "Default" +#define DEFPROFILE_RAW "${G}\\Default" // Default bundled profile name to use for Standard images #define DEFPROFILE_IMG "Neutral" // Profile name to use for internal values' profile -#define DEFPROFILE_INTERNAL "Internal" +#define DEFPROFILE_INTERNAL "Neutral" class SaveFormat { @@ -271,6 +271,8 @@ class Options { bool is_extention_enabled(Glib::ustring ext); bool is_defProfRawMissing() { return defProfRawMissing; } bool is_defProfImgMissing() { return defProfImgMissing; } + void setDefProfRawMissing(bool value) { defProfRawMissing = value; } + void setDefProfImgMissing(bool value) { defProfImgMissing = value; } }; extern Options options; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 4a18f086d..e5424286b 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -31,7 +31,7 @@ extern Options options; extern Glib::ustring argv0; -Preferences::Preferences (RTWindow *rtwindow):parent(rtwindow) { +Preferences::Preferences (RTWindow *rtwindow) : rprofiles(NULL), iprofiles(NULL), parent(rtwindow) { splash = NULL; @@ -89,6 +89,8 @@ Preferences::Preferences (RTWindow *rtwindow):parent(rtwindow) { #endif nb->set_current_page (0); + profileStore.addListener(this); + fillPreferences (); show_all_children (); @@ -98,6 +100,7 @@ Preferences::Preferences (RTWindow *rtwindow):parent(rtwindow) { Preferences::~Preferences () { + profileStore.removeListener(this); options.preferencesWidth = get_width(); options.preferencesHeight = get_height(); } @@ -328,23 +331,26 @@ Gtk::Widget* Preferences::getProcParamsPanel () { Gtk::VBox* mvbpp = Gtk::manage (new Gtk::VBox ()); Gtk::Frame* fpp = Gtk::manage (new Gtk::Frame (M("PREFERENCES_IMPROCPARAMS"))); + Gtk::VBox* vbpp = Gtk::manage (new Gtk::VBox ()); + vbpp->set_border_width(4); Gtk::Label* drlab = Gtk::manage (new Gtk::Label (M("PREFERENCES_FORRAW")+":", Gtk::ALIGN_LEFT)); - rprofiles = Gtk::manage (new Gtk::ComboBoxText ()); + rprofiles = Gtk::manage (new ProfileStoreComboBox ()); rprofiles->set_size_request(50, -1); - rprofiles->signal_changed().connect( sigc::mem_fun(*this, &Preferences::forRAWComboChanged) ); - forRAWComboChanged(); // update the tooltip + rpconn = rprofiles->signal_changed().connect( sigc::mem_fun(*this, &Preferences::forRAWComboChanged) ); Gtk::Label* drimg = Gtk::manage (new Gtk::Label (M("PREFERENCES_FORIMAGE")+":", Gtk::ALIGN_LEFT)); - iprofiles = Gtk::manage (new Gtk::ComboBoxText ()); + iprofiles = Gtk::manage (new ProfileStoreComboBox ()); iprofiles->set_size_request(50, -1); - iprofiles->signal_changed().connect( sigc::mem_fun(*this, &Preferences::forImageComboChanged) ); - forImageComboChanged(); // update the tooltip + ipconn = iprofiles->signal_changed().connect( sigc::mem_fun(*this, &Preferences::forImageComboChanged) ); Gtk::Table* defpt = Gtk::manage (new Gtk::Table (2, 2)); defpt->attach (*drlab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); defpt->attach (*rprofiles, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); defpt->attach (*drimg, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 2); defpt->attach (*iprofiles, 1, 2, 1, 2, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); - fpp->add (*defpt); - + vbpp->pack_start (*defpt, Gtk::PACK_SHRINK, 4); + useBundledProfiles = Gtk::manage (new Gtk::CheckButton (M("PREFERENCES_USEBUNDLEDPROFILES"))); + bpconn = useBundledProfiles->signal_clicked().connect ( sigc::mem_fun(*this, &Preferences::bundledProfilesChanged) ); + vbpp->pack_start (*useBundledProfiles, Gtk::PACK_SHRINK, 4); + fpp->add (*vbpp); mvbpp->pack_start (*fpp, Gtk::PACK_SHRINK, 4); Gtk::Frame* fdp = Gtk::manage (new Gtk::Frame (M("PREFERENCES_PROFILEHANDLING"))); @@ -400,14 +406,6 @@ Gtk::Widget* Preferences::getProcParamsPanel () { //ffconn = flatFieldDir->signal_file_set().connect ( sigc::mem_fun(*this, &Preferences::flatFieldChanged), true); ffconn = flatFieldDir->signal_current_folder_changed().connect ( sigc::mem_fun(*this, &Preferences::flatFieldChanged), true); - - std::vector pnames; - parseDir (options.getUserProfilePath(), pnames, paramFileExtension); - parseDir (options.getGlobalProfilePath(), pnames, paramFileExtension); - for (size_t i=0; iappend_text (pnames[i]); - iprofiles->append_text (pnames[i]); - } Gtk::Frame* fmd = Gtk::manage (new Gtk::Frame (M("PREFERENCES_METADATA"))); Gtk::VBox* vbmd = Gtk::manage (new Gtk::VBox ()); @@ -1073,10 +1071,14 @@ void Preferences::parseDir (Glib::ustring dirname, std::vector& i void Preferences::storePreferences () { - moptions.defProfRaw = rprofiles->get_active_text(); - if (moptions.defProfRaw.empty()) moptions.defProfRaw = DEFPROFILE_RAW; - moptions.defProfImg = iprofiles->get_active_text(); - if (moptions.defProfImg.empty()) moptions.defProfImg = DEFPROFILE_IMG; + // With the new mechanism, we can't be sure of the availability of the DEFPROFILE_RAW & DEFPROFILE_IMG profiles, + // because useBundledProfiles may be false. We're now using DEFPROFILE_INTERNAL instead, which is always available. + moptions.defProfRaw = rprofiles->getFullPathFromActiveRow(); + if (moptions.defProfRaw.empty()) moptions.defProfRaw = DEFPROFILE_INTERNAL; + moptions.defProfImg = iprofiles->getFullPathFromActiveRow(); + if (moptions.defProfImg.empty()) moptions.defProfImg = DEFPROFILE_INTERNAL; + + moptions.dateFormat = dateformat->get_text(); moptions.panAccelFactor = (int)panFactor->get_value(); moptions.fbShowDateTime = showDateTime->get_active (); @@ -1160,6 +1162,7 @@ void Preferences::storePreferences () { moptions.saveParamsFile = saveParamsFile->get_active (); moptions.saveParamsCache = saveParamsCache->get_active (); moptions.paramsLoadLocation = (PPLoadLocation)loadParamsPreference->get_active_row_number (); + moptions.useBundledProfiles = useBundledProfiles->get_active (); moptions.tunnelMetaData = ckbTunnelMetaData->get_active (); @@ -1201,9 +1204,14 @@ void Preferences::fillPreferences () { sconn.block (true); dfconn.block (true); ffconn.block (true); + rpconn.block(true); + ipconn.block(true); + bpconn.block(true); - rprofiles->set_active_text (moptions.defProfRaw); - iprofiles->set_active_text (moptions.defProfImg); + rprofiles->setActiveRowFromFullPath (moptions.defProfRaw); + forRAWComboChanged(); // update the tooltip + iprofiles->setActiveRowFromFullPath (moptions.defProfImg); + forImageComboChanged(); // update the tooltip dateformat->set_text (moptions.dateFormat); panFactor->set_value(moptions.panAccelFactor); if (safe_file_test (moptions.rtSettings.monitorProfile, Glib::FILE_TEST_EXISTS)) @@ -1286,7 +1294,8 @@ void Preferences::fillPreferences () { saveParamsFile->set_active (moptions.saveParamsFile); saveParamsCache->set_active (moptions.saveParamsCache); - loadParamsPreference->set_active (moptions.paramsLoadLocation); + loadParamsPreference->set_active (moptions.paramsLoadLocation); + useBundledProfiles->set_active (moptions.useBundledProfiles); ckbTunnelMetaData->set_active (moptions.tunnelMetaData); @@ -1332,6 +1341,9 @@ void Preferences::fillPreferences () { sconn.block (false); dfconn.block (false); ffconn.block (false); + rpconn.block(true); + ipconn.block(true); + bpconn.block(false); chOverwriteOutputFile->set_active (moptions.overwriteOutputFile); @@ -1399,7 +1411,17 @@ void Preferences::cancelPressed () { // set the initial font back if (fontbutton->get_font_name() != options.font) switchFontTo(options.font); - hide (); + + // update the profileStore + if (useBundledProfiles->get_active () != options.useBundledProfiles) { + // we have to rescan with the old value; + bpconn.block(true); + useBundledProfiles->set_active (false); + bundledProfilesChanged(); + bpconn.block(false); + } + + hide (); } void Preferences::selectStartupDir () { @@ -1435,17 +1457,94 @@ void Preferences::themeChanged () { } void Preferences::forRAWComboChanged () { - rprofiles->set_tooltip_text(rprofiles->get_active_text()); + if (!rprofiles) + return; + const ProfileStoreEntry *selectedEntry = rprofiles->getSelectedEntry(); + if (!selectedEntry) + return; + + if (selectedEntry->type == PSET_FOLDER) { + rpconn.block(true); + rprofiles->set_active(currRawRow); + rpconn.block(false); + } + else + currRawRow = rprofiles->get_active(); + + rprofiles->set_tooltip_text(selectedEntry->label); } void Preferences::forImageComboChanged () { - iprofiles->set_tooltip_text(iprofiles->get_active_text()); + if (!iprofiles) + return; + const ProfileStoreEntry *selectedEntry = iprofiles->getSelectedEntry(); + if (!selectedEntry) + return; + + if (selectedEntry->type == PSET_FOLDER) { + ipconn.block(true); + iprofiles->set_active(currImgRow); + ipconn.block(false); + } + else + currImgRow = rprofiles->get_active(); + iprofiles->set_tooltip_text(iprofiles->getSelectedEntry()->label); } void Preferences::layoutComboChanged () { editorLayout->set_tooltip_text(editorLayout->get_active_text()); } +void Preferences::bundledProfilesChanged () { + rpconn.block (true); + ipconn.block (true); + + // parseProfiles does use options.useBundledProfiles, so we temporarily change its value + bool currValue = options.useBundledProfiles; + options.useBundledProfiles = useBundledProfiles->get_active (); + + // rescan the file's tree + profileStore.parseProfiles(); // This will call Preferences::updateProfileList in return + + // restoring back the old value + options.useBundledProfiles = currValue; + + ipconn.block (false); + rpconn.block (false); +} + +void Preferences::storeCurrentValue() { + // TODO: Find a way to get and restore the current selection; the following line can't work anymore + storedValueRaw = rprofiles->getFullPathFromActiveRow(); + storedValueImg = iprofiles->getFullPathFromActiveRow(); +} + +void Preferences::updateProfileList() { + rprofiles->updateProfileList(); + iprofiles->updateProfileList(); +} + +void Preferences::restoreValue() { + if (!rprofiles->setActiveRowFromFullPath(storedValueRaw)) { + moptions.defProfRaw = DEFPROFILE_INTERNAL; + rpconn.block(true); + rprofiles->setInternalEntry(); + rpconn.block(false); + } + currRawRow = rprofiles->get_active(); + + if (!iprofiles->setActiveRowFromFullPath(storedValueImg)) { + moptions.defProfImg = DEFPROFILE_INTERNAL; + ipconn.block(true); + iprofiles->setInternalEntry(); + ipconn.block(false); + } + currImgRow = iprofiles->get_active(); + + storedValueRaw = ""; + storedValueImg = ""; +} + void Preferences::fontChanged () { switchFontTo(fontbutton->get_font_name()); diff --git a/rtgui/preferences.h b/rtgui/preferences.h index b363d2056..0b77b03b0 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -25,7 +25,7 @@ #include #include "rtwindow.h" -class Preferences : public Gtk::Dialog { +class Preferences : public Gtk::Dialog, public ProfileStoreListener { class ExtensionColumns : public Gtk::TreeModel::ColumnRecord { public: @@ -52,8 +52,10 @@ class Preferences : public Gtk::Dialog { protected: Splash* splash; - Gtk::ComboBoxText* rprofiles; - Gtk::ComboBoxText* iprofiles; + ProfileStoreComboBox* rprofiles; + Gtk::TreeIter currRawRow; // :) + ProfileStoreComboBox* iprofiles; + Gtk::TreeIter currImgRow; Gtk::ComboBoxText* languages; Gtk::CheckButton* ckbLangAutoDetect; Gtk::Entry* dateformat; @@ -125,6 +127,7 @@ class Preferences : public Gtk::Dialog { Gtk::CheckButton* saveParamsFile; Gtk::CheckButton* saveParamsCache; + Gtk::CheckButton* useBundledProfiles; Gtk::ComboBoxText* loadParamsPreference; Gtk::ComboBoxText* editorLayout; RTWindow* parent; @@ -146,9 +149,11 @@ class Preferences : public Gtk::Dialog { Gtk::CheckButton* ckbSquareDetailWindow; Gtk::CheckButton* ckbUseIconNoText; + Glib::ustring storedValueRaw; + Glib::ustring storedValueImg; Options moptions; - sigc::connection tconn, sconn, fconn, usethcon, addc, setc, dfconn, ffconn; + sigc::connection tconn, sconn, fconn, usethcon, addc, setc, dfconn, ffconn, bpconn, rpconn, ipconn; sigc::connection autoMonProfileConn, sndEnableConn, langAutoDetectConn, autocielabConn; Glib::ustring initialTheme; Glib::ustring initialFont; @@ -165,6 +170,7 @@ class Preferences : public Gtk::Dialog { void forRAWComboChanged (); void forImageComboChanged (); void layoutComboChanged (); + void bundledProfilesChanged(); void switchThemeTo (Glib::ustring newTheme, bool slimInterface); void switchFontTo (Glib::ustring newFont); bool splashClosed(GdkEventAny* event); @@ -207,6 +213,10 @@ class Preferences : public Gtk::Dialog { void behAddAllPressed (); void behSetAllPressed (); + virtual void storeCurrentValue(); + virtual void updateProfileList(); + virtual void restoreValue(); + // void selectICCProfileDir (); // void selectMonitorProfile (); }; diff --git a/rtgui/profilepanel.cc b/rtgui/profilepanel.cc index c47606abb..d67730305 100644 --- a/rtgui/profilepanel.cc +++ b/rtgui/profilepanel.cc @@ -38,7 +38,7 @@ void ProfilePanel::cleanup () { delete partialProfileDlg; } -ProfilePanel::ProfilePanel (bool readOnly) : lastFilename(""), imagePath("") { +ProfilePanel::ProfilePanel (bool readOnly) : storedPProfile(NULL), lastFilename(""), imagePath("") { tpc = NULL; @@ -50,7 +50,9 @@ ProfilePanel::ProfilePanel (bool readOnly) : lastFilename(""), imagePath("") { fillMode->signal_toggled().connect ( sigc::mem_fun(*this, &ProfilePanel::profileFillModeToggled) ); fillMode->set_tooltip_text(M("PROFILEPANEL_MODE_TIP")); - profiles = Gtk::manage (new MyComboBoxText ()); + // Create the Combobox + profiles = Gtk::manage (new ProfileStoreComboBox ()); + Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox ()); hbox->show (); // pack_start (*profiles, Gtk::PACK_SHRINK, 4); @@ -82,10 +84,8 @@ ProfilePanel::ProfilePanel (bool readOnly) : lastFilename(""), imagePath("") { lastsaved = NULL; dontupdate = false; - refreshProfileList (); + profileStore.addListener(this); - profiles->set_active (0); - old = profiles->get_active_text(); changeconn = profiles->signal_changed().connect( sigc::mem_fun(*this, &ProfilePanel::selection_changed) ); load->set_tooltip_markup (M("PROFILEPANEL_TOOLTIPLOAD")); @@ -98,36 +98,115 @@ ProfilePanel::ProfilePanel (bool readOnly) : lastFilename(""), imagePath("") { ProfilePanel::~ProfilePanel () { + profileStore.removeListener(this); if (custom) { custom->deleteInstance(); delete custom; } if (lastsaved) { lastsaved->deleteInstance(); delete lastsaved; } delete profileFillModeOnImage; delete profileFillModeOffImage; } -void ProfilePanel::refreshProfileList () { +bool ProfilePanel::isCustomSelected() { + if (profiles->getCurrentLabel() == Glib::ustring ("(" + M("PROFILEPANEL_PCUSTOM") + ")")) + return true; + return false; +} + +bool ProfilePanel::isLastSavedSelected() { + if (profiles->getCurrentLabel() == Glib::ustring ("(" + M("PROFILEPANEL_PLASTSAVED") + ")")) + return true; + return false; +} + +Gtk::TreeIter ProfilePanel::getCustomRow() { + Gtk::TreeIter row; + if (custom) + row = profiles->getRowFromLabel(Glib::ustring ("(" + M("PROFILEPANEL_PCUSTOM") + ")")); + return row; +} + +Gtk::TreeIter ProfilePanel::getLastSavedRow() { + Gtk::TreeIter row; + if (lastsaved) { + row = profiles->getRowFromLabel(Glib::ustring ("(" + M("PROFILEPANEL_PLASTSAVED") + ")")); + } + return row; +} + +Gtk::TreeIter ProfilePanel::addCustomRow() { + const ProfileStoreEntry *customPSE = new ProfileStoreEntry(Glib::ustring ("(" + M("PROFILEPANEL_PCUSTOM") + ")"), PSET_FILE, 0, 0); + Gtk::TreeIter newEntry = profiles->addRow(customPSE); + return newEntry; +} + +Gtk::TreeIter ProfilePanel::addLastSavedRow() { + const ProfileStoreEntry *lastSavedPSE = new ProfileStoreEntry(Glib::ustring ("(" + M("PROFILEPANEL_PLASTSAVED") + ")"), PSET_FILE, 0, 0); + Gtk::TreeIter newEntry = profiles->addRow(lastSavedPSE); + return newEntry; +} + +void ProfilePanel::storeCurrentValue () { + // TODO: Find a way to get and restore the current selection; the following line can't work anymore + storedValue = profiles->getFullPathFromActiveRow(); + if (!isCustomSelected() && !isLastSavedSelected()) { + // storing the current entry's procparams, if not "Custom" or "LastSaved" + + // for now, the storedPProfile has default internal values + const ProfileStoreEntry *entry = profiles->getSelectedEntry(); + const PartialProfile *currProfile; + if (entry && (currProfile = profileStore.getProfile(entry))!=NULL) { + // now storedPProfile has the current entry's values + storedPProfile = new PartialProfile(currProfile->pparams, currProfile->pedited, true); + } + else + storedPProfile = new PartialProfile(true); + } +} + +/* Get the ProfileStore's entry list and recreate the combobox entries + * If you want want to update the ProfileStore list itself (rescan the dir tree), use its "parseProfiles" method instead + */ +void ProfilePanel::updateProfileList () { - Glib::ustring oldsel = profiles->get_active_text (); changeconn.block (true); - // clear items - profiles->clear_items (); - pparams.clear (); - - // re-parse profile directories (deletes old ones) - profileStore.parseProfiles (); - pparams = profileStore.getProfileNames (); - for (unsigned int i=0; iappend_text (pparams[i]); + // rescan file tree + profiles->updateProfileList(); if (custom) - profiles->append_text (Glib::ustring("(") + M("PROFILEPANEL_PCUSTOM") + ")"); - if (lastsaved) - profiles->append_text (Glib::ustring("(") + M("PROFILEPANEL_PLASTSAVED") + ")"); + addCustomRow(); + + if (lastsaved) + addLastSavedRow(); - profiles->set_active_text (oldsel); changeconn.block (false); } +void ProfilePanel::restoreValue () { + changeconn.block (true); + + if (!profiles->setActiveRowFromFullPath(storedValue) && storedPProfile) { + if (custom) delete custom; + custom = new PartialProfile (storedPProfile->pparams, storedPProfile->pedited, true); + Gtk::TreeIter custRow = getCustomRow(); + if (custRow) + profiles->set_active(custRow); + else + profiles->set_active (addCustomRow()); + } + + currRow = profiles->get_active(); + + changeconn.block (false); + + storedValue = ""; + + if (storedPProfile) { + storedPProfile->deleteInstance(); + delete storedPProfile; + storedPProfile = NULL; + } +} + void ProfilePanel::save_clicked (GdkEventButton* event) { if (event->button != 1) @@ -190,12 +269,14 @@ void ProfilePanel::save_clicked (GdkEventButton* event) { lastFilename = Glib::path_get_basename (fname); const PartialProfile* toSave; - if (profiles->get_active_text() == Glib::ustring("(") + M("PROFILEPANEL_PCUSTOM") + ")") + if (isCustomSelected()) toSave = custom; - else if (profiles->get_active_text() == Glib::ustring("(") + M("PROFILEPANEL_PLASTSAVED") + ")") + else if (isLastSavedSelected()) toSave = lastsaved; - else - toSave = profileStore.getProfile (profiles->get_active_text()); + else { + const ProfileStoreEntry* entry = profiles->getSelectedEntry(); + toSave = entry ? profileStore.getProfile (profiles->getSelectedEntry()) : NULL; + } if (toSave) { if (event->state & Gdk::CONTROL_MASK) { @@ -215,7 +296,9 @@ void ProfilePanel::save_clicked (GdkEventButton* event) { writeFailed(dialog, fname); else { done=true; - refreshProfileList (); + changeconn.block (true); + profileStore.parseProfiles(); + changeconn.block (false); } } else { @@ -225,7 +308,9 @@ void ProfilePanel::save_clicked (GdkEventButton* event) { writeFailed(dialog, fname); else { done=true; - refreshProfileList (); + changeconn.block (true); + profileStore.parseProfiles(); + changeconn.block (false); } } } @@ -245,12 +330,14 @@ void ProfilePanel::copy_clicked (GdkEventButton* event) { return; const PartialProfile* toSave; - if (profiles->get_active_text() == Glib::ustring("(") + M("PROFILEPANEL_PCUSTOM") + ")") + if (isCustomSelected()) toSave = custom; - else if (profiles->get_active_text() == Glib::ustring("(") + M("PROFILEPANEL_PLASTSAVED") + ")") + else if (isLastSavedSelected()) toSave = lastsaved; - else - toSave = profileStore.getProfile (profiles->get_active_text()); + else { + const ProfileStoreEntry* entry = profiles->getSelectedEntry(); + toSave = entry ? profileStore.getProfile (profiles->getSelectedEntry()) : NULL; + } // toSave has to be a complete procparams if (toSave) { @@ -347,10 +434,9 @@ void ProfilePanel::load_clicked (GdkEventButton* event) { int err = custom->load (fname); if (!err) { bool prevState = changeconn.block(true); - Glib::ustring newEntry = Glib::ustring("(") + M("PROFILEPANEL_PCUSTOM") + ")"; - profiles->append_text (newEntry); - profiles->set_active_text (newEntry); - old = profiles->get_active_text(); + Gtk::TreeIter newEntry = addCustomRow(); + profiles->set_active (newEntry); + currRow = profiles->get_active(); changeconn.block(prevState); if (event->state & Gdk::CONTROL_MASK) { @@ -371,7 +457,7 @@ void ProfilePanel::load_clicked (GdkEventButton* event) { else if (customCreated) { // we delete custom custom->deleteInstance(); - delete custom; + delete custom; custom = NULL; } } return; @@ -396,19 +482,21 @@ void ProfilePanel::paste_clicked (GdkEventButton* event) { } bool prevState = changeconn.block(true); - Glib::ustring newEntry = Glib::ustring("(") + M("PROFILEPANEL_PCUSTOM") + ")"; if (!custom) { custom = new PartialProfile (true); custom->pedited->set(true); - profiles->append_text (newEntry); + profiles->set_active (addCustomRow()); + currRow = profiles->get_active(); } + else { + profiles->set_active(getCustomRow()); + currRow = profiles->get_active(); + } + ProcParams pp = clipboard.getProcParams (); *custom->pparams = pp; - profiles->set_active_text (newEntry); - old = profiles->get_active_text(); - changeconn.block(prevState); if (event->state & Gdk::CONTROL_MASK) { @@ -435,27 +523,37 @@ void ProfilePanel::changeTo (const PartialProfile* newpp, Glib::ustring profname void ProfilePanel::selection_changed () { - Glib::ustring entry; - if (profiles->get_active_text() == (entry = Glib::ustring("(") + M("PROFILEPANEL_PCUSTOM") + ")")) { + if (isCustomSelected()) { if (!dontupdate) - changeTo (custom, entry); + changeTo (custom, Glib::ustring ("(" + M("PROFILEPANEL_PCUSTOM") + ")")); } - else if (profiles->get_active_text() == (entry = Glib::ustring("(") + M("PROFILEPANEL_PLASTSAVED") + ")")) - changeTo (lastsaved, entry); + else if (isLastSavedSelected()) + changeTo (lastsaved, Glib::ustring ("(" + M("PROFILEPANEL_PLASTSAVED") + ")")); else { - const PartialProfile* s = profileStore.getProfile (profiles->get_active_text()); + const ProfileStoreEntry *pse = profiles->getSelectedEntry(); + if (pse->type == PSET_FOLDER) { + // this entry is invalid, restoring the old value + changeconn.block(true); + profiles->set_active(currRow); + changeconn.block(false); + dontupdate = false; + return; + } + else + currRow = profiles->get_active(); + + const PartialProfile* s = profileStore.getProfile (pse); if (s) { if (fillMode->get_active() && s->pedited) { ParamsEdited pe; pe.set(true); PartialProfile s2(s->pparams, &pe, false); - changeTo (&s2, profiles->get_active_text()+"+"); + changeTo (&s2, pse->label+"+"); } else - changeTo (s, profiles->get_active_text()); + changeTo (s, pse->label); } } - old = profiles->get_active_text (); dontupdate = false; } @@ -465,31 +563,36 @@ void ProfilePanel::procParamsChanged (rtengine::procparams::ProcParams* p, rteng if (ev==EvProfileChanged || ev==EvPhotoLoaded) return; - Glib::ustring entry = Glib::ustring("(") + M("PROFILEPANEL_PCUSTOM") + ")"; - if (profiles->get_active_text() != entry) { + if (!isCustomSelected()) { dontupdate = true; if (!custom) { custom = new PartialProfile (true); custom->set(true); - profiles->append_text (entry); + profiles->set_active (addCustomRow()); + currRow = profiles->get_active(); + } + else { + profiles->set_active(getCustomRow()); + currRow = profiles->get_active(); } - profiles->set_active_text (entry); - old = profiles->get_active_text(); } *custom->pparams = *p; } -void ProfilePanel::initProfile (const Glib::ustring& profname, ProcParams* lastSaved) { +/** @brief Initialize the Profile panel with a default profile, overridden by the last saved profile if provided + * + * The file tree has already been created on object's construction. We add here the Custom, LastSaved and/or Internal item. + * + * @param profileFullPath full path of the profile; must start by the virtual root (${G} or ${U}, and without suffix + * @param lastSaved pointer to the last saved ProcParam; may be NULL + */ +void ProfilePanel::initProfile (const Glib::ustring& profileFullPath, ProcParams* lastSaved) { + + const ProfileStoreEntry *pse = NULL; + const PartialProfile *defprofile = NULL; changeconn.block (true); - profiles->clear_items (); - pparams.clear (); - - pparams = profileStore.getProfileNames (); - for (unsigned int i=0; iappend_text (pparams[i]); - if (custom) { custom->deleteInstance(); delete custom; custom = NULL; @@ -502,51 +605,50 @@ void ProfilePanel::initProfile (const Glib::ustring& profname, ProcParams* lastS if (lastSaved) { ParamsEdited* pe = new ParamsEdited(); pe->set(true); + // copying the provided last saved profile to ProfilePanel::lastsaved lastsaved = new PartialProfile(lastSaved, pe); } - Glib::ustring defline = profname; - const PartialProfile* defprofile = profileStore.getProfile (profname); + // update the content of the combobox; will add 'custom' and 'lastSaved' if necessary + updateProfileList(); + + Gtk::TreeIter lasSavedEntry; + // adding the Last Saved combobox entry, if needed + if (lastsaved) { + defprofile = lastsaved; + lasSavedEntry = getLastSavedRow(); + } + + if (!(pse = profileStore.findEntryFromFullPath(profileFullPath))) { + // entry not found, pse = the Internal ProfileStoreEntry + pse = profileStore.getInternalDefaultPSE(); + } + + defprofile = profileStore.getProfile (pse); + + // selecting the "Internal" entry + profiles->setInternalEntry (); + currRow = profiles->get_active(); if (lastsaved) { - defline = Glib::ustring("(") + M("PROFILEPANEL_PLASTSAVED") + ")"; - defprofile = lastsaved; - profiles->append_text (defline); - } - - if (tpc) { - if (lastsaved) - tpc->setDefaults (lastsaved->pparams); - else - tpc->setDefaults (profileStore.getProfile (profname)->pparams); - } - if (defprofile) { - old = defline; - profiles->set_active_text (defline); - changeconn.block (false); - if (tpc) - tpc->profileChange (defprofile, EvPhotoLoaded, defline); + if (lasSavedEntry) profiles->set_active (lasSavedEntry); + currRow = profiles->get_active(); + if (tpc) { + tpc->setDefaults (lastsaved->pparams); + tpc->profileChange (lastsaved, EvPhotoLoaded, profiles->getSelectedEntry()->label); + } } else { - // select first valid profile - old = ""; - profiles->set_active (0); - const PartialProfile* s = profileStore.getProfile (profiles->get_active_text()); - if (!s) { - changeconn.block (false); - PartialProfile s2(true); - s2.pedited->set(true); - if (tpc) - tpc->profileChange (&s2, EvPhotoLoaded, DEFPROFILE_INTERNAL); - s2.deleteInstance(); + if (pse) { + profiles->setActiveRowFromEntry(pse); + currRow = profiles->get_active(); } - else { - Glib::ustring cProfile = profiles->get_active_text(); - changeconn.block (false); - if (tpc) - tpc->profileChange (s, EvPhotoLoaded, cProfile); + if (tpc) { + tpc->setDefaults (defprofile->pparams); + tpc->profileChange (defprofile, EvPhotoLoaded, profiles->getSelectedEntry()->label); } } + changeconn.block (false); } void ProfilePanel::setInitialFileName (const Glib::ustring& filename) { diff --git a/rtgui/profilepanel.h b/rtgui/profilepanel.h index 647e0cfad..b8e6b0a77 100644 --- a/rtgui/profilepanel.h +++ b/rtgui/profilepanel.h @@ -24,21 +24,31 @@ #include "../rtengine/rtengine.h" #include "pparamschangelistener.h" #include "profilechangelistener.h" +#include "profilestore.h" #include "partialpastedlg.h" #include "guiutils.h" #include "rtimage.h" -class ProfilePanel : public Gtk::VBox, public PParamsChangeListener { +class ProfilePanel : public Gtk::VBox, public PParamsChangeListener, public ProfileStoreListener { private: + rtengine::procparams::PartialProfile* storedPProfile; + Glib::ustring storedValue; Glib::ustring lastFilename; Glib::ustring imagePath; RTImage *profileFillModeOnImage; RTImage *profileFillModeOffImage; Gtk::ToggleButton* fillMode; + Gtk::TreeIter currRow; - void profileFillModeToggled(); + void profileFillModeToggled (); + bool isCustomSelected (); + bool isLastSavedSelected (); + Gtk::TreeIter getCustomRow (); + Gtk::TreeIter getLastSavedRow (); + Gtk::TreeIter addCustomRow (); + Gtk::TreeIter addLastSavedRow (); protected: @@ -47,17 +57,14 @@ class ProfilePanel : public Gtk::VBox, public PParamsChangeListener { Gtk::Button* load; Gtk::Button* copy; Gtk::Button* paste; - MyComboBoxText* profiles; - std::vector pparams; + ProfileStoreComboBox* profiles; rtengine::procparams::PartialProfile* custom; rtengine::procparams::PartialProfile* lastsaved; - Glib::ustring old; ProfileChangeListener* tpc; bool dontupdate; sigc::connection changeconn; void changeTo (const rtengine::procparams::PartialProfile* newpp, Glib::ustring profname); - void refreshProfileList (); public: @@ -68,8 +75,11 @@ class ProfilePanel : public Gtk::VBox, public PParamsChangeListener { static void init (); static void cleanup (); + void storeCurrentValue(); + void updateProfileList (); + void restoreValue(); - void initProfile (const Glib::ustring& profname, rtengine::procparams::ProcParams* lastSaved); + void initProfile (const Glib::ustring& profileFullPath, rtengine::procparams::ProcParams* lastSaved); void setInitialFileName (const Glib::ustring& filename); // PParamsChangeListener interface diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 13026b663..30763d0ae 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -19,6 +19,7 @@ #include "profilestore.h" #include "options.h" #include "toolpanel.h" +#include "guiutils.h" #include "../rtengine/safegtk.h" ProfileStore profileStore; @@ -26,9 +27,9 @@ ProfileStore profileStore; using namespace rtengine; using namespace rtengine::procparams; -ProfileStore::ProfileStore () { - storeState = STORESTATE_NOTINITIALIZED; - parseMutex = NULL; +ProfileStore::ProfileStore () : parseMutex(NULL), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(NULL), internalDefaultEntry(NULL) { + internalDefaultProfile = new AutoPartialProfile(); + internalDefaultProfile->set(true); } bool ProfileStore::init () { @@ -40,7 +41,7 @@ bool ProfileStore::init () { _parseProfiles (); storeState = STORESTATE_INITIALIZED; } - return true; + return true; } ProfileStore::~ProfileStore () { @@ -49,12 +50,10 @@ ProfileStore::~ProfileStore () { storeState = STORESTATE_DELETED; MyMutex::MyLock lock(*parseMutex); - for (std::map::iterator i = partProfiles.begin(); i!=partProfiles.end(); i++) { - if (i->second->pparams) delete i->second->pparams; - if (i->second->pedited) delete i->second->pedited; - delete i->second; - } + clearProfileList (); partProfiles.clear (); + clearFileList(); + delete internalDefaultProfile; lock.release(); delete parseMutex; parseMutex = NULL; @@ -65,111 +64,273 @@ ProfileStore::~ProfileStore () { * Since there's a race condition in the multithreaded environment on this object, * parseProfiles may need to ask for initialization of this object, and then will * ask a mutex lock on it, has it been initialized by this call or not + * + * This method will scan the directory tree again and update the profile list. When finished, + * the listeners will be called in order to update with the new list */ void ProfileStore::parseProfiles () { if (!init()) // I don't even know if this situation can occur return; + + for (std::list::iterator i=listeners.begin(); i!=listeners.end(); ++i) + (*i)->storeCurrentValue(); + + { MyMutex::MyLock lock(*parseMutex); _parseProfiles (); + } + + for (std::list::iterator i=listeners.begin(); i!=listeners.end(); ++i) { + (*i)->updateProfileList(); + (*i)->restoreValue(); + } } void ProfileStore::_parseProfiles () { + // Acquire the GUI, since the tree model can interact with combobox + GThreadLock threadLock; + // clear loaded profiles - for (std::map::iterator i = partProfiles.begin(); i!=partProfiles.end(); i++) { - delete i->second->pparams; - delete i->second->pedited; - delete i->second; - } - partProfiles.clear (); + folders.clear(); + clearFileList(); + clearProfileList (); - parseDir (options.getUserProfilePath()); - parseDir (options.getGlobalProfilePath()); + folders.push_back("<<< ROOT >>>"); // Fake path, so parentFolderId == 0 will be used to attach a ProfileStoreEntry to the root container, not sub-menu + + Glib::ustring p1 = options.getUserProfilePath(); + Glib::ustring p2 = options.getGlobalProfilePath(); + bool displayLevel0 = options.useBundledProfiles && !p1.empty() && !p2.empty() && p1 != p2; + + Glib::ustring virtualPath("${U}"); + Glib::ustring currDir("${U}"); + parseDir (p1, virtualPath, currDir, 0, 0, displayLevel0); + if (displayLevel0) { + virtualPath = "${G}"; + currDir = "${G}"; + parseDir (p2, virtualPath, currDir, 0, 0, displayLevel0); + } + // entries and partProfiles are empty, but the entry and profiles already exist (they have survived to clearFileList and clearProfileList) + if (!internalDefaultEntry) + internalDefaultEntry = new ProfileStoreEntry(Glib::ustring("(") + M("PROFILEPANEL_PINTERNAL") + Glib::ustring(")"), PSET_FILE, 0, 0); + entries.push_back(internalDefaultEntry); + partProfiles[internalDefaultEntry] = internalDefaultProfile; + + + // Check if the default profiles has been found. + if (findEntryFromFullPathU(options.defProfRaw) == NULL) { + options.setDefProfRawMissing(true); + if (options.rtSettings.verbose) + printf("WARNING: Default profile \"%s\" for raw images not found!\n", options.defProfRaw.c_str()); + } + if (findEntryFromFullPathU(options.defProfImg) == NULL) { + options.setDefProfImgMissing(true); + if (options.rtSettings.verbose) + printf("WARNING: Default profile \"%s\" for standard images not found!\n", options.defProfImg.c_str()); + } } -void ProfileStore::parseDir (const Glib::ustring& pdir) { +/// @return Returns true if some files has been found (directories are ignored) +bool ProfileStore::parseDir (Glib::ustring& realPath, Glib::ustring& virtualPath, Glib::ustring& currDir, unsigned int parentId, unsigned char level, bool displayLevel0) { - // reload the available profiles from the profile dir - if (pdir!="" && safe_file_test(pdir, Glib::FILE_TEST_EXISTS) && safe_file_test(pdir, Glib::FILE_TEST_IS_DIR)) { - // process directory - Glib::ustring dirname = pdir; - Glib::Dir* dir = NULL; - dir = new Glib::Dir (dirname); - dirname = dirname + "/"; - for (Glib::DirIterator i = dir->begin(); i!=dir->end(); ++i) { - Glib::ustring fname = dirname + *i; - Glib::ustring sname = *i; - // ignore directories - if (!safe_file_test (fname, Glib::FILE_TEST_IS_DIR)) { - size_t lastdot = sname.find_last_of ('.'); - if (lastdot!=Glib::ustring::npos && lastdot<=sname.size()-4 && !sname.casefold().compare (lastdot, 4, paramFileExtension)) { - if( options.rtSettings.verbose ) - printf ("Processing file %s...\n", fname.c_str()); - Glib::ustring name = sname.substr(0,lastdot); - std::map::iterator j = partProfiles.find(name); - if (j!=partProfiles.end()) { - j->second->deleteInstance(); - delete j->second; - partProfiles.erase (j); - } - PartialProfile* pProf = new PartialProfile (true); - int res = pProf->load (fname); - if (!res && pProf->pparams->ppVersion>=220) { - partProfiles[name] = pProf; - } - else { - pProf->deleteInstance(); - delete pProf; - } + bool fileFound = false; + unsigned int folder = 0; // folder's own Id + + // reload the available profiles from the profile dir + if (!realPath.empty() && safe_file_test(realPath, Glib::FILE_TEST_EXISTS) && safe_file_test (realPath, Glib::FILE_TEST_IS_DIR)) { + + // add this entry to the folder list + folders.push_back(virtualPath); + folder = (unsigned int)(folders.size())-1; + + if (level>0 || displayLevel0) { + // replace the virtual folder name by a localized text + if (currDir == "${U}") + currDir = M("PROFILEPANEL_MYPROFILES"); + else if (currDir == "${G}") + currDir = M("PROFILEPANEL_GLOBALPROFILES"); + + // add this localized text to the file list + entries.push_back( new ProfileStoreEntry(currDir, PSET_FOLDER, parentId, folder) ); } - } + + // walking through the directory + Glib::Dir* dir = NULL; + dir = new Glib::Dir (realPath); + for (Glib::DirIterator i = dir->begin(); i!=dir->end(); ++i) { + currDir = *i; + if (currDir == "." || currDir == "..") + continue; + + Glib::ustring fname = Glib::build_filename(realPath, currDir); + if (safe_file_test (fname, Glib::FILE_TEST_IS_DIR)) { + Glib::ustring vp(Glib::build_filename(virtualPath, currDir)); + Glib::ustring rp(Glib::build_filename(realPath, currDir)); + parseDir (rp, vp, currDir, folder, level+1, 0); + } + else { + size_t lastdot = currDir.find_last_of ('.'); + if (lastdot!=Glib::ustring::npos && lastdot<=currDir.size()-4 && !currDir.casefold().compare (lastdot, 4, paramFileExtension)) { + // file found + if( options.rtSettings.verbose ) + printf ("Processing file %s...", fname.c_str()); + + Glib::ustring name = currDir.substr(0,lastdot); + + // create the partial profile + AutoPartialProfile *pProf = new AutoPartialProfile(); + int res = pProf->load (fname); + if (!res && pProf->pparams->ppVersion>=220) { + fileFound = true; + + if( options.rtSettings.verbose ) + printf ("OK\n"); + + // adding this file to the list + ProfileStoreEntry* filePSE = new ProfileStoreEntry(name, PSET_FILE, folder, 0); + entries.push_back(filePSE); + + // map the partial profile + partProfiles[filePSE] = pProf; + //partProfiles.insert( std::pair (folderPSE, pProf) ); + } + else if( options.rtSettings.verbose ) { + printf ("failed!\n"); + } + } + } + } + delete dir; } - delete dir; - } - // Check if the default profiles has been found. If no, create default instance - // This operation is safe: if the profile is finally found in another directory, the profile will be updated - if (partProfiles.find(options.defProfRaw) == partProfiles.end()) { - PartialProfile* pProf = new PartialProfile (true); - pProf->set(true); - partProfiles[options.defProfRaw] = pProf; - } - if (partProfiles.find(options.defProfImg) == partProfiles.end()) { - PartialProfile* pProf = new PartialProfile (true); - pProf->set(true); - partProfiles[options.defProfImg] = pProf; - } + + if (!fileFound && (level>0 || displayLevel0)) { + // no files found in this level, we delete the subdirectory entry + folders.pop_back(); + entries.pop_back(); + } + + return fileFound; } -const PartialProfile* ProfileStore::getProfile (const Glib::ustring& profname) { +int ProfileStore::findFolderId(const Glib::ustring &path) { + for (std::vector::iterator i=folders.begin(); i!=folders.end(); i++) { + if (*i == path) { + return i - folders.begin(); + } + } + return -1; +} - if (!init()) - // I don't even know if this situation can occur +/** @brief Return the ProfileStoreEntry object that match the given file and path + * + * @param fullPath Path of the file; the filename may end by the standard extension, + * but have to begin with a virtual location ( ${G} or ${U} ) + * Will return null on invalid path or if the entry can't be found + */ +const ProfileStoreEntry* ProfileStore::findEntryFromFullPathU(Glib::ustring path) { + + if (path.empty()) return NULL; - MyMutex::MyLock lock(*parseMutex); - if (partProfiles.find(profname) != partProfiles.end()) { - return partProfiles[profname]; + if (path == DEFPROFILE_INTERNAL) + return internalDefaultEntry; + + size_t lastdot = path.find_last_of ('.'); + if (lastdot!=Glib::ustring::npos && lastdot<=path.size()-4 && !path.casefold().compare (lastdot, 4, paramFileExtension)) + // removing the extension + path = path.substr(0,lastdot); + + // removing the filename + Glib::ustring fName = Glib::path_get_basename(path); + if (!fName.empty()) { + path = path.substr(0, path.length()-fName.length()); } else { + // path is malformed, returning NULL; + return NULL; + } + + path = Glib::path_get_dirname(path); + + // 1. find the path in the folder list + int parentFolderId = findFolderId(path); + + if (parentFolderId == -1) { + return NULL; + } + + // 2. find the entry that match the given filename and parentFolderId + for (std::vector::iterator i=entries.begin(); i!=entries.end(); i++) { + if (((*i)->parentFolderId)==parentFolderId && (*i)->label==fName) + return *i; + } + return NULL; +} + +/** Protected version of findEntryFromFullPathU */ +const ProfileStoreEntry* ProfileStore::findEntryFromFullPath(Glib::ustring path) { + MyMutex::MyLock lock(*parseMutex); + return findEntryFromFullPathU(path); +} + +const PartialProfile* ProfileStore::getProfile (Glib::ustring path) { + + if (!init()) + // I don't even know if this situation can occur + return NULL; + + const ProfileStoreEntry *pse = findEntryFromFullPath(path); + if (!pse) + return NULL; + + return getProfile(pse); +} + +const PartialProfile* ProfileStore::getProfile (const ProfileStoreEntry* entry) { + + if (!init()) + // I don't even know if this situation can occur + return NULL; + + MyMutex::MyLock lock(*parseMutex); + + if (entry == internalDefaultEntry) + return internalDefaultProfile; + + std::map::iterator iter = partProfiles.find(entry); + if (iter != partProfiles.end()) { + return iter->second; + } + else { + // This shouldn't happen! + #ifndef NDEBUG + printf("WARNING! Profile not found!\n"); + #endif return NULL; } } -std::vector ProfileStore::getProfileNames () { - - std::vector ret; - - if (!init()) +/** @brief Get a pointer to the profile's vector list + * + * This method grants you unique access to the vector list through Mutex locking. + * When you're done with the file list, you MUST call the releaseFileList method to release the lock. + */ +const std::vector* ProfileStore::getFileList () { + /*if (!init()) { // I don't even know if this situation can occur - return ret; - MyMutex::MyLock lock(*parseMutex); + return NULL; + }*/ - for (std::map::iterator i = partProfiles.begin(); i!=partProfiles.end(); i++) - ret.push_back (i->first); - return ret; + parseMutex->lock(); + + return &entries; +} + +void ProfileStore::releaseFileList() { + parseMutex->unlock(); } /* @@ -185,7 +346,9 @@ const ProcParams* ProfileStore::getDefaultProcParams (bool isRaw) { //Note: the mutex is locked in getProfile, called below const PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg); - // NOTE: pProf should not be NULL anymore, since init() should have created the default profiles already + + if (!pProf) pProf = internalDefaultProfile; + return pProf->pparams; } @@ -202,7 +365,309 @@ const PartialProfile* ProfileStore::getDefaultPartialProfile (bool isRaw) { //Note: the mutex is locked in getProfile, called below const PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg); - // NOTE: pProf should not be NULL anymore, since init() should have created the default profiles already + + if (!pProf) pProf = internalDefaultProfile; + return pProf; } +const Glib::ustring ProfileStore::getPathFromId(int folderId) { + return folders.at(folderId); +} + + +void ProfileStore::clearFileList() { + for (std::vector::iterator i=entries.begin(); i!=entries.end(); ++i) + if (*i != internalDefaultEntry) delete *i; + entries.clear(); +} + +void ProfileStore::clearProfileList() { + for (std::map::iterator i=partProfiles.begin(); i!=partProfiles.end(); ++i) + if (i->second != internalDefaultProfile) delete i->second; + partProfiles.clear(); +} + +void ProfileStore::addListener(ProfileStoreListener *listener) { + listeners.push_back(listener); +} + +void ProfileStore::removeListener(ProfileStoreListener *listener) { + listeners.remove(listener); +} + +ProfileStoreEntry::ProfileStoreEntry() : label(""), type(PSET_FOLDER), parentFolderId(0), folderId(0) {} + +ProfileStoreEntry::ProfileStoreEntry(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder) : label(label), type(type), parentFolderId(parentFolder), folderId(folder) {} + +void ProfileStoreEntry::setValues(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder) { + this->label = label; + this->type = type; + parentFolderId = parentFolder; + folderId = folder; +} + +ProfileStoreLabel::ProfileStoreLabel(const ProfileStoreEntry *entry) : Gtk::Label(entry->label), entry(entry) { + set_alignment(0, 0.5); + show(); +} + +ProfileStoreComboBox::ProfileStoreComboBox () { + updateProfileList(); +} + +Glib::ustring ProfileStoreComboBox::getCurrentLabel() { + Glib::ustring currLabel; + Gtk::TreeModel::iterator currRow = get_active(); + + if (currRow) { + const ProfileStoreEntry *currEntry = (*currRow)[methodColumns.profileStoreEntry]; + return currEntry->label; + } + return currLabel; +} + +const ProfileStoreEntry* ProfileStoreComboBox::getSelectedEntry() { + Gtk::TreeModel::iterator currRow_ = get_active(); + Gtk::TreeModel::Row currRow = *currRow_; + if (currRow) + return currRow[methodColumns.profileStoreEntry]; + else + return NULL; +} + +/** @brief Recursive method to update the combobox entries */ +void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow, int parentFolderId, bool initial, const std::vector *entryList) { + for (std::vector::const_iterator i=entryList->begin(); i!=entryList->end(); i++) { + if ((*i)->parentFolderId == parentFolderId) { // filtering the entry of the same folder + if ((*i)->type == PSET_FOLDER) { + Glib::ustring folderPath( profileStore.getPathFromId((*i)->folderId) ); + if (options.useBundledProfiles || ((folderPath != "${G}" ) && (folderPath != "${U}" ))) { + // creating the new submenu + Gtk::TreeModel::Row newSubMenu; + if (initial) { + newSubMenu = *(refTreeModel->append()); + } + else { + newSubMenu = *(refTreeModel->append(parentRow->children())); + } + + // creating and assigning the custom Label object + newSubMenu[methodColumns.label] = (*i)->label; + newSubMenu[methodColumns.profileStoreEntry] = *i; + + refreshProfileList_ (&newSubMenu, (*i)->folderId, false, entryList); + } + else + refreshProfileList_ (parentRow, (*i)->folderId, true, entryList); + } + else { + Gtk::TreeModel::Row newItem; + // creating a menu entry + if (initial) + newItem = *(refTreeModel->append()); + else + newItem = *(refTreeModel->append(parentRow->children())); + newItem[methodColumns.label] = (*i)->label; + newItem[methodColumns.profileStoreEntry] = *i; + } + } + } +} + +/** @brief Get the ProfileStore's entry list and recreate the combobox entries. + * If you want to update the ProfileStore list itself (rescan the dir tree), use the "ProfileStore::parseProfiles" method instead + * + * This method has to be called by the ProfileStoreListener having a ProfileStoreComboBox. + */ +void ProfileStoreComboBox::updateProfileList () { + + // clear items + clear(); + refTreeModel.clear(); + // Create the Tree model + refTreeModel = Gtk::TreeStore::create(methodColumns); + // Assign the model to the Combobox + set_model(refTreeModel); + + + // this will lock the profilestore's entry list too + const std::vector *entryList = profileStore.getFileList(); + + Gtk::TreeModel::Row root; + refreshProfileList_ (&root, entryList->at(0)->parentFolderId, true, entryList); + + if (entryList->at(0)->parentFolderId != 0) { + // special case for the Internal default entry + addRow(profileStore.getInternalDefaultPSE()); + } + + // releasing the profilestore's entry list mutex + profileStore.releaseFileList(); + + pack_start(methodColumns.label, false); +} + +Gtk::TreeIter ProfileStoreComboBox::findRowFromEntry_ (Gtk::TreeModel::Children childs, const ProfileStoreEntry *pse) { + Gtk::TreeModel::Row row; + Gtk::TreeIter rowInSubLevel; + for(Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { + row = *iter; + // Hombre: is there a smarter way of knowing if this row has childs? + const ProfileStoreEntry *pse_ = row[methodColumns.profileStoreEntry]; + if (pse_->type == PSET_FOLDER) { + rowInSubLevel = findRowFromEntry_ (iter->children(), pse); + if (rowInSubLevel) { + // entry found + return rowInSubLevel; + } + } + else if (pse_ == pse) { + // entry found + return iter; + } + } + return childs.end(); +} + +Gtk::TreeIter ProfileStoreComboBox::findRowFromEntry (const ProfileStoreEntry *pse) { + Gtk::TreeModel::Children childs = refTreeModel->children(); + if (pse) { + Gtk::TreeIter row = findRowFromEntry_ (childs, pse); + return row; + } + return childs.end(); +} + +Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath_ (Gtk::TreeModel::Children childs, int parentFolderId, Glib::ustring &name) { + Gtk::TreeModel::Row row; + Gtk::TreeIter rowInSubLevel; + for(Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { + row = *iter; + // Hombre: is there a smarter way of knowing if this row has childs? + const ProfileStoreEntry *pse = row[methodColumns.profileStoreEntry]; + if (pse->type == PSET_FOLDER) { + rowInSubLevel = findRowFromFullPath_ (iter->children(), parentFolderId, name); + if (rowInSubLevel) { + // entry found + return rowInSubLevel; + } + } + else if (parentFolderId==pse->parentFolderId && name==pse->label) { + // entry found + return iter; + } + } + return childs.end(); +} + +Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath (Glib::ustring path) { + Gtk::TreeIter row; + + if (path.empty()) + return row; + + if (path == DEFPROFILE_INTERNAL) { + row = findRowFromEntry(profileStore.getInternalDefaultPSE()); + return row; + } + + // removing the filename + Glib::ustring fName = Glib::path_get_basename(path); + if (!fName.empty()) { + path = path.substr(0, path.length()-fName.length()); + } + else { + // path is malformed; + return row; + } + + path = Glib::path_get_dirname(path); + int parentFolderId = profileStore.findFolderId(path); + + // 1. find the path in the folder list + if (parentFolderId != -1) + row = findRowFromFullPath_ (refTreeModel->children(), parentFolderId, fName); + + return row; +} + +/** @brief Get the absolute full path of the active row entry. + * @return The absolute full path of the active row entry, or the "Internal" keyword, + * or an empty string if the ComboBox is in an invalid state + */ +Glib::ustring ProfileStoreComboBox::getFullPathFromActiveRow() { + Glib::ustring path; + Gtk::TreeModel::iterator currRowI = get_active(); + if (!currRowI) + return path; + + Gtk::TreeModel::Row currRow = *currRowI; + + if (currRow) { + + const ProfileStoreEntry *currEntry = currRow[methodColumns.profileStoreEntry]; + if (!currEntry) + return path; + + if (currEntry == profileStore.getInternalDefaultPSE()) + return Glib::ustring(DEFPROFILE_INTERNAL); + + path = Glib::build_filename(profileStore.getPathFromId(currEntry->parentFolderId), currEntry->label); + } + return path; +} + +bool ProfileStoreComboBox::setActiveRowFromFullPath(Glib::ustring path) { + if (!path.empty()) { + Gtk::TreeIter row = findRowFromFullPath(path); + if (row) { + set_active(row); + return true; + } + } + return false; +} + +bool ProfileStoreComboBox::setActiveRowFromEntry(const ProfileStoreEntry *pse) { + if (pse) { + Gtk::TreeIter row = findRowFromEntry(pse); + if (row) { + set_active(row); + return true; + } + } + return false; +} + +bool ProfileStoreComboBox::setInternalEntry () { + return setActiveRowFromEntry(profileStore.getInternalDefaultPSE()); +} + +/** @brief Get the row from the first level of the tree that match the provided name */ +Gtk::TreeIter ProfileStoreComboBox::getRowFromLabel(Glib::ustring name) { + Gtk::TreeIter row; + Gtk::TreeModel::Children childs = refTreeModel->children(); + if (!name.empty()) { + Gtk::TreeModel::Row currRow; + for(Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { + currRow = *iter; + const ProfileStoreEntry *pse = currRow[methodColumns.profileStoreEntry]; + if (pse->label == name) { + return currRow; + } + } + } + return childs.end(); + //return refTreeModel->get_iter(""); // is this fast? We want to send back a null, anvalid or end() iterator object here +} + +/** @brief Add a new row to the first level of the tree */ +Gtk::TreeIter ProfileStoreComboBox::addRow(const ProfileStoreEntry *profileStoreEntry) { + Gtk::TreeIter newEntry = refTreeModel->append(); + Gtk::TreeModel::Row row = *newEntry; + row[methodColumns.label] = profileStoreEntry->label; + row[methodColumns.profileStoreEntry] = profileStoreEntry; + return newEntry; +} + diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h index c90037fb1..db75ede05 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -24,8 +24,97 @@ #include "../rtengine/rtengine.h" #include "threadutils.h" #include "paramsedited.h" +#include "guiutils.h" #include + +/** @brief This will implement callback functions for the ProfileStore + * + */ +class ProfileStoreListener { + + public: + virtual ~ProfileStoreListener() {} + + /** @brief Called whenever the current value has to be stored before update. */ + virtual void storeCurrentValue() {} + /** @brief Called whenever the file list has been updated and the content of the listener has to be updated. */ + virtual void updateProfileList() =0; + /** @brief Called whenever the profile list has changed and the old value have to be restored (if possible). */ + virtual void restoreValue() {} +}; + +/// @brief ProfileStoreEntry type (folder or file) +typedef enum PSE_TYPE { + PSET_FOLDER, + PSET_FILE +} PSEType; + + +/** + * @brief Entry of the profiles store, consisting of a type (folder/file) & label. + * + * Will be used as key element in the name / PartialProfile mapping + */ +class ProfileStoreEntry { + + public: + + Glib::ustring label; /// Label to be used in menu or combobox = profile's filename + PSEType type; /// either PSET_FOLDER or PSET_FILE + unsigned short parentFolderId; /// index of the element's path in the folder list; id == 0 is reserved + unsigned short folderId; /// index of the folder's own path in the folder list; will be null for file entries + + /** @brief Create a new ProfileStoreLabel with null values, that will have to be set later with setValues or the copy operator + */ + ProfileStoreEntry(); + + /** @brief Create a new ProfileStoreLabel with values + * @param label Label to be used in menu or combobox; also used as profile's filename + * @param type either PSET_FOLDER or PSET_FILE + * @param parentFolder index of the elements's path in the folder list + * @param folder index of the folder's own path in the folder list + */ + ProfileStoreEntry(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder); + + /** @brief Set the values of the object after its instantiation + * @param label Label to be used in menu or combobox; also used as profile's filename + * @param type either PSET_FOLDER or PSET_FILE + * @param parentFolder index of the elements's path in the folder list + * @param folder index of the folder's own path in the folder list + */ + void setValues(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder); +}; + + +/** + * @brief subclass of Gtk::Label with extra fields for Combobox and Menu, to link with a ProfileStoreEntry + */ +class ProfileStoreLabel : public Gtk::Label { + + public: + const ProfileStoreEntry *entry; + +#ifndef NDEBUG + ProfileStoreLabel() : Gtk::Label("*** error ***"), entry(NULL) {} +#else + ProfileStoreLabel() : Gtk::Label(""), entry(NULL) {} +#endif + + /** @brief Create a new ProfileStoreLabel + * + * @param entry Pointer to the ProfileStoreEntry object, be it a directory or a file + */ + ProfileStoreLabel(const ProfileStoreEntry *entry); + ProfileStoreLabel (const ProfileStoreLabel &other); +}; + + +/** @brief Store the profiles bundled with RawTharapee and created by the user. + * + * This store can be queried by the GUI to display a Tree of the profiles available + * in the user's and system's profile directory and subdirectories. + */ class ProfileStore { typedef enum { @@ -35,11 +124,43 @@ class ProfileStore { STORESTATE_DELETED } StoreState; + private: MyMutex *parseMutex; StoreState storeState; - std::map partProfiles; - void parseDir (const Glib::ustring& pdir); + rtengine::procparams::AutoPartialProfile *internalDefaultProfile; + ProfileStoreEntry *internalDefaultEntry; + + /** Alphabetically ordered list of folder and files through Gtk::Label sub-class; + * ready to be used in Menu and Combobox + * The first element (#0) will be a fake path so ProfileStoreEntry can be attached to the root container */ + std::vector folders; + + /** Alphabetically ordered list of folder and files through Gtk::Label derived class; + * ready to be used in Menu and Combobox */ + std::vector entries; + + /** List of PartialProfiles from the indexed files */ + std::map partProfiles; + + /** List of the client of this store */ + std::list listeners; + + /** @brief Method to recursively parse a profile folder with a level depth arbitrarily limited to 3 + * + * @param realPath current full path of the scanned directory ; e.g.: ~/MyProfiles/ + * @param virtualPath current full path that will be saved in "options" ; must start with either ${U} or ${G}, + * standing for User's and Global's (RT) profile folder, respectively + * @param currDir name of the directory to scan; it's the last element of the virtualPath + * @param parentId path entry of the parent folder + * @param level current level of the directory tree + * @param displayLevel0 if true, level 0 is created in order to have a User's and Bundled profiles separation (i.e. 2 root directories are expected) + * if false, only one root directory is expected + */ + bool parseDir (Glib::ustring& realPath, Glib::ustring& virtualPath, Glib::ustring& currDir, unsigned int parentId, unsigned char level, bool displayLevel0); void _parseProfiles (); + void clearFileList (); + void clearProfileList (); + const ProfileStoreEntry* findEntryFromFullPathU(Glib::ustring path); public: @@ -47,10 +168,51 @@ class ProfileStore { ~ProfileStore(); bool init (); void parseProfiles (); - const rtengine::procparams::PartialProfile* getProfile (const Glib::ustring& profname); - std::vector getProfileNames (); - const rtengine::procparams::ProcParams* getDefaultProcParams (bool isRaw); - const rtengine::procparams::PartialProfile* getDefaultPartialProfile (bool isRaw); + int findFolderId(const Glib::ustring &path); + const ProfileStoreEntry* findEntryFromFullPath(Glib::ustring path); + const rtengine::procparams::PartialProfile* getProfile (Glib::ustring path); + const rtengine::procparams::PartialProfile* getProfile (const ProfileStoreEntry* entry); + const std::vector* getFileList (); + void releaseFileList (); + const rtengine::procparams::ProcParams* getDefaultProcParams (bool isRaw); + const rtengine::procparams::PartialProfile* getDefaultPartialProfile (bool isRaw); + const Glib::ustring getPathFromId(int folderId); + const ProfileStoreEntry* getInternalDefaultPSE() { return internalDefaultEntry; } + + void addListener(ProfileStoreListener *listener); + void removeListener(ProfileStoreListener *listener); + +}; + +class ProfileStoreComboBox : public MyComboBox { + + protected: + class MethodColumns : public Gtk::TreeModel::ColumnRecord { + public: + Gtk::TreeModelColumn label; + Gtk::TreeModelColumn profileStoreEntry; + MethodColumns() { add(label); add(profileStoreEntry); } + }; + + Glib::RefPtr refTreeModel; + MethodColumns methodColumns; + void refreshProfileList_ (Gtk::TreeModel::Row *parentRow, int parentFolderId, bool initial, const std::vector *entryList); + Gtk::TreeIter findRowFromEntry_ (Gtk::TreeModel::Children childs, const ProfileStoreEntry *pse); + Gtk::TreeIter findRowFromFullPath_(Gtk::TreeModel::Children childs, int parentFolderId, Glib::ustring &name); + + public: + ProfileStoreComboBox(); + void updateProfileList(); + Glib::ustring getCurrentLabel(); + const ProfileStoreEntry* getSelectedEntry(); + Gtk::TreeIter findRowFromEntry (const ProfileStoreEntry *pse); + Gtk::TreeIter findRowFromFullPath (Glib::ustring path); + Glib::ustring getFullPathFromActiveRow (); + bool setActiveRowFromFullPath (Glib::ustring oldPath); + bool setActiveRowFromEntry (const ProfileStoreEntry *pse); + bool setInternalEntry (); + Gtk::TreeIter getRowFromLabel(Glib::ustring name); + Gtk::TreeIter addRow(const ProfileStoreEntry *profileStoreEntry); }; extern ProfileStore profileStore; diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index e802bfaa9..d8710ebb8 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -206,7 +206,7 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu if (!options.customProfileBuilder.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { // For the filename etc. do NOT use streams, since they are not UTF8 safe Glib::ustring cmdLine=Glib::ustring("\"") + options.customProfileBuilder + Glib::ustring("\" \"") + fname + Glib::ustring("\" \"") - + Glib::build_filename(defaultPparamsPath, defProf + paramFileExtension) + Glib::ustring("\" "); + + (defaultPparamsPath == DEFPROFILE_INTERNAL ? "Neutral" : Glib::build_filename(defaultPparamsPath, defProf + paramFileExtension)) + Glib::ustring("\" "); // ustring doesn't know int etc formatting, so take these via (unsafe) stream std::ostringstream strm;