1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 #pragma once 27 28 29 //============================================================================== 30 class FileGroupInformationComponent : public Component, 31 private ListBoxModel, 32 private ValueTree::Listener 33 { 34 public: FileGroupInformationComponent(const Project::Item & group)35 FileGroupInformationComponent (const Project::Item& group) 36 : item (group), 37 header (item.getName(), { getIcons().openFolder, Colours::transparentBlack }) 38 { 39 list.setHeaderComponent (std::make_unique<ListBoxHeader> (Array<String> { "File", "Binary Resource", "Xcode Resource", "Compile", "Skip PCH", "Compiler Flag Scheme" }, 40 Array<float> { 0.25f, 0.125f, 0.125f, 0.125f, 0.125f, 0.25f })); 41 list.setModel (this); 42 list.setColour (ListBox::backgroundColourId, Colours::transparentBlack); 43 addAndMakeVisible (list); 44 list.updateContent(); 45 list.setRowHeight (30); 46 item.state.addListener (this); 47 lookAndFeelChanged(); 48 49 addAndMakeVisible (header); 50 } 51 ~FileGroupInformationComponent()52 ~FileGroupInformationComponent() override 53 { 54 item.state.removeListener (this); 55 } 56 57 //============================================================================== paint(Graphics & g)58 void paint (Graphics& g) override 59 { 60 g.setColour (findColour (secondaryBackgroundColourId)); 61 g.fillRect (getLocalBounds().reduced (12, 0)); 62 } 63 resized()64 void resized() override 65 { 66 auto bounds = getLocalBounds().reduced (12, 0); 67 68 header.setBounds (bounds.removeFromTop (40)); 69 list.setBounds (bounds.reduced (10, 4)); 70 } 71 parentSizeChanged()72 void parentSizeChanged() override 73 { 74 setSize (jmax (550, getParentWidth()), getParentHeight()); 75 } 76 getNumRows()77 int getNumRows() override 78 { 79 return item.getNumChildren(); 80 } 81 paintListBoxItem(int rowNumber,Graphics & g,int width,int height,bool)82 void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool /*rowIsSelected*/) override 83 { 84 g.setColour (findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId 85 : secondaryWidgetBackgroundColourId)); 86 g.fillRect (0, 0, width, height - 1); 87 } 88 refreshComponentForRow(int rowNumber,bool,Component * existingComponentToUpdate)89 Component* refreshComponentForRow (int rowNumber, bool /*isRowSelected*/, Component* existingComponentToUpdate) override 90 { 91 std::unique_ptr<Component> existing (existingComponentToUpdate); 92 93 if (rowNumber < getNumRows()) 94 { 95 auto child = item.getChild (rowNumber); 96 97 if (existingComponentToUpdate == nullptr 98 || dynamic_cast<FileOptionComponent*> (existing.get())->item != child) 99 { 100 existing.reset(); 101 existing.reset (new FileOptionComponent (child, dynamic_cast<ListBoxHeader*> (list.getHeaderComponent()))); 102 } 103 } 104 105 return existing.release(); 106 } 107 getGroupPath()108 String getGroupPath() const { return item.getFile().getFullPathName(); } 109 110 //============================================================================== valueTreePropertyChanged(ValueTree &,const Identifier &)111 void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); } valueTreeChildAdded(ValueTree &,ValueTree &)112 void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); } valueTreeChildRemoved(ValueTree &,ValueTree &,int)113 void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); } valueTreeChildOrderChanged(ValueTree &,int,int)114 void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); } valueTreeParentChanged(ValueTree &)115 void valueTreeParentChanged (ValueTree&) override { itemChanged(); } 116 117 private: 118 Project::Item item; 119 ListBox list; 120 ContentViewHeader header; 121 itemChanged()122 void itemChanged() 123 { 124 list.updateContent(); 125 repaint(); 126 } 127 128 //============================================================================== 129 class FileOptionComponent : public Component 130 { 131 public: FileOptionComponent(const Project::Item & fileItem,ListBoxHeader * listBoxHeader)132 FileOptionComponent (const Project::Item& fileItem, ListBoxHeader* listBoxHeader) 133 : item (fileItem), 134 header (listBoxHeader), 135 compilerFlagSchemeSelector (item) 136 { 137 if (item.isFile()) 138 { 139 auto isSourceFile = item.isSourceFile(); 140 141 if (isSourceFile) 142 { 143 addAndMakeVisible (compileButton); 144 compileButton.getToggleStateValue().referTo (item.getShouldCompileValue()); 145 compileButton.onStateChange = [this] { compileEnablementChanged(); }; 146 } 147 148 addAndMakeVisible (binaryResourceButton); 149 binaryResourceButton.getToggleStateValue().referTo (item.getShouldAddToBinaryResourcesValue()); 150 151 addAndMakeVisible (xcodeResourceButton); 152 xcodeResourceButton.getToggleStateValue().referTo (item.getShouldAddToXcodeResourcesValue()); 153 154 if (isSourceFile) 155 { 156 addChildComponent (skipPCHButton); 157 skipPCHButton.getToggleStateValue().referTo (item.getShouldSkipPCHValue()); 158 159 addChildComponent (compilerFlagSchemeSelector); 160 161 compileEnablementChanged(); 162 } 163 } 164 } 165 paint(Graphics & g)166 void paint (Graphics& g) override 167 { 168 if (header != nullptr) 169 { 170 auto textBounds = getLocalBounds().removeFromLeft (roundToInt (header->getProportionAtIndex (0) * (float) getWidth())); 171 172 auto iconBounds = textBounds.removeFromLeft (25); 173 174 if (item.isImageFile()) 175 iconBounds.reduce (5, 5); 176 177 item.getIcon().withColour (findColour (treeIconColourId)).draw (g, iconBounds.toFloat(), item.isIconCrossedOut()); 178 179 g.setColour (findColour (widgetTextColourId)); 180 181 g.drawText (item.getName(), textBounds, Justification::centredLeft); 182 } 183 } 184 resized()185 void resized() override 186 { 187 if (header != nullptr) 188 { 189 auto bounds = getLocalBounds(); 190 auto width = (float) getWidth(); 191 192 bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (0) * width)); 193 194 binaryResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (1) * width))); 195 xcodeResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (2) * width))); 196 compileButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (3) * width))); 197 skipPCHButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (4) * width))); 198 compilerFlagSchemeSelector.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (5) * width))); 199 } 200 } 201 202 Project::Item item; 203 204 private: 205 //============================================================================== 206 class CompilerFlagSchemeSelector : public Component, 207 private Value::Listener 208 { 209 public: CompilerFlagSchemeSelector(Project::Item & it)210 CompilerFlagSchemeSelector (Project::Item& it) 211 : item (it) 212 { 213 schemeBox.setTextWhenNothingSelected ("None"); 214 updateCompilerFlagSchemeComboBox(); 215 schemeBox.onChange = [this] { handleComboBoxSelection(); }; 216 217 addAndMakeVisible (schemeBox); 218 addChildComponent (newSchemeLabel); 219 220 newSchemeLabel.setEditable (true); 221 newSchemeLabel.setJustificationType (Justification::centredLeft); 222 newSchemeLabel.onEditorHide = [this] 223 { 224 newSchemeLabel.setVisible (false); 225 schemeBox.setVisible (true); 226 227 auto newScheme = newSchemeLabel.getText(); 228 229 item.project.addCompilerFlagScheme (newScheme); 230 231 if (item.getCompilerFlagSchemeString().isEmpty()) 232 item.setCompilerFlagScheme (newScheme); 233 234 updateCompilerFlagSchemeComboBox(); 235 }; 236 237 selectScheme (item.getCompilerFlagSchemeString()); 238 239 projectCompilerFlagSchemesValue = item.project.getProjectValue (Ids::compilerFlagSchemes); 240 projectCompilerFlagSchemesValue.addListener (this); 241 242 lookAndFeelChanged(); 243 } 244 resized()245 void resized() override 246 { 247 auto b = getLocalBounds(); 248 249 schemeBox.setBounds (b); 250 newSchemeLabel.setBounds (b); 251 } 252 253 private: valueChanged(Value &)254 void valueChanged (Value&) override { updateCompilerFlagSchemeComboBox(); } 255 lookAndFeelChanged()256 void lookAndFeelChanged() override 257 { 258 schemeBox.setColour (ComboBox::outlineColourId, Colours::transparentBlack); 259 schemeBox.setColour (ComboBox::textColourId, findColour (defaultTextColourId)); 260 } 261 updateCompilerFlagSchemeComboBox()262 void updateCompilerFlagSchemeComboBox() 263 { 264 auto itemScheme = item.getCompilerFlagSchemeString(); 265 auto allSchemes = item.project.getCompilerFlagSchemes(); 266 267 if (itemScheme.isNotEmpty() && ! allSchemes.contains (itemScheme)) 268 { 269 item.clearCurrentCompilerFlagScheme(); 270 itemScheme = {}; 271 } 272 273 schemeBox.clear(); 274 275 schemeBox.addItemList (allSchemes, 1); 276 schemeBox.addSeparator(); 277 schemeBox.addItem ("Add a new scheme...", -1); 278 schemeBox.addItem ("Delete selected scheme", -2); 279 schemeBox.addItem ("Clear", -3); 280 281 selectScheme (itemScheme); 282 } 283 handleComboBoxSelection()284 void handleComboBoxSelection() 285 { 286 auto selectedID = schemeBox.getSelectedId(); 287 288 if (selectedID > 0) 289 { 290 item.setCompilerFlagScheme (schemeBox.getItemText (selectedID - 1)); 291 } 292 else if (selectedID == -1) 293 { 294 newSchemeLabel.setText ("NewScheme", dontSendNotification); 295 296 schemeBox.setVisible (false); 297 newSchemeLabel.setVisible (true); 298 299 newSchemeLabel.showEditor(); 300 301 if (auto* ed = newSchemeLabel.getCurrentTextEditor()) 302 ed->setInputRestrictions (64, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"); 303 } 304 else if (selectedID == -2) 305 { 306 auto currentScheme = item.getCompilerFlagSchemeString(); 307 308 if (currentScheme.isNotEmpty()) 309 { 310 item.project.removeCompilerFlagScheme (currentScheme); 311 item.clearCurrentCompilerFlagScheme(); 312 } 313 314 updateCompilerFlagSchemeComboBox(); 315 } 316 else if (selectedID == -3) 317 { 318 schemeBox.setSelectedId (0); 319 item.clearCurrentCompilerFlagScheme(); 320 } 321 } 322 selectScheme(const String & schemeToSelect)323 void selectScheme (const String& schemeToSelect) 324 { 325 if (schemeToSelect.isNotEmpty()) 326 { 327 for (int i = 0; i < schemeBox.getNumItems(); ++i) 328 { 329 if (schemeBox.getItemText (i) == schemeToSelect) 330 { 331 schemeBox.setSelectedItemIndex (i); 332 return; 333 } 334 } 335 } 336 337 schemeBox.setSelectedId (0); 338 } 339 340 Project::Item& item; 341 Value projectCompilerFlagSchemesValue; 342 343 ComboBox schemeBox; 344 Label newSchemeLabel; 345 }; 346 compileEnablementChanged()347 void compileEnablementChanged() 348 { 349 auto shouldBeCompiled = compileButton.getToggleState(); 350 351 skipPCHButton.setVisible (shouldBeCompiled); 352 compilerFlagSchemeSelector.setVisible (shouldBeCompiled); 353 } 354 355 //============================================================================== 356 ListBoxHeader* header; 357 358 ToggleButton compileButton, binaryResourceButton, xcodeResourceButton, skipPCHButton; 359 CompilerFlagSchemeSelector compilerFlagSchemeSelector; 360 }; 361 362 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileGroupInformationComponent) 363 }; 364