1 /* 2 * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 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 TR_CONTEXT 28 #define TR_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 = 3.0f; 126 //if (be_control_look) 127 // padding = be_control_look->DefaultItemSpacing(); 128 // this seems to be very large! 129 130 fExtensionControl = new BTextControl(B_TRANSLATE("Extension:"), 131 extension, NULL); 132 fExtensionControl->SetModificationMessage( 133 new BMessage(kMsgExtensionUpdated)); 134 fExtensionControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT); 135 136 // filter out invalid characters that can't be part of an extension 137 BTextView* textView = fExtensionControl->TextView(); 138 const char* disallowedCharacters = "/:"; 139 for (int32 i = 0; disallowedCharacters[i]; i++) { 140 textView->DisallowChar(disallowedCharacters[i]); 141 } 142 143 fAcceptButton = new BButton(extension 144 ? B_TRANSLATE("Done") : B_TRANSLATE("Add"), 145 new BMessage(kMsgAccept)); 146 fAcceptButton->SetEnabled(false); 147 148 BButton* button = new BButton(B_TRANSLATE("Cancel"), 149 new BMessage(B_QUIT_REQUESTED)); 150 151 AddChild(BGridLayoutBuilder(padding, padding) 152 .Add(fExtensionControl->CreateLabelLayoutItem(), 0, 0) 153 .Add(fExtensionControl->CreateTextViewLayoutItem(), 1, 0) 154 .Add(fAcceptButton, 0, 1) 155 .Add(button, 1, 1) 156 .SetInsets(padding, padding, padding, padding) 157 ); 158 159 // omit the leading dot 160 if (fExtension.ByteAt(0) == '.') 161 fExtension.Remove(0, 1); 162 163 fAcceptButton->MakeDefault(true); 164 fExtensionControl->MakeFocus(true); 165 166 target->PlaceSubWindow(this); 167 AddToSubset(target); 168 } 169 170 171 ExtensionWindow::~ExtensionWindow() 172 { 173 } 174 175 176 void 177 ExtensionWindow::MessageReceived(BMessage* message) 178 { 179 switch (message->what) { 180 case kMsgExtensionUpdated: 181 { 182 bool enabled = fExtensionControl->Text() != NULL 183 && fExtensionControl->Text()[0] != '\0'; 184 if (enabled) { 185 // There is some text, but we only accept it, if it 186 // changed the previous extension 187 enabled = strcmp(fExtensionControl->Text(), fExtension.String()); 188 } 189 190 if (fAcceptButton->IsEnabled() != enabled) 191 fAcceptButton->SetEnabled(enabled); 192 break; 193 } 194 195 case kMsgAccept: 196 { 197 const char* newExtension = fExtensionControl->Text(); 198 // omit the leading dot 199 if (newExtension[0] == '.') 200 newExtension++; 201 202 status_t status = replace_extension(fMimeType, newExtension, 203 fExtension.String()); 204 if (status != B_OK) 205 error_alert(B_TRANSLATE("Could not change file extensions"), 206 status); 207 208 PostMessage(B_QUIT_REQUESTED); 209 break; 210 } 211 212 default: 213 BWindow::MessageReceived(message); 214 break; 215 } 216 } 217