1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ide.eclipse.adt.internal.editors.layout.configuration; 18 19 import com.android.ide.eclipse.adt.internal.editors.IconFactory; 20 import com.android.ide.eclipse.adt.internal.resources.ResourceType; 21 import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration; 22 import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier; 23 import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier; 24 import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier; 25 import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier; 26 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier; 27 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier; 28 import com.android.ide.eclipse.adt.internal.resources.configurations.VersionQualifier; 29 import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density; 30 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation; 31 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; 32 import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice; 33 import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager; 34 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 35 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.LanguageRegionVerifier; 36 import com.android.layoutlib.api.IResourceValue; 37 import com.android.layoutlib.api.IStyleResourceValue; 38 39 import org.eclipse.draw2d.geometry.Rectangle; 40 import org.eclipse.swt.SWT; 41 import org.eclipse.swt.events.SelectionAdapter; 42 import org.eclipse.swt.events.SelectionEvent; 43 import org.eclipse.swt.events.SelectionListener; 44 import org.eclipse.swt.layout.GridData; 45 import org.eclipse.swt.layout.GridLayout; 46 import org.eclipse.swt.widgets.Button; 47 import org.eclipse.swt.widgets.Combo; 48 import org.eclipse.swt.widgets.Composite; 49 import org.eclipse.swt.widgets.Label; 50 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.List; 54 import java.util.Map; 55 import java.util.Set; 56 import java.util.SortedSet; 57 58 /** 59 * A composite that displays the current configuration displayed in a Graphical Layout Editor. 60 */ 61 public class ConfigurationComposite extends Composite { 62 63 private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$ 64 65 private Button mClippingButton; 66 private Label mCurrentLayoutLabel; 67 68 private Combo mLocale; 69 private Combo mDeviceList; 70 private Combo mDeviceConfigs; 71 private Combo mThemeCombo; 72 private Button mCreateButton; 73 74 75 private int mPlatformThemeCount = 0; 76 private boolean mDisableUpdates = false; 77 78 /** The {@link FolderConfiguration} representing the state of the UI controls */ 79 private final FolderConfiguration mCurrentConfig = new FolderConfiguration(); 80 81 private List<LayoutDevice> mDevices; 82 83 private final ArrayList<ResourceQualifier[] > mLocaleList = 84 new ArrayList<ResourceQualifier[]>(); 85 86 private final IConfigListener mListener; 87 88 private boolean mClipping = true; 89 90 private LayoutDevice mCurrentDevice; 91 92 /** 93 * Interface implemented by the part which owns a {@link ConfigurationComposite}. 94 * This notifies the owners when the configuration change. 95 * The owner must also provide methods to provide the configuration that will 96 * be displayed. 97 */ 98 public interface IConfigListener { onConfigurationChange()99 void onConfigurationChange(); onThemeChange()100 void onThemeChange(); onCreate()101 void onCreate(); OnClippingChange()102 void OnClippingChange(); 103 getProjectResources()104 ProjectResources getProjectResources(); getFrameworkResources()105 ProjectResources getFrameworkResources(); getConfiguredProjectResources()106 Map<String, Map<String, IResourceValue>> getConfiguredProjectResources(); getConfiguredFrameworkResources()107 Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources(); 108 } 109 ConfigurationComposite(IConfigListener listener, Composite parent, int style)110 public ConfigurationComposite(IConfigListener listener, Composite parent, int style) { 111 super(parent, style); 112 mListener = listener; 113 114 GridLayout gl; 115 GridData gd; 116 int cols = 10; // device*2+config*2+locale*2+separator*2+theme+createBtn 117 118 // ---- First line: collapse button, clipping button, editing config display. 119 Composite labelParent = new Composite(this, SWT.NONE); 120 labelParent.setLayout(gl = new GridLayout(3, false)); 121 gl.marginWidth = gl.marginHeight = 0; 122 labelParent.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 123 gd.horizontalSpan = cols; 124 125 new Label(labelParent, SWT.NONE).setText("Editing config:"); 126 mCurrentLayoutLabel = new Label(labelParent, SWT.NONE); 127 mCurrentLayoutLabel.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 128 gd.widthHint = 50; 129 130 mClippingButton = new Button(labelParent, SWT.TOGGLE | SWT.FLAT); 131 mClippingButton.setSelection(mClipping); 132 mClippingButton.setToolTipText("Toggles screen clipping on/off"); 133 mClippingButton.setImage(IconFactory.getInstance().getIcon("clipping")); //$NON-NLS-1$ 134 mClippingButton.addSelectionListener(new SelectionAdapter() { 135 @Override 136 public void widgetSelected(SelectionEvent e) { 137 OnClippingChange(); 138 } 139 }); 140 141 // ---- 2nd line: device/config/locale/theme Combos, create button. 142 143 setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 144 setLayout(gl = new GridLayout(cols, false)); 145 gl.marginHeight = 0; 146 gl.horizontalSpacing = 0; 147 148 new Label(this, SWT.NONE).setText("Devices"); 149 mDeviceList = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); 150 mDeviceList.setLayoutData(new GridData( 151 GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); 152 mDeviceList.addSelectionListener(new SelectionAdapter() { 153 @Override 154 public void widgetSelected(SelectionEvent e) { 155 onDeviceChange(true /* recomputeLayout*/); 156 } 157 }); 158 159 new Label(this, SWT.NONE).setText("Config"); 160 mDeviceConfigs = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); 161 mDeviceConfigs.setLayoutData(new GridData( 162 GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); 163 mDeviceConfigs.addSelectionListener(new SelectionAdapter() { 164 @Override 165 public void widgetSelected(SelectionEvent e) { 166 onDeviceConfigChange(); 167 } 168 }); 169 170 new Label(this, SWT.NONE).setText("Locale"); 171 mLocale = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); 172 mLocale.setLayoutData(new GridData( 173 GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); 174 mLocale.addVerifyListener(new LanguageRegionVerifier()); 175 mLocale.addSelectionListener(new SelectionListener() { 176 public void widgetDefaultSelected(SelectionEvent e) { 177 onLocaleChange(); 178 } 179 public void widgetSelected(SelectionEvent e) { 180 onLocaleChange(); 181 } 182 }); 183 184 // first separator 185 Label separator = new Label(this, SWT.SEPARATOR | SWT.VERTICAL); 186 separator.setLayoutData(gd = new GridData( 187 GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); 188 gd.heightHint = 0; 189 190 mThemeCombo = new Combo(this, SWT.READ_ONLY | SWT.DROP_DOWN); 191 mThemeCombo.setEnabled(false); 192 updateUIFromResources(); 193 mThemeCombo.addSelectionListener(new SelectionAdapter() { 194 @Override 195 public void widgetSelected(SelectionEvent e) { 196 onThemeChange(); 197 } 198 }); 199 200 // second separator 201 separator = new Label(this, SWT.SEPARATOR | SWT.VERTICAL); 202 separator.setLayoutData(gd = new GridData( 203 GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); 204 gd.heightHint = 0; 205 206 mCreateButton = new Button(this, SWT.PUSH | SWT.FLAT); 207 mCreateButton.setText("Create..."); 208 mCreateButton.setEnabled(false); 209 mCreateButton.addSelectionListener(new SelectionAdapter() { 210 @Override 211 public void widgetSelected(SelectionEvent e) { 212 if (mListener != null) { 213 mListener.onCreate(); 214 } 215 } 216 }); 217 218 initUiWithDevices(); 219 220 onDeviceConfigChange(); 221 } 222 223 getCurrentConfig()224 public FolderConfiguration getCurrentConfig() { 225 return mCurrentConfig; 226 } 227 getCurrentConfig(FolderConfiguration config)228 public void getCurrentConfig(FolderConfiguration config) { 229 config.set(mCurrentConfig); 230 } 231 232 /** 233 * Returns the currently selected {@link Density}. This is guaranteed to be non null. 234 */ getDensity()235 public Density getDensity() { 236 if (mCurrentConfig != null) { 237 PixelDensityQualifier qual = mCurrentConfig.getPixelDensityQualifier(); 238 if (qual != null) { 239 // just a sanity check 240 Density d = qual.getValue(); 241 if (d != Density.NODPI) { 242 return d; 243 } 244 } 245 } 246 247 // no config? return medium as the default density. 248 return Density.MEDIUM; 249 } 250 251 /** 252 * Returns the current device xdpi. 253 */ getXDpi()254 public float getXDpi() { 255 if (mCurrentDevice != null) { 256 float dpi = mCurrentDevice.getXDpi(); 257 if (Float.isNaN(dpi) == false) { 258 return dpi; 259 } 260 } 261 262 // get the pixel density as the density. 263 return getDensity().getDpiValue(); 264 } 265 266 /** 267 * Returns the current device ydpi. 268 */ getYDpi()269 public float getYDpi() { 270 if (mCurrentDevice != null) { 271 float dpi = mCurrentDevice.getYDpi(); 272 if (Float.isNaN(dpi) == false) { 273 return dpi; 274 } 275 } 276 277 // get the pixel density as the density. 278 return getDensity().getDpiValue(); 279 } 280 getScreenBounds()281 public Rectangle getScreenBounds() { 282 // get the orientation from the current device config 283 ScreenOrientationQualifier qual = mCurrentConfig.getScreenOrientationQualifier(); 284 ScreenOrientation orientation = ScreenOrientation.PORTRAIT; 285 if (qual != null) { 286 orientation = qual.getValue(); 287 } 288 289 // get the device screen dimension 290 ScreenDimensionQualifier qual2 = mCurrentConfig.getScreenDimensionQualifier(); 291 int s1, s2; 292 if (qual2 != null) { 293 s1 = qual2.getValue1(); 294 s2 = qual2.getValue2(); 295 } else { 296 s1 = 480; 297 s2 = 320; 298 } 299 300 switch (orientation) { 301 default: 302 case PORTRAIT: 303 return new Rectangle(0, 0, s2, s1); 304 case LANDSCAPE: 305 return new Rectangle(0, 0, s1, s2); 306 case SQUARE: 307 return new Rectangle(0, 0, s1, s1); 308 } 309 } 310 311 /** 312 * Updates the UI from values in the resources, such as languages, regions, themes, etc... 313 * This must be called from the UI thread. 314 */ updateUIFromResources()315 public void updateUIFromResources() { 316 if (mListener == null) { 317 return; // can't do anything w/o it. 318 } 319 320 ProjectResources frameworkProject = mListener.getFrameworkResources(); 321 322 mDisableUpdates = true; 323 324 // Reset stuff 325 int selection = mThemeCombo.getSelectionIndex(); 326 mThemeCombo.removeAll(); 327 mPlatformThemeCount = 0; 328 329 mLocale.removeAll(); 330 mLocaleList.clear(); 331 332 SortedSet<String> languages = null; 333 ArrayList<String> themes = new ArrayList<String>(); 334 335 // get the themes, and languages from the Framework. 336 if (frameworkProject != null) { 337 // get the configured resources for the framework 338 Map<String, Map<String, IResourceValue>> frameworResources = 339 mListener.getConfiguredFrameworkResources(); 340 341 if (frameworResources != null) { 342 // get the styles. 343 Map<String, IResourceValue> styles = frameworResources.get( 344 ResourceType.STYLE.getName()); 345 346 347 // collect the themes out of all the styles. 348 for (IResourceValue value : styles.values()) { 349 String name = value.getName(); 350 if (name.startsWith("Theme.") || name.equals("Theme")) { 351 themes.add(value.getName()); 352 mPlatformThemeCount++; 353 } 354 } 355 356 // sort them and add them to the combo 357 Collections.sort(themes); 358 359 for (String theme : themes) { 360 mThemeCombo.add(theme); 361 } 362 363 mPlatformThemeCount = themes.size(); 364 themes.clear(); 365 } 366 } 367 368 // now get the themes and languages from the project. 369 ProjectResources project = mListener.getProjectResources(); 370 // in cases where the opened file is not linked to a project, this could be null. 371 if (project != null) { 372 // get the configured resources for the project 373 Map<String, Map<String, IResourceValue>> configuredProjectRes = 374 mListener.getConfiguredProjectResources(); 375 376 if (configuredProjectRes != null) { 377 // get the styles. 378 Map<String, IResourceValue> styleMap = configuredProjectRes.get( 379 ResourceType.STYLE.getName()); 380 381 if (styleMap != null) { 382 // collect the themes out of all the styles, ie styles that extend, 383 // directly or indirectly a platform theme. 384 for (IResourceValue value : styleMap.values()) { 385 if (isTheme(value, styleMap)) { 386 themes.add(value.getName()); 387 } 388 } 389 390 // sort them and add them the to the combo. 391 if (mPlatformThemeCount > 0 && themes.size() > 0) { 392 mThemeCombo.add(THEME_SEPARATOR); 393 } 394 395 Collections.sort(themes); 396 397 for (String theme : themes) { 398 mThemeCombo.add(theme); 399 } 400 } 401 } 402 403 // now get the languages from the project. 404 languages = project.getLanguages(); 405 } 406 407 // add the languages to the Combo 408 mLocale.add("Default"); 409 mLocaleList.add(new ResourceQualifier[] { null, null }); 410 411 if (project != null && languages != null && languages.size() > 0) { 412 for (String language : languages) { 413 // first the language alone 414 mLocale.add(language); 415 LanguageQualifier qual = new LanguageQualifier(language); 416 mLocaleList.add(new ResourceQualifier[] { qual, null }); 417 418 // now find the matching regions and add them 419 SortedSet<String> regions = project.getRegions(language); 420 for (String region : regions) { 421 mLocale.add(String.format("%1$s_%2$s", language, region)); //$NON-NLS-1$ 422 RegionQualifier qual2 = new RegionQualifier(region); 423 mLocaleList.add(new ResourceQualifier[] { qual, qual2 }); 424 } 425 426 } 427 } 428 429 mDisableUpdates = false; 430 431 // handle default selection of themes 432 if (mThemeCombo.getItemCount() > 0) { 433 mThemeCombo.setEnabled(true); 434 if (selection == -1) { 435 selection = 0; 436 } 437 438 if (mThemeCombo.getItemCount() <= selection) { 439 mThemeCombo.select(0); 440 } else { 441 mThemeCombo.select(selection); 442 } 443 } else { 444 mThemeCombo.setEnabled(false); 445 } 446 447 mThemeCombo.getParent().layout(); 448 } 449 450 /** 451 * Returns the current theme, or null if the combo has no selection. 452 */ getTheme()453 public String getTheme() { 454 int themeIndex = mThemeCombo.getSelectionIndex(); 455 if (themeIndex != -1) { 456 return mThemeCombo.getItem(themeIndex); 457 } 458 459 return null; 460 } 461 462 /** 463 * Returns whether the current theme selection is a project theme. 464 * <p/>The returned value is meaningless if {@link #getTheme()} returns <code>null</code>. 465 * @return true for project theme, false for framework theme 466 */ isProjectTheme()467 public boolean isProjectTheme() { 468 return mThemeCombo.getSelectionIndex() >= mPlatformThemeCount; 469 } 470 getClipping()471 public boolean getClipping() { 472 return mClipping; 473 } 474 setEnabledCreate(boolean enabled)475 public void setEnabledCreate(boolean enabled) { 476 mCreateButton.setEnabled(enabled); 477 } 478 setClippingSupport(boolean b)479 public void setClippingSupport(boolean b) { 480 mClippingButton.setEnabled(b); 481 if (b) { 482 mClippingButton.setToolTipText("Toggles screen clipping on/off"); 483 } else { 484 mClipping = true; 485 mClippingButton.setSelection(true); 486 mClippingButton.setToolTipText("Non clipped rendering is not supported"); 487 } 488 } 489 490 /** 491 * Update the UI controls state with a given {@link FolderConfiguration}. 492 * <p/>If <var>force</var> is set to <code>true</code> the UI will be changed to exactly reflect 493 * <var>config</var>, otherwise, if a qualifier is not present in <var>config</var>, 494 * the UI control is not modified. However if the value in the control is not the default value, 495 * a warning icon is shown. 496 * @param config The {@link FolderConfiguration} to set. 497 * @param force Whether the UI should be changed to exactly match the received configuration. 498 */ setConfiguration(FolderConfiguration config, boolean force)499 public void setConfiguration(FolderConfiguration config, boolean force) { 500 mDisableUpdates = true; // we do not want to trigger onXXXChange when setting new values in the widgets. 501 502 // TODO: find a device that can display this particular config or create a custom one if needed. 503 504 505 // update the string showing the folder name 506 String current = config.toDisplayString(); 507 mCurrentLayoutLabel.setText(current != null ? current : "(Default)"); 508 509 mDisableUpdates = false; 510 } 511 512 /** 513 * Reloads the list of {@link LayoutDevice} from the {@link Sdk}. 514 * @param notifyListener 515 */ reloadDevices(boolean notifyListener)516 public void reloadDevices(boolean notifyListener) { 517 loadDevices(); 518 initUiWithDevices(); 519 onDeviceChange(notifyListener); 520 } 521 loadDevices()522 private void loadDevices() { 523 mDevices = null; 524 525 Sdk sdk = Sdk.getCurrent(); 526 if (sdk != null) { 527 LayoutDeviceManager manager = sdk.getLayoutDeviceManager(); 528 mDevices = manager.getCombinedList(); 529 } 530 } 531 532 /** 533 * Init the UI with the list of Devices. 534 */ initUiWithDevices()535 private void initUiWithDevices() { 536 // remove older devices if applicable 537 mDeviceList.removeAll(); 538 mDeviceConfigs.removeAll(); 539 540 // fill with the devices 541 if (mDevices != null) { 542 for (LayoutDevice device : mDevices) { 543 mDeviceList.add(device.getName()); 544 } 545 mDeviceList.select(0); 546 547 if (mDevices.size() > 0) { 548 Map<String, FolderConfiguration> configs = mDevices.get(0).getConfigs(); 549 Set<String> configNames = configs.keySet(); 550 for (String name : configNames) { 551 mDeviceConfigs.add(name); 552 } 553 mDeviceConfigs.select(0); 554 if (configNames.size() == 1) { 555 mDeviceConfigs.setEnabled(false); 556 } 557 } 558 } 559 560 // add the custom item 561 mDeviceList.add("Custom..."); 562 } 563 564 /** 565 * Call back for language combo selection 566 */ onLocaleChange()567 private void onLocaleChange() { 568 // because mLanguage triggers onLanguageChange at each modification, the filling 569 // of the combo with data will trigger notifications, and we don't want that. 570 if (mDisableUpdates == true) { 571 return; 572 } 573 574 int localeIndex = mLocale.getSelectionIndex(); 575 ResourceQualifier[] localeQualifiers = mLocaleList.get(localeIndex); 576 577 mCurrentConfig.setLanguageQualifier((LanguageQualifier)localeQualifiers[0]); // language 578 mCurrentConfig.setRegionQualifier((RegionQualifier)localeQualifiers[1]); // region 579 580 if (mListener != null) { 581 mListener.onConfigurationChange(); 582 } 583 } 584 onDeviceChange(boolean recomputeLayout)585 private void onDeviceChange(boolean recomputeLayout) { 586 587 int deviceIndex = mDeviceList.getSelectionIndex(); 588 if (deviceIndex != -1) { 589 // check if the user is ask for the custom item 590 if (deviceIndex == mDeviceList.getItemCount() - 1) { 591 ConfigManagerDialog dialog = new ConfigManagerDialog(getShell()); 592 dialog.open(); 593 594 // save the user devices 595 Sdk.getCurrent().getLayoutDeviceManager().save(); 596 597 // reload the combo with the new content. 598 loadDevices(); 599 initUiWithDevices(); 600 601 // at this point we need to reset the combo to something (hopefully) valid. 602 // look for the previous selected device 603 int index = mDevices.indexOf(mCurrentDevice); 604 if (index != -1) { 605 mDeviceList.select(index); 606 } else { 607 // we should at least have one built-in device, so we select it 608 mDeviceList.select(0); 609 } 610 611 // force a redraw 612 onDeviceChange(true /*recomputeLayout*/); 613 614 return; 615 } 616 617 mCurrentDevice = mDevices.get(deviceIndex); 618 } else { 619 mCurrentDevice = null; 620 } 621 622 mDeviceConfigs.removeAll(); 623 624 if (mCurrentDevice != null) { 625 Set<String> configNames = mCurrentDevice.getConfigs().keySet(); 626 for (String name : configNames) { 627 mDeviceConfigs.add(name); 628 } 629 630 mDeviceConfigs.select(0); 631 mDeviceConfigs.setEnabled(configNames.size() > 1); 632 } 633 if (recomputeLayout) { 634 onDeviceConfigChange(); 635 } 636 } 637 onDeviceConfigChange()638 private void onDeviceConfigChange() { 639 if (mCurrentDevice != null) { 640 int configIndex = mDeviceConfigs.getSelectionIndex(); 641 String name = mDeviceConfigs.getItem(configIndex); 642 FolderConfiguration config = mCurrentDevice.getConfigs().get(name); 643 644 // get the current qualifiers from the current config 645 LanguageQualifier lang = mCurrentConfig.getLanguageQualifier(); 646 RegionQualifier region = mCurrentConfig.getRegionQualifier(); 647 VersionQualifier version = mCurrentConfig.getVersionQualifier(); 648 649 // replace the config with the one from the device 650 mCurrentConfig.set(config); 651 652 // and put back the rest of the qualifiers 653 mCurrentConfig.addQualifier(lang); 654 mCurrentConfig.addQualifier(region); 655 mCurrentConfig.addQualifier(version); 656 657 if (mListener != null) { 658 mListener.onConfigurationChange(); 659 } 660 } 661 } 662 onThemeChange()663 private void onThemeChange() { 664 int themeIndex = mThemeCombo.getSelectionIndex(); 665 if (themeIndex != -1) { 666 String theme = mThemeCombo.getItem(themeIndex); 667 668 if (theme.equals(THEME_SEPARATOR)) { 669 mThemeCombo.select(0); 670 } 671 672 if (mListener != null) { 673 mListener.onThemeChange(); 674 } 675 } 676 } 677 OnClippingChange()678 protected void OnClippingChange() { 679 mClipping = mClippingButton.getSelection(); 680 if (mListener != null) { 681 mListener.OnClippingChange(); 682 } 683 } 684 685 /** 686 * Returns whether the given <var>style</var> is a theme. 687 * This is done by making sure the parent is a theme. 688 * @param value the style to check 689 * @param styleMap the map of styles for the current project. Key is the style name. 690 * @return True if the given <var>style</var> is a theme. 691 */ isTheme(IResourceValue value, Map<String, IResourceValue> styleMap)692 private boolean isTheme(IResourceValue value, Map<String, IResourceValue> styleMap) { 693 if (value instanceof IStyleResourceValue) { 694 IStyleResourceValue style = (IStyleResourceValue)value; 695 696 boolean frameworkStyle = false; 697 String parentStyle = style.getParentStyle(); 698 if (parentStyle == null) { 699 // if there is no specified parent style we look an implied one. 700 // For instance 'Theme.light' is implied child style of 'Theme', 701 // and 'Theme.light.fullscreen' is implied child style of 'Theme.light' 702 String name = style.getName(); 703 int index = name.lastIndexOf('.'); 704 if (index != -1) { 705 parentStyle = name.substring(0, index); 706 } 707 } else { 708 // remove the useless @ if it's there 709 if (parentStyle.startsWith("@")) { 710 parentStyle = parentStyle.substring(1); 711 } 712 713 // check for framework identifier. 714 if (parentStyle.startsWith("android:")) { 715 frameworkStyle = true; 716 parentStyle = parentStyle.substring("android:".length()); 717 } 718 719 // at this point we could have the format style/<name>. we want only the name 720 if (parentStyle.startsWith("style/")) { 721 parentStyle = parentStyle.substring("style/".length()); 722 } 723 } 724 725 if (parentStyle != null) { 726 if (frameworkStyle) { 727 // if the parent is a framework style, it has to be 'Theme' or 'Theme.*' 728 return parentStyle.equals("Theme") || parentStyle.startsWith("Theme."); 729 } else { 730 // if it's a project style, we check this is a theme. 731 value = styleMap.get(parentStyle); 732 if (value != null) { 733 return isTheme(value, styleMap); 734 } 735 } 736 } 737 } 738 739 return false; 740 } 741 } 742 743