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 "FileTypes.h"
8#include "FileTypesWindow.h"
9#include "NewFileTypeWindow.h"
10
11#include <Button.h>
12#include <Catalog.h>
13#include <ControlLook.h>
14#include <LayoutBuilder.h>
15#include <Locale.h>
16#include <MenuBar.h>
17#include <MenuField.h>
18#include <MenuItem.h>
19#include <Mime.h>
20#include <PopUpMenu.h>
21#include <SpaceLayoutItem.h>
22#include <String.h>
23#include <StringView.h>
24#include <TextControl.h>
25
26#include <string.h>
27
28
29#undef B_TRANSLATION_CONTEXT
30#define B_TRANSLATION_CONTEXT "New File Type Window"
31
32
33const uint32 kMsgSupertypeChosen = 'sptc';
34const uint32 kMsgNewSupertypeChosen = 'nstc';
35
36const uint32 kMsgNameUpdated = 'nmup';
37
38const uint32 kMsgAddType = 'atyp';
39
40
41NewFileTypeWindow::NewFileTypeWindow(FileTypesWindow* target,
42	const char* currentType)
43	:
44	BWindow(BRect(100, 100, 350, 200), B_TRANSLATE("New file type"),
45		B_MODAL_WINDOW, B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE
46			| B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS ),
47	fTarget(target)
48{
49	fSupertypesMenu = new BPopUpMenu("supertypes");
50	BMenuItem* item;
51	BMessage types;
52	if (BMimeType::GetInstalledSupertypes(&types) == B_OK) {
53		const char* type;
54		int32 i = 0;
55		while (types.FindString("super_types", i++, &type) == B_OK) {
56			fSupertypesMenu->AddItem(item = new BMenuItem(type,
57				new BMessage(kMsgSupertypeChosen)));
58
59			// select super type close to the current type
60			if (currentType != NULL) {
61				if (!strncmp(type, currentType, strlen(type)))
62					item->SetMarked(true);
63			} else if (i == 1)
64				item->SetMarked(true);
65		}
66
67		if (i > 1)
68			fSupertypesMenu->AddSeparatorItem();
69	}
70
71	fSupertypesMenu->AddItem(new BMenuItem(B_TRANSLATE("Add new group"),
72		new BMessage(kMsgNewSupertypeChosen)));
73	BMenuField* typesMenuField = new BMenuField(NULL, fSupertypesMenu);
74
75	BStringView* typesMenuLabel = new BStringView(NULL, B_TRANSLATE("Group:"));
76		// Create a separate label view, otherwise things don't line up right
77	typesMenuLabel->SetAlignment(B_ALIGN_LEFT);
78	typesMenuLabel->SetExplicitAlignment(
79		BAlignment(B_ALIGN_LEFT, B_ALIGN_USE_FULL_HEIGHT));
80
81	fNameControl = new BTextControl(B_TRANSLATE("Internal name:"), "", NULL);
82	fNameControl->SetModificationMessage(new BMessage(kMsgNameUpdated));
83
84	// filter out invalid characters that can't be part of a MIME type name
85	BTextView* nameControlTextView = fNameControl->TextView();
86	const char* disallowedCharacters = "/<>@,;:\"()[]?= ";
87	for (int32 i = 0; disallowedCharacters[i]; i++) {
88		nameControlTextView->DisallowChar(disallowedCharacters[i]);
89	}
90
91	fAddButton = new BButton(B_TRANSLATE("Add type"),
92		new BMessage(kMsgAddType));
93
94	float padding = be_control_look->DefaultItemSpacing();
95
96	BLayoutBuilder::Grid<>(this, padding, padding)
97		.SetInsets(padding)
98		.Add(typesMenuLabel, 0, 0)
99		.Add(typesMenuField, 1, 0, 2)
100		.Add(fNameControl->CreateLabelLayoutItem(), 0, 1)
101		.Add(fNameControl->CreateTextViewLayoutItem(), 1, 1, 2)
102		.Add(BSpaceLayoutItem::CreateGlue(), 0, 2)
103		.Add(new BButton(B_TRANSLATE("Cancel"),
104			new BMessage(B_QUIT_REQUESTED)), 1, 2)
105		.Add(fAddButton, 2, 2)
106		.SetColumnWeight(0, 3);
107
108	BAlignment fullSize = BAlignment(B_ALIGN_USE_FULL_WIDTH,
109		B_ALIGN_USE_FULL_HEIGHT);
110	typesMenuField->MenuBar()->SetExplicitAlignment(fullSize);
111	fNameControl->TextView()->SetExplicitAlignment(fullSize);
112
113	BLayoutItem* nameControlLabelItem = fNameControl->CreateLabelLayoutItem();
114	nameControlLabelItem->SetExplicitMinSize(nameControlLabelItem->MinSize());
115		// stops fNameControl's label from truncating under certain conditions
116
117	fAddButton->MakeDefault(true);
118	fNameControl->MakeFocus(true);
119
120	target->PlaceSubWindow(this);
121}
122
123
124NewFileTypeWindow::~NewFileTypeWindow()
125{
126}
127
128
129void
130NewFileTypeWindow::MessageReceived(BMessage* message)
131{
132	switch (message->what) {
133		case kMsgSupertypeChosen:
134			fAddButton->SetLabel(B_TRANSLATE("Add type"));
135			fNameControl->SetLabel(B_TRANSLATE("Internal name:"));
136			fNameControl->MakeFocus(true);
137			InvalidateLayout(true);
138			break;
139
140		case kMsgNewSupertypeChosen:
141			fAddButton->SetLabel(B_TRANSLATE("Add group"));
142			fNameControl->SetLabel(B_TRANSLATE("Group name:"));
143			fNameControl->MakeFocus(true);
144			InvalidateLayout(true);
145			break;
146
147		case kMsgNameUpdated:
148		{
149			bool empty = fNameControl->Text() == NULL
150				|| fNameControl->Text()[0] == '\0';
151
152			if (fAddButton->IsEnabled() == empty)
153				fAddButton->SetEnabled(!empty);
154			break;
155		}
156
157		case kMsgAddType:
158		{
159			BMenuItem* item = fSupertypesMenu->FindMarked();
160			if (item != NULL) {
161				BString type;
162				if (fSupertypesMenu->IndexOf(item)
163						!= fSupertypesMenu->CountItems() - 1) {
164					// add normal type
165					type = item->Label();
166					type.Append("/");
167				}
168
169				type.Append(fNameControl->Text());
170
171				BMimeType mimeType(type.String());
172				if (mimeType.IsInstalled()) {
173					error_alert(B_TRANSLATE("This file type already exists"));
174					break;
175				}
176
177				status_t status = mimeType.Install();
178				if (status != B_OK)
179					error_alert(B_TRANSLATE("Could not install file type"),
180						status);
181				else {
182					BMessage update(kMsgSelectNewType);
183					update.AddString("type", type.String());
184
185					fTarget.SendMessage(&update);
186				}
187			}
188			PostMessage(B_QUIT_REQUESTED);
189			break;
190		}
191
192		default:
193			BWindow::MessageReceived(message);
194			break;
195	}
196}
197
198
199bool
200NewFileTypeWindow::QuitRequested()
201{
202	fTarget.SendMessage(kMsgNewTypeWindowClosed);
203	return true;
204}
205
206
207