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