1 /* 2 * Copyright 2002-2006, Haiku, Inc. 3 * Copyright 2002, François Revol, revol@free.fr. 4 * This file is distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * François Revol, revol@free.fr 8 * Axel Dörfler, axeld@pinc-software.de 9 * Oliver "Madison" Kohl, 10 * Matt Madia 11 */ 12 13 /*! 14 Workspaces window trick found by Michael "Minox" Paine. 15 (using B_ALL_WORKSPACES as flags in BWindow) 16 */ 17 18 19 #include <Alert.h> 20 #include <Application.h> 21 #include <Entry.h> 22 #include <File.h> 23 #include <FindDirectory.h> 24 #include <Path.h> 25 #include <Roster.h> 26 #include <Screen.h> 27 #include <TextView.h> 28 #include <Window.h> 29 30 #include <ctype.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 #include "WindowPrivate.h" 36 37 static const char *kWorkspacesSignature = "application/x-vnd.Be-WORK"; 38 static const char *kWorkspacesSettingFile = "Workspace_data"; 39 40 static const float kScreenBorderOffset = 10.0; 41 42 43 class WorkspacesPreferences { 44 public: 45 WorkspacesPreferences(); 46 virtual ~WorkspacesPreferences(); 47 48 BRect WindowFrame() const { return fWindowFrame; } 49 BRect ScreenFrame() const { return fScreenFrame; } 50 51 void UpdateFramesForScreen(BRect screenFrame); 52 void UpdateScreenFrame(); 53 void SetWindowFrame(BRect); 54 55 private: 56 BRect fWindowFrame, fScreenFrame; 57 }; 58 59 class WorkspacesWindow : public BWindow { 60 public: 61 WorkspacesWindow(WorkspacesPreferences *fPreferences); 62 virtual ~WorkspacesWindow(); 63 64 virtual void ScreenChanged(BRect frame, color_space mode); 65 virtual void FrameMoved(BPoint origin); 66 virtual void FrameResized(float width, float height); 67 virtual void Zoom(BPoint origin, float width, float height); 68 69 virtual void MessageReceived(BMessage *msg); 70 virtual bool QuitRequested(); 71 72 private: 73 WorkspacesPreferences *fPreferences; 74 BRect fPreviousFrame; 75 }; 76 77 class WorkspacesApp : public BApplication { 78 public: 79 WorkspacesApp(); 80 virtual ~WorkspacesApp(); 81 82 virtual void AboutRequested(); 83 virtual void ArgvReceived(int32 argc, char **argv); 84 virtual void ReadyToRun(); 85 86 void Usage(const char *programName); 87 88 private: 89 BWindow *fWindow; 90 }; 91 92 93 WorkspacesPreferences::WorkspacesPreferences() 94 { 95 UpdateScreenFrame(); 96 97 bool settingsValid = false; 98 BPath path; 99 100 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 101 path.Append(kWorkspacesSettingFile); 102 BFile file(path.Path(), B_READ_ONLY); 103 if (file.InitCheck() == B_OK 104 && file.Read(&fWindowFrame, sizeof(BRect)) == sizeof(BRect)) { 105 // we now also store the frame of the screen to know 106 // in which context the window frame has been chosen 107 BScreen screen; 108 BRect frame; 109 if (file.Read(&frame, sizeof(BRect)) == sizeof(BRect)) { 110 fScreenFrame = frame; 111 // if the current screen frame is different from the one 112 // just loaded, we need to alter the window frame accordingly 113 if (fScreenFrame != screen.Frame()) 114 UpdateFramesForScreen(screen.Frame()); 115 } 116 117 // check if loaded values are valid 118 if (screen.Frame().right >= fWindowFrame.right 119 && screen.Frame().bottom >= fWindowFrame.bottom 120 && fWindowFrame.right > 0 && fWindowFrame.bottom > 0) 121 settingsValid = true; 122 } 123 } 124 125 if (!settingsValid) { 126 // set to some usable defaults 127 fWindowFrame = fScreenFrame; 128 fWindowFrame.OffsetBy(-kScreenBorderOffset, -kScreenBorderOffset); 129 fWindowFrame.left = fWindowFrame.right - 160; 130 fWindowFrame.top = fWindowFrame.bottom - 140; 131 } 132 } 133 134 135 WorkspacesPreferences::~WorkspacesPreferences() 136 { 137 // write settings file 138 BPath path; 139 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) < B_OK) 140 return; 141 142 path.Append(kWorkspacesSettingFile); 143 144 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE); 145 if (file.InitCheck() == B_OK) { 146 file.Write(&fWindowFrame, sizeof(BRect)); 147 file.Write(&fScreenFrame, sizeof(BRect)); 148 } 149 } 150 151 152 void 153 WorkspacesPreferences::UpdateFramesForScreen(BRect newScreenFrame) 154 { 155 // don't change the position if the screen frame hasn't changed 156 if (newScreenFrame == fScreenFrame) 157 return; 158 159 // adjust horizontal position 160 if (fWindowFrame.right > fScreenFrame.right / 2) 161 fWindowFrame.OffsetTo(newScreenFrame.right 162 - (fScreenFrame.right - fWindowFrame.left), fWindowFrame.top); 163 164 // adjust vertical position 165 if (fWindowFrame.bottom > fScreenFrame.bottom / 2) 166 fWindowFrame.OffsetTo(fWindowFrame.left, 167 newScreenFrame.bottom - (fScreenFrame.bottom - fWindowFrame.top)); 168 169 fScreenFrame = newScreenFrame; 170 } 171 172 173 void 174 WorkspacesPreferences::UpdateScreenFrame() 175 { 176 BScreen screen; 177 fScreenFrame = screen.Frame(); 178 } 179 180 181 void 182 WorkspacesPreferences::SetWindowFrame(BRect frame) 183 { 184 fWindowFrame = frame; 185 } 186 187 188 // #pragma mark - 189 190 191 WorkspacesWindow::WorkspacesWindow(WorkspacesPreferences *preferences) 192 : BWindow(preferences->WindowFrame(), "Workspaces", B_TITLED_WINDOW_LOOK, 193 B_NORMAL_WINDOW_FEEL, 194 kWorkspacesWindowFlag | B_AVOID_FRONT | B_WILL_ACCEPT_FIRST_CLICK, 195 B_ALL_WORKSPACES), 196 fPreferences(preferences) 197 { 198 fPreviousFrame = Frame(); 199 } 200 201 202 WorkspacesWindow::~WorkspacesWindow() 203 { 204 delete fPreferences; 205 } 206 207 208 void 209 WorkspacesWindow::ScreenChanged(BRect rect, color_space mode) 210 { 211 fPreviousFrame = fPreferences->WindowFrame(); 212 // work-around for a bug in BeOS, see explanation in FrameMoved() 213 214 fPreferences->UpdateFramesForScreen(rect); 215 MoveTo(fPreferences->WindowFrame().LeftTop()); 216 } 217 218 219 void 220 WorkspacesWindow::FrameMoved(BPoint origin) 221 { 222 if (origin == fPreviousFrame.LeftTop()) { 223 // This works around a bug in BeOS; when you change the window 224 // position in WorkspaceActivated() or ScreenChanged(), it will 225 // send an old repositioning message *after* the FrameMoved() 226 // that originated your change has arrived 227 return; 228 } 229 230 fPreferences->SetWindowFrame(Frame()); 231 } 232 233 234 void 235 WorkspacesWindow::FrameResized(float width, float height) 236 { 237 fPreferences->SetWindowFrame(Frame()); 238 } 239 240 241 void 242 WorkspacesWindow::Zoom(BPoint origin, float width, float height) 243 { 244 BScreen screen; 245 origin = screen.Frame().RightBottom(); 246 origin.x -= kScreenBorderOffset + fPreferences->WindowFrame().Width(); 247 origin.y -= kScreenBorderOffset + fPreferences->WindowFrame().Height(); 248 249 MoveTo(origin); 250 } 251 252 253 void 254 WorkspacesWindow::MessageReceived(BMessage *msg) 255 { 256 if (msg->what == 'DATA') { 257 // Drop from Tracker 258 entry_ref ref; 259 for (int i = 0; (msg->FindRef("refs", i, &ref) == B_OK); i++) 260 be_roster->Launch(&ref); 261 } else 262 BWindow::MessageReceived(msg); 263 } 264 265 266 bool 267 WorkspacesWindow::QuitRequested() 268 { 269 be_app->PostMessage(B_QUIT_REQUESTED); 270 return true; 271 } 272 273 274 // #pragma mark - 275 276 277 WorkspacesApp::WorkspacesApp() 278 : BApplication(kWorkspacesSignature) 279 { 280 fWindow = new WorkspacesWindow(new WorkspacesPreferences()); 281 } 282 283 284 WorkspacesApp::~WorkspacesApp() 285 { 286 } 287 288 289 void 290 WorkspacesApp::AboutRequested() 291 { 292 BAlert *alert = new BAlert("about", "Workspaces\n" 293 "\twritten by François Revol, Axel Dörfler,\n" 294 "\t\tand Matt Madia.\n" 295 "\tCopyright 2002-2006, Haiku.\n", "Ok"); 296 BTextView *view = alert->TextView(); 297 BFont font; 298 299 view->SetStylable(true); 300 301 view->GetFont(&font); 302 font.SetSize(18); 303 font.SetFace(B_BOLD_FACE); 304 view->SetFontAndColor(0, 10, &font); 305 306 alert->Go(); 307 } 308 309 310 void 311 WorkspacesApp::Usage(const char *programName) 312 { 313 printf("Usage: %s [options] [workspace]\n" 314 "where \"options\" is one of:\n" 315 " --notitle\t\ttitle bar removed. border and resize kept.\n" 316 " --noborder\t\ttitle, border, and resize removed.\n" 317 " --avoidfocus\t\tprevents the window from being the target of keyboard events.\n" 318 " --alwaysontop\t\tkeeps window on top\n" 319 " --notmovable\t\twindow can't be moved around\n" 320 " --help\t\tdisplay this help and exit\n" 321 "and \"workspace\" is the number of the Workspace to which to switch (0-31)\n", 322 programName); 323 324 // quit only if we aren't running already 325 if (IsLaunching()) 326 Quit(); 327 } 328 329 330 void 331 WorkspacesApp::ArgvReceived(int32 argc, char **argv) 332 { 333 for (int i = 1; i < argc; i++) { 334 if (argv[i][0] == '-' && argv[i][1] == '-') { 335 // evaluate --arguments 336 if (!strcmp(argv[i], "--notitle")) 337 fWindow->SetLook(B_MODAL_WINDOW_LOOK); 338 else if (!strcmp(argv[i], "--noborder")) 339 fWindow->SetLook(B_NO_BORDER_WINDOW_LOOK); 340 else if (!strcmp(argv[i], "--avoidfocus")) 341 fWindow->SetFlags(fWindow->Flags() | B_AVOID_FOCUS); 342 else if (!strcmp(argv[i], "--notmovable")) 343 fWindow->SetFlags(fWindow->Flags() | B_NOT_MOVABLE); 344 else if (!strcmp(argv[i], "--alwaysontop")) 345 fWindow->SetFeel(B_FLOATING_ALL_WINDOW_FEEL); 346 else { 347 const char *programName = strrchr(argv[0], '/'); 348 programName = programName ? programName + 1 : argv[0]; 349 350 Usage(programName); 351 } 352 } else if (isdigit(*argv[i])) { 353 // check for a numeric arg, if not already given 354 activate_workspace(atoi(argv[i])); 355 356 // if the app is running, don't quit 357 // but if it isn't, cancel the complete run, so it doesn't 358 // open any window 359 if (IsLaunching()) 360 Quit(); 361 } else if (!strcmp(argv[i], "-")) { 362 activate_workspace(current_workspace() - 1); 363 364 if (IsLaunching()) 365 Quit(); 366 } else if (!strcmp(argv[i], "+")) { 367 activate_workspace(current_workspace() + 1); 368 369 if (IsLaunching()) 370 Quit(); 371 } else { 372 // some unknown arguments were specified 373 fprintf(stderr, "Invalid argument: %s\n", argv[i]); 374 375 if (IsLaunching()) 376 Quit(); 377 } 378 } 379 } 380 381 382 void 383 WorkspacesApp::ReadyToRun() 384 { 385 fWindow->Show(); 386 } 387 388 389 // #pragma mark - 390 391 392 int 393 main(int32 argc, char **argv) 394 { 395 WorkspacesApp app; 396 app.Run(); 397 398 return 0; 399 } 400