1 /* 2 * Copyright 2002-2016, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Mattias Sundblad 7 * Andrew Bachmann 8 * Jonas Sundström 9 */ 10 11 12 #include "Constants.h" 13 #include "StyledEditApp.h" 14 #include "StyledEditWindow.h" 15 16 #include <Alert.h> 17 #include <Autolock.h> 18 #include <Catalog.h> 19 #include <Locale.h> 20 #include <MenuBar.h> 21 #include <CharacterSet.h> 22 #include <CharacterSetRoster.h> 23 #include <FilePanel.h> 24 #include <MenuItem.h> 25 #include <Message.h> 26 #include <Path.h> 27 #include <Screen.h> 28 29 #include <stdio.h> 30 31 32 using namespace BPrivate; 33 34 35 static BRect sWindowRect(7, 26, 507, 426); 36 static float sCascadeOffset = 15; 37 static BPoint sTopLeft = BPoint(7, 26); 38 39 40 namespace 41 { 42 void 43 cascade() 44 { 45 BScreen screen; 46 BRect screenBorder = screen.Frame(); 47 float left = sWindowRect.left + sCascadeOffset; 48 if (left + sWindowRect.Width() > screenBorder.right) 49 left = sTopLeft.x; 50 51 float top = sWindowRect.top + sCascadeOffset; 52 if (top + sWindowRect.Height() > screenBorder.bottom) 53 top = sTopLeft.y; 54 55 sWindowRect.OffsetTo(BPoint(left, top)); 56 } 57 58 59 void 60 uncascade() 61 { 62 BScreen screen; 63 BRect screenBorder = screen.Frame(); 64 65 float left = sWindowRect.left - sCascadeOffset; 66 if (left < sTopLeft.x) { 67 left = screenBorder.right - sWindowRect.Width() - sTopLeft.x; 68 left = left - ((int)left % (int)sCascadeOffset) + sTopLeft.x; 69 } 70 71 float top = sWindowRect.top - sCascadeOffset; 72 if (top < sTopLeft.y) { 73 top = screenBorder.bottom - sWindowRect.Height() - sTopLeft.y; 74 top = top - ((int)left % (int)sCascadeOffset) + sTopLeft.y; 75 } 76 77 sWindowRect.OffsetTo(BPoint(left, top)); 78 } 79 } 80 81 82 // #pragma mark - 83 84 85 #undef B_TRANSLATION_CONTEXT 86 #define B_TRANSLATION_CONTEXT "Open_and_SaveAsPanel" 87 88 89 StyledEditApp::StyledEditApp() 90 : 91 BApplication(APP_SIGNATURE), 92 fOpenPanel(NULL) 93 { 94 B_TRANSLATE_MARK_SYSTEM_NAME_VOID("StyledEdit"); 95 96 fOpenPanel = new BFilePanel(); 97 fOpenAsEncoding = 0; 98 99 BMenuBar* menuBar 100 = dynamic_cast<BMenuBar*>(fOpenPanel->Window()->FindView("MenuBar")); 101 if (menuBar != NULL) { 102 fOpenPanelEncodingMenu = new BMenu(B_TRANSLATE("Encoding")); 103 fOpenPanelEncodingMenu->SetRadioMode(true); 104 105 menuBar->AddItem(fOpenPanelEncodingMenu); 106 107 BCharacterSetRoster roster; 108 BCharacterSet charset; 109 while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) { 110 BString name; 111 if (charset.GetFontID() == B_UNICODE_UTF8) 112 name = B_TRANSLATE("Default"); 113 else 114 name = charset.GetPrintName(); 115 116 const char* mime = charset.GetMIMEName(); 117 if (mime != NULL) { 118 name.Append(" ("); 119 name.Append(mime); 120 name.Append(")"); 121 } 122 BMenuItem* item 123 = new BMenuItem(name.String(), new BMessage(OPEN_AS_ENCODING)); 124 item->SetTarget(this); 125 fOpenPanelEncodingMenu->AddItem(item); 126 if (charset.GetFontID() == fOpenAsEncoding) 127 item->SetMarked(true); 128 } 129 } else 130 fOpenPanelEncodingMenu = NULL; 131 132 fWindowCount = 0; 133 fNextUntitledWindow = 1; 134 fBadArguments = false; 135 136 float factor = be_plain_font->Size() / 12.0f; 137 sCascadeOffset *= factor; 138 sTopLeft.x *= factor; 139 sTopLeft.y *= factor; 140 sWindowRect.left *= factor; 141 sWindowRect.top *= factor; 142 sWindowRect.right *= factor; 143 sWindowRect.bottom *= factor; 144 sWindowRect.PrintToStream(); 145 } 146 147 148 StyledEditApp::~StyledEditApp() 149 { 150 delete fOpenPanel; 151 } 152 153 154 void 155 StyledEditApp::MessageReceived(BMessage* message) 156 { 157 switch (message->what) { 158 case MENU_NEW: 159 OpenDocument(); 160 break; 161 case MENU_OPEN: 162 fOpenPanel->Show(); 163 break; 164 case B_SILENT_RELAUNCH: 165 OpenDocument(); 166 break; 167 case OPEN_AS_ENCODING: 168 void* ptr; 169 if (message->FindPointer("source", &ptr) == B_OK 170 && fOpenPanelEncodingMenu != NULL) { 171 fOpenAsEncoding = (uint32)fOpenPanelEncodingMenu->IndexOf( 172 (BMenuItem*)ptr); 173 } 174 break; 175 176 default: 177 BApplication::MessageReceived(message); 178 break; 179 } 180 } 181 182 183 void 184 StyledEditApp::OpenDocument() 185 { 186 new StyledEditWindow(sWindowRect, fNextUntitledWindow++, fOpenAsEncoding); 187 cascade(); 188 fWindowCount++; 189 } 190 191 192 status_t 193 StyledEditApp::OpenDocument(entry_ref* ref, BMessage* message) 194 { 195 // traverse eventual symlink 196 BEntry entry(ref, true); 197 entry.GetRef(ref); 198 199 if (entry.IsDirectory()) { 200 BPath path(&entry); 201 fprintf(stderr, 202 "Can't open directory \"%s\" for editing.\n", 203 path.Path()); 204 return B_ERROR; 205 } 206 207 BEntry parent; 208 entry.GetParent(&parent); 209 210 if (!entry.Exists() && !parent.Exists()) { 211 fprintf(stderr, 212 "Can't create file. Missing parent directory.\n"); 213 return B_ERROR; 214 } 215 216 BWindow* window = NULL; 217 StyledEditWindow* document = NULL; 218 219 for (int32 index = 0; ; index++) { 220 window = WindowAt(index); 221 if (window == NULL) 222 break; 223 224 document = dynamic_cast<StyledEditWindow*>(window); 225 if (document == NULL) 226 continue; 227 228 if (document->IsDocumentEntryRef(ref)) { 229 if (document->Lock()) { 230 document->Activate(); 231 document->Unlock(); 232 if (message != NULL) 233 document->PostMessage(message); 234 return B_OK; 235 } 236 } 237 } 238 239 document = new StyledEditWindow(sWindowRect, ref, fOpenAsEncoding); 240 cascade(); 241 242 if (message != NULL) 243 document->PostMessage(message); 244 245 fWindowCount++; 246 247 return B_OK; 248 } 249 250 251 void 252 StyledEditApp::CloseDocument() 253 { 254 uncascade(); 255 fWindowCount--; 256 if (fWindowCount == 0) { 257 BAutolock lock(this); 258 Quit(); 259 } 260 } 261 262 263 void 264 StyledEditApp::RefsReceived(BMessage* message) 265 { 266 int32 index = 0; 267 entry_ref ref; 268 269 while (message->FindRef("refs", index, &ref) == B_OK) { 270 int32 line; 271 if (message->FindInt32("be:line", index, &line) != B_OK) 272 line = -1; 273 int32 start, length; 274 if (message->FindInt32("be:selection_length", index, &length) != B_OK 275 || message->FindInt32("be:selection_offset", index, &start) != B_OK) 276 { 277 start = -1; 278 length = -1; 279 } 280 281 BMessage* selection = NULL; 282 if (line >= 0 || (start >= 0 && length >= 0)) { 283 selection = new BMessage(UPDATE_LINE_SELECTION); 284 if (line >= 0) 285 selection->AddInt32("be:line", line); 286 if (start >= 0) { 287 selection->AddInt32("be:selection_offset", start); 288 selection->AddInt32("be:selection_length", max_c(0, length)); 289 } 290 } 291 292 OpenDocument(&ref, selection); 293 index++; 294 } 295 } 296 297 298 void 299 StyledEditApp::ArgvReceived(int32 argc, char* argv[]) 300 { 301 // If StyledEdit is already running and gets invoked again 302 // we need to account for a possible mismatch in current 303 // working directory. The paths of the new arguments are 304 // relative to the cwd of the invocation, if they are not 305 // absolute. This cwd we find as a string named "cwd" in 306 // the BLooper's current message. 307 308 const char* cwd = ""; 309 BMessage* message = CurrentMessage(); 310 311 if (message != NULL) { 312 if (message->FindString("cwd", &cwd) != B_OK) 313 cwd = ""; 314 } 315 316 for (int i = 1 ; (i < argc) ; i++) { 317 BPath path; 318 if (argv[i][0] == '/') { 319 path.SetTo(argv[i]); 320 } else { 321 path.SetTo(cwd, argv[i]); 322 // patch relative paths only 323 } 324 325 entry_ref ref; 326 get_ref_for_path(path.Path(), &ref); 327 328 status_t status; 329 status = OpenDocument(&ref); 330 331 if (status != B_OK && IsLaunching()) 332 fBadArguments = true; 333 } 334 } 335 336 337 void 338 StyledEditApp::ReadyToRun() 339 { 340 if (fWindowCount > 0) 341 return; 342 343 if (fBadArguments) 344 Quit(); 345 else 346 OpenDocument(); 347 } 348 349 350 int32 351 StyledEditApp::NumberOfWindows() 352 { 353 return fWindowCount; 354 } 355 356 357 // #pragma mark - 358 359 360 int 361 main(int argc, char** argv) 362 { 363 StyledEditApp styledEdit; 364 styledEdit.Run(); 365 return 0; 366 } 367 368