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