1 /* 2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ExtensionWindow.h" 8 #include "FileTypes.h" 9 #include "FileTypesWindow.h" 10 11 #include <Button.h> 12 #include <Catalog.h> 13 #include <ControlLook.h> 14 #include <GridLayoutBuilder.h> 15 #include <GroupLayout.h> 16 #include <Locale.h> 17 #include <MenuField.h> 18 #include <MenuItem.h> 19 #include <Mime.h> 20 #include <PopUpMenu.h> 21 #include <String.h> 22 #include <TextControl.h> 23 24 #include <string.h> 25 26 27 #undef B_TRANSLATE_CONTEXT 28 #define B_TRANSLATE_CONTEXT "Extension Window" 29 30 31 const uint32 kMsgExtensionUpdated = 'exup'; 32 const uint32 kMsgAccept = 'acpt'; 33 34 35 static int 36 compare_extensions(const void* _a, const void* _b) 37 { 38 const char* a = *(const char **)_a; 39 const char* b = *(const char **)_b; 40 41 int compare = strcasecmp(a, b); 42 if (compare != 0) 43 return compare; 44 45 // sort lower case characters first 46 return -strcmp(a, b); 47 } 48 49 50 //! newExtensionsList contains all the entries (char*) which are to be added. 51 status_t 52 merge_extensions(BMimeType& type, const BList& newExtensionsList, 53 const char* removeExtension) 54 { 55 BMessage extensions; 56 status_t status = type.GetFileExtensions(&extensions); 57 if (status < B_OK) 58 return status; 59 60 // replace the entry, and remove any equivalent entries 61 BList mergedList; 62 mergedList.AddList(&newExtensionsList); 63 int32 originalCount = mergedList.CountItems(); 64 65 const char* extension; 66 for (int32 i = 0; extensions.FindString("extensions", i, 67 &extension) == B_OK; i++) { 68 69 for (int32 j = originalCount; j-- > 0;) { 70 if (!strcmp((const char*)mergedList.ItemAt(j), extension)) { 71 // Do not add this old item again, since it's already 72 // there. 73 mergedList.RemoveItem(j); 74 originalCount--; 75 } 76 } 77 78 // The item will be added behind "originalCount", so we cannot 79 // remove it accidentally in the next iterations, it's is added 80 // for good. 81 if (removeExtension == NULL || strcmp(removeExtension, extension)) 82 mergedList.AddItem((void *)extension); 83 } 84 85 mergedList.SortItems(compare_extensions); 86 87 // Copy them to a new message (their memory is still part of the 88 // original BMessage) 89 BMessage newExtensions; 90 for (int32 i = 0; i < mergedList.CountItems(); i++) { 91 newExtensions.AddString("extensions", 92 (const char*)mergedList.ItemAt(i)); 93 } 94 95 return type.SetFileExtensions(&newExtensions); 96 } 97 98 99 status_t 100 replace_extension(BMimeType& type, const char* newExtension, 101 const char* oldExtension) 102 { 103 BList list; 104 list.AddItem((void *)newExtension); 105 106 return merge_extensions(type, list, oldExtension); 107 } 108 109 110 // #pragma mark - 111 112 113 ExtensionWindow::ExtensionWindow(FileTypesWindow* target, BMimeType& type, 114 const char* extension) 115 : BWindow(BRect(100, 100, 350, 200), B_TRANSLATE("Extension"), 116 B_MODAL_WINDOW_LOOK, B_MODAL_SUBSET_WINDOW_FEEL, 117 B_NOT_ZOOMABLE | B_NOT_RESIZABLE 118 | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 119 fTarget(target), 120 fMimeType(type.Type()), 121 fExtension(extension) 122 { 123 SetLayout(new BGroupLayout(B_VERTICAL)); 124 125 float padding = be_control_look->DefaultItemSpacing(); 126 127 fExtensionControl = new BTextControl(B_TRANSLATE("Extension:"), 128 extension, NULL); 129 fExtensionControl->SetModificationMessage( 130 new BMessage(kMsgExtensionUpdated)); 131 fExtensionControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT); 132 133 // filter out invalid characters that can't be part of an extension 134 BTextView* textView = fExtensionControl->TextView(); 135 const char* disallowedCharacters = "/:"; 136 for (int32 i = 0; disallowedCharacters[i]; i++) { 137 textView->DisallowChar(disallowedCharacters[i]); 138 } 139 140 fAcceptButton = new BButton(extension 141 ? B_TRANSLATE("Done") : B_TRANSLATE("Add"), 142 new BMessage(kMsgAccept)); 143 fAcceptButton->SetEnabled(false); 144 145 BButton* button = new BButton(B_TRANSLATE("Cancel"), 146 new BMessage(B_QUIT_REQUESTED)); 147 148 AddChild(BGridLayoutBuilder(padding, padding) 149 .Add(fExtensionControl->CreateLabelLayoutItem(), 0, 0) 150 .Add(fExtensionControl->CreateTextViewLayoutItem(), 1, 0) 151 .Add(fAcceptButton, 0, 1) 152 .Add(button, 1, 1) 153 .SetInsets(padding, padding, padding, padding) 154 ); 155 156 // omit the leading dot 157 if (fExtension.ByteAt(0) == '.') 158 fExtension.Remove(0, 1); 159 160 fAcceptButton->MakeDefault(true); 161 fExtensionControl->MakeFocus(true); 162 163 target->PlaceSubWindow(this); 164 AddToSubset(target); 165 } 166 167 168 ExtensionWindow::~ExtensionWindow() 169 { 170 } 171 172 173 void 174 ExtensionWindow::MessageReceived(BMessage* message) 175 { 176 switch (message->what) { 177 case kMsgExtensionUpdated: 178 { 179 bool enabled = fExtensionControl->Text() != NULL 180 && fExtensionControl->Text()[0] != '\0'; 181 if (enabled) { 182 // There is some text, but we only accept it, if it 183 // changed the previous extension 184 enabled = strcmp(fExtensionControl->Text(), fExtension.String()); 185 } 186 187 if (fAcceptButton->IsEnabled() != enabled) 188 fAcceptButton->SetEnabled(enabled); 189 break; 190 } 191 192 case kMsgAccept: 193 { 194 const char* newExtension = fExtensionControl->Text(); 195 // omit the leading dot 196 if (newExtension[0] == '.') 197 newExtension++; 198 199 status_t status = replace_extension(fMimeType, newExtension, 200 fExtension.String()); 201 if (status != B_OK) 202 error_alert(B_TRANSLATE("Could not change file extensions"), 203 status); 204 205 PostMessage(B_QUIT_REQUESTED); 206 break; 207 } 208 209 default: 210 BWindow::MessageReceived(message); 211 break; 212 } 213 } 214